Skip to content

Commit

Permalink
Merge pull request #142 from kuhnroyal/fix/should-update-model-rebuild
Browse files Browse the repository at this point in the history
Account for existing _forceLastValidStreamState when rebuilding
  • Loading branch information
marcglasberg committed Oct 10, 2022
2 parents d89f87b + 97f0eab commit 0ecedd6
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 1 deletion.
20 changes: 20 additions & 0 deletions .github/workflows/test.yaml
@@ -0,0 +1,20 @@
name: Build

on:
push:
branches:
- master
- develop
pull_request:

jobs:
test:
name: Run tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
channel: stable
- run: flutter pub get
- run: flutter test
8 changes: 7 additions & 1 deletion lib/src/store_connector.dart
Expand Up @@ -378,6 +378,12 @@ class _StoreStreamListenerState<St, Model> //
}

_computeLatestModel();
if (widget.shouldUpdateModel != null) {
// The initial state has to be valid at this point.
// This is needed so that the first stream event
// can be compared against a baseline.
_mostRecentValidState = widget.store.state;
}

if ((widget.onInitialBuild != null) && (_latestModel != null)) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Expand Down Expand Up @@ -417,7 +423,7 @@ class _StoreStreamListenerState<St, Model> //
void _computeLatestModel() {
try {
_latestError = null;
_latestModel = getLatestModel(widget.store.state);
_latestModel = getLatestModel(_forceLastValidStreamState ?? widget.store.state);
} catch (error, stacktrace) {
_latestModel = null;
_latestError = _ConverterError(error, stacktrace, widget.debug);
Expand Down
1 change: 1 addition & 0 deletions test/persistence_test.dart
@@ -1,3 +1,4 @@
@Skip('Requires precise timing')
import 'dart:async';

import "package:async_redux/async_redux.dart";
Expand Down
141 changes: 141 additions & 0 deletions test/store_connector_test.dart
@@ -0,0 +1,141 @@
import "package:async_redux/async_redux.dart";
import "package:flutter/material.dart";
import "package:flutter_test/flutter_test.dart";

void main() {
group(StoreConnector, () {
// TODO shouldUpdateModel does not currently work with converter
testWidgets(
"shouldUpdateModel.converter",
(tester) async {
final storeTester = StoreTester<int>(initialState: 0);

await tester.pumpWidget(StoreProvider<int>(
store: storeTester.store,
child: MaterialApp(
home: StoreConnector<int, int>(
converter: (store) => store.state,
shouldUpdateModel: (state) => state % 2 == 0,
builder: (context, value) {
return Text(value.toString());
},
),
),
));

expect(find.text("0"), findsOneWidget);

await storeTester.dispatchState(1);
await tester.pumpAndSettle();
expect(find.text("0"), findsOneWidget);

await storeTester.dispatchState(2);
await tester.pumpAndSettle();
expect(find.text("2"), findsOneWidget);
},
skip: true,
);

testWidgets(
"shouldUpdateModel.vm",
(tester) async {
final storeTester = StoreTester<int>(initialState: 0);

await tester.pumpWidget(StoreProvider<int>(
store: storeTester.store,
child: _TestWidget(),
));

expect(find.text("0"), findsOneWidget);

await storeTester.dispatchState(1);
await tester.pumpAndSettle();
expect(find.text("0"), findsOneWidget);

await storeTester.dispatchState(2);
await tester.pumpAndSettle();
expect(find.text("2"), findsOneWidget);
},
);

testWidgets(
"shouldUpdateModel.vm with external rebuild",
(tester) async {
final storeTester = StoreTester<int>(initialState: 0);

await tester.pumpWidget(StoreProvider<int>(
store: storeTester.store,
child: _TestWidget(),
));

expect(find.text("0"), findsOneWidget);

storeTester.dispatchState(1);
await tester.pump();
await tester.pump();
expect(find.text("0"), findsOneWidget);

tester.firstState<_TestWidgetState>(find.byType(_TestWidget)).forceRebuild();
await tester.pump();
await tester.pump();
expect(find.text("0"), findsOneWidget);
},
);
});
}

class _TestWidget extends StatefulWidget {
_TestWidget({
Key? key,
}) : super(key: key);

@override
State<_TestWidget> createState() => _TestWidgetState();
}

class _TestWidgetState extends State<_TestWidget> {
void forceRebuild() => setState(() {});

@override
Widget build(BuildContext context) {
return MaterialApp(
home: _TestContent(key: const ValueKey("tester")),
);
}
}

class _TestContent extends StatelessWidget {
_TestContent({
Key? key,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return StoreConnector<int, ViewModel>(
vm: () => Factory(this),
shouldUpdateModel: (state) => state % 2 == 0,
builder: (context, vm) {
return Text(vm.counter.toString());
},
);
}
}

class Factory extends VmFactory<int, _TestContent> {
Factory(widget) : super(widget);

@override
ViewModel fromStore() {
return ViewModel(
counter: state,
);
}
}

class ViewModel extends Vm {
final int counter;

ViewModel({
required this.counter,
}) : super(equals: [counter]);
}

0 comments on commit 0ecedd6

Please sign in to comment.