Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: rrousselGit/riverpod
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: riverpod_generator-v2.3.7
Choose a base ref
...
head repository: rrousselGit/riverpod
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: riverpod_generator-v2.3.8
Choose a head ref
  • 5 commits
  • 20 files changed
  • 2 contributors

Commits on Nov 20, 2023

  1. Fix multiple root ProviderScopes

    fixes #3124
    fixes #3118
    josh-burton authored Nov 20, 2023
    Copy the full SHA
    1066baa View commit details
  2. Revert "[DRAFT] adds failing test for multiple provider scopes throwi…

    …ng ‘Only one tas…" (#3139)
    
    Reverts #3117
    rrousselGit authored Nov 20, 2023
    Copy the full SHA
    e31a470 View commit details
  3. Fix multiple root ProviderScopes

    fixes #3124
    fixes #3118
    rrousselGit authored Nov 20, 2023
    Copy the full SHA
    91acc5c View commit details
  4. Release (#3141)

    riverpod            : 2.4.7 -> 2.4.8
    riverpod_annotation : 2.3.1 -> 2.3.2
    riverpod_generator  : 2.3.7 -> 2.3.7
    riverpod_lint       : 2.3.5 -> 2.3.5
    flutter_riverpod    : 2.4.7 -> 2.4.8
    hooks_riverpod      : 2.4.7 -> 2.4.8
    rrousselGit authored Nov 20, 2023
    Copy the full SHA
    cab8aeb View commit details
  5. Fix changelog (#3142)

    rrousselGit authored Nov 20, 2023
    Copy the full SHA
    0234ba5 View commit details
4 changes: 4 additions & 0 deletions packages/flutter_riverpod/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.4.8 - 2023-11-20

Fix exceptions when using multiple root `ProviderContainers`/`ProviderScopes`.

## 2.4.7 - 2023-11-20

- Fix `ProviderObserver.didUpdateProvider` being called with an incorrect
8 changes: 4 additions & 4 deletions packages/flutter_riverpod/lib/src/framework.dart
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ import 'package:meta/meta.dart';

import 'internals.dart';

/// {@template riverpod.providerscope}
/// {@template riverpod.provider_scope}
/// A widget that stores the state of providers.
///
/// All Flutter applications using Riverpod must contain a [ProviderScope] at
@@ -78,7 +78,7 @@ import 'internals.dart';
/// {@endtemplate}
@sealed
class ProviderScope extends StatefulWidget {
/// {@macro riverpod.providerscope}
/// {@macro riverpod.provider_scope}
const ProviderScope({
super.key,
this.overrides = const [],
@@ -305,7 +305,7 @@ class _UncontrolledProviderScopeElement extends InheritedElement {
debugCanModifyProviders ??= _debugCanModifyProviders;
}

flutterVsyncs.add(_flutterVsync);
_containerOf(widget).scheduler.flutterVsyncs.add(_flutterVsync);
super.mount(parent, newSlot);
}

@@ -380,7 +380,7 @@ To fix this problem, you have one of two solutions:
debugCanModifyProviders = null;
}

flutterVsyncs.remove(_flutterVsync);
_containerOf(widget).scheduler.flutterVsyncs.remove(_flutterVsync);

super.unmount();
}
4 changes: 2 additions & 2 deletions packages/flutter_riverpod/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ name: flutter_riverpod
description: >
A simple way to access state from anywhere in your application
while robust and testable.
version: 2.4.7
version: 2.4.8
homepage: https://riverpod.dev
repository: https://github.com/rrousselGit/riverpod
issue_tracker: https://github.com/rrousselGit/riverpod/issues
@@ -18,7 +18,7 @@ dependencies:
flutter:
sdk: flutter
meta: ^1.4.0
riverpod: 2.4.7
riverpod: 2.4.8
state_notifier: ">=0.7.2 <2.0.0"

dev_dependencies:
64 changes: 56 additions & 8 deletions packages/flutter_riverpod/test/framework_test.dart
Original file line number Diff line number Diff line change
@@ -47,6 +47,48 @@ void main() {
);
});

testWidgets('Supports multiple ProviderScope roots in the same tree',
(tester) async {
final a = StateProvider((_) => 0);
final b = Provider((ref) => ref.watch(a));

await tester.pumpWidget(
// No root scope. We want to test cases where there are multiple roots
Column(
mainAxisSize: MainAxisSize.min,
children: [
for (var i = 0; i < 2; i++)
SizedBox(
width: 100,
height: 100,
child: ProviderScope(
child: Consumer(
builder: (context, ref, _) {
ref.watch(a);
ref.watch(b);
return Container();
},
),
),
),
],
),
);

final containers = tester.allElements
.where((e) => e.widget is Consumer)
.map(ProviderScope.containerOf)
.toList();

expect(containers, hasLength(2));

for (final container in containers) {
container.read(a.notifier).state++;
}

await tester.pump();
});

