added sqlite and hive as local storage options to todo-app
parent
017fd647b1
commit
4757d2975f
|
@ -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,
|
||||
),
|
||||
|
|
|
@ -2,7 +2,7 @@ class ToDo {
|
|||
String title;
|
||||
String? description;
|
||||
DateTime deadline;
|
||||
String priority; // High, Medium, Low
|
||||
String priority;
|
||||
bool isCompleted;
|
||||
|
||||
ToDo({
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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]);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
|
|
106
pubspec.lock
106
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:
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue