Skip to content

Commit 970a500

Browse files
committed
feat: Refactor model and add sqlite database
Signed-off-by: Tiago Góes <[email protected]>
1 parent 95849a0 commit 970a500

15 files changed

+464
-75
lines changed

ios/Flutter/Debug.xcconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
12
#include "Generated.xcconfig"

ios/Flutter/Release.xcconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
12
#include "Generated.xcconfig"

ios/Podfile

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Uncomment this line to define a global platform for your project
2+
# platform :ios, '12.0'
3+
4+
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5+
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6+
7+
project 'Runner', {
8+
'Debug' => :debug,
9+
'Profile' => :release,
10+
'Release' => :release,
11+
}
12+
13+
def flutter_root
14+
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15+
unless File.exist?(generated_xcode_build_settings_path)
16+
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17+
end
18+
19+
File.foreach(generated_xcode_build_settings_path) do |line|
20+
matches = line.match(/FLUTTER_ROOT\=(.*)/)
21+
return matches[1].strip if matches
22+
end
23+
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24+
end
25+
26+
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27+
28+
flutter_ios_podfile_setup
29+
30+
target 'Runner' do
31+
use_frameworks!
32+
use_modular_headers!
33+
34+
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35+
target 'RunnerTests' do
36+
inherit! :search_paths
37+
end
38+
end
39+
40+
post_install do |installer|
41+
installer.pods_project.targets.each do |target|
42+
flutter_additional_ios_build_settings(target)
43+
end
44+
end

lib/main.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import 'package:flutter/material.dart';
22
import 'package:provider/provider.dart';
3-
import 'package:todo/models/task_model.dart';
3+
import 'package:todo/repositories/sqlite_task_repository.dart';
44
import 'package:todo/screens/home_screen.dart';
55

66
void main() {
7+
WidgetsFlutterBinding.ensureInitialized();
78
runApp(ChangeNotifierProvider(
8-
create: (context) => TaskModel(),
9+
create: (context) => SQLiteTaskRepository(),
910
child: const App(),
1011
));
1112
}

lib/models/task.dart

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import 'package:uuid/uuid.dart';
2+
3+
class Task {
4+
String id;
5+
String title;
6+
String description;
7+
DateTime createdAt;
8+
DateTime? completedAt;
9+
bool isCompleted;
10+
int? rewardInSatoshis;
11+
12+
Task({
13+
String? id,
14+
required this.title,
15+
required this.description,
16+
required this.createdAt,
17+
this.completedAt,
18+
this.isCompleted = false,
19+
this.rewardInSatoshis = 0,
20+
}) : id = id ?? const Uuid().v4();
21+
22+
Map<String, dynamic> toMap() {
23+
return {
24+
'id': id,
25+
'title': title,
26+
'description': description,
27+
'createdAt': createdAt.toIso8601String(),
28+
'completedAt': completedAt?.toIso8601String(),
29+
'isCompleted': isCompleted,
30+
'rewardInSatoshis': rewardInSatoshis,
31+
};
32+
}
33+
34+
factory Task.fromMap(Map<String, dynamic> map) {
35+
return Task(
36+
id: map['id'],
37+
title: map['title'],
38+
description: map['description'],
39+
createdAt: DateTime.parse(map['createdAt']),
40+
completedAt: (map['completedAt'] == null || map['completedAt'] == 'null')
41+
? null
42+
: DateTime.parse(map['completedAt']),
43+
isCompleted: map['isCompleted'] == 0 ? false : true,
44+
rewardInSatoshis: map['rewardInSatoshis'],
45+
);
46+
}
47+
}

