Skip to content

Commit f2de358

Browse files
authored
Match dev for notifier modifier (#4063)
1 parent db76a7c commit f2de358

File tree

44 files changed

+855
-115
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+855
-115
lines changed

.github/workflows/changelog.yml

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Check CHANGELOG.md
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
- dev
8+
pull_request:
9+
10+
11+
jobs:
12+
changelog:
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- uses: actions/[email protected]
17+
with:
18+
fetch-depth: 2
19+
- uses: subosito/[email protected]
20+
with:
21+
channel: master
22+
23+
- name: Add pub cache bin to PATH
24+
run: echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH
25+
- name: Add pub cache to PATH
26+
run: echo "PUB_CACHE="$HOME/.pub-cache"" >> $GITHUB_ENV
27+
28+
- name: Install "semantic_changelog"
29+
run: dart pub global activate -s git https://github.com/rrousselGit/semantic_changelog
30+
31+
- run: git fetch origin master:refs/remotes/origin/master
32+
- run: semantic_changelog check origin/master

examples/pub/lib/detail.g.dart

+14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/riverpod/CHANGELOG.md

+19-10
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,34 @@
11
## Unreleased minor
22

3+
### Previews: New code-gen-free syntax for using providers
4+
5+
An experimental syntax has been added, available by importing `package:riverpod/experimental/...`.
6+
Anything inside the `riverpod/experimental` is not stable and may change without a major version.
7+
8+
TL;DR
9+
10+
- Unified syntax for defining providers
11+
- Mutations support
12+
- Reworked scoping mechanism
13+
14+
### New features:
15+
316
- Added a test-only `ProviderContainer.test`.
417
This automatically disposes the `ProviderContainer` after tests end.
18+
19+
### Depreciation notices
20+
521
- In 3.0.0, `AsyncValue.value` will be removed and `valueOrNull` will be
622
renamed to `value`.
723
To match this, both are deprecated, and a temporary `AsyncValue.value2` is added.
824
- `StateNotifierProvider/StateProvider` and relevant APIs are considered deprecated.
925
To keep using them, import `package:riverpod/legacy.dart'`.
10-
- Added various experimental features:
11-
- A new syntax for providers
12-
- Mutations
13-
- A revamped scoping mechanism
14-
**Warning**:
15-
Those features are **experimental**.
16-
They may be removed or modified in ways that do not respect Semantic Versioning.
17-
Use with caution.
26+
1827
- Added `ProviderListenableTransformer` to easily make custom `ProviderListenable` extensions
1928

20-
## Unreleased patch
29+
### Misc
2130

22-
- Upgrade to Dart 3.6.0
31+
- Upgraded to Dart 3.6.0
2332
- Marked various APIs as `@internal`
2433

2534
## 2.6.1 - 2024-10-22

packages/riverpod/lib/src/core/element.dart

+8-1
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ The provider ${_debugCurrentlyBuildingElement!.origin} modified $origin while bu
797797
}
798798

799799
@override
800-
void listenSelf(
800+
void Function() listenSelf(
801801
void Function(StateT? previous, StateT next) listener, {
802802
void Function(Object error, StackTrace stackTrace)? onError,
803803
}) {
@@ -811,6 +811,13 @@ The provider ${_debugCurrentlyBuildingElement!.origin} modified $origin while bu
811811
_onErrorSelfListeners ??= [];
812812
_onErrorSelfListeners!.add(onError);
813813
}
814+
815+
return () {
816+
_onChangeSelfListeners?.remove(listener);
817+
if (onError != null) {
818+
_onErrorSelfListeners?.remove(onError);
819+
}
820+
};
814821
}
815822

