Skip to content

Commit bc20d21

Browse files
committed
add todo op was complated.
1 parent 38dcf7f commit bc20d21

File tree

11 files changed

+162
-35
lines changed

11 files changed

+162
-35
lines changed

assets/langs/app_en.arb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,6 @@
3838
"okButton": "OK",
3939
"loginFailed": "Login Failed!",
4040
"loginSuccess": "Login Successful!",
41-
"loginSuccessContent": "You logged in successful."
41+
"loginSuccessContent": "You logged in successful.",
42+
"addTodoSuccess": "Todo successfully added in your list."
4243
}

lib/core/utils/app_validators.dart

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,20 @@ class AppValidators {
5151
return null;
5252
}
5353

54-
String? titleCheck(String? content) {
55-
if (content == null || content.isEmpty) {
54+
String? titleCheck(String? title) {
55+
if (title == null || title.isEmpty) {
5656
return 'Please enter a title!';
57-
} else if(content.length < 10 ){
58-
return 'Your title must be longer than 10 char.';
57+
} else if(title.length < 5 ){
58+
return 'Your title must be longer than 5 char.';
5959
}
6060
return null;
6161
}
6262

63-
String? contentCheck(String? content) {
64-
if (content == null || content.isEmpty) {
65-
return 'Please enter a content!';
66-
} else if (content.length < 20) {
67-
return 'Your content must be longer than 20 char.';
63+
String? subtitleCheck(String? subtitle) {
64+
if (subtitle == null || subtitle.isEmpty) {
65+
return 'Please enter a subtitle!';
66+
} else if (subtitle.length < 10) {
67+
return 'Your subtitle must be longer than 10 char.';
6868
}
6969
return null;
7070
}

lib/main.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import 'package:provider/provider.dart';
33
import 'package:todo_app/products/views/common/splash_view.dart';
44
import 'core/base/base_singleton.dart';
55
import 'core/constants/app_constants.dart';
6-
import 'core/helpers/token.dart';
7-
import 'products/views/auth/login_view.dart';
86
import 'package:firebase_core/firebase_core.dart';
97
import 'firebase_options.dart';
108

lib/products/viewmodels/auth_view_model.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ class AuthViewModel with BaseSingleton {
8181
"email": email,
8282
},
8383
);
84-
print(response.user!.uid);
8584
} catch (err) {
8685
uiGlobals.showAlertDialog(
8786
context: context,

lib/products/viewmodels/todo_view_model.dart

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
// ignore_for_file: avoid_print
1+
// ignore_for_file: avoid_print, use_build_context_synchronously
22

33
import 'package:cloud_firestore/cloud_firestore.dart';
44
import 'package:flutter/material.dart';
5+
import 'package:todo_app/core/base/base_singleton.dart';
6+
import 'package:todo_app/core/utils/navigation_service.dart';
57
import 'package:todo_app/products/models/todo_model.dart';
6-
8+
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
79
import '../../core/helpers/token.dart';
810

9-
class TodoViewModel extends ChangeNotifier {
11+
class TodoViewModel extends ChangeNotifier with BaseSingleton {
1012
List<TodoModel> _todoList = [];
1113
List<TodoModel> get todoList => _todoList;
1214
List<TodoModel> _searchList = [];
1315
List<TodoModel> get searchList => _searchList;
1416
final FirebaseFirestore _fireStore = FirebaseFirestore.instance;
17+
final BuildContext context = NavigationService.navigatorKey.currentContext!;
1518

1619
Future<void> get getTodos async {
1720
String? userId = await Token.readToken("user");
@@ -26,6 +29,14 @@ class TodoViewModel extends ChangeNotifier {
2629
(e) => TodoModel.fromJson(e.data()),
2730
)
2831
.toList();
32+
_todoList.sort((a, b) {
33+
if (a.createdAt != null && b.createdAt != null) {
34+
return b.createdAt!.compareTo(a.createdAt!);
35+
}
36+
else {
37+
return b.createdAt!.compareTo(a.createdAt ?? Timestamp.now());
38+
}
39+
});
2940
} catch (error) {
3041
print(error);
3142
_todoList = [];
@@ -34,6 +45,31 @@ class TodoViewModel extends ChangeNotifier {
3445
}
3546
}
3647

48+
Future<int> addTodo({required Map<String, dynamic> obj}) async {
49+
String? userId = await Token.readToken("user");
50+
if (userId != null) {
51+
try {
52+
CollectionReference userRef = _fireStore.collection("User");
53+
var todos = userRef.doc(userId).collection("todos");
54+
await todos.add(obj);
55+
await getTodos;
56+
uiGlobals.showSnackBar(
57+
content: AppLocalizations.of(context)!.addTodoSuccess,
58+
context: context,
59+
);
60+
Navigator.pop(context);
61+
return 200;
62+
} catch (e) {
63+
uiGlobals.showSnackBar(
64+
content: e.toString(),
65+
context: context,
66+
);
67+
return 400;
68+
}
69+
}
70+
return 500;
71+
}
72+
3773
void searchTodo(String query) {
3874
if (query.isNotEmpty) {
3975
final suggestions = todoList.where((todo) {

lib/products/views/auth/login_view.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ class LoginView extends StatelessWidget with BaseSingleton {
2929
);
3030
if (user != null) {
3131
btnStateController.update(ButtonState.success);
32-
print(user);
3332
Token.saveToken(token: user.uid, key: "user");
3433
} else {
3534
btnStateController.update(ButtonState.failure);

lib/products/views/common/navbar_view.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class NavbarView extends StatelessWidget with BaseSingleton {
1414
_goToAddTodoPage(BuildContext context) => Navigator.push(
1515
context,
1616
MaterialPageRoute(
17-
builder: (context) => const AddTodoView(),
17+
builder: (context) => AddTodoView(),
1818
),
1919
);
2020

lib/products/views/common/splash_view.dart

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:animate_do/animate_do.dart';
12
import 'package:flutter/material.dart';
23
import 'package:provider/provider.dart';
34
import 'package:todo_app/core/base/base_singleton.dart';
@@ -36,15 +37,19 @@ class SplashView extends StatelessWidget with BaseSingleton {
3637
child: ListView(
3738
shrinkWrap: true,
3839
children: [
39-
Icon(
40-
Icons.article,
41-
size: context.dynamicWidth(0.2),
40+
FadeInLeft(
41+
child: Icon(
42+
Icons.article,
43+
size: context.dynamicWidth(0.2),
44+
),
4245
),
4346
context.emptySizedHeightBox1x,
44-
Center(
45-
child: Text(
46-
constants.appTitle,
47-
style: context.textTheme.headline5,
47+
FadeInRight(
48+
child: Center(
49+
child: Text(
50+
constants.appTitle,
51+
style: context.textTheme.headline5,
52+
),
4853
),
4954
)
5055
],

lib/products/views/home/add_todo_view.dart

Lines changed: 82 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
import 'package:animate_do/animate_do.dart';
2+
import 'package:async_button/async_button.dart';
3+
import 'package:cloud_firestore/cloud_firestore.dart';
24
import 'package:flutter/material.dart';
35
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
6+
import 'package:provider/provider.dart';
47
import 'package:todo_app/core/base/base_singleton.dart';
58
import 'package:todo_app/core/extensions/ui_extensions.dart';
9+
import 'package:todo_app/uikit/button/special_async_button.dart';
610
import 'package:todo_app/uikit/button/special_button.dart';
711
import 'package:todo_app/uikit/decoration/special_container_decoration.dart';
812
import 'package:todo_app/uikit/textformfield/default_text_form_field.dart';
13+
import 'package:uuid/uuid.dart';
14+
15+
import '../../viewmodels/todo_view_model.dart';
916

1017
class AddTodoView extends StatelessWidget with BaseSingleton {
11-
const AddTodoView({super.key});
18+
final _formKey = GlobalKey<FormState>();
19+
final _titleController = TextEditingController();
20+
final _subtitleController = TextEditingController();
21+
22+
AddTodoView({super.key});
1223

1324
@override
1425
Widget build(BuildContext context) {
@@ -17,13 +28,16 @@ class AddTodoView extends StatelessWidget with BaseSingleton {
1728
title: _appBarTitle(context),
1829
),
1930
body: FadeInLeft(
20-
child: ListView(
21-
padding: context.padding2x,
22-
children: [
23-
_descriptionsSection(context),
24-
context.emptySizedHeightBox3x,
25-
_fieldsAndAddTodoButtonContainer(context)
26-
],
31+
child: Form(
32+
key: _formKey,
33+
child: ListView(
34+
padding: context.padding2x,
35+
children: [
36+
_descriptionsSection(context),
37+
context.emptySizedHeightBox3x,
38+
_fieldsAndAddTodoButtonContainer(context)
39+
],
40+
),
2741
),
2842
),
2943
);
@@ -117,6 +131,8 @@ class AddTodoView extends StatelessWidget with BaseSingleton {
117131
fillColor: colors.grey1,
118132
context: context,
119133
labelText: AppLocalizations.of(context)!.todoTitleLabel,
134+
controller: _titleController,
135+
validator: (title) => validators.titleCheck(title),
120136
);
121137
}
122138

@@ -127,14 +143,38 @@ class AddTodoView extends StatelessWidget with BaseSingleton {
127143
fillColor: colors.grey1,
128144
context: context,
129145
labelText: AppLocalizations.of(context)!.todoSubtitleLabel,
146+
controller: _subtitleController,
147+
validator: (subtitle) => validators.subtitleCheck(subtitle),
130148
);
131149
}
132150

133-
SizedBox _addNewTodoButton(BuildContext context) {
151+
Widget _addNewTodoButton(BuildContext context) {
134152
bool isHasIcon = true;
135153
return SizedBox(
136154
width: context.maxFinite,
137-
child: SpecialButton(
155+
child: SpecialAsyncButton(
156+
onTap: (btnStateController) async {
157+
btnStateController.update(ButtonState.loading);
158+
_formKey.currentState!.save();
159+
if (_formKey.currentState!.validate()) {
160+
final pv = Provider.of<TodoViewModel>(context, listen: false);
161+
var uuid = const Uuid();
162+
Map<String, dynamic> obj = {
163+
"id": uuid.v1(),
164+
"isActive": true,
165+
"isDone": false,
166+
"title": _titleController.text,
167+
"subtitle": _subtitleController.text,
168+
"createdAt": Timestamp.now(),
169+
};
170+
int statusCode = await pv.addTodo(obj: obj);
171+
statusCode == 200
172+
? btnStateController.update(ButtonState.success)
173+
: btnStateController.update(ButtonState.failure);
174+
} else {
175+
btnStateController.update(ButtonState.failure);
176+
}
177+
},
138178
buttonLabel: AppLocalizations.of(context)!.addNewTodoButton,
139179
borderRadius: context.borderRadius3x,
140180
padding: context.padding2x,
@@ -143,4 +183,36 @@ class AddTodoView extends StatelessWidget with BaseSingleton {
143183
),
144184
);
145185
}
186+
187+
// SizedBox _addNewTodoButton(BuildContext context) {
188+
// bool isHasIcon = true;
189+
// return SizedBox(
190+
// width: context.maxFinite,
191+
// child: SpecialButton(
192+
// buttonLabel: AppLocalizations.of(context)!.addNewTodoButton,
193+
// borderRadius: context.borderRadius3x,
194+
// padding: context.padding2x,
195+
// isHasIcon: isHasIcon,
196+
// icon: Icons.add,
197+
// onTap: () async {
198+
// _formKey.currentState!.save();
199+
// if (_formKey.currentState!.validate()) {
200+
// final pv = Provider.of<TodoViewModel>(context, listen: false);
201+
// var uuid = const Uuid();
202+
// Map<String, dynamic> obj = {
203+
// "id": uuid.v1(),
204+
// "isActive": true,
205+
// "isDone": false,
206+
// "title": _titleController.text,
207+
// "subtitle": _subtitleController.text,
208+
// "createdAt": Timestamp.now(),
209+
// };
210+
// await pv.addTodo(obj: obj);
211+
// } else {
212+
213+
// }
214+
// },
215+
// ),
216+
// );
217+
// }
146218
}

pubspec.lock

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ packages:
7878
url: "https://pub.dartlang.org"
7979
source: hosted
8080
version: "1.16.0"
81+
crypto:
82+
dependency: transitive
83+
description:
84+
name: crypto
85+
url: "https://pub.dartlang.org"
86+
source: hosted
87+
version: "3.0.2"
8188
cupertino_icons:
8289
dependency: "direct main"
8390
description:
@@ -348,6 +355,13 @@ packages:
348355
url: "https://pub.dartlang.org"
349356
source: hosted
350357
version: "1.3.1"
358+
uuid:
359+
dependency: "direct main"
360+
description:
361+
name: uuid
362+
url: "https://pub.dartlang.org"
363+
source: hosted
364+
version: "3.0.7"
351365
vector_math:
352366
dependency: transitive
353367
description:

0 commit comments

Comments
 (0)