diff --git a/lib/main.dart b/lib/main.dart index 63b2dba..734a7e5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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, ), diff --git a/lib/models/todo.dart b/lib/models/todo.dart index 46e6163..1683137 100644 --- a/lib/models/todo.dart +++ b/lib/models/todo.dart @@ -2,7 +2,7 @@ class ToDo { String title; String? description; DateTime deadline; - String priority; // High, Medium, Low + String priority; bool isCompleted; ToDo({ diff --git a/lib/providers/todo_provider.dart b/lib/providers/todo_provider.dart index cd18f18..d35743f 100644 --- a/lib/providers/todo_provider.dart +++ b/lib/providers/todo_provider.dart @@ -1,46 +1,53 @@ import 'package:flutter/foundation.dart'; import '../models/todo.dart'; +import '../storage/todo_storage.dart'; class ToDoProvider with ChangeNotifier { List _todos = []; + final ToDoStorage storage; + + ToDoProvider(this.storage) { + loadTasks(); + } List 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 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 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(); } } \ No newline at end of file diff --git a/lib/screens/todo_list_screen.dart b/lib/screens/todo_list_screen.dart index 13e76ec..99ea2f9 100644 --- a/lib/screens/todo_list_screen.dart +++ b/lib/screens/todo_list_screen.dart @@ -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 { + @override + void initState() { + super.initState(); + Provider.of(context, listen: false).loadTasks(); + } + @override Widget build(BuildContext context) { var todoProvider = Provider.of(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( onSelected: (value) { switch (value) { diff --git a/lib/storage/hive_todo_storage.dart b/lib/storage/hive_todo_storage.dart new file mode 100644 index 0000000..7173707 --- /dev/null +++ b/lib/storage/hive_todo_storage.dart @@ -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 get _box async { + return Hive.box(_boxName); + } + + @override + Future addTask(ToDo todo) async { + final box = await _box; + await box.add(todo.toJson()); + } + + @override + Future removeTask(int index) async { + final box = await _box; + await box.deleteAt(index); + } + + @override + Future updateTask(int index, ToDo todo) async { + final box = await _box; + await box.putAt(index, todo.toJson()); + } + + @override + Future> fetchTasks() async { + final box = await _box; + return box.values.map((e) => ToDo.fromJson(Map.from(e))).toList(); + } +} \ No newline at end of file diff --git a/lib/storage/sqlite_todo_storage.dart b/lib/storage/sqlite_todo_storage.dart new file mode 100644 index 0000000..9740db2 --- /dev/null +++ b/lib/storage/sqlite_todo_storage.dart @@ -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 get database async { + if (_database != null) return _database!; + _database = await _initDatabase(); + return _database!; + } + + Future _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 addTask(ToDo todo) async { + final db = await database; + await db.insert( + 'todos', + todo.toJson(), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + } + + @override + Future removeTask(int index) async { + final db = await database; + await db.delete( + 'todos', + where: 'id = ?', + whereArgs: [index], + ); + } + + @override + Future updateTask(int index, ToDo todo) async { + final db = await database; + await db.update( + 'todos', + todo.toJson(), + where: 'id = ?', + whereArgs: [index], + ); + } + + @override + Future> fetchTasks() async { + final db = await database; + final maps = await db.query('todos'); + return List.generate(maps.length, (i) { + return ToDo.fromJson(maps[i]); + }); + } +} \ No newline at end of file diff --git a/lib/storage/todo_storage.dart b/lib/storage/todo_storage.dart new file mode 100644 index 0000000..6b0bfca --- /dev/null +++ b/lib/storage/todo_storage.dart @@ -0,0 +1,8 @@ +import '../models/todo.dart'; + +abstract class ToDoStorage { + Future addTask(ToDo todo); + Future removeTask(int index); + Future updateTask(int index, ToDo todo); + Future> fetchTasks(); +} \ No newline at end of file diff --git a/lib/widgets/todo_form.dart b/lib/widgets/todo_form.dart index 2da456a..e8908b2 100644 --- a/lib/widgets/todo_form.dart +++ b/lib/widgets/todo_form.dart @@ -17,11 +17,11 @@ class _ToDoFormState extends State { 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 { 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 { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Priority'), // Label for Priority + Text('Priority'), DropdownButton( value: _selectedPriority, onChanged: (newValue) { @@ -115,7 +114,7 @@ class _ToDoFormState extends State { style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith( (Set states) { - return _isFormValid ? Colors.blue : Colors.grey; // Button enabled only if form is valid + return _isFormValid ? Colors.blue : Colors.grey; }, ), ), diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 724bb2a..d0e7d18 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -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")) } diff --git a/pubspec.lock b/pubspec.lock index 92652d2..3890d6e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -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: diff --git a/pubspec.yaml b/pubspec.yaml index 2e27aab..62ddf69 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -33,6 +33,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.