From 401ee053a4ffdd385f06da5577970b7279fd95e7 Mon Sep 17 00:00:00 2001 From: SomnusVeritas Date: Wed, 8 Nov 2023 13:26:06 +0100 Subject: [PATCH] visibility toggle for undone todos --- lib/pages/dashboard_page.dart | 19 ++++++-- lib/services/dbhelper.dart | 7 ++- lib/widgets/todo_list.dart | 80 ++++++++++++++++++++++------------ lib/widgets/todo_listtile.dart | 47 ++++++++++++++++++++ 4 files changed, 121 insertions(+), 32 deletions(-) create mode 100644 lib/widgets/todo_listtile.dart diff --git a/lib/pages/dashboard_page.dart b/lib/pages/dashboard_page.dart index 6016017..cbebd41 100644 --- a/lib/pages/dashboard_page.dart +++ b/lib/pages/dashboard_page.dart @@ -3,14 +3,27 @@ import 'package:flutter/material.dart'; import '../widgets/todo_list.dart'; import 'todo_create_page.dart'; -class DashboardPage extends StatelessWidget { +class DashboardPage extends StatefulWidget { const DashboardPage({super.key}); static const routeName = '/'; + @override + State createState() => _DashboardPageState(); +} + +class _DashboardPageState extends State { + bool _showDoneTodos = false; @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(), + appBar: AppBar( + actions: [ + IconButton( + onPressed: () => setState(() => _showDoneTodos = !_showDoneTodos), + icon: const Icon(Icons.visibility), + ) + ], + ), floatingActionButton: FloatingActionButton( onPressed: () => Navigator.of(context).pushNamed(CreateTodoPage.routeName), @@ -19,7 +32,7 @@ class DashboardPage extends StatelessWidget { body: Center( child: SizedBox( width: MediaQuery.of(context).size.width * 0.9, - child: const TodoList(), + child: TodoList(showDoneTodos: _showDoneTodos), ), ), ); diff --git a/lib/services/dbhelper.dart b/lib/services/dbhelper.dart index 3efb614..f7cc69e 100644 --- a/lib/services/dbhelper.dart +++ b/lib/services/dbhelper.dart @@ -13,12 +13,15 @@ class DbHelper { schemas: [TodoSchema], directory: dir.path, ); - _isar.write((isar) => isar.todos.clear()); + // _isar.write((isar) => isar.todos.clear()); } - static List fetchTodos() => + static List fetchUndoneTodos() => _isar.todos.where().doneEqualTo(false).sortByCreatedAtDesc().findAll(); + static List fetchAllTodos() => + _isar.todos.where().sortByDone().thenByCreatedAt().findAll(); + static void addOrUpdateTodo(Todo todo) => _isar.write((isar) => isar.todos.put(todo)); diff --git a/lib/widgets/todo_list.dart b/lib/widgets/todo_list.dart index 75a0027..8f4eb80 100644 --- a/lib/widgets/todo_list.dart +++ b/lib/widgets/todo_list.dart @@ -2,57 +2,41 @@ import 'package:flutter/material.dart'; import '../models/todo.dart'; import '../services/dbhelper.dart'; +import 'todo_listtile.dart'; class TodoList extends StatefulWidget { - const TodoList({super.key}); + const TodoList({super.key, required this.showDoneTodos}); + final bool showDoneTodos; @override State createState() => _TodoListState(); } class _TodoListState extends State { - List _todos = DbHelper.fetchTodos(); + List _todos = []; @override void initState() { super.initState(); DbHelper.todosChangedStream.listen((event) { setState(() { - _todos = DbHelper.fetchTodos(); + // _todos = _updateTodos(); }); }); } - @override - Widget build(BuildContext context) { - return ListView.builder( - padding: const EdgeInsets.only(bottom: 60), - itemBuilder: _listBuilder, - itemCount: _todos.length, - ); - } - Widget _listBuilder(BuildContext context, int index) { final todo = _todos.elementAt(index); - return ListTile( - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - tileColor: Theme.of(context).colorScheme.primaryContainer, - title: Text(todo.title), - trailing: IconButton( - onPressed: () { - todo.done = true; - DbHelper.addOrUpdateTodo(todo); - _showSnackbar(todo); - }, - icon: Icon( - Icons.check_box, - color: Theme.of(context).colorScheme.primary, - ), + return Padding( + padding: const EdgeInsets.symmetric(vertical: 5), + child: TodoListTile( + todo: todo, + onPressed: () => _onTodoPressed(todo), ), ); } - void _showSnackbar(Todo todo) { + void _showDoneConfirmationSnackbar(Todo todo) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('Todo done'), @@ -65,4 +49,46 @@ class _TodoListState extends State { ), ); } + + void _showDeleteConfirmationSnackbar(Todo todo) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: const Text('Todo deleted'), + action: SnackBarAction( + label: 'Undo', + onPressed: () { + DbHelper.addOrUpdateTodo(todo); + }), + ), + ); + } + + void _onTodoPressed(Todo todo) { + if (todo.done) { + DbHelper.deleteTodo(todo); + _showDeleteConfirmationSnackbar(todo); + } else { + todo.done = true; + DbHelper.addOrUpdateTodo(todo); + _showDoneConfirmationSnackbar(todo); + } + } + + @override + Widget build(BuildContext context) { + _todos = _updateTodos(); + return ListView.builder( + padding: const EdgeInsets.only(bottom: 60), + itemBuilder: _listBuilder, + itemCount: _todos.length, + ); + } + + List _updateTodos() { + if (widget.showDoneTodos) { + return DbHelper.fetchAllTodos(); + } else { + return DbHelper.fetchUndoneTodos(); + } + } } diff --git a/lib/widgets/todo_listtile.dart b/lib/widgets/todo_listtile.dart new file mode 100644 index 0000000..13acb4a --- /dev/null +++ b/lib/widgets/todo_listtile.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; + +import '../models/todo.dart'; + +class TodoListTile extends StatelessWidget { + const TodoListTile({super.key, required this.todo, required this.onPressed}); + final Todo todo; + final VoidCallback onPressed; + + @override + Widget build(BuildContext context) { + if (!todo.done) { + return undoneTodo(context); + } + return doneTodo(context); + } + + Widget undoneTodo(BuildContext context) { + return ListTile( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + tileColor: Theme.of(context).colorScheme.primaryContainer, + title: Text(todo.title), + trailing: IconButton( + onPressed: onPressed, + icon: Icon( + Icons.check_box, + color: Theme.of(context).colorScheme.primary, + ), + ), + ); + } + + Widget doneTodo(BuildContext context) { + return ListTile( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + tileColor: Theme.of(context).colorScheme.surfaceVariant, + title: Text(todo.title), + trailing: IconButton( + onPressed: onPressed, + icon: Icon( + Icons.delete, + color: Theme.of(context).colorScheme.error, + ), + ), + ); + } +}