lib/models/task_model.dart

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import 'package:flutter/cupertino.dart';
2+
import 'package:sqflite/sqflite.dart';
3+
import 'package:path/path.dart';
4+
import '../models/task.dart';
5+
import 'task_repository.dart';
6+
7+
class SQLiteTaskRepository extends ChangeNotifier implements TaskRepository {
8+
Database? _database;
9+
10+
Future<Database> get database async {
11+
if (_database != null) return _database!;
12+
_database = await _initDatabase();
13+
return _database!;
14+
}
15+
16+
Future<Database> _initDatabase() async {
17+
// await deleteDatabase(join(await getDatabasesPath(), 'tasks.db'));
18+
return openDatabase(
19+
join(await getDatabasesPath(), 'tasks.db'),
20+
onCreate: (db, version) {
21+
return db.execute(
22+
'CREATE TABLE tasks(id TEXT PRIMARY KEY, title TEXT, description TEXT, createdAt TEXT, completedAt TEXT, isCompleted INTEGER, rewardInSatoshis INTEGER)',
23+
);
24+
},
25+
version: 1,
26+
);
27+
}
28+
29+
@override
30+
Future<List<Task>> getAllTasks() async {
31+
final db = await database;
32+
final List<Map<String, dynamic>> maps =
33+
await db.query('tasks', orderBy: 'createdAt DESC');
34+
35+
return List.generate(maps.length, (i) {
36+
return Task.fromMap(maps[i]);
37+
});
38+
}
39+
40+
@override
41+
Future<Task?> getTaskById(String id) async {
42+
final db = await database;
43+
final List<Map<String, dynamic>> maps = await db.query(
44+
'tasks',
45+
where: 'id = ?',
46+
whereArgs: [id],
47+
);
48+
49+
if (maps.isNotEmpty) {
50+
return Task.fromMap(maps.first);
51+
} else {
52+
return null;
53+
}
54+
}
55+
56+
@override
57+
Future<void> addTask(Task task) async {
58+
final db = await database;
59+
await db.insert(
60+
'tasks',
61+
task.toMap(),
62+
conflictAlgorithm: ConflictAlgorithm.replace,
63+
);
64+
notifyListeners();
65+
}
66+
67+
@override
68+
Future<void> updateTask(Task task) async {
69+
final db = await database;
70+
await db.update(
71+
'tasks',
72+
task.toMap(),
73+
where: 'id = ?',
74+
whereArgs: [task.id],
75+
);
76+
}
77+
78+
@override
79+
Future<void> deleteTask(String id) async {
80+
final db = await database;
81+
await db.delete(
82+
'tasks',
83+
where: 'id = ?',
84+
whereArgs: [id],
85+
);
86+
}
87+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import '../models/task.dart';
2+
3+
abstract class TaskRepository {
4+
Future<List<Task>> getAllTasks();
5+
Future<Task?> getTaskById(String id);
6+
Future<void> addTask(Task task);
7+
Future<void> updateTask(Task task);
8+
Future<void> deleteTask(String id);
9+
}

lib/widgets/task_add_widget.dart

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import 'package:flutter/material.dart';
22
import 'package:provider/provider.dart';
3-
import 'package:todo/models/task_model.dart';
3+
import 'package:todo/models/task.dart';
4+
import 'package:todo/repositories/task_repository.dart';
5+
6+
import '../repositories/sqlite_task_repository.dart';
47

58
class AddTask extends StatefulWidget {
69
const AddTask({
@@ -12,6 +15,15 @@ class AddTask extends StatefulWidget {
1215

1316
class _AddTaskState extends State<AddTask> {
1417
final textController = TextEditingController();
18+
late SQLiteTaskRepository taskRepository;
19+
late Future<List<Task>> taskListFuture;
20+
21+
@override
22+
void initState() {
23+
super.initState();
24+
taskRepository = SQLiteTaskRepository();
25+
taskListFuture = taskRepository.getAllTasks();
26+
}
1527

1628
@override
1729
void dispose() {
@@ -28,6 +40,7 @@ class _AddTaskState extends State<AddTask> {
2840
child: Padding(
2941
padding: const EdgeInsets.only(left: 20.0),
3042
child: TextField(
43+
key: const Key("task-input"),
3144
controller: textController,
3245
decoration: const InputDecoration(
3346
border: OutlineInputBorder(),
@@ -39,16 +52,24 @@ class _AddTaskState extends State<AddTask> {
3952
flex: 1,
4053
child: Padding(
4154
padding: const EdgeInsets.all(8.0),
42-
child: Consumer(
43-
builder: (context, value, child) => ElevatedButton(
44-
onPressed: () {
45-
final widget = context.read<TaskModel>();
46-
if (textController.text.isNotEmpty) {
47-
widget.addTask(textController.text);
48-
textController.clear();
49-
}
50-
},
51-
child: const Text("ADD")))),
55+
child: ElevatedButton(
56+
key: const Key("task-add-button"),
57+
onPressed: () async {
58+
if (textController.text.isNotEmpty) {
59+
Task task = Task(
60+
title: textController.text,
61+
description: '',
62+
createdAt: DateTime.now(),
63+
isCompleted: false,
64+
rewardInSatoshis: 0,
65+
);
66+
await Provider.of<SQLiteTaskRepository>(context,
67+
listen: false)
68+
.addTask(task);
69+
textController.clear();
70+
}
71+
},
72+
child: const Text("ADD"))),
5273
),
5374
],
5475
);

lib/widgets/task_item_widget.dart

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import 'package:flutter/material.dart';
22

3+
import '../models/task.dart';
4+
35
class TaskItem extends StatefulWidget {
4-
final String itemText;
6+
final Task task;
57
const TaskItem(
6-
this.itemText, {
8+
this.task, {
79
super.key,
810
});
911

@@ -12,28 +14,26 @@ class TaskItem extends StatefulWidget {
1214
}
1315

1416
class _TaskItemState extends State<TaskItem> {
15-
bool isCompleted = false;
16-
1717
@override
1818
Widget build(BuildContext context) {
1919
return Padding(
2020
padding: const EdgeInsets.only(bottom: 20),
2121
child: Row(
2222
children: [
2323
Checkbox(
24-
value: isCompleted,
24+
value: widget.task.isCompleted,
2525
onChanged: (bool? value) {
2626
setState(() {
27-
isCompleted = value!;
27+
widget.task.isCompleted = value!;
2828
});
2929
}),
3030
SizedBox(
3131
width: 350,
32-
child: Text(widget.itemText,
32+
child: Text(widget.task.title,
3333
style: TextStyle(
3434
fontSize: 20,
3535
overflow: TextOverflow.clip,
36-
decoration: isCompleted
36+
decoration: widget.task.isCompleted
3737
? TextDecoration.lineThrough
3838
: TextDecoration.none)),
3939
)

0 commit comments

Comments
 (0)