diff --git a/README.md b/README.md index 97d209623..e4bf7c039 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,14 @@ Parse().initialize( ApplicationConstants.keyApplicationId, ApplicationConstants.keyParseServerUrl); ``` +if you want to use secure storage also that's allow using sdk on desktop application +```dart + Parse().initialize(keyParseApplicationId, keyParseServerUrl, + masterKey: keyParseMasterKey, + debug: true, + coreStore: CoreStoreImp.getInstance()); +``` It's possible to add other params, such as ... ```dart diff --git a/example/assets/parse.png b/example/assets/parse.png new file mode 100644 index 000000000..fc7e48dd6 Binary files /dev/null and b/example/assets/parse.png differ diff --git a/example/lib/data/model/diet_plan.dart b/example/lib/data/model/diet_plan.dart index 8819d6189..d58f111a9 100644 --- a/example/lib/data/model/diet_plan.dart +++ b/example/lib/data/model/diet_plan.dart @@ -32,6 +32,6 @@ class DietPlan extends ParseObject implements ParseCloneable { num get fat => get(keyFat); set fat(num fat) => set(keyFat, fat); - int get status => get(keyStatus); - set status(int status) => set(keyStatus, status); + bool get status => get(keyStatus); + set status(bool status) => set(keyStatus, status); } diff --git a/example/lib/main.dart b/example/lib/main.dart index 13b452e2e..3e26e8e18 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -10,6 +10,7 @@ import 'package:flutter_plugin_example/data/repositories/diet_plan/repository_di import 'package:flutter_plugin_example/data/repositories/user/repository_user.dart'; import 'package:flutter_plugin_example/domain/constants/application_constants.dart'; import 'package:flutter_plugin_example/domain/utils/db_utils.dart'; +import 'package:flutter_plugin_example/pages/decision_page.dart'; import 'package:flutter_stetho/flutter_stetho.dart'; import 'package:parse_server_sdk/parse_server_sdk.dart'; @@ -40,26 +41,21 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { DietPlanRepository dietPlanRepo; UserRepository userRepo; - String text = ""; - @override void initState() { super.initState(); - initData(); + // initData(); } @override Widget build(BuildContext context) { return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Plugin example app'), - ), - body: Center( - child: Text(text), + debugShowCheckedModeBanner: false, + theme: ThemeData( + primarySwatch: Colors.blue, ), - ), - ); + title: 'Parse Server Example', + home: DecisionPage()); } Future initData() async { @@ -70,14 +66,20 @@ class _MyAppState extends State { Parse().initialize(keyParseApplicationId, keyParseServerUrl, masterKey: keyParseMasterKey, debug: true); + //parse serve with secure store and desktop support + +// Parse().initialize(keyParseApplicationId, keyParseServerUrl, +// masterKey: keyParseMasterKey, +// debug: true, +// coreStore: CoreStoreImp.getInstance()); + // Check server is healthy and live - Debug is on in this instance so check logs for result final ParseResponse response = await Parse().healthCheck(); if (response.success) { await runTestQueries(); - text += 'runTestQueries\n'; + print('runTestQueries'); } else { - text += 'Server health check failed'; print('Server health check failed'); } } @@ -96,9 +98,9 @@ class _MyAppState extends State { // getConfigs(); // query(); // initUser(); - var instalattion = await ParseInstallation.currentInstallation(); - var rees = instalattion.create(); - print(rees); +// var instalattion = await ParseInstallation.currentInstallation(); +// var rees = instalattion.create(); +// print(rees); //function(); //functionWithParameters(); // test(); @@ -357,13 +359,13 @@ class _MyAppState extends State { dietPlanRepo ??= DietPlanRepository.init(await getDB()); userRepo ??= UserRepository.init(await getDB()); } -} -const String dietPlansToAdd = - '[{"className":"Diet_Plans","Name":"Textbook","Description":"For an active lifestyle and a straight forward macro plan, we suggest this plan.","Fat":25,"Carbs":50,"Protein":25,"Status":0},' - '{"className":"Diet_Plans","Name":"Body Builder","Description":"Default Body Builders Diet","Fat":20,"Carbs":40,"Protein":40,"Status":0},' - '{"className":"Diet_Plans","Name":"Zone Diet","Description":"Popular with CrossFit users. Zone Diet targets similar macros.","Fat":30,"Carbs":40,"Protein":30,"Status":0},' - '{"className":"Diet_Plans","Name":"Low Fat","Description":"Low fat diet.","Fat":15,"Carbs":60,"Protein":25,"Status":0},' - '{"className":"Diet_Plans","Name":"Low Carb","Description":"Low Carb diet, main focus on quality fats and protein.","Fat":35,"Carbs":25,"Protein":40,"Status":0},' - '{"className":"Diet_Plans","Name":"Paleo","Description":"Paleo diet.","Fat":60,"Carbs":25,"Protein":10,"Status":0},' - '{"className":"Diet_Plans","Name":"Ketogenic","Description":"High quality fats, low carbs.","Fat":65,"Carbs":5,"Protein":30,"Status":0}]'; + String dietPlansToAdd = + '[{"className":"Diet_Plans","Name":"Textbook","Description":"For an active lifestyle and a straight forward macro plan, we suggest this plan.","Fat":25,"Carbs":50,"Protein":25,"Status":0},' + '{"className":"Diet_Plans","Name":"Body Builder","Description":"Default Body Builders Diet","Fat":20,"Carbs":40,"Protein":40,"Status":0},' + '{"className":"Diet_Plans","Name":"Zone Diet","Description":"Popular with CrossFit users. Zone Diet targets similar macros.","Fat":30,"Carbs":40,"Protein":30,"Status":0},' + '{"className":"Diet_Plans","Name":"Low Fat","Description":"Low fat diet.","Fat":15,"Carbs":60,"Protein":25,"Status":0},' + '{"className":"Diet_Plans","Name":"Low Carb","Description":"Low Carb diet, main focus on quality fats and protein.","Fat":35,"Carbs":25,"Protein":40,"Status":0},' + '{"className":"Diet_Plans","Name":"Paleo","Description":"Paleo diet.","Fat":60,"Carbs":25,"Protein":10,"Status":0},' + '{"className":"Diet_Plans","Name":"Ketogenic","Description":"High quality fats, low carbs.","Fat":65,"Carbs":5,"Protein":30,"Status":0}]'; +} diff --git a/example/lib/pages/decision_page.dart b/example/lib/pages/decision_page.dart new file mode 100644 index 000000000..2875a3131 --- /dev/null +++ b/example/lib/pages/decision_page.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_plugin_example/data/repositories/diet_plan/provider_api_diet_plan.dart'; +import 'package:flutter_plugin_example/domain/constants/application_constants.dart'; +import 'package:parse_server_sdk/parse_server_sdk.dart'; + +import 'home_page.dart'; +import 'login_page.dart'; + +class DecisionPage extends StatefulWidget { + @override + _DecisionPageState createState() => _DecisionPageState(); +} + +class _DecisionPageState extends State { + String _parseServerState = 'Checking Parse Server...'; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + _initParse(); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Container( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _showLogo(), + const SizedBox( + height: 20, + ), + Center( + child: Text(_parseServerState), + ), + ], + ), + ), + ), + ); + } + + Widget _showLogo() { + return Hero( + tag: 'hero', + child: Padding( + padding: const EdgeInsets.fromLTRB(0.0, 70.0, 0.0, 0.0), + child: CircleAvatar( + backgroundColor: Colors.transparent, + radius: 48.0, + child: Image.asset('assets/parse.png'), + ), + ), + ); + } + + Future _initParse() async { + try { + Parse().initialize(keyParseApplicationId, keyParseServerUrl, + masterKey: keyParseMasterKey, debug: true); + final ParseResponse response = await Parse().healthCheck(); + if (response.success) { + final ParseUser user = await ParseUser.currentUser(); + if (user != null) { + _redirectToPage(context, HomePage(DietPlanProviderApi())); + } else { + _redirectToPage(context, LoginPage()); + } + } else { + setState(() { + _parseServerState = + 'Parse Server Not avaiable\n due to ${response.error.toString()}'; + }); + } + } catch (e) { + setState(() { + _parseServerState = e.toString(); + }); + } + } + + Future _redirectToPage(BuildContext context, Widget page) async { + final MaterialPageRoute newRoute = + MaterialPageRoute(builder: (BuildContext context) => page); + + bool nav = await Navigator.of(context) + .pushAndRemoveUntil(newRoute, ModalRoute.withName('/')); + if (nav == true) { + _initParse(); + } + } +} diff --git a/example/lib/pages/home_page.dart b/example/lib/pages/home_page.dart new file mode 100644 index 000000000..21578b102 --- /dev/null +++ b/example/lib/pages/home_page.dart @@ -0,0 +1,133 @@ +import 'dart:convert'; +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_plugin_example/data/base/api_response.dart'; +import 'package:flutter_plugin_example/data/model/diet_plan.dart'; +import 'package:flutter_plugin_example/data/repositories/diet_plan/contract_provider_diet_plan.dart'; +import 'package:parse_server_sdk/parse_server_sdk.dart'; + +class HomePage extends StatefulWidget { + HomePage(this._dietPlanProvider); + final DietPlanProviderContract _dietPlanProvider; + + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State { + List randomDietPlans = []; + + @override + void initState() { + super.initState(); + final List json = const JsonDecoder().convert(dietPlansToAdd); + for (final Map element in json) { + final DietPlan dietPlan = DietPlan().fromJson(element); + randomDietPlans.add(dietPlan); + } + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async => false, + child: Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + title: const Text('Parse Server demo'), + actions: [ + FlatButton( + child: Text('Logout', + style: TextStyle(fontSize: 17.0, color: Colors.white)), + onPressed: () async { + final ParseUser user = await ParseUser.currentUser(); + user.logout(deleteLocalUserData: true); + Navigator.pop(context, true); + }) + ], + ), + body: _showDietList(), + floatingActionButton: FloatingActionButton( + onPressed: () async { + DietPlan dietPlan = + randomDietPlans[Random().nextInt(randomDietPlans.length - 1)]; + ParseUser user = await ParseUser.currentUser(); + dietPlan.set('user', user); + await widget._dietPlanProvider.add(dietPlan); + setState(() {}); + }, + tooltip: 'Add Diet Plans', + child: const Icon(Icons.add), + )), + ); + } + + Widget _showDietList() { + return FutureBuilder( + future: widget._dietPlanProvider.getAll(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData) { + if (snapshot.data.success) { + if (snapshot.data.results == null || + snapshot.data.results.isEmpty) { + return Center( + child: const Text('No Data'), + ); + } + } + return ListView.builder( + shrinkWrap: true, + itemCount: snapshot.data.results.length, + itemBuilder: (BuildContext context, int index) { + DietPlan dietPlan = snapshot.data.results[index]; + String id = dietPlan.objectId; + String name = dietPlan.name; + String description = dietPlan.description; + bool status = dietPlan.status; + return Dismissible( + key: Key(id), + background: Container(color: Colors.red), + onDismissed: (direction) async { + widget._dietPlanProvider.remove(dietPlan); + }, + child: ListTile( + title: Text( + name, + style: TextStyle(fontSize: 20.0), + ), + subtitle: Text(description), + trailing: IconButton( + icon: status + ? const Icon( + Icons.done_outline, + color: Colors.green, + size: 20.0, + ) + : const Icon(Icons.done, + color: Colors.grey, size: 20.0), + onPressed: () async { + dietPlan.status = !dietPlan.status; + await dietPlan.save(); + setState(() {}); + }), + ), + ); + }); + } else { + return Center( + child: const Text('No Data'), + ); + } + }); + } + + String dietPlansToAdd = + '[{"className":"Diet_Plans","Name":"Textbook","Description":"For an active lifestyle and a straight forward macro plan, we suggest this plan.","Fat":25,"Carbs":50,"Protein":25,"Status":false},' + '{"className":"Diet_Plans","Name":"Body Builder","Description":"Default Body Builders Diet","Fat":20,"Carbs":40,"Protein":40,"Status":true},' + '{"className":"Diet_Plans","Name":"Zone Diet","Description":"Popular with CrossFit users. Zone Diet targets similar macros.","Fat":30,"Carbs":40,"Protein":30,"Status":true},' + '{"className":"Diet_Plans","Name":"Low Fat","Description":"Low fat diet.","Fat":15,"Carbs":60,"Protein":25,"Status":false},' + '{"className":"Diet_Plans","Name":"Low Carb","Description":"Low Carb diet, main focus on quality fats and protein.","Fat":35,"Carbs":25,"Protein":40,"Status":true},' + '{"className":"Diet_Plans","Name":"Paleo","Description":"Paleo diet.","Fat":60,"Carbs":25,"Protein":10,"Status":false},' + '{"className":"Diet_Plans","Name":"Ketogenic","Description":"High quality fats, low carbs.","Fat":65,"Carbs":5,"Protein":30,"Status":true}]'; +} diff --git a/example/lib/pages/login_page.dart b/example/lib/pages/login_page.dart new file mode 100644 index 000000000..e02824fd8 --- /dev/null +++ b/example/lib/pages/login_page.dart @@ -0,0 +1,245 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_plugin_example/data/model/user.dart'; +import 'package:parse_server_sdk/parse_server_sdk.dart'; + +enum FormMode { LOGIN, SIGNUP } + +class LoginPage extends StatefulWidget { + @override + _LoginPageState createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + final _formKey = GlobalKey(); + + String _email; + String _password; + String _errorMessage; + + // Initial form is login form + FormMode _formMode = FormMode.LOGIN; + bool _isLoading; + + // Check if form is valid before perform login or signup + bool _validateAndSave() { + final form = _formKey.currentState; + if (form.validate()) { + form.save(); + return true; + } + return false; + } + + // Perform login or signup + Future _validateAndSubmit() async { + setState(() { + _errorMessage = ""; + _isLoading = true; + }); + if (_validateAndSave()) { + User user = User(_email, _password, _email); + + ParseResponse response; + try { + if (_formMode == FormMode.LOGIN) { + response = await user.login(); + print('Signed in'); + } else { + response = await user.signUp(); + print('Signed up user:'); + } + setState(() { + _isLoading = false; + }); + if (response.success) { + if (_formMode == FormMode.LOGIN) { + Navigator.pop(context, true); + } + } else { + setState(() { + _isLoading = false; + _errorMessage = response.error.toString(); + }); + } + } catch (e) { + print('Error: $e'); + setState(() { + _isLoading = false; + _errorMessage = e.message; + }); + } + } + } + + @override + void initState() { + _errorMessage = ""; + _isLoading = false; + super.initState(); + } + + void _changeFormToSignUp() { + _formKey.currentState.reset(); + _errorMessage = ""; + setState(() { + _formMode = FormMode.SIGNUP; + }); + } + + void _changeFormToLogin() { + _formKey.currentState.reset(); + _errorMessage = ""; + setState(() { + _formMode = FormMode.LOGIN; + }); + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async => false, + child: Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + title: const Text('Parse Server demo'), + ), + body: Stack( + children: [ + _showBody(), + _showCircularProgress(), + ], + )), + ); + } + + Widget _showCircularProgress() { + if (_isLoading) { + return Center(child: const CircularProgressIndicator()); + } + return Container( + height: 0.0, + width: 0.0, + ); + } + + Widget _showBody() { + return Container( + padding: const EdgeInsets.all(16.0), + child: Form( + key: _formKey, + child: ListView( + shrinkWrap: true, + children: [ + _showLogo(), + _showEmailInput(), + _showPasswordInput(), + _showPrimaryButton(), + _showSecondaryButton(), + _showErrorMessage(), + ], + ), + )); + } + + Widget _showErrorMessage() { + if (_errorMessage.isNotEmpty && _errorMessage != null) { + return Text( + _errorMessage, + style: TextStyle( + fontSize: 13.0, + color: Colors.red, + height: 1.0, + fontWeight: FontWeight.w300), + ); + } else { + return Container( + height: 0.0, + ); + } + } + + Widget _showLogo() { + return Hero( + tag: 'hero', + child: Padding( + padding: const EdgeInsets.fromLTRB(0.0, 70.0, 0.0, 0.0), + child: CircleAvatar( + backgroundColor: Colors.transparent, + radius: 48.0, + child: Image.asset('assets/parse.png'), + ), + ), + ); + } + + Widget _showEmailInput() { + return Padding( + padding: const EdgeInsets.fromLTRB(0.0, 100.0, 0.0, 0.0), + child: TextFormField( + maxLines: 1, + keyboardType: TextInputType.emailAddress, + autofocus: false, + decoration: InputDecoration( + hintText: 'Email', + icon: const Icon( + Icons.mail, + color: Colors.grey, + )), + validator: (value) => value.isEmpty ? 'Email can\'t be empty' : null, + onSaved: (value) => _email = value, + ), + ); + } + + Widget _showPasswordInput() { + return Padding( + padding: const EdgeInsets.fromLTRB(0.0, 15.0, 0.0, 0.0), + child: TextFormField( + maxLines: 1, + obscureText: true, + autofocus: false, + decoration: InputDecoration( + hintText: 'Password', + icon: const Icon( + Icons.lock, + color: Colors.grey, + )), + validator: (value) => value.isEmpty ? 'Password can\'t be empty' : null, + onSaved: (value) => _password = value, + ), + ); + } + + Widget _showSecondaryButton() { + return FlatButton( + child: _formMode == FormMode.LOGIN + ? Text('Create an account', + style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.w300)) + : Text('Have an account? Sign in', + style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.w300)), + onPressed: _formMode == FormMode.LOGIN + ? _changeFormToSignUp + : _changeFormToLogin, + ); + } + + Widget _showPrimaryButton() { + return Padding( + padding: const EdgeInsets.fromLTRB(0.0, 45.0, 0.0, 0.0), + child: SizedBox( + height: 40.0, + child: RaisedButton( + elevation: 5.0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30.0)), + color: Colors.blue, + child: _formMode == FormMode.LOGIN + ? Text('Login', + style: TextStyle(fontSize: 20.0, color: Colors.white)) + : Text('Create account', + style: TextStyle(fontSize: 20.0, color: Colors.white)), + onPressed: _validateAndSubmit, + ), + )); + } +} diff --git a/example/pubspec.yaml b/example/pubspec.yaml index ca94b4900..e1bfed68f 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -31,9 +31,9 @@ flutter: uses-material-design: true # To add assets to your application, add an assets section, like this: - # assets: + assets: # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + - assets/parse.png # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.io/assets-and-images/#resolution-aware. diff --git a/example/test/data/repository/repository_mock_utils.dart b/example/test/data/repository/repository_mock_utils.dart index 7d3a9e9ff..469be11a8 100644 --- a/example/test/data/repository/repository_mock_utils.dart +++ b/example/test/data/repository/repository_mock_utils.dart @@ -35,7 +35,7 @@ DietPlan getDummyDietPlan() { ..protein = 40 ..carbs = 40 ..fat = 20 - ..status = 0; + ..status = false; } Future deleteFromApi(List results) async { diff --git a/lib/parse_server_sdk.dart b/lib/parse_server_sdk.dart index cc7908429..865958e86 100644 --- a/lib/parse_server_sdk.dart +++ b/lib/parse_server_sdk.dart @@ -4,8 +4,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:sembast/sembast.dart'; -import 'package:sembast/sembast_io.dart'; + import 'package:devicelocale/devicelocale.dart'; import 'package:http/http.dart'; import 'package:http/io_client.dart'; @@ -13,74 +12,48 @@ import 'package:meta/meta.dart'; import 'package:package_info/package_info.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; +import 'package:sembast/sembast.dart'; +import 'package:sembast/sembast_io.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:uuid/uuid.dart'; import 'package:web_socket_channel/io.dart'; import 'package:xxtea/xxtea.dart'; -part 'package:parse_server_sdk/src/objects/response/parse_response_utils.dart'; - part 'package:parse_server_sdk/src/objects/response/parse_error_response.dart'; - part 'package:parse_server_sdk/src/objects/response/parse_exception_response.dart'; - part 'package:parse_server_sdk/src/objects/response/parse_response_builder.dart'; - +part 'package:parse_server_sdk/src/objects/response/parse_response_utils.dart'; part 'package:parse_server_sdk/src/objects/response/parse_success_no_results.dart'; - part 'src/base/parse_constants.dart'; - +part 'src/data/core_store.dart'; +part 'src/data/core_store_impl.dart'; part 'src/data/parse_core_data.dart'; - +part 'src/data/parse_shared_preferences_corestore.dart'; +part 'src/data/xxtea_codec.dart'; part 'src/enums/parse_enum_api_rq.dart'; - part 'src/network/parse_http_client.dart'; - part 'src/network/parse_live_query.dart'; - part 'src/network/parse_query.dart'; - +part 'src/objects/parse_acl.dart'; part 'src/objects/parse_base.dart'; - part 'src/objects/parse_cloneable.dart'; - part 'src/objects/parse_config.dart'; - part 'src/objects/parse_error.dart'; - part 'src/objects/parse_file.dart'; - part 'src/objects/parse_function.dart'; - part 'src/objects/parse_geo_point.dart'; - part 'src/objects/parse_installation.dart'; - part 'src/objects/parse_object.dart'; - part 'src/objects/parse_response.dart'; - part 'src/objects/parse_session.dart'; - part 'src/objects/parse_user.dart'; - -part 'src/objects/parse_acl.dart'; - +part 'src/utils/parse_date_format.dart'; part 'src/utils/parse_decoder.dart'; - part 'src/utils/parse_encoder.dart'; - part 'src/utils/parse_file_extensions.dart'; - part 'src/utils/parse_logger.dart'; - part 'src/utils/parse_utils.dart'; -part 'src/utils/parse_date_format.dart'; - -part 'src/data/core_store.dart'; -part 'src/data/core_store_impl.dart'; -part 'src/data/xxtea_codec.dart'; - class Parse { ParseCoreData data; bool _hasBeenInitialized = false; @@ -106,7 +79,7 @@ class Parse { String sessionId, bool autoSendSessionId, SecurityContext securityContext, - CoreStore coreStore}) { + FutureOr coreStore}) { final String url = removeTrailingSlash(serverUrl); ParseCoreData.init(appId, url, diff --git a/lib/src/data/parse_core_data.dart b/lib/src/data/parse_core_data.dart index 4c234e985..b0e98593f 100644 --- a/lib/src/data/parse_core_data.dart +++ b/lib/src/data/parse_core_data.dart @@ -23,10 +23,11 @@ class ParseCoreData { String sessionId, bool autoSendSessionId, SecurityContext securityContext, - CoreStore store}) { + FutureOr store}) { _instance = ParseCoreData._init(appId, serverUrl); - _instance.storage ??= - store ?? CoreStoreImp.getInstance(password: masterKey); + _instance.storage ??= store ?? + Future.value( + SharedPreferencesCoreStore(SharedPreferences.getInstance())); if (debug != null) _instance.debug = debug; if (appName != null) _instance.appName = appName; if (liveQueryUrl != null) _instance.liveQueryURL = liveQueryUrl; @@ -48,7 +49,7 @@ class ParseCoreData { bool autoSendSessionId; SecurityContext securityContext; bool debug; - Future storage; + FutureOr storage; /// Sets the current sessionId. /// diff --git a/lib/src/data/parse_shared_preferences_corestore.dart b/lib/src/data/parse_shared_preferences_corestore.dart new file mode 100644 index 000000000..2f50b4a80 --- /dev/null +++ b/lib/src/data/parse_shared_preferences_corestore.dart @@ -0,0 +1,68 @@ +part of flutter_parse_sdk; + +class SharedPreferencesCoreStore implements CoreStore { + SharedPreferencesCoreStore(FutureOr sharedPreference) + : _sharedPreferencesFuture = Future.value(sharedPreference); + + Future _sharedPreferencesFuture; + + @override + Future clear() async { + final SharedPreferences sharedPreferences = await _sharedPreferencesFuture; + final bool result = await sharedPreferences.clear(); + return result; + } + + @override + Future get(String key) => + _sharedPreferencesFuture.then((shared) => shared.get(key)); + + @override + Future getBool(String key) => + _sharedPreferencesFuture.then((shared) => shared.getBool(key)); + + @override + Future getDouble(String key) => + _sharedPreferencesFuture.then((shared) => shared.getDouble(key)); + + @override + Future getInt(String key) => + _sharedPreferencesFuture.then((shared) => shared.getInt(key)); + + @override + Future getString(String key) => + _sharedPreferencesFuture.then((shared) => shared.getString(key)); + + @override + Future> getStringList(String key) => + _sharedPreferencesFuture.then((shared) => shared.getStringList(key)); + + @override + Future remove(String key) => + _sharedPreferencesFuture.then((shared) => shared.remove(key)); + + @override + Future setBool(String key, bool value) => + _sharedPreferencesFuture.then((shared) => shared.setBool(key, value)); + + @override + Future setDouble(String key, double value) => + _sharedPreferencesFuture.then((shared) => shared.setDouble(key, value)); + + @override + Future setInt(String key, int value) => + _sharedPreferencesFuture.then((shared) => shared.setInt(key, value)); + + @override + Future setString(String key, String value) => + _sharedPreferencesFuture.then((shared) => shared.setString(key, value)); + + @override + Future setStringList(String key, List values) => + _sharedPreferencesFuture + .then((shared) => shared.setStringList(key, values)); + + @override + Future containsKey(String key) => + _sharedPreferencesFuture.then((shared) => shared.containsKey(key)); +} diff --git a/pubspec.yaml b/pubspec.yaml index c92768445..48c9e1a9c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: http: ^0.12.0 #Database + shared_preferences: ^0.5.2 sembast: ^1.15.1 xxtea: ^2.0.2