Skip to content

Commit 8e7487a

Browse files
committed
chore: Add getFromStream to have ViewModel properties that automatically update based on Stream sources.
1 parent a2dcecd commit 8e7487a

4 files changed

Lines changed: 34 additions & 3 deletions

File tree

doc/Architecture.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,17 @@ These defined using accessors that call the `get` (or variants such as `getLazy`
121121
Here is an example of a ViewModel showcasing the usage of dynamic properties.
122122
```dart
123123
class HomePageViewModel extends ViewModel {
124-
// Regular property don't trigger rebuild if changed.
124+
// Regular properties don't trigger rebuild if changed.
125125
final String title = 'Flutter Demo Home Page';
126126
127127
// Dynamic properties triggers rebuild if changed.
128128
int get counter => get('counter', 0);
129129
set counter(int value) => set('counter', value);
130130
131+
// Dynamic properties can be made using complex sources, such as streams.
132+
int get autoCounter => getFromStream('autoCounter',
133+
() => Stream.periodic(const Duration(seconds: 1), (i) => i), 0);
134+
131135
List<HomeItemViewModel> get items => getLazy(
132136
'items',
133137
() => [

src/app/lib/presentation/mvvm/mvvm_widget.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class _MvvmWidgetState<TViewModel extends ViewModel>
5555
void dispose() {
5656
super.dispose();
5757
_viewModel.removeListener(onPropertyChanged);
58+
_viewModel.dispose();
5859
}
5960

6061
@override

src/app/lib/presentation/mvvm/view_model.dart

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1+
import 'dart:async';
12
import 'dart:collection';
23

34
import 'package:flutter/foundation.dart';
45

56
abstract class ViewModel extends ChangeNotifier {
67
final Map<String, dynamic> _properties = {};
78
final HashSet<String> _propertiesToNotify = HashSet<String>();
9+
final Map<String, StreamSubscription> _streamSubscriptions = {};
810

911
bool _isRecordingPropertiesToNotify = false;
10-
12+
1113
void startRecordingPropertiesToNotify() {
1214
_propertiesToNotify.clear();
1315
_isRecordingPropertiesToNotify = true;
1416
}
17+
1518
void stopRecordingPropertiesToNotify() {
1619
_isRecordingPropertiesToNotify = false;
1720
}
18-
void _recordPropertyName(String propertyName){
21+
22+
void _recordPropertyName(String propertyName) {
1923
if (_isRecordingPropertiesToNotify) {
2024
_propertiesToNotify.add(propertyName);
2125
}
@@ -41,4 +45,23 @@ abstract class ViewModel extends ChangeNotifier {
4145
_properties[propertyName] = value;
4246
notifyPropertyChanged(propertyName);
4347
}
48+
49+
T getFromStream<T>(String propertyName, Stream<T> Function() getStream, T initialValue) {
50+
if (!_streamSubscriptions.containsKey(propertyName)) {
51+
final subscription = getStream().listen((value) {
52+
set(propertyName, value);
53+
});
54+
_streamSubscriptions[propertyName] = subscription;
55+
}
56+
return get(propertyName, initialValue);
57+
}
58+
59+
@override
60+
void dispose() {
61+
super.dispose();
62+
for (final subscription in _streamSubscriptions.values) {
63+
subscription.cancel();
64+
}
65+
_streamSubscriptions.clear();
66+
}
4467
}

src/cli/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55

66
Prefix your items with `(Template)` if the change is about the template and not the resulting application.
77

8+
## 0.24.1
9+
- Add `getFromStream` to have ViewModel properties that automatically update based on `Stream` sources.
10+
811
## 0.24.0
912
- Replace Riverpod in favor of a custom MVVM recipe with ViewModels to better align with the recommended [app architecture](https://docs.flutter.dev/app-architecture).
1013

0 commit comments

Comments
 (0)