Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion doc/Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,17 @@ These defined using accessors that call the `get` (or variants such as `getLazy`
Here is an example of a ViewModel showcasing the usage of dynamic properties.
```dart
class HomePageViewModel extends ViewModel {
// Regular property don't trigger rebuild if changed.
// Regular properties don't trigger rebuild if changed.
final String title = 'Flutter Demo Home Page';

// Dynamic properties triggers rebuild if changed.
int get counter => get('counter', 0);
set counter(int value) => set('counter', value);

// Dynamic properties can be made using complex sources, such as streams.
int get autoCounter => getFromStream('autoCounter',
() => Stream.periodic(const Duration(seconds: 1), (i) => i), 0);

List<HomeItemViewModel> get items => getLazy(
'items',
() => [
Expand Down
1 change: 1 addition & 0 deletions src/app/lib/presentation/mvvm/mvvm_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class _MvvmWidgetState<TViewModel extends ViewModel>
void dispose() {
super.dispose();
_viewModel.removeListener(onPropertyChanged);
_viewModel.dispose();
}

@override
Expand Down
27 changes: 25 additions & 2 deletions src/app/lib/presentation/mvvm/view_model.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import 'dart:async';
import 'dart:collection';

import 'package:flutter/foundation.dart';

abstract class ViewModel extends ChangeNotifier {
final Map<String, dynamic> _properties = {};
final HashSet<String> _propertiesToNotify = HashSet<String>();
final Map<String, StreamSubscription> _streamSubscriptions = {};

bool _isRecordingPropertiesToNotify = false;

void startRecordingPropertiesToNotify() {
_propertiesToNotify.clear();
_isRecordingPropertiesToNotify = true;
}

void stopRecordingPropertiesToNotify() {
_isRecordingPropertiesToNotify = false;
}
void _recordPropertyName(String propertyName){

void _recordPropertyName(String propertyName) {
if (_isRecordingPropertiesToNotify) {
_propertiesToNotify.add(propertyName);
}
Expand All @@ -41,4 +45,23 @@ abstract class ViewModel extends ChangeNotifier {
_properties[propertyName] = value;
notifyPropertyChanged(propertyName);
}

T getFromStream<T>(String propertyName, Stream<T> Function() getStream, T initialValue) {
if (!_streamSubscriptions.containsKey(propertyName)) {
final subscription = getStream().listen((value) {
set(propertyName, value);
});
_streamSubscriptions[propertyName] = subscription;
}
return get(propertyName, initialValue);
}

@override
void dispose() {
super.dispose();
for (final subscription in _streamSubscriptions.values) {
subscription.cancel();
}
_streamSubscriptions.clear();
}
}
3 changes: 3 additions & 0 deletions src/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)

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

## 0.24.1
- Add `getFromStream` to have ViewModel properties that automatically update based on `Stream` sources.

## 0.24.0
- 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).

Expand Down