Skip to content

Commit

Permalink
feat: Refactor model and add sqlite database
Browse files Browse the repository at this point in the history
Signed-off-by: Tiago Góes <[email protected]>
  • Loading branch information
tiagodread committed Aug 2, 2024
1 parent 95849a0 commit 970a500
Show file tree
Hide file tree
Showing 15 changed files with 464 additions and 75 deletions.
1 change: 1 addition & 0 deletions ios/Flutter/Debug.xcconfig
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
1 change: 1 addition & 0 deletions ios/Flutter/Release.xcconfig
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
44 changes: 44 additions & 0 deletions ios/Podfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}

def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end

File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end

require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)

flutter_ios_podfile_setup

target 'Runner' do
use_frameworks!
use_modular_headers!

flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end

post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
5 changes: 3 additions & 2 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:todo/models/task_model.dart';
import 'package:todo/repositories/sqlite_task_repository.dart';
import 'package:todo/screens/home_screen.dart';

void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(ChangeNotifierProvider(
create: (context) => TaskModel(),
create: (context) => SQLiteTaskRepository(),
child: const App(),
));
}
Expand Down
47 changes: 47 additions & 0 deletions lib/models/task.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'package:uuid/uuid.dart';

class Task {
String id;
String title;
String description;
DateTime createdAt;
DateTime? completedAt;
bool isCompleted;
int? rewardInSatoshis;

Task({
String? id,
required this.title,
required this.description,
required this.createdAt,
this.completedAt,
this.isCompleted = false,
this.rewardInSatoshis = 0,
}) : id = id ?? const Uuid().v4();

Map<String, dynamic> toMap() {
return {
'id': id,
'title': title,
'description': description,
'createdAt': createdAt.toIso8601String(),
'completedAt': completedAt?.toIso8601String(),
'isCompleted': isCompleted,
'rewardInSatoshis': rewardInSatoshis,
};
}

factory Task.fromMap(Map<String, dynamic> map) {
return Task(
id: map['id'],
title: map['title'],
description: map['description'],
createdAt: DateTime.parse(map['createdAt']),
completedAt: (map['completedAt'] == null || map['completedAt'] == 'null')
? null
: DateTime.parse(map['completedAt']),
isCompleted: map['isCompleted'] == 0 ? false : true,
rewardInSatoshis: map['rewardInSatoshis'],
);
}
}
11 changes: 0 additions & 11 deletions lib/models/task_model.dart

This file was deleted.

87 changes: 87 additions & 0 deletions lib/repositories/sqlite_task_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import 'package:flutter/cupertino.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import '../models/task.dart';
import 'task_repository.dart';

class SQLiteTaskRepository extends ChangeNotifier implements TaskRepository {
Database? _database;

Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}

Future<Database> _initDatabase() async {
// await deleteDatabase(join(await getDatabasesPath(), 'tasks.db'));
return openDatabase(
join(await getDatabasesPath(), 'tasks.db'),
onCreate: (db, version) {
return db.execute(
'CREATE TABLE tasks(id TEXT PRIMARY KEY, title TEXT, description TEXT, createdAt TEXT, completedAt TEXT, isCompleted INTEGER, rewardInSatoshis INTEGER)',
);
},
version: 1,
);
}

@override
Future<List<Task>> getAllTasks() async {
final db = await database;
final List<Map<String, dynamic>> maps =
await db.query('tasks', orderBy: 'createdAt DESC');

return List.generate(maps.length, (i) {
return Task.fromMap(maps[i]);
});
}

@override
Future<Task?> getTaskById(String id) async {
final db = await database;
final List<Map<String, dynamic>> maps = await db.query(
'tasks',
where: 'id = ?',
whereArgs: [id],
);

if (maps.isNotEmpty) {
return Task.fromMap(maps.first);
} else {
return null;
}
}

@override
Future<void> addTask(Task task) async {
final db = await database;
await db.insert(
'tasks',
task.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
notifyListeners();
}

@override
Future<void> updateTask(Task task) async {
final db = await database;
await db.update(
'tasks',
task.toMap(),
where: 'id = ?',
whereArgs: [task.id],
);
}

@override
Future<void> deleteTask(String id) async {
final db = await database;
await db.delete(
'tasks',
where: 'id = ?',
whereArgs: [id],
);
}
}
9 changes: 9 additions & 0 deletions lib/repositories/task_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import '../models/task.dart';

