11 import 'package:flutter/material.dart';
22 import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
33
44 void main() async {
55 WidgetsFlutterBinding.ensureInitialized();
66
77 final keyApplicationId = 'YOUR_APP_ID_HERE';
88 final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
99 final keyParseServerUrl = 'https://parseapi.back4app.com';
1010
1111 await Parse().initialize(keyApplicationId, keyParseServerUrl,
1212 clientKey: keyClientKey,
1313 debug: true);
1414
1515 runApp(MyApp());
1616 }
1717
1818 class MyApp extends StatelessWidget {
1919 Future<bool> hasUserLogged() async {
2020 return Future.value(false);
2121 }
2222
2323 @override
2424 Widget build(BuildContext context) {
2525 return MaterialApp(
2626 title: 'Flutter - Parse Server',
2727 theme: ThemeData(
2828 primarySwatch: Colors.blue,
2929 visualDensity: VisualDensity.adaptivePlatformDensity,
3030 ),
3131 home: FutureBuilder<bool>(
3232 future: hasUserLogged(),
3333 builder: (context, snapshot) {
3434 switch (snapshot.connectionState) {
3535 case ConnectionState.none:
3636 case ConnectionState.waiting:
3737 return Scaffold(
3838 body: Center(
3939 child: Container(
4040 width: 100,
4141 height: 100,
4242 child: CircularProgressIndicator()),
4343 ),
4444 );
4545 default:
4646 if (snapshot.hasData && snapshot.data!) {
4747 return UserPage();
4848 } else {
4949 return LoginPage();
5050 }
5151 }
5252 }),
5353 );
5454 }
5555 }
5656
5757 class LoginPage extends StatefulWidget {
5858 @override
5959 _LoginPageState createState() => _LoginPageState();
6060 }
6161
6262 class _LoginPageState extends State<LoginPage> {
6363 final controllerUsername = TextEditingController();
6464 final controllerPassword = TextEditingController();
6565 bool isLoggedIn = false;
6666
6767 @override
6868 Widget build(BuildContext context) {
6969 return Scaffold(
7070 appBar: AppBar(
7171 title: const Text('Flutter - Parse Server'),
7272 ),
7373 body: Center(
7474 child: SingleChildScrollView(
7575 padding: const EdgeInsets.all(8),
7676 child: Column(
7777 crossAxisAlignment: CrossAxisAlignment.stretch,
7878 children: [
7979 Container(
8080 height: 200,
8181 child: Image.network(
8282 'https://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
8383 ),
8484 Center(
8585 child: const Text('Flutter on Back4App',
8686 style:
8787 TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
8888 ),
8989 SizedBox(
9090 height: 16,
9191 ),
9292 TextField(
9393 controller: controllerUsername,
9494 enabled: !isLoggedIn,
9595 keyboardType: TextInputType.text,
9696 textCapitalization: TextCapitalization.none,
9797 autocorrect: false,
9898 decoration: InputDecoration(
9999 border: OutlineInputBorder(
100100 borderSide: BorderSide(color: Colors.black)),
101101 labelText: 'Username'),
102102 ),
103103 SizedBox(
104104 height: 8,
105105 ),
106106 TextField(
107107 controller: controllerPassword,
108108 enabled: !isLoggedIn,
109109 obscureText: true,
110110 keyboardType: TextInputType.text,
111111 textCapitalization: TextCapitalization.none,
112112 autocorrect: false,
113113 decoration: InputDecoration(
114114 border: OutlineInputBorder(
115115 borderSide: BorderSide(color: Colors.black)),
116116 labelText: 'Password'),
117117 ),
118118 SizedBox(
119119 height: 16,
120120 ),
121121 Container(
122122 height: 50,
123123 child: ElevatedButton(
124124 child: const Text('Login'),
125125 onPressed: isLoggedIn ? null : () => doUserLogin(),
126126 ),
127127 ),
128128 SizedBox(
129129 height: 16,
130130 ),
131131 Container(
132132 height: 50,
133133 child: ElevatedButton(
134134 child: const Text('Sign Up'),
135135 onPressed: () => navigateToSignUp(),
136136 ),
137137 ),
138138 SizedBox(
139139 height: 16,
140140 ),
141141 Container(
142142 height: 50,
143143 child: ElevatedButton(
144144 child: const Text('Reset Password'),
145145 onPressed: () => navigateToResetPassword(),
146146 ),
147147 )
148148 ),
149149 ),
150150 ),
151151 ));
152152 }
153153
154154 void doUserLogin() async {
155155 final username = controllerUsername.text.trim();
156156 final password = controllerPassword.text.trim();
157157
158158 final user = ParseUser(username, password, null);
159159
160160 var response = await user.login();
161161
162162 if (response.success) {
163163 navigateToUser();
164164 } else {
165165 Message.showError(context: context, message: response.error!.message);
166166 }
167167 }
168168
169169 void navigateToUser() {
170170 Navigator.pushAndRemoveUntil(
171171 context,
172172 MaterialPageRoute(builder: (context) => UserPage()),
173173 (Route<dynamic> route) => false,
174174 );
175175 }
176176
177177 void navigateToSignUp() {
178178 Navigator.push(
179179 context,
180180 MaterialPageRoute(builder: (context) => SignUpPage()),
181181 );
182182 }
183183
184184 void navigateToResetPassword() {
185185 Navigator.push(
186186 context,
187187 MaterialPageRoute(builder: (context) => ResetPasswordPage()),
188188 );
189189 }
190190 }
191191
192192 class SignUpPage extends StatefulWidget {
193193 @override
194194 _SignUpPageState createState() => _SignUpPageState();
195195 }
196196
197197 class _SignUpPageState extends State<SignUpPage> {
198198 final controllerUsername = TextEditingController();
199199 final controllerPassword = TextEditingController();
200200 final controllerEmail = TextEditingController();
201201
202202 @override
203203 Widget build(BuildContext context) {
204204 return Scaffold(
205205 appBar: AppBar(
206206 title: const Text('Flutter Sign Up'),
207207 ),
208208 body: Center(
209209 child: SingleChildScrollView(
210210 padding: const EdgeInsets.all(8),
211211 child: Column(
212212 crossAxisAlignment: CrossAxisAlignment.stretch,
213213 children: [
214214 Container(
215215 height: 200,
216216 child: Image.network(
217217 'https://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
218218 ),
219219 Center(
220220 child: const Text('Flutter on Back4App',
221221 style:
222222 TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
223223 ),
224224 SizedBox(
225225 height: 16,
226226 ),
227227 Center(
228228 child: const Text('User registration',
229229 style: TextStyle(fontSize: 16)),
230230 ),
231231 SizedBox(
232232 height: 16,
233233 ),
234234 TextField(
235235 controller: controllerUsername,
236236 keyboardType: TextInputType.text,
237237 textCapitalization: TextCapitalization.none,
238238 autocorrect: false,
239239 decoration: InputDecoration(
240240 border: OutlineInputBorder(
241241 borderSide: BorderSide(color: Colors.black)),
242242 labelText: 'Username'),
243243 ),
244244 SizedBox(
245245 height: 8,
246246 ),
247247 TextField(
248248 controller: controllerEmail,
249249 keyboardType: TextInputType.emailAddress,
250250 textCapitalization: TextCapitalization.none,
251251 autocorrect: false,
252252 decoration: InputDecoration(
253253 border: OutlineInputBorder(
254254 borderSide: BorderSide(color: Colors.black)),
255255 labelText: 'E-mail'),
256256 ),
257257 SizedBox(
258258 height: 8,
259259 ),
260260 TextField(
261261 controller: controllerPassword,
262262 obscureText: true,
263263 keyboardType: TextInputType.text,
264264 textCapitalization: TextCapitalization.none,
265265 autocorrect: false,
266266 decoration: InputDecoration(
267267 border: OutlineInputBorder(
268268 borderSide: BorderSide(color: Colors.black)),
269269 labelText: 'Password'),
270270 ),
271271 SizedBox(
272272 height: 8,
273273 ),
274274 Container(
275275 height: 50,
276276 child: ElevatedButton(
277277 child: const Text('Sign Up'),
278278 onPressed: () => doUserRegistration(),
279279 ),
280280 )
281281 ],
282282 ),
283283 ),
284284 ));
285285 }
286286
287287 void doUserRegistration() async {
288288 final username = controllerUsername.text.trim();
289289 final email = controllerEmail.text.trim();
290290 final password = controllerPassword.text.trim();
291291
292292 final user = ParseUser.createUser(username, password, email);
293293
294294 var response = await user.signUp();
295295
296296 if (response.success) {
297297 Message.showSuccess(
298298 context: context,
299299 message: 'User was successfully created!',
300300 onPressed: () async {
301301 Navigator.pushAndRemoveUntil(
302302 context,
303303 MaterialPageRoute(builder: (context) => UserPage()),
304304 (Route<dynamic> route) => false,
305305 );
306306 });
307307 } else {
308308 Message.showError(context: context, message: response.error!.message);
309309 }
310310 }
311311 }
312312
313313 class UserPage extends StatelessWidget {
314314 ParseUser? currentUser;
315315
316316 Future<ParseUser?> getUser() async {
317317 }
318318
319319 @override
320320 Widget build(BuildContext context) {
321321 void doUserLogout() async {
322322 var response = await currentUser!.logout();
323323 if (response.success) {
324324 Message.showSuccess(
325325 context: context,
326326 message: 'User was successfully logout!',
327327 onPressed: () {
328328 Navigator.pushAndRemoveUntil(
329329 context,
330330 MaterialPageRoute(builder: (context) => LoginPage()),
331331 (Route<dynamic> route) => false,
332332 );
333333 });
334334 } else {
335335 Message.showError(context: context, message: response.error!.message);
336336 }
337337 }
338338
339339 return Scaffold(
340340 appBar: AppBar(
341341 title: Text('User logged in - Current User'),
342342 ),
343343 body: FutureBuilder<ParseUser?>(
344344 future: getUser(),
345345 builder: (context, snapshot) {
346346 switch (snapshot.connectionState) {
347347 case ConnectionState.none:
348348 case ConnectionState.waiting:
349349 return Center(
350350 child: Container(
351351 width: 100,
352352 height: 100,
353353 child: CircularProgressIndicator()),
354354 );
355355 default:
356356 return Padding(
357357 padding: const EdgeInsets.all(8.0),
358358 child: Column(
359359 crossAxisAlignment: CrossAxisAlignment.stretch,
360360 mainAxisAlignment: MainAxisAlignment.center,
361361 children: [
362362 Center(
363363 child: Text('Hello, ${snapshot.data!.username}')),
364364 SizedBox(
365365 height: 16,
366366 ),
367367 Container(
368368 height: 50,
369369 child: ElevatedButton(
370370 child: const Text('Logout'),
371371 onPressed: () => doUserLogout(),
372372 ),
373373 ),
374374 ],
375375 ),
376376 );
377377 }
378378 }));
379379 }
380380 }
381381
382382 class ResetPasswordPage extends StatefulWidget {
383383 @override
384384 _ResetPasswordPageState createState() => _ResetPasswordPageState();
385385 }
386386
387387 class _ResetPasswordPageState extends State<ResetPasswordPage> {
388388 final controllerEmail = TextEditingController();
389389
390390 @override
391391 Widget build(BuildContext context) {
392392 return Scaffold(
393393 appBar: AppBar(
394394 title: Text('Reset Password'),
395395 ),
396396 body: SingleChildScrollView(
397397 padding: const EdgeInsets.all(8),
398398 child: Column(
399399 crossAxisAlignment: CrossAxisAlignment.stretch,
400400 children: [
401401 TextField(
402402 controller: controllerEmail,
403403 keyboardType: TextInputType.emailAddress,
404404 textCapitalization: TextCapitalization.none,
405405 autocorrect: false,
406406 decoration: InputDecoration(
407407 border: OutlineInputBorder(
408408 borderSide: BorderSide(color: Colors.black)),
409409 labelText: 'E-mail'),
410410 ),
411411 SizedBox(
412412 height: 8,
413413 ),
414414 Container(
415415 height: 50,
416416 child: ElevatedButton(
417417 child: const Text('Reset Password'),
418418 onPressed: () => doUserResetPassword(),
419419 ),
420420 )
421421 ],
422422 ),
423423 ));
424424 }
425425
426426 void doUserResetPassword() async {}
427427 }
428428
429429 class Message {
430430 static void showSuccess(
431431 {required BuildContext context,
432432 required String message,
433433 VoidCallback? onPressed}) {
434434 showDialog(
435435 context: context,
436436 builder: (BuildContext context) {
437437 return AlertDialog(
438438 title: const Text("Success!"),
439439 content: Text(message),
440440 actions: <Widget>[
441441 new ElevatedButton(
442442 child: const Text("OK"),
443443 onPressed: () {
444444 Navigator.of(context).pop();
445445 if (onPressed != null) {
446446 onPressed();
447447 }
448448 },
449449 ),
450450 ],
451451 );
452452 },
453453 );
454454 }
455455
456456 static void showError(
457457 {required BuildContext context,
458458 required String message,
459459 VoidCallback? onPressed}) {
460460 showDialog(
461461 context: context,
462462 builder: (BuildContext context) {
463463 return AlertDialog(
464464 title: const Text("Error!"),
465465 content: Text(message),
466466 actions: <Widget>[
467467 new ElevatedButton(
468468 child: const Text("OK"),
469469 onPressed: () {
470470 Navigator.of(context).pop();
471471 if (onPressed != null) {
472472 onPressed();
473473 }
474474 },
475475 ),
476476 ],
477477 );
478478 },
479479 );
480480 }
481481 }
482482