added sqlite and hive as local storage options to todo-app

master
rmtrms 2024-10-29 17:37:12 +01:00
parent 017fd647b1
commit 4757d2975f
11 changed files with 278 additions and 25 deletions

View File

@ -1,20 +1,33 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'providers/todo_provider.dart';
import 'screens/todo_list_screen.dart';
import 'storage/hive_todo_storage.dart';
import 'storage/todo_storage.dart';
void main() {
runApp(ToDoApp());
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Hive.initFlutter();
await Hive.openBox('todoBox');
//choose preferred storage type here
final storage = HiveToDoStorage();
runApp(ToDoApp(storage: storage));
}
class ToDoApp extends StatelessWidget {
final ToDoStorage storage;
const ToDoApp({required this.storage});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => ToDoProvider(),
create: (_) => ToDoProvider(storage),
child: MaterialApp(
title: 'To-Do list',
title: 'To-Do List',
theme: ThemeData(
primarySwatch: Colors.green,
),

View File

@ -2,7 +2,7 @@ class ToDo {
String title;
String? description;
DateTime deadline;
String priority; // High, Medium, Low
String priority;
bool isCompleted;
ToDo({

View File

@ -1,46 +1,53 @@
import 'package:flutter/foundation.dart';
import '../models/todo.dart';
import '../storage/todo_storage.dart';
class ToDoProvider with ChangeNotifier {
List<ToDo> _todos = [];
final ToDoStorage storage;
ToDoProvider(this.storage) {
loadTasks();
}
List<ToDo> get todos => _todos;
void addTask(ToDo todo) {
_todos.add(todo);
storage.addTask(todo);
notifyListeners();
}
void removeTask(int index) {
storage.removeTask(index);
_todos.removeAt(index);
notifyListeners();
}
void toggleCompletion(int index) {
_todos[index].isCompleted = !_todos[index].isCompleted;
storage.updateTask(index, _todos[index]);
notifyListeners();
}
Future<void> loadTasks() async {
_todos = await storage.fetchTasks();
notifyListeners();
}
// sorting logic for deadline
void sortByDeadline() {
_todos.sort((a, b) {
if (a.deadline == null) return 1; // Tasks without deadline should come last
if (b.deadline == null) return -1;
return a.deadline!.compareTo(b.deadline!);
});
_todos.sort((a, b) => a.deadline.compareTo(b.deadline));
notifyListeners();
}
// sorting logic for priority
void sortByPriority() {
Map<String, int> priorityMap = {'High': 1, 'Medium': 2, 'Low': 3};
_todos.sort((a, b) => priorityMap[a.priority]!.compareTo(priorityMap[b.priority]!));
notifyListeners();
}
// sorting logic for staus
void sortByStatus() {
_todos.sort((a, b) => a.isCompleted ? 1 : -1); // Uncompleted first
_todos.sort((a, b) => a.isCompleted ? 1 : -1);
notifyListeners();
}
}

View File

@ -3,12 +3,22 @@ import 'package:provider/provider.dart';
import '../providers/todo_provider.dart';
import '../widgets/todo_form.dart';
class ToDoListScreen extends StatelessWidget {
class ToDoListScreen extends StatefulWidget {
@override
_ToDoListScreenState createState() => _ToDoListScreenState();
}
class _ToDoListScreenState extends State<ToDoListScreen> {
@override
void initState() {
super.initState();
Provider.of<ToDoProvider>(context, listen: false).loadTasks();
}
@override
Widget build(BuildContext context) {
var todoProvider = Provider.of<ToDoProvider>(context);
// Helper function to format DateTime
String formatDate(DateTime date) {
return '${date.day.toString().padLeft(2, '0')}/${date.month.toString().padLeft(2, '0')}/${date.year}';
}
@ -17,7 +27,6 @@ class ToDoListScreen extends StatelessWidget {
appBar: AppBar(
title: Text('To-Do list'),
actions: [
// Filter Dropdown Menü
PopupMenuButton<String>(
onSelected: (value) {
switch (value) {

View File

@ -0,0 +1,35 @@
import 'package:hive/hive.dart';
import '../models/todo.dart';
import 'todo_storage.dart';
class HiveToDoStorage implements ToDoStorage {
static const String _boxName = 'todoBox';
Future<Box> get _box async {
return Hive.box(_boxName);
}
@override
Future<void> addTask(ToDo todo) async {
final box = await _box;
await box.add(todo.toJson());
}
@override
Future<void> removeTask(int index) async {
final box = await _box;
await box.deleteAt(index);
}
@override
Future<void> updateTask(int index, ToDo todo) async {
final box = await _box;
await box.putAt(index, todo.toJson());
}
@override
Future<List<ToDo>> fetchTasks() async {
final box = await _box;
return box.values.map((e) => ToDo.fromJson(Map<String, dynamic>.from(e))).toList();
}
}

View File

@ -0,0 +1,68 @@
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import '../models/todo.dart';
import 'todo_storage.dart';
class SQLiteToDoStorage implements ToDoStorage {
Database? _database;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
Future<Database> _initDatabase() async {
final path = join(await getDatabasesPath(), 'todo_database.db');
return openDatabase(
path,
onCreate: (db, version) {
return db.execute(
'CREATE TABLE todos(id INTEGER PRIMARY KEY, title TEXT, description TEXT, deadline TEXT, priority TEXT, isCompleted INTEGER)',
);
},
version: 1,
);
}
@override
Future<void> addTask(ToDo todo) async {
final db = await database;
await db.insert(
'todos',
todo.toJson(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
@override
Future<void> removeTask(int index) async {
final db = await database;
await db.delete(
'todos',
where: 'id = ?',
whereArgs: [index],
);
}
@override
Future<void> updateTask(int index, ToDo todo) async {
final db = await database;
await db.update(
'todos',
todo.toJson(),
where: 'id = ?',
whereArgs: [index],
);
}
@override
Future<List<ToDo>> fetchTasks() async {
final db = await database;
final maps = await db.query('todos');
return List.generate(maps.length, (i) {
return ToDo.fromJson(maps[i]);
});
}
}

View File

@ -0,0 +1,8 @@
import '../models/todo.dart';
abstract class ToDoStorage {
Future<void> addTask(ToDo todo);
Future<void> removeTask(int index);
Future<void> updateTask(int index, ToDo todo);
Future<List<ToDo>> fetchTasks();
}

View File

@ -17,11 +17,11 @@ class _ToDoFormState extends State<ToDoForm> {
bool get _isFormValid => _titleController.text.isNotEmpty && _selectedDate != null;
void _submitForm() {
if (!_isFormValid) return; // Ensure the form is valid before submitting
if (!_isFormValid) return;
var newTodo = ToDo(
title: _titleController.text,
description: _descriptionController.text.trim().isEmpty ? '' : _descriptionController.text, // Korrektur hier
description: _descriptionController.text.trim().isEmpty ? '' : _descriptionController.text,
deadline: _selectedDate!,
priority: _selectedPriority,
);
@ -45,10 +45,9 @@ class _ToDoFormState extends State<ToDoForm> {
hintText: 'Enter task title',
),
onChanged: (_) {
setState(() {}); // Trigger UI update when title changes
setState(() {});
},
),
// Description input (optional)
TextField(
controller: _descriptionController,
decoration: InputDecoration(
@ -81,7 +80,7 @@ class _ToDoFormState extends State<ToDoForm> {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Priority'), // Label for Priority
Text('Priority'),
DropdownButton<String>(
value: _selectedPriority,
onChanged: (newValue) {
@ -115,7 +114,7 @@ class _ToDoFormState extends State<ToDoForm> {
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) {
return _isFormValid ? Colors.blue : Colors.grey; // Button enabled only if form is valid
return _isFormValid ? Colors.blue : Colors.grey;
},
),
),

View File

@ -5,8 +5,12 @@
import FlutterMacOS
import Foundation
import path_provider_foundation
import shared_preferences_foundation
import sqflite_darwin
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
}

View File

@ -41,6 +41,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.18.0"
crypto:
dependency: transitive
description:
name: crypto
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
cupertino_icons:
dependency: "direct main"
description:
@ -96,6 +104,22 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
hive:
dependency: "direct main"
description:
name: hive
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
url: "https://pub.dev"
source: hosted
version: "2.2.3"
hive_flutter:
dependency: "direct main"
description:
name: hive_flutter
sha256: dca1da446b1d808a51689fb5d0c6c9510c0a2ba01e22805d492c73b68e33eecc
url: "https://pub.dev"
source: hosted
version: "1.1.0"
leak_tracker:
dependency: transitive
description:
@ -161,13 +185,37 @@ packages:
source: hosted
version: "1.0.0"
path:
dependency: transitive
dependency: "direct main"
description:
name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.9.0"
path_provider:
dependency: transitive
description:
name: path_provider
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
url: "https://pub.dev"
source: hosted
version: "2.1.4"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a
url: "https://pub.dev"
source: hosted
version: "2.2.12"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
url: "https://pub.dev"
source: hosted
version: "2.4.0"
path_provider_linux:
dependency: transitive
description:
@ -285,6 +333,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.0"
sqflite:
dependency: "direct main"
description:
name: sqflite
sha256: "79a297dc3cc137e758c6a4baf83342b039e5a6d2436fcdf3f96a00adaaf2ad62"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "4468b24876d673418a7b7147e5a08a715b4998a7ae69227acafaab762e0e5490"
url: "https://pub.dev"
source: hosted
version: "2.5.4+5"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "769733dddf94622d5541c73e4ddc6aa7b252d865285914b6fcd54a63c4b4f027"
url: "https://pub.dev"
source: hosted
version: "2.4.1-1"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
stack_trace:
dependency: transitive
description:
@ -309,6 +397,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"
url: "https://pub.dev"
source: hosted
version: "3.3.0+3"
term_glyph:
dependency: transitive
description:
@ -325,6 +421,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.2"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vector_math:
dependency: transitive
description:

View File

@ -34,6 +34,12 @@ dependencies:
provider: ^6.0.0
shared_preferences: ^2.0.0
# local storage dependencies
sqflite: ^2.0.0+4
path: ^1.8.0
hive: ^2.0.0
hive_flutter: ^1.1.0
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.