816823
/// Returns the currently exposed by a provider
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
part of '../../framework.dart';
2+
3+
/// An error thrown if a Notifier is associated multiple times with a provider.
4+
@internal
5+
const alreadyInitializedError = '''
6+
A NotifierProvider returned a Notifier instance that is already associated
7+
with another provider.
8+
9+
To fix, do not reuse the same Notifier instance multiple times.
10+
NotifierProviders are expected to always create a new Notifier instance.
11+
''';
12+
13+
/// The error message for when a notifier is used when uninitialized.
14+
@internal
15+
const uninitializedElementError = '''
16+
Tried to use a notifier in an uninitialized state.
17+
This means that you tried to either:
18+
- Use ref/state inside the constructor of a notifier.
19+
In this case you should move your logic inside the "build" method instead.
20+
- Use ref/state after the notifier was disposed.
21+
In this case, consider using `ref.onDispose` earlier in your notifier's lifecycle
22+
to abort any pending logic that could try to use `ref/state`.
23+
''';
24+
25+
/// The prototype of `Notifier.build` overrides.
26+
@internal
27+
typedef RunNotifierBuild<NotifierT, CreatedT> = CreatedT Function(
28+
Ref ref,
29+
NotifierT notifier,
30+
);
31+
32+
/// A base class for all "notifiers".
33+
///
34+
/// This is a good interface to target for writing mixins for Notifiers.
35+
///
36+
/// To perform logic before/after the `build` method of a notifier, you can override
37+
/// [runBuild]:
38+
///
39+
/// ```dart
40+
/// mixin MyMixin<T> extends AnyNotifier<T, FutureOr<T>> {
41+
/// @override
42+
/// FutureOr<User> runBuild() {
43+
/// // It is safe to use "ref" here.
44+
/// ref.listenSelf((prev, next) => print("New state $next"));
45+
///
46+
/// // Before
47+
/// final result = super.runBuild();
48+
/// // After
49+
/// return result;
50+
/// }
51+
/// }
52+
/// ```
53+
mixin AnyNotifier<StateT> {
54+
ProviderElementBase<StateT>? _ref;
55+
56+
/// Documented in subclass
57+
@protected
58+
Ref<StateT> get ref => $ref;
59+
60+
/// Listens to changes on the value exposed by this provider.
61+
///
62+
/// The listener will be called immediately after the provider completes building.
63+
///
64+
/// As opposed to [Ref.listen], the listener will be called even if
65+
/// [updateShouldNotify] returns false, meaning that the previous
66+
/// and new value can potentially be identical.
67+
///
68+
/// Returns a function which can be called to remove the listener.
69+
@protected
70+
void Function() listenSelf(
71+
void Function(StateT? previous, StateT next) listener, {
72+
void Function(Object error, StackTrace stackTrace)? onError,
73+
}) {
74+
return $ref.listenSelf(listener, onError: onError);
75+
}
76+
77+
/// Documented in subclass
78+
@visibleForTesting
79+
@protected
80+
StateT get state;
81+
82+
@visibleForTesting
83+
@protected
84+
set state(StateT newState);
85+
86+
/// Documented in subclass
87+
@visibleForOverriding
88+
bool updateShouldNotify(StateT previous, StateT next);
89+
90+
/// Internal, do not use.
91+
@internal
92+
void runBuild();
93+
}
94+
95+
@internal
96+
abstract class $AsyncNotifierBase<ValueT>
97+
with AnyNotifier<AsyncValue<ValueT>> {}
98+
99+
@internal
100+
abstract class $SyncNotifierBase<StateT> with AnyNotifier<StateT> {}
101+
102+
@internal
103+
extension ClassBaseX<StateT> on AnyNotifier<StateT> {
104+
$ClassProviderElement<AnyNotifier<StateT>, StateT, Object?, Object?>?
105+
element() => _ref as $ClassProviderElement<AnyNotifier<StateT>, StateT,
106+
Object?, Object?>?;
107+
108+
@internal
109+
// ignore: library_private_types_in_public_api, not public
110+
Ref<StateT> get $ref {
111+
final ref = _ref;
112+
if (ref == null) throw StateError(uninitializedElementError);
113+
114+
return ref;
115+
}
116+
}
117+
118+
/// Implementation detail of `riverpod_generator`.
119+
/// Do not use.
120+
@internal
121+
abstract base class $ClassProvider< //
122+
NotifierT extends AnyNotifier<StateT>,
123+
StateT,
124+
ValueT,
125+
CreatedT> extends ProviderBase<StateT> {
126+
const $ClassProvider({
127+
required super.name,
128+
required super.from,
129+
required super.argument,
130+
required super.dependencies,
131+
required super.allTransitiveDependencies,
132+
required this.runNotifierBuildOverride,
133+
required super.debugGetCreateSourceHash,
134+
});
135+
136+
Refreshable<NotifierT> get notifier {
137+
return ProviderElementProxy<StateT, NotifierT>(
138+
this,
139+
(element) => (element
140+
as $ClassProviderElement<NotifierT, StateT, ValueT, CreatedT>)
141+
.classListenable,
142+
);
143+
}
144+
145+
@internal
146+
final RunNotifierBuild<NotifierT, CreatedT>? runNotifierBuildOverride;
147+
148+
@internal
149+
NotifierT create();
150+
151+
@visibleForOverriding
152+
$ClassProvider<NotifierT, StateT, ValueT, CreatedT> $copyWithCreate(
153+
NotifierT Function() create,
154+
);
155+
156+
@visibleForOverriding
157+
$ClassProvider<NotifierT, StateT, ValueT, CreatedT> $copyWithBuild(
158+
RunNotifierBuild<NotifierT, CreatedT> build,
159+
);
160+
161+
/// {@macro riverpod.override_with}
162+
Override overrideWith(NotifierT Function() create) {
163+
return ProviderOverride(
164+
origin: this,
165+
override: $copyWithCreate(create),
166+
);
167+
}
168+
169+
/// {@template riverpod.override_with_build}
170+
/// Hello world
171+
/// {@endtemplate}
172+
Override overrideWithBuild(
173+
RunNotifierBuild<NotifierT, CreatedT> build,
174+
) {
175+
return ProviderOverride(
176+
origin: this,
177+
override: $copyWithBuild(build),
178+
);
179+
}
180+
181+
@override
182+
$ClassProviderElement<NotifierT, StateT, ValueT, CreatedT> createElement();
183+
}
184+
185+
@internal
186+
abstract class $ClassProviderElement< //
187+
NotifierT extends AnyNotifier<StateT>,
188+
StateT,
189+
ValueT,
190+
CreatedT> //
191+
extends ProviderElementBase<StateT> {
192+
$ClassProviderElement(super._provider);
193+
194+
@override
195+
$ClassProvider<NotifierT, StateT, ValueT, CreatedT> get provider;
196+
197+
final classListenable = $ElementLense<NotifierT>();
198+
199+
@mustCallSuper
200+
@override
201+
void create({required bool didChangeDependency}) {
202+
final result = classListenable.result ??= $Result.guard(() {
203+
final notifier = provider.create();
204+
if (notifier._ref != null) {
205+
throw StateError(alreadyInitializedError);
206+
}
207+
208+
notifier._ref = this;
209+
return notifier;
210+
});
211+
212+
switch (result) {
213+
case $ResultData():
214+
try {
215+
if (provider.runNotifierBuildOverride case final override?) {
216+
final created = override(this, result.value);
217+
handleValue(this, created);
218+
} else {
219+
result.value.runBuild();
220+
}
221+
} catch (err, stack) {
222+
handleError(this, err, stack);
223+
}
224+
case $ResultError():
225+
handleError(this, result.error, result.stackTrace);
226+
}
227+
}
228+
229+
void handleValue(Ref<StateT> ref, CreatedT created);
230+
void handleError(Ref<StateT> ref, Object error, StackTrace stackTrace);
231+
232+
@override
233+
bool updateShouldNotify(StateT previous, StateT next) {
234+
return classListenable.result?.value?.updateShouldNotify(previous, next) ??
235+
true;
236+
}
237+
238+
@override
239+
void visitListenables(
240+
void Function($ElementLense element) listenableVisitor,
241+
) {
242+
super.visitListenables(listenableVisitor);
243+
listenableVisitor(classListenable);
244+
}
245+
}

packages/riverpod/lib/src/core/ref.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ abstract class Ref<State extends Object?> {
9090
/// As opposed to [listen], the listener will be called even if
9191
/// `updateShouldNotify` returns false, meaning that the previous
9292
/// and new value can potentially be identical.
93-
void listenSelf(
93+
void Function() listenSelf(
9494
void Function(State? previous, State next) listener, {
9595
void Function(Object error, StackTrace stackTrace)? onError,
9696
});

packages/riverpod/lib/src/framework.dart

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ part 'core/modifiers/select_async.dart';
2121
part 'core/override_with_value.dart';
2222
part 'core/provider/provider.dart';
2323
part 'core/provider/functional_provider.dart';
24+
part 'core/provider/notifier_provider.dart';
2425
part 'core/provider_container.dart';
2526
part 'core/proxy_provider_listenable.dart';
2627
part 'core/ref.dart';

0 commit comments

Comments
 (0)