Skip to content

Commit

Permalink
chore: use custom behavioursubject instead
Browse files Browse the repository at this point in the history
  • Loading branch information
RobertBrunhage committed Sep 29, 2024
1 parent 80fbc5a commit 15130f2
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 22 deletions.
14 changes: 7 additions & 7 deletions lib/features/todo/todo_page_view_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,15 @@ class TodoPageViewModel {
final ValueNotifier<List<Todo>> todosNotifier = ValueNotifier([]);
final ValueNotifier<bool> showCompletedTodosNotifier = ValueNotifier(false);

StreamSubscription<List<Todo>>? _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;
});
}

Future<void> add({required String title}) async {
Expand All @@ -55,6 +54,7 @@ class TodoPageViewModel {
}

void dispose() {
_todoRepository.removeListener(_onUpdateTodos);
_subscription?.cancel();
_subscription = null;
}
}
64 changes: 50 additions & 14 deletions lib/features/todo/todo_repository.dart
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
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<List<TodoEntity>> _todosNotifier = ValueNotifier([]);
List<TodoEntity> get todos => _todosNotifier.value;
final BehaviorSubject<List<Todo>> _todosController = BehaviorSubject([]);

void addListener(void Function() listener) =>
_todosNotifier.addListener(listener);
void removeListener(void Function() listener) =>
_todosNotifier.removeListener(listener);
Stream<List<Todo>> watch() {
return _todosController.stream;
}

Future<void> addTodo({required String title}) async {
const uuid = Uuid();
final id = uuid.v4();
final todo = TodoEntity(id: id, title: title, completed: false);

_fakeLocalDataSource.add();
_todosNotifier.value = [..._todosNotifier.value, todo];
_todosController.add([..._todosController.value, todo.toTodo()]);
}

Future<void> 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<void> 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 Todo(id: t.id, title: t.title, completed: !t.completed);
}
return t;
}).toList();
_todosNotifier.value = updatedTodos;

_todosController.add(updatedTodos);
}
}

Expand All @@ -53,3 +53,39 @@ class FakeLocalDataSource {

Future<void> update() async {}
}

class BehaviorSubject<T> {
// StreamController with broadcast mode
final StreamController<T> _controller;
T _currentValue;

// Constructor to initialize with an initial value
BehaviorSubject(T initialValue)
: _currentValue = initialValue,
_controller = StreamController<T>.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<T> get stream => _controller.stream;

// Subscribes a listener and immediately emits the current value
StreamSubscription<T> 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<void> close() async {
await _controller.close();
}
}
4 changes: 3 additions & 1 deletion lib/shared/locator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ void setupLocators() {
locator.registerLazySingleton<DateService>(() => DateService());

locator.registerLazySingleton<TodoRepository>(
() => TodoRepository(),
() => TodoRepository(
fakeLocalDataSource: FakeLocalDataSource(),
),
);
}

0 comments on commit 15130f2

Please sign in to comment.