abstract class TaskRepository {
Future<List<Task>> getAllTasks();
Future<Task?> getTaskById(String id);
Future<void> addTask(Task task);
Future<void> updateTask(Task task);
Future<void> deleteTask(String id);
}
43 changes: 32 additions & 11 deletions lib/widgets/task_add_widget.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:todo/models/task_model.dart';
import 'package:todo/models/task.dart';
import 'package:todo/repositories/task_repository.dart';

import '../repositories/sqlite_task_repository.dart';

class AddTask extends StatefulWidget {
const AddTask({
Expand All @@ -12,6 +15,15 @@ class AddTask extends StatefulWidget {

class _AddTaskState extends State<AddTask> {
final textController = TextEditingController();
late SQLiteTaskRepository taskRepository;
late Future<List<Task>> taskListFuture;

@override
void initState() {
super.initState();
taskRepository = SQLiteTaskRepository();
taskListFuture = taskRepository.getAllTasks();
}

@override
void dispose() {
Expand All @@ -28,6 +40,7 @@ class _AddTaskState extends State<AddTask> {
child: Padding(
padding: const EdgeInsets.only(left: 20.0),
child: TextField(
key: const Key("task-input"),
controller: textController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
Expand All @@ -39,16 +52,24 @@ class _AddTaskState extends State<AddTask> {
flex: 1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Consumer(
builder: (context, value, child) => ElevatedButton(
onPressed: () {
final widget = context.read<TaskModel>();
if (textController.text.isNotEmpty) {
widget.addTask(textController.text);
textController.clear();
}
},
child: const Text("ADD")))),
child: ElevatedButton(
key: const Key("task-add-button"),
onPressed: () async {
if (textController.text.isNotEmpty) {
Task task = Task(
title: textController.text,
description: '',
createdAt: DateTime.now(),
isCompleted: false,
rewardInSatoshis: 0,
);
await Provider.of<SQLiteTaskRepository>(context,
listen: false)
.addTask(task);
textController.clear();
}
},
child: const Text("ADD"))),
),
],
);
Expand Down
16 changes: 8 additions & 8 deletions lib/widgets/task_item_widget.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'package:flutter/material.dart';

import '../models/task.dart';

class TaskItem extends StatefulWidget {
final String itemText;
final Task task;
const TaskItem(
this.itemText, {
this.task, {
super.key,
});

Expand All @@ -12,28 +14,26 @@ class TaskItem extends StatefulWidget {
}

class _TaskItemState extends State<TaskItem> {
bool isCompleted = false;

@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Row(
children: [
Checkbox(
value: isCompleted,
value: widget.task.isCompleted,
onChanged: (bool? value) {
setState(() {
isCompleted = value!;
widget.task.isCompleted = value!;
});
}),
SizedBox(
width: 350,
child: Text(widget.itemText,
child: Text(widget.task.title,
style: TextStyle(
fontSize: 20,
overflow: TextOverflow.clip,
decoration: isCompleted
decoration: widget.task.isCompleted
? TextDecoration.lineThrough
: TextDecoration.none)),
)
Expand Down
47 changes: 31 additions & 16 deletions lib/widgets/task_list_widget.dart
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:todo/models/task_model.dart';
import 'package:todo/repositories/sqlite_task_repository.dart';
import 'package:todo/widgets/task_item_widget.dart';

class TaskList extends StatelessWidget {
import '../models/task.dart';

class TaskList extends StatefulWidget {
const TaskList({super.key});

@override
Widget build(BuildContext context) {
return Consumer<TaskModel>(builder: (context, value, child) {

final widget = context.read<TaskModel>();
final List<TaskItem> taskItems = [];

widget.taskList.forEach((task) {
taskItems.add(TaskItem(task.toString()));
});
State<TaskList> createState() => _TaskListState();
}

return ListView(
children: taskItems,
);
});
class _TaskListState extends State<TaskList> {
@override
Widget build(BuildContext context) {
return Consumer<SQLiteTaskRepository>(
builder: (context, taskRepository, child) => FutureBuilder<List<Task>>(
future: taskRepository.getAllTasks(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
return const Center(child: Text('Add some work to do!'));
} else {
List<Task> tasks = snapshot.data!;
return ListView.builder(
itemCount: tasks.length,
itemBuilder: (context, index) {
Task task = tasks[index];
return TaskItem(task);
},
);
}
}));
}
}
Loading

0 comments on commit 970a500

Please sign in to comment.