testWidgets('ref.invalidate can invalidate a family', (tester) async {
final listener = Listener<String>();
final listener2 = Listener<String>();
@@ -284,8 +326,9 @@ void main() {
testWidgets('UncontrolledProviderScope gracefully handles vsync',
(tester) async {
final container = createContainer();
final container2 = createContainer(parent: container);

expect(flutterVsyncs, isEmpty);
expect(container.scheduler.flutterVsyncs, isEmpty);

await tester.pumpWidget(
UncontrolledProviderScope(
@@ -294,18 +337,21 @@ void main() {
),
);

expect(flutterVsyncs, hasLength(1));
expect(container.scheduler.flutterVsyncs, hasLength(1));
expect(container2.scheduler.flutterVsyncs, isEmpty);

await tester.pumpWidget(
UncontrolledProviderScope(
container: container,
child: ProviderScope(
child: UncontrolledProviderScope(
container: container2,
child: Container(),
),
),
);

expect(flutterVsyncs, hasLength(2));
expect(container.scheduler.flutterVsyncs, hasLength(1));
expect(container2.scheduler.flutterVsyncs, hasLength(1));

await tester.pumpWidget(
UncontrolledProviderScope(
@@ -314,11 +360,13 @@ void main() {
),
);

expect(flutterVsyncs, hasLength(1));
expect(container.scheduler.flutterVsyncs, hasLength(1));
expect(container2.scheduler.flutterVsyncs, isEmpty);

await tester.pumpWidget(Container());

expect(flutterVsyncs, isEmpty);
expect(container.scheduler.flutterVsyncs, isEmpty);
expect(container2.scheduler.flutterVsyncs, isEmpty);
});

testWidgets('When there are multiple vsyncs, rebuild providers only once',
@@ -625,7 +673,7 @@ void main() {
await tester.pumpWidget(
ProviderScope(
overrides: [
provider.overrideWithValue('rootoverride'),
provider.overrideWithValue('rootOverride'),
],
child: ProviderScope(
child: Consumer(
@@ -643,7 +691,7 @@ void main() {
);

expect(find.text('root root2'), findsNothing);
expect(find.text('rootoverride root2'), findsOneWidget);
expect(find.text('rootOverride root2'), findsOneWidget);
});

testWidgets('ProviderScope throws if ancestorOwner changed', (tester) async {
4 changes: 4 additions & 0 deletions packages/hooks_riverpod/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.4.8 - 2023-11-20

Fix exceptions when using multiple root `ProviderContainers`/`ProviderScopes`.

## 2.4.7 - 2023-11-20

- Fix `ProviderObserver.didUpdateProvider` being called with an incorrect
6 changes: 3 additions & 3 deletions packages/hooks_riverpod/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ name: hooks_riverpod
description: >
A simple way to access state from anywhere in your application
while robust and testable.
version: 2.4.7
version: 2.4.8
homepage: https://riverpod.dev
repository: https://github.com/rrousselGit/riverpod
issue_tracker: https://github.com/rrousselGit/riverpod/issues
@@ -18,8 +18,8 @@ dependencies:
flutter:
sdk: flutter
flutter_hooks: '>=0.18.0 <0.21.0'
flutter_riverpod: 2.4.7
riverpod: 2.4.7
flutter_riverpod: 2.4.8
riverpod: 2.4.8
state_notifier: ">=0.7.2 <2.0.0"

dev_dependencies:
4 changes: 4 additions & 0 deletions packages/riverpod/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.4.8 - 2023-11-20

Fix exceptions when using multiple root `ProviderContainers`/`ProviderScopes`.

## 2.4.7 - 2023-11-20

- Fix `ProviderObserver.didUpdateProvider` being called with an incorrect
3 changes: 1 addition & 2 deletions packages/riverpod/lib/riverpod.dart
Original file line number Diff line number Diff line change
@@ -24,9 +24,8 @@ export 'src/common.dart' hide AsyncTransition;

export 'src/framework.dart'
hide
ProviderScheduler,
debugCanModifyProviders,
vsync,
flutterVsyncs,
Vsync,
ValueProviderElement,
ValueProvider,
2 changes: 1 addition & 1 deletion packages/riverpod/lib/src/framework/auto_dispose.dart
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ mixin AutoDisposeProviderElementMixin<State> on ProviderElementBase<State>

// ignore: deprecated_member_use_from_same_package
if (!maintainState && !hasListeners && (links == null || links.isEmpty)) {
_container._scheduler.scheduleProviderDispose(this);
_container.scheduler.scheduleProviderDispose(this);
}
}

14 changes: 10 additions & 4 deletions packages/riverpod/lib/src/framework/container.dart
Original file line number Diff line number Diff line change
@@ -157,8 +157,8 @@ class ProviderContainer implements Node {
void Function(void Function() task)? vsyncOverride;

/// The object that handles when providers are refreshed and disposed.
late final _ProviderScheduler _scheduler =
_parent?._scheduler ?? _ProviderScheduler();
@internal
late final ProviderScheduler scheduler = ProviderScheduler();

/// How deep this [ProviderContainer] is in the graph of containers.
///
@@ -214,7 +214,13 @@ class ProviderContainer implements Node {

/// Awaits for providers to rebuild/be disposed and for listeners to be notified.
Future<void> pump() async {
return _scheduler.pendingFuture;
final a = scheduler.pendingFuture;
final b = _parent?.scheduler.pendingFuture;

await Future.wait<void>([
if (a != null) a,
if (b != null) b,
]);
}

/// Reads a provider without listening to it and returns the currently
@@ -630,7 +636,7 @@ final b = Provider((ref) => ref.watch(a), dependencies: [a]);
element.dispose();
}

if (_root == null) _scheduler.dispose();
if (_root == null) scheduler.dispose();
}

/// Traverse the [ProviderElementBase]s associated with this [ProviderContainer].
2 changes: 1 addition & 1 deletion packages/riverpod/lib/src/framework/element.dart
Original file line number Diff line number Diff line change
@@ -296,7 +296,7 @@ abstract class ProviderElementBase<State> implements Ref<State>, Node {

_mustRecomputeState = true;
runOnDispose();
_container._scheduler.scheduleProviderRefresh(this);
_container.scheduler.scheduleProviderRefresh(this);

// We don't call this._markDependencyMayHaveChanged here because we voluntarily
// do not want to set the _dependencyMayHaveChanged flag to true.
65 changes: 33 additions & 32 deletions packages/riverpod/lib/src/framework/scheduler.dart
Original file line number Diff line number Diff line change
@@ -5,48 +5,49 @@ part of '../framework.dart';
@internal
typedef Vsync = void Function(void Function());

/// A way to override [vsync], used by Flutter to synchronize a container
/// with the widget tree.
@internal
final flutterVsyncs = <Vsync>{};

void _defaultVsync(void Function() task) {
Future(task);
}

/// A function that controls the refresh rate of providers.
///
/// Defaults to refreshing providers at the end of the next event-loop.
@internal
void Function(void Function()) get vsync {
if (flutterVsyncs.isNotEmpty) {
// Notify all InheritedWidgets of a possible rebuild.
// At the same time, we only execute the task once, in whichever
// InheritedWidget that rebuilds first.
return (task) {
var invoked = false;
void invoke() {
if (invoked) return;
invoked = true;
task();
}

for (final flutterVsync in flutterVsyncs) {
flutterVsync(invoke);
}
};
}

return _defaultVsync;
}

/// The object that handles when providers are refreshed and disposed.
///
/// Providers are typically refreshed at the end of the frame where they
/// notified that they wanted to rebuild.
///
/// Providers are disposed if they spent at least one full frame without any listener.
class _ProviderScheduler {
@internal
class ProviderScheduler {
/// A way to override [vsync], used by Flutter to synchronize a container
/// with the widget tree.
@internal
final flutterVsyncs = <Vsync>{};

/// A function that controls the refresh rate of providers.
///
/// Defaults to refreshing providers at the end of the next event-loop.
@internal
void Function(void Function()) get vsync {
if (flutterVsyncs.isNotEmpty) {
// Notify all InheritedWidgets of a possible rebuild.
// At the same time, we only execute the task once, in whichever
// InheritedWidget that rebuilds first.
return (task) {
var invoked = false;
void invoke() {
if (invoked) return;
invoked = true;
task();
}

for (final flutterVsync in flutterVsyncs) {
flutterVsync(invoke);
}
};
}

return _defaultVsync;
}

final _stateToDispose = <AutoDisposeProviderElementMixin<Object?>>[];
final _stateToRefresh = <ProviderElementBase<Object?>>[];

2 changes: 1 addition & 1 deletion packages/riverpod/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ name: riverpod
description: >
A simple way to access state from anywhere in your application while robust
and testable.
version: 2.4.7
version: 2.4.8
homepage: https://riverpod.dev
repository: https://github.com/rrousselGit/riverpod
issue_tracker: https://github.com/rrousselGit/riverpod/issues
4 changes: 2 additions & 2 deletions packages/riverpod/test/framework/provider_container_test.dart
Original file line number Diff line number Diff line change
@@ -399,7 +399,7 @@ void main() {

group('.pump', () {
test(
'waits for providers to rebuild or get disposed, no matter from which container they are associated in the graph',
'Waits for providers associated with this container and its parents to rebuild',
() async {
final dep = StateProvider((ref) => 0);
final a = Provider((ref) => ref.watch(dep));
@@ -417,7 +417,7 @@ void main() {
verifyOnly(bListener, bListener(null, 0));

root.read(dep.notifier).state++;
await root.pump();
await scoped.pump();

verifyOnly(aListener, aListener(0, 1));
verifyOnly(bListener, bListener(0, 1));
4 changes: 4 additions & 0 deletions packages/riverpod_annotation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.3.2 - 2023-11-20

- `riverpod` upgraded to `2.4.8`

## 2.3.1 - 2023-11-20

- `riverpod` upgraded to `2.4.7`
Loading