diff --git a/lib/features/todo/todo_page_view_model.dart b/lib/features/todo/todo_page_view_model.dart index 1ed06e2..1d09dbb 100644 --- a/lib/features/todo/todo_page_view_model.dart +++ b/lib/features/todo/todo_page_view_model.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:todo/features/todo/todo.dart'; +import 'package:todo/features/todo/todo_entity.dart'; import 'package:todo/features/todo/todo_repository.dart'; import 'package:todo/shared/date_service.dart'; @@ -22,16 +23,15 @@ class TodoPageViewModel { final ValueNotifier> todosNotifier = ValueNotifier([]); final ValueNotifier showCompletedTodosNotifier = ValueNotifier(false); + StreamSubscription>? _subscription; + bool get hasNonCompletedTodos => todosNotifier.value.where((element) => element.completed).isNotEmpty; void init() { - _todoRepository.addListener(_onUpdateTodos); - } - - void _onUpdateTodos() { - todosNotifier.value = - _todoRepository.todos.map((entity) => entity.toTodo()).toList(); + _subscription = _todoRepository.watch().listen((todos) { + todosNotifier.value = todos.map((entity) => entity.toTodo()).toList(); + }); } Future add({required String title}) async { @@ -55,6 +55,7 @@ class TodoPageViewModel { } void dispose() { - _todoRepository.removeListener(_onUpdateTodos); + _subscription?.cancel(); + _subscription = null; } } diff --git a/lib/features/todo/todo_repository.dart b/lib/features/todo/todo_repository.dart index 51e6e2c..3038e39 100644 --- a/lib/features/todo/todo_repository.dart +++ b/lib/features/todo/todo_repository.dart @@ -1,21 +1,21 @@ -import 'package:flutter/foundation.dart'; +import 'dart:async'; + import 'package:todo/features/todo/todo.dart'; import 'package:todo/features/todo/todo_entity.dart'; import 'package:uuid/uuid.dart'; -// provide a uniform way for services and view models to interact with your data +// Provide a uniform way for services and view models to interact with your data class TodoRepository { TodoRepository({required FakeLocalDataSource fakeLocalDataSource}) : _fakeLocalDataSource = fakeLocalDataSource; final FakeLocalDataSource _fakeLocalDataSource; - final ValueNotifier> _todosNotifier = ValueNotifier([]); - List get todos => _todosNotifier.value; + final BehaviorSubject> _todosController = + BehaviorSubject([]); - void addListener(void Function() listener) => - _todosNotifier.addListener(listener); - void removeListener(void Function() listener) => - _todosNotifier.removeListener(listener); + Stream> watch() { + return _todosController.stream; + } Future addTodo({required String title}) async { const uuid = Uuid(); @@ -23,26 +23,26 @@ class TodoRepository { final todo = TodoEntity(id: id, title: title, completed: false); _fakeLocalDataSource.add(); - _todosNotifier.value = [..._todosNotifier.value, todo]; + _todosController.add([..._todosController.value, todo]); } Future removeTodo(Todo todo) async { _fakeLocalDataSource.remove(); - _todosNotifier.value = - _todosNotifier.value.where((t) => t.id != todo.id).toList(); + _todosController + .add(_todosController.value.where((t) => t.id != todo.id).toList()); } Future toggleDone(Todo todo) async { _fakeLocalDataSource.update(); - final updatedTodos = _todosNotifier.value.map((t) { + final updatedTodos = _todosController.value.map((t) { if (t.id == todo.id) { return TodoEntity(id: t.id, title: t.title, completed: !t.completed); } return t; }).toList(); - _todosNotifier.value = updatedTodos; + _todosController.add(updatedTodos); } } @@ -53,3 +53,39 @@ class FakeLocalDataSource { Future update() async {} } + +class BehaviorSubject { + // StreamController with broadcast mode + final StreamController _controller; + T _currentValue; + + // Constructor to initialize with an initial value + BehaviorSubject(T initialValue) + : _currentValue = initialValue, + _controller = StreamController.broadcast(); + + // Getter for the current value + T get value => _currentValue; + + // Adds a new value and broadcasts it to all listeners + void add(T newValue) { + _currentValue = newValue; + _controller.add(newValue); + } + + // Exposes the stream to listen to changes + Stream get stream => _controller.stream; + + // Subscribes a listener and immediately emits the current value + StreamSubscription listen(void Function(T) onData) { + final subscription = _controller.stream.listen(onData); + // Emit the current value immediately + onData(_currentValue); + return subscription; + } + + // Closes the StreamController when done + Future close() async { + await _controller.close(); + } +} diff --git a/lib/shared/locator.dart b/lib/shared/locator.dart index 6c119c6..e0b3147 100644 --- a/lib/shared/locator.dart +++ b/lib/shared/locator.dart @@ -8,6 +8,8 @@ void setupLocators() { locator.registerLazySingleton(() => DateService()); locator.registerLazySingleton( - () => TodoRepository(), + () => TodoRepository( + fakeLocalDataSource: FakeLocalDataSource(), + ), ); }