Skip to content

Commit 5f7e0e5

Browse files
yulingtianxiaphillwiggins
authored andcommitted
Support Relation (parse-community#179)
* create CoreStore interface to allow different implements for local storage (parse-community#166) implement default local store using sembast and with encryption using XXTEA algorithm add desktop support in example project * make parse shared preferences default store (parse-community#168) * add decion page to detect parse server availability throw execption if server url is empty navigate to correct page base on current user state * working on example throw exception if sever url is empty add decision page to check availability of parse server navigate to screen base on current user state * create example flow for login and signup display list of diet plan items and allow to remove and update it's status * add shared preferences corestore fix bug when create custom corestore implementation * use shared shared preferences in example * make parse shared preferences default store * Update application_constants.dart * README.md updated from https://stackedit.io/ * Support Relation. * delete vscode config * Update .gitignore
1 parent e33e9bb commit 5f7e0e5

17 files changed

+648
-36
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ build/
1010
.idea
1111
example/ios/Frameworks/
1212
example/lib/ui/
13+
14+
.vscode/

README.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,14 @@ Parse().initialize(
2525
ApplicationConstants.keyApplicationId,
2626
ApplicationConstants.keyParseServerUrl);
2727
```
28+
if you want to use secure storage also that's allow using sdk on desktop application
29+
```dart
2830
31+
Parse().initialize(keyParseApplicationId, keyParseServerUrl,
32+
masterKey: keyParseMasterKey,
33+
debug: true,
34+
coreStore: CoreStoreImp.getInstance());
35+
```
2936
It's possible to add other params, such as ...
3037

3138
```dart
@@ -530,6 +537,21 @@ final Map<String, String> params = <String, String>{'plan': 'paid'};
530537
function.execute(parameters: params);
531538
```
532539

540+
## Relation
541+
The SDK supports Relation.
542+
543+
To Retrive a relation instance for user, call:
544+
```dart
545+
final relation = user.getRelation('dietPlans');
546+
```
547+
548+
and then you can add a relation to the passed in object.
549+
550+
```dart
551+
relation.add(dietPlan);
552+
final result = await user.save();
553+
```
554+
533555
## Other Features of this library
534556
Main:
535557
* Installation (View the example application)
@@ -552,5 +574,5 @@ Objects:
552574
## Author:-
553575
This project was authored by Phill Wiggins. You can contact me at [email protected]
554576
<!--stackedit_data:
555-
eyJoaXN0b3J5IjpbNzE4NjUwNDIwXX0=
556-
-->
577+
eyJoaXN0b3J5IjpbLTU4MDA4MDUwNCw3MTg2NTA0MjBdfQ==
578+
-->

example/assets/parse.png

123 KB
Loading

example/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@
4444
24DF2572E6AEEB9F7CE180C9 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
4545
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
4646
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
47+
5804EFBD11740E02FC51BC3E /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
4748
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
4849
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
4950
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
51+
96499D95196B10F296043703 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
5052
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
5153
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
5254
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };

example/lib/data/model/diet_plan.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ class DietPlan extends ParseObject implements ParseCloneable {
3232
num get fat => get<num>(keyFat);
3333
set fat(num fat) => set<num>(keyFat, fat);
3434

35-
int get status => get<int>(keyStatus);
36-
set status(int status) => set<int>(keyStatus, status);
35+
bool get status => get<bool>(keyStatus);
36+
set status(bool status) => set<bool>(keyStatus, status);
3737
}

example/lib/pages/decision_page.dart

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_plugin_example/data/repositories/diet_plan/provider_api_diet_plan.dart';
3+
import 'package:flutter_plugin_example/domain/constants/application_constants.dart';
4+
import 'package:parse_server_sdk/parse_server_sdk.dart';
5+
6+
import 'home_page.dart';
7+
import 'login_page.dart';
8+
9+
class DecisionPage extends StatefulWidget {
10+
@override
11+
_DecisionPageState createState() => _DecisionPageState();
12+
}
13+
14+
class _DecisionPageState extends State<DecisionPage> {
15+
String _parseServerState = 'Checking Parse Server...';
16+
17+
@override
18+
void initState() {
19+
super.initState();
20+
WidgetsBinding.instance.addPostFrameCallback((_) {
21+
_initParse();
22+
});
23+
}
24+
25+
@override
26+
Widget build(BuildContext context) {
27+
return Scaffold(
28+
body: Center(
29+
child: Container(
30+
child: Column(
31+
mainAxisAlignment: MainAxisAlignment.center,
32+
crossAxisAlignment: CrossAxisAlignment.center,
33+
children: <Widget>[
34+
_showLogo(),
35+
const SizedBox(
36+
height: 20,
37+
),
38+
Center(
39+
child: Text(_parseServerState),
40+
),
41+
],
42+
),
43+
),
44+
),
45+
);
46+
}
47+
48+
Widget _showLogo() {
49+
return Hero(
50+
tag: 'hero',
51+
child: Padding(
52+
padding: const EdgeInsets.fromLTRB(0.0, 70.0, 0.0, 0.0),
53+
child: CircleAvatar(
54+
backgroundColor: Colors.transparent,
55+
radius: 48.0,
56+
child: Image.asset('assets/parse.png'),
57+
),
58+
),
59+
);
60+
}
61+
62+
Future<void> _initParse() async {
63+
try {
64+
Parse().initialize(keyParseApplicationId, keyParseServerUrl,
65+
masterKey: keyParseMasterKey, debug: true);
66+
final ParseResponse response = await Parse().healthCheck();
67+
if (response.success) {
68+
final ParseUser user = await ParseUser.currentUser();
69+
if (user != null) {
70+
_redirectToPage(context, HomePage(DietPlanProviderApi()));
71+
} else {
72+
_redirectToPage(context, LoginPage());
73+
}
74+
} else {
75+
setState(() {
76+
_parseServerState =
77+
'Parse Server Not avaiable\n due to ${response.error.toString()}';
78+
});
79+
}
80+
} catch (e) {
81+
setState(() {
82+
_parseServerState = e.toString();
83+
});
84+
}
85+
}
86+
87+
Future<void> _redirectToPage(BuildContext context, Widget page) async {
88+
final MaterialPageRoute<bool> newRoute =
89+
MaterialPageRoute<bool>(builder: (BuildContext context) => page);
90+
91+
bool nav = await Navigator.of(context)
92+
.pushAndRemoveUntil<bool>(newRoute, ModalRoute.withName('/'));
93+
if (nav == true) {
94+
_initParse();
95+
}
96+
}
97+
}

example/lib/pages/home_page.dart

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import 'dart:convert';
2+
import 'dart:math';
3+
4+
import 'package:flutter/material.dart';
5+
import 'package:flutter_plugin_example/data/base/api_response.dart';
6+
import 'package:flutter_plugin_example/data/model/diet_plan.dart';
7+
import 'package:flutter_plugin_example/data/repositories/diet_plan/contract_provider_diet_plan.dart';
8+
import 'package:parse_server_sdk/parse_server_sdk.dart';
9+
10+
class HomePage extends StatefulWidget {
11+
HomePage(this._dietPlanProvider);
12+
final DietPlanProviderContract _dietPlanProvider;
13+
14+
@override
15+
_HomePageState createState() => _HomePageState();
16+
}
17+
18+
class _HomePageState extends State<HomePage> {
19+
List<DietPlan> randomDietPlans = [];
20+
21+
@override
22+
void initState() {
23+
super.initState();
24+
final List<dynamic> json = const JsonDecoder().convert(dietPlansToAdd);
25+
for (final Map<String, dynamic> element in json) {
26+
final DietPlan dietPlan = DietPlan().fromJson(element);
27+
randomDietPlans.add(dietPlan);
28+
}
29+
}
30+
31+
@override
32+
Widget build(BuildContext context) {
33+
return WillPopScope(
34+
onWillPop: () async => false,
35+
child: Scaffold(
36+
appBar: AppBar(
37+
automaticallyImplyLeading: false,
38+
title: const Text('Parse Server demo'),
39+
actions: <Widget>[
40+
FlatButton(
41+
child: Text('Logout',
42+
style: TextStyle(fontSize: 17.0, color: Colors.white)),
43+
onPressed: () async {
44+
final ParseUser user = await ParseUser.currentUser();
45+
user.logout(deleteLocalUserData: true);
46+
Navigator.pop(context, true);
47+
})
48+
],
49+
),
50+
body: _showDietList(),
51+
floatingActionButton: FloatingActionButton(
52+
onPressed: () async {
53+
DietPlan dietPlan =
54+
randomDietPlans[Random().nextInt(randomDietPlans.length - 1)];
55+
ParseUser user = await ParseUser.currentUser();
56+
dietPlan.set('user', user);
57+
await widget._dietPlanProvider.add(dietPlan);
58+
setState(() {});
59+
},
60+
tooltip: 'Add Diet Plans',
61+
child: const Icon(Icons.add),
62+
)),
63+
);
64+
}
65+
66+
Widget _showDietList() {
67+
return FutureBuilder<ApiResponse>(
68+
future: widget._dietPlanProvider.getAll(),
69+
builder: (BuildContext context, AsyncSnapshot<ApiResponse> snapshot) {
70+
if (snapshot.hasData) {
71+
if (snapshot.data.success) {
72+
if (snapshot.data.results == null ||
73+
snapshot.data.results.isEmpty) {
74+
return Center(
75+
child: const Text('No Data'),
76+
);
77+
}
78+
}
79+
return ListView.builder(
80+
shrinkWrap: true,
81+
itemCount: snapshot.data.results.length,
82+
itemBuilder: (BuildContext context, int index) {
83+
DietPlan dietPlan = snapshot.data.results[index];
84+
String id = dietPlan.objectId;
85+
String name = dietPlan.name;
86+
String description = dietPlan.description;
87+
bool status = dietPlan.status;
88+
return Dismissible(
89+
key: Key(id),
90+
background: Container(color: Colors.red),
91+
onDismissed: (direction) async {
92+
widget._dietPlanProvider.remove(dietPlan);
93+
},
94+
child: ListTile(
95+
title: Text(
96+
name,
97+
style: TextStyle(fontSize: 20.0),
98+
),
99+
subtitle: Text(description),
100+
trailing: IconButton(
101+
icon: status
102+
? const Icon(
103+
Icons.done_outline,
104+
color: Colors.green,
105+
size: 20.0,
106+
)
107+
: const Icon(Icons.done,
108+
color: Colors.grey, size: 20.0),
109+
onPressed: () async {
110+
dietPlan.status = !dietPlan.status;
111+
await dietPlan.save();
112+
setState(() {});
113+
}),
114+
),
115+
);
116+
});
117+
} else {
118+
return Center(
119+
child: const Text('No Data'),
120+
);
121+
}
122+
});
123+
}
124+
125+
String dietPlansToAdd =
126+
'[{"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},'
127+
'{"className":"Diet_Plans","Name":"Body Builder","Description":"Default Body Builders Diet","Fat":20,"Carbs":40,"Protein":40,"Status":true},'
128+
'{"className":"Diet_Plans","Name":"Zone Diet","Description":"Popular with CrossFit users. Zone Diet targets similar macros.","Fat":30,"Carbs":40,"Protein":30,"Status":true},'
129+
'{"className":"Diet_Plans","Name":"Low Fat","Description":"Low fat diet.","Fat":15,"Carbs":60,"Protein":25,"Status":false},'
130+
'{"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},'
131+
'{"className":"Diet_Plans","Name":"Paleo","Description":"Paleo diet.","Fat":60,"Carbs":25,"Protein":10,"Status":false},'
132+
'{"className":"Diet_Plans","Name":"Ketogenic","Description":"High quality fats, low carbs.","Fat":65,"Carbs":5,"Protein":30,"Status":true}]';
133+
}

0 commit comments

Comments
 (0)