From 6031730a580eef328f08186000774e0695ed7499 Mon Sep 17 00:00:00 2001 From: Remi Rousselet Date: Fri, 10 Mar 2023 18:46:05 +0100 Subject: [PATCH] Update docs a bit (#2300) --- packages/riverpod/CHANGELOG.md | 8 ++ packages/riverpod/lib/src/async_notifier.dart | 53 +++++------ .../lib/src/async_notifier/auto_dispose.dart | 8 +- .../async_notifier/auto_dispose_family.dart | 8 +- .../riverpod/lib/src/async_notifier/base.dart | 25 +++++ .../lib/src/async_notifier/family.dart | 8 +- packages/riverpod/lib/src/common.dart | 95 +++++++++---------- .../lib/src/framework/always_alive.dart | 2 +- .../lib/src/framework/async_selector.dart | 2 - .../lib/src/framework/auto_dispose.dart | 2 +- .../riverpod/lib/src/framework/element.dart | 10 +- .../riverpod/lib/src/framework/family.dart | 19 ++-- .../riverpod/lib/src/future_provider.dart | 6 +- packages/riverpod/lib/src/notifier.dart | 1 + .../lib/src/notifier/auto_dispose.dart | 10 +- .../lib/src/notifier/auto_dispose_family.dart | 10 +- packages/riverpod/lib/src/notifier/base.dart | 14 +++ .../riverpod/lib/src/notifier/family.dart | 4 +- .../lib/src/stream_notifier/auto_dispose.dart | 2 +- .../stream_notifier/auto_dispose_family.dart | 2 +- .../lib/src/stream_notifier/base.dart | 25 ++++- .../lib/src/stream_notifier/family.dart | 6 +- 22 files changed, 203 insertions(+), 117 deletions(-) diff --git a/packages/riverpod/CHANGELOG.md b/packages/riverpod/CHANGELOG.md index deed3dcee..5f703a3ff 100644 --- a/packages/riverpod/CHANGELOG.md +++ b/packages/riverpod/CHANGELOG.md @@ -1,3 +1,11 @@ +## Unreleased patch + + +- Deprecated the generic parameter of `Family`. + This will enable implementing generic providers in `riverpod_generator` once + it is removed. +- Updated documentation + ## 2.3.1 - 2023-03-09 - Updated `AsyncValue.value/valueOrNull` docs to cover the "previous value" cases (thanks to @AhmedLSayed9) diff --git a/packages/riverpod/lib/src/async_notifier.dart b/packages/riverpod/lib/src/async_notifier.dart index 0765c22e5..2b7784893 100644 --- a/packages/riverpod/lib/src/async_notifier.dart +++ b/packages/riverpod/lib/src/async_notifier.dart @@ -10,12 +10,12 @@ import 'listenable.dart'; import 'notifier.dart'; import 'pragma.dart'; import 'result.dart'; +import 'stream_provider.dart'; part 'async_notifier/auto_dispose.dart'; part 'async_notifier/auto_dispose_family.dart'; part 'async_notifier/base.dart'; part 'async_notifier/family.dart'; - part 'stream_notifier.dart'; part 'stream_notifier/auto_dispose.dart'; part 'stream_notifier/auto_dispose_family.dart'; @@ -32,13 +32,16 @@ abstract class AsyncNotifierBase { void _setElement(ProviderElementBase> element); - /// The value currently exposed by this [Notifier]. + /// The value currently exposed by this [AsyncNotifier]. + /// + /// Defaults to [AsyncLoading] inside the [AsyncNotifier.build] method. /// /// Invoking the setter will notify listeners if [updateShouldNotify] returns true. - /// By default, this will compare the previous and new value using [identical]. + /// By default, this always notifies listeners (unless going from "loading" + /// to "loading", in which case the change is ignored). /// /// Reading [state] if the provider is out of date (such as if one of its - /// dependency has changed) will trigger [Notifier.build] to be re-executed. + /// dependency has changed) will trigger [AsyncNotifier.build] to be re-executed. @protected AsyncValue get state { _element.flush(); @@ -62,8 +65,8 @@ abstract class AsyncNotifierBase { /// If [state] is modified before [AsyncNotifier.build] completes, then [future] /// will resolve with that new [state] value. /// - /// The future will fail if [AsyncNotifier.build] throws or returns a future - /// that fails. + /// The future will fail if [state] is in error state. In which case the + /// error will be the same as [AsyncValue.error] and its stacktrace. /// {@endtemplate} Future get future { _element.flush(); @@ -73,12 +76,17 @@ abstract class AsyncNotifierBase { /// A function to update [state] from its previous value, while /// abstracting loading/error cases for [state]. /// + /// This method neither causes [state] to go back to "loading" while the + /// operation is pending. Neither does it cause [state] to go to error state + /// if the operation fails. + /// /// If [state] was in error state, the callback will not be invoked and instead /// the error will be returned. Alternatively, [onError] can specified to /// gracefully handle error states. /// /// See also: /// - [future], for manually awaiting the resolution of [state]. + /// - [AsyncValue.guard], and alternate way to perform asynchronous operations. @protected Future update( FutureOr Function(State) cb, { @@ -92,31 +100,22 @@ abstract class AsyncNotifierBase { } /// A method invoked when the state exposed by this [AsyncNotifier] changes. - /// It compares the previous and new value, and return whether listeners - /// should be notified. - /// - /// By default, the previous and new value are compared using [identical] - /// for performance reasons. - /// - /// Doing so ensured that doing: /// - /// ```dart - /// state = const AsyncData(42); - /// state = const AsyncData(42); - /// ``` - /// - /// does not notify listeners twice. - /// - /// But at the same time, for very complex objects with potentially dozens - /// if not hundreds of properties, Riverpod won't deeply compare every single + /// As opposed to with [Notifier.updateShouldNotify], this method + /// does not filter out changes to [state] that are equal to the previous /// value. + /// By default, any change to [state] will emit an update. + /// This method can be overridden to implement custom filtering logic if that + /// is undesired. /// - /// This ensures that the comparison stays efficient for the most common scenarios. - /// But it also means that listeners should be notified even if the - /// previous and new values are considered "equal". + /// The reasoning for this default behavior is that [AsyncNotifier.build] + /// returns a [Future]. As such, the value of [state] typically transitions + /// from "loading" to "data" or "error". In that scenario, the value equality + /// does not matter. Checking `==` would only hinder performances for no reason. /// - /// If you do not want that, you can override this method to perform a deep - /// comparison of the previous and new values. + /// See also: + /// - [ProviderBase.select] and [AsyncSelector.selectAsync], which are + /// alternative ways to filter out changes to [state]. @protected bool updateShouldNotify(AsyncValue previous, AsyncValue next) { return FutureHandlerProviderElementMixin.handleUpdateShouldNotify( diff --git a/packages/riverpod/lib/src/async_notifier/auto_dispose.dart b/packages/riverpod/lib/src/async_notifier/auto_dispose.dart index 95ceab3f8..053f663d2 100644 --- a/packages/riverpod/lib/src/async_notifier/auto_dispose.dart +++ b/packages/riverpod/lib/src/async_notifier/auto_dispose.dart @@ -20,7 +20,9 @@ abstract class BuildlessAutoDisposeAsyncNotifier AutoDisposeAsyncNotifierProviderRef get ref => _element; } -/// {@macro riverpod.asyncnotifier} +/// {@macro riverpod.async_notifier_provider} +/// +/// {@macro riverpod.async_notifier_provider_modifier} abstract class AutoDisposeAsyncNotifier extends BuildlessAutoDisposeAsyncNotifier { /// {@macro riverpod.asyncnotifier.build} @@ -32,7 +34,9 @@ abstract class AutoDisposeAsyncNotifier abstract class AutoDisposeAsyncNotifierProviderRef implements AsyncNotifierProviderRef, AutoDisposeRef> {} -/// {@macro riverpod.asyncnotifier} +/// {@macro riverpod.async_notifier_provider} +/// +/// {@macro riverpod.async_notifier_provider_modifier} typedef AutoDisposeAsyncNotifierProvider< NotifierT extends AutoDisposeAsyncNotifier, T> = AutoDisposeAsyncNotifierProviderImpl; diff --git a/packages/riverpod/lib/src/async_notifier/auto_dispose_family.dart b/packages/riverpod/lib/src/async_notifier/auto_dispose_family.dart index 453977558..9d4255476 100644 --- a/packages/riverpod/lib/src/async_notifier/auto_dispose_family.dart +++ b/packages/riverpod/lib/src/async_notifier/auto_dispose_family.dart @@ -1,6 +1,8 @@ part of '../async_notifier.dart'; -/// {@macro riverpod.asyncnotifier} +/// {@macro riverpod.async_notifier_provider} +/// +/// {@macro riverpod.async_notifier_provider_modifier} abstract class AutoDisposeFamilyAsyncNotifier extends BuildlessAutoDisposeAsyncNotifier { /// {@template riverpod.notifier.family_arg} @@ -17,7 +19,9 @@ abstract class AutoDisposeFamilyAsyncNotifier FutureOr build(Arg arg); } -/// {@macro riverpod.asyncnotifier} +/// {@macro riverpod.async_notifier_provider} +/// +/// {@macro riverpod.async_notifier_provider_modifier} typedef AutoDisposeFamilyAsyncNotifierProvider< NotifierT extends AutoDisposeFamilyAsyncNotifier, T, Arg> = AutoDisposeFamilyAsyncNotifierProviderImpl; diff --git a/packages/riverpod/lib/src/async_notifier/base.dart b/packages/riverpod/lib/src/async_notifier/base.dart index 720608e20..f6f58b35d 100644 --- a/packages/riverpod/lib/src/async_notifier/base.dart +++ b/packages/riverpod/lib/src/async_notifier/base.dart @@ -22,6 +22,9 @@ abstract class BuildlessAsyncNotifier extends AsyncNotifierBase { /// {@template riverpod.asyncnotifier} /// A [Notifier] implementation that is asynchronously initialized. /// +/// This is similar to a [FutureProvider] but allows to perform side-effects +/// by defining public methods. +/// /// It is commonly used for: /// - Caching a network request while also allowing to perform side-effects. /// For example, `build` could fetch information about the current "user". @@ -30,6 +33,8 @@ abstract class BuildlessAsyncNotifier extends AsyncNotifierBase { /// - Initializing a [Notifier] from an asynchronous source of data. /// For example, obtaining the initial state of [Notifier] from a local database. /// {@endtemplate} +/// +/// {@macro riverpod.async_notifier_provider_modifier} // TODO add usage example abstract class AsyncNotifier extends BuildlessAsyncNotifier { /// {@template riverpod.asyncnotifier.build} @@ -53,6 +58,23 @@ abstract class AsyncNotifier extends BuildlessAsyncNotifier { abstract class AsyncNotifierProviderRef implements Ref> {} /// {@template riverpod.async_notifier_provider} +/// A provider which creates and listen to an [AsyncNotifier]. +/// +/// This is similar to [FutureProvider] but allows to perform side-effects. +/// +/// The syntax for using this provider is slightly different from the others +/// in that the provider's function doesn't receive a "ref" (and in case +/// of `family`, doesn't receive an argument either). +/// Instead the ref (and argument) are directly accessible in the associated +/// [AsyncNotifier]. +/// {@endtemplate} +/// +/// {@template riverpod.async_notifier_provider_modifier} +/// When using `autoDispose` or `family`, your notifier type changes. +/// Instead of extending [AsyncNotifier], you should extend either: +/// - [AutoDisposeAsyncNotifier] for `autoDispose` +/// - [FamilyAsyncNotifier] for `family` +/// - [AutoDisposeFamilyAsyncNotifier] for `autoDispose.family` /// {@endtemplate} typedef AsyncNotifierProvider, T> = AsyncNotifierProviderImpl; @@ -68,6 +90,8 @@ class AsyncNotifierProviderImpl, T> extends AsyncNotifierProviderBase with AlwaysAliveProviderBase>, AlwaysAliveAsyncSelector { /// {@macro riverpod.async_notifier_provider} + /// + /// {@macro riverpod.async_notifier_provider_modifier} AsyncNotifierProviderImpl( super._createNotifier, { super.name, @@ -138,6 +162,7 @@ typedef CancelAsyncSubscription = void Function(); /// Mixin to help implement logic for listening to [Future]s/[Stream]s and setup /// `provider.future` + convert the object into an [AsyncValue]. +@internal mixin FutureHandlerProviderElementMixin on ProviderElementBase> { /// A default implementation for [ProviderElementBase.updateShouldNotify]. diff --git a/packages/riverpod/lib/src/async_notifier/family.dart b/packages/riverpod/lib/src/async_notifier/family.dart index 29aed74bb..75442455e 100644 --- a/packages/riverpod/lib/src/async_notifier/family.dart +++ b/packages/riverpod/lib/src/async_notifier/family.dart @@ -1,6 +1,8 @@ part of '../async_notifier.dart'; /// {@macro riverpod.asyncnotifier} +/// +/// {@macro riverpod.async_notifier_provider_modifier} abstract class FamilyAsyncNotifier extends BuildlessAsyncNotifier { /// {@template riverpod.notifier.family_arg} @@ -27,9 +29,9 @@ abstract class FamilyAsyncNotifier FutureOr build(Arg arg); } -/// {@template riverpod.async_notifier_family_provider} -/// The provider for [AsyncNotifierProviderFamily]. -/// {@endtemplate} +/// {@macro riverpod.async_notifier_provider} +/// +/// {@macro riverpod.async_notifier_provider_modifier} typedef AsyncNotifierFamilyProvider< NotifierT extends FamilyAsyncNotifier, T, Arg> = FamilyAsyncNotifierProviderImpl; diff --git a/packages/riverpod/lib/src/common.dart b/packages/riverpod/lib/src/common.dart index eebe5a31b..f4808149f 100644 --- a/packages/riverpod/lib/src/common.dart +++ b/packages/riverpod/lib/src/common.dart @@ -66,20 +66,23 @@ extension AsyncTransition on ProviderElementBase> { /// ``` /// /// If a consumer of an [AsyncValue] does not care about the loading/error -/// state, consider using [value] to read the state: +/// state, consider using [value]/[valueOrNull] to read the state: /// /// ```dart /// Widget build(BuildContext context, WidgetRef ref) { -/// // reads the data state directly – will be throw during loading/error states +/// // Reading .value will be throw during error and return null on "loading" states. /// final User user = ref.watch(userProvider).value; /// -/// return Text('Hello ${user.name}'); +/// // Reading .value will be throw both on loading and errorstates. +/// final User user2 = ref.watch(userProvider).requiredValue; +/// +/// ... /// } /// ``` /// /// See also: /// -/// - [FutureProvider] and [StreamProvider], which transforms a [Future] into +/// - [FutureProvider], [StreamProvider] which transforms a [Future] into /// an [AsyncValue]. /// - [AsyncValue.guard], to simplify transforming a [Future] into an [AsyncValue]. @sealed @@ -87,20 +90,20 @@ extension AsyncTransition on ProviderElementBase> { abstract class AsyncValue { const AsyncValue._(); + /// {@template asyncvalue.data} /// Creates an [AsyncValue] with a data. - /// - /// The data can be `null`. + /// {@endtemplate} // coverage:ignore-start const factory AsyncValue.data(T value) = AsyncData; - // coverage:ignore-end + /// {@template asyncvalue.loading} /// Creates an [AsyncValue] in loading state. /// /// Prefer always using this constructor with the `const` keyword. + /// {@endtemplate} // coverage:ignore-start const factory AsyncValue.loading() = AsyncLoading; - // coverage:ignore-end /// {@template asyncvalue.error_ctor} @@ -116,20 +119,19 @@ abstract class AsyncValue { // coverage:ignore-start const factory AsyncValue.error(Object error, StackTrace stackTrace) = AsyncError; - // coverage:ignore-end /// Transforms a [Future] that may fail into something that is safe to read. /// - /// This is useful to avoid having to do a tedious `try/catch`. Instead of: + /// This is useful to avoid having to do a tedious `try/catch`. Instead of + /// writing: /// /// ```dart - /// class MyNotifier extends StateNotifier { - /// MyNotifier(): super(const AsyncValue.loading()) { - /// _fetchData(); - /// } + /// class MyNotifier extends AsyncNotifier { + /// @override + /// Future build() => Future.value(MyData()); /// - /// Future _fetchData() async { + /// Future sideEffect() async { /// state = const AsyncValue.loading(); /// try { /// final response = await dio.get('my_api/data'); @@ -142,17 +144,14 @@ abstract class AsyncValue { /// } /// ``` /// - /// which is redundant as the application grows and we need more and more of this - /// pattern – we can use [guard] to simplify it: - /// + /// We can use [guard] to simplify it: /// /// ```dart - /// class MyNotifier extends StateNotifier> { - /// MyNotifier(): super(const AsyncValue.loading()) { - /// _fetchData(); - /// } + /// class MyNotifier extends AsyncNotifier { + /// @override + /// Future build() => Future.value(MyData()); /// - /// Future _fetchData() async { + /// Future sideEffect() async { /// state = const AsyncValue.loading(); /// // does the try/catch for us like previously /// state = await AsyncValue.guard(() async { @@ -180,24 +179,23 @@ abstract class AsyncValue { /// /// Even if [hasValue] is true, it is still possible for [isLoading]/[hasError] /// to also be true. - /// - /// [hasValue] correctly supports a null [value]. bool get hasValue; /// The value currently exposed. /// /// It will return the previous value during loading/error state. - /// /// If there is no previous value, reading [value] during loading state will - /// return null. while during error state, the error will be rethrown instead. + /// return null. While during error state, the error will be rethrown instead. /// /// If you do not want to return previous value during loading/error states, - /// consider using [unwrapPrevious] : + /// consider using [asData]: /// /// ```dart - /// ref.watch(provider).unwrapPrevious().value + /// ref.watch(provider).asData()?.value; /// ``` /// + /// This will return null during loading/error states. + /// /// See also [valueOrNull], which does not throw during error state. T? get value; @@ -280,20 +278,15 @@ abstract class AsyncValue { runtimeType, isLoading, hasValue, - // Fallback null values to 0, making sure Object.hash hashes all values - valueOrNull ?? 0, - error ?? 0, - stackTrace ?? 0, + valueOrNull, + error, + stackTrace, ); } -/// Creates an [AsyncValue] with a data. -/// -/// The data can be `null`. +/// {@macro asyncvalue.data} class AsyncData extends AsyncValue { - /// Creates an [AsyncValue] with a data. - /// - /// The data can be `null`. + /// {@macro asyncvalue.data} const AsyncData(T value) : this._( value, @@ -342,13 +335,9 @@ class AsyncData extends AsyncValue { } } -/// Creates an [AsyncValue] in loading state. -/// -/// Prefer always using this constructor with the `const` keyword. +/// {@macro asyncvalue.loading} class AsyncLoading extends AsyncValue { - /// Creates an [AsyncValue] in loading state. - /// - /// Prefer always using this constructor with the `const` keyword. + /// {@macro asyncvalue.loading} const AsyncLoading() : hasValue = false, value = null, @@ -498,8 +487,9 @@ class AsyncError extends AsyncValue { /// An extension that adds methods like [when] to an [AsyncValue]. extension AsyncValueX on AsyncValue { - /// If [hasValue] is true, returns the value. If in error, rethrows the error. - /// Otherwise if in loading state, throws a [StateError]. + /// If [hasValue] is true, returns the value. + /// Otherwise if [hasError], rethrows the error. + /// Finally if in loading state, throws a [StateError]. /// /// This is typically used for when the UI assumes that [value] is always present. T get requireValue { @@ -520,10 +510,10 @@ extension AsyncValueX on AsyncValue { /// This is different from [value], which will rethrow the error instead of returning null. /// /// If you do not want to return previous value during loading/error states, - /// consider using [unwrapPrevious] : + /// consider using [asData] : /// /// ```dart - /// ref.watch(provider).unwrapPrevious().valueOrNull + /// ref.watch(provider).asData()?.valueOrNull; /// ``` T? get valueOrNull { if (hasValue) return value; @@ -531,7 +521,7 @@ extension AsyncValueX on AsyncValue { } /// Whether the associated provider was forced to recompute even though - /// none of its dependencies has changed. + /// none of its dependencies has changed, after at least one [value]/[error] was emitted. /// /// This is usually the case when rebuilding a provider with either /// [Ref.invalidate]/[Ref.refresh]. @@ -544,8 +534,8 @@ extension AsyncValueX on AsyncValue { /// Whether the associated provider was recomputed because of a dependency change /// (using [Ref.watch]), after at least one [value]/[error] was emitted. /// - /// If a provider rebuilds because one of its dependencies changes (using [Ref.watch]), - /// then [isRefreshing] will be false. + /// If a provider rebuilds because one of its dependencies changed (using [Ref.watch]), + /// then [isReloading] will be false. /// /// See also [isRefreshing] for manual provider rebuild. bool get isReloading => (hasValue || hasError) && this is AsyncLoading; @@ -555,6 +545,7 @@ extension AsyncValueX on AsyncValue { /// Even if [hasError] is true, it is still possible for [hasValue]/[isLoading] /// to also be true. // It is safe to check it through `error != null` because `error` is non-nullable + // on the AsyncError constructor. bool get hasError => error != null; /// Upcast [AsyncValue] into an [AsyncData], or return null if the [AsyncValue] diff --git a/packages/riverpod/lib/src/framework/always_alive.dart b/packages/riverpod/lib/src/framework/always_alive.dart index cd951a454..26b3e200c 100644 --- a/packages/riverpod/lib/src/framework/always_alive.dart +++ b/packages/riverpod/lib/src/framework/always_alive.dart @@ -2,7 +2,7 @@ part of '../framework.dart'; /// A base class for all providers, used to consume a provider. /// -/// It is used by [ProviderContainer.listen] and `ref.watch` to listen to +/// It is used by [ProviderContainer.listen] and [Ref.watch] to listen to /// both a provider and `provider.select`. /// /// Do not implement or extend. diff --git a/packages/riverpod/lib/src/framework/async_selector.dart b/packages/riverpod/lib/src/framework/async_selector.dart index 51ae4c06a..cc6f749d8 100644 --- a/packages/riverpod/lib/src/framework/async_selector.dart +++ b/packages/riverpod/lib/src/framework/async_selector.dart @@ -161,7 +161,6 @@ class _AsyncSelector with ProviderListenable> { bool callListeners = true, }) { void onLoading(AsyncValue loading) { - // if (selectedCompleter == null) { if (selectedFuture == null) { // The first time a future is emitted @@ -171,7 +170,6 @@ class _AsyncSelector with ProviderListenable> { // We don't notify listeners when the future changes since // they want to filter rebuilds based on the result - // } } value.map( diff --git a/packages/riverpod/lib/src/framework/auto_dispose.dart b/packages/riverpod/lib/src/framework/auto_dispose.dart index a7ba7fdd1..b9d56d79a 100644 --- a/packages/riverpod/lib/src/framework/auto_dispose.dart +++ b/packages/riverpod/lib/src/framework/auto_dispose.dart @@ -52,7 +52,7 @@ mixin AutoDisposeProviderElementMixin on ProviderElementBase } } -/// A object which maintains a provider alive +/// A object which maintains a provider alive. class KeepAliveLink { KeepAliveLink._(this._close); diff --git a/packages/riverpod/lib/src/framework/element.dart b/packages/riverpod/lib/src/framework/element.dart index a6d8c7a85..9658ec3b4 100644 --- a/packages/riverpod/lib/src/framework/element.dart +++ b/packages/riverpod/lib/src/framework/element.dart @@ -32,11 +32,19 @@ abstract class AlwaysAliveRefreshable @internal void Function()? debugCanModifyProviders; +/// {@template riverpod.provider_element_base} /// An internal class that handles the state of a provider. /// +/// This is what keeps track of the state of a provider, and notifies listeners +/// when the state changes. It is also responsible for rebuilding the provider +/// when once of its dependencies changes. +/// +/// This class is not meant to be used directly and is an implemnetation detail +/// of providers. /// Do not use. +/// {@endtemplate} abstract class ProviderElementBase implements Ref, Node { - /// Do not use. + /// {@macro riverpod.provider_element_base} ProviderElementBase(this._provider); static ProviderElementBase? _debugCurrentlyBuildingElement; diff --git a/packages/riverpod/lib/src/framework/family.dart b/packages/riverpod/lib/src/framework/family.dart index b21edd000..cd75abc38 100644 --- a/packages/riverpod/lib/src/framework/family.dart +++ b/packages/riverpod/lib/src/framework/family.dart @@ -1,6 +1,6 @@ part of '../framework.dart'; -/// A typedef representing the constructor of a provider. +/// A typedef representing the constructor of any classical provider. @internal typedef ProviderCreate = ProviderT Function( @@ -13,7 +13,7 @@ typedef ProviderCreate = ProviderT Object? argument, }); -/// A typedef representing the constructor of a provider. +/// A typedef representing the constructor of a [NotifierProvider]. @internal typedef ProviderNotifierCreate = ProviderT Function( @@ -28,14 +28,16 @@ typedef ProviderNotifierCreate = ProviderT /// A [Create] equivalent used by [Family]. @internal -typedef FamilyCreate = T Function( - R ref, - Arg arg, -); +typedef FamilyCreate = T Function(R ref, Arg arg); /// A base class for all families -abstract class Family - implements FamilyOverride, ProviderOrFamily { +abstract class Family< + @Deprecated( + 'The generic parameter will be removed in version 3.0.0. ' + 'This is to enable riverpod_generator to implement families with generic parameters', +) + // ignore: deprecated_member_use_from_same_package + State> implements FamilyOverride, ProviderOrFamily { /// A base class for all families const Family(); @@ -43,6 +45,7 @@ abstract class Family Family? get from => null; @override + // ignore: deprecated_member_use_from_same_package Family get overriddenFamily => this; } diff --git a/packages/riverpod/lib/src/future_provider.dart b/packages/riverpod/lib/src/future_provider.dart index 3b9feff78..c17f6097c 100644 --- a/packages/riverpod/lib/src/future_provider.dart +++ b/packages/riverpod/lib/src/future_provider.dart @@ -26,7 +26,7 @@ ProviderElementProxy, Future> _future( } /// {@template riverpod.futureprovider} -/// A provider that asynchronously creates a single value. +/// A provider that asynchronously creates a value. /// /// [FutureProvider] can be considered as a combination of [Provider] and /// `FutureBuilder`. @@ -85,6 +85,8 @@ ProviderElementProxy, Future> _future( /// /// See also: /// +/// - [AsyncNotifierProvider], similar to [FutureProvider] but also enables +/// modifying the state from the UI. /// - [Provider], a provider that synchronously creates a value /// - [StreamProvider], a provider that asynchronously exposes a value that /// can change over time. @@ -120,7 +122,7 @@ abstract class _FutureProviderBase extends ProviderBase> { /// return await http.get('${configs.host}/products'); /// }); /// ``` - ProviderListenable> get future; + Refreshable> get future; FutureOr _create(covariant FutureProviderElement ref); } diff --git a/packages/riverpod/lib/src/notifier.dart b/packages/riverpod/lib/src/notifier.dart index be62af04e..3bc9b795d 100644 --- a/packages/riverpod/lib/src/notifier.dart +++ b/packages/riverpod/lib/src/notifier.dart @@ -4,6 +4,7 @@ import 'async_notifier.dart'; import 'builders.dart'; import 'framework.dart'; import 'listenable.dart'; +import 'provider.dart'; import 'result.dart'; part 'notifier/auto_dispose.dart'; diff --git a/packages/riverpod/lib/src/notifier/auto_dispose.dart b/packages/riverpod/lib/src/notifier/auto_dispose.dart index 7da971f83..1da3093b0 100644 --- a/packages/riverpod/lib/src/notifier/auto_dispose.dart +++ b/packages/riverpod/lib/src/notifier/auto_dispose.dart @@ -20,6 +20,8 @@ abstract class BuildlessAutoDisposeNotifier extends NotifierBase { } /// {@template riverpod.notifier} +/// +/// {@macro riverpod.notifier_provider_modifier} abstract class AutoDisposeNotifier extends BuildlessAutoDisposeNotifier { /// {@macro riverpod.asyncnotifier.build} @@ -31,7 +33,9 @@ abstract class AutoDisposeNotifier abstract class AutoDisposeNotifierProviderRef implements NotifierProviderRef, AutoDisposeRef {} -/// {@macro riverpod.notifier} +/// {@macro riverpod.notifier_provider} +/// +/// {@macro riverpod.notifier_provider_modifier} typedef AutoDisposeNotifierProvider, T> = AutoDisposeNotifierProviderImpl; @@ -43,7 +47,9 @@ typedef AutoDisposeNotifierProvider, T> @internal class AutoDisposeNotifierProviderImpl, T> extends NotifierProviderBase { - /// {@macro riverpod.notifier} + /// {@macro riverpod.notifier_provider} + /// + /// {@macro riverpod.notifier_provider_modifier} AutoDisposeNotifierProviderImpl( super._createNotifier, { super.name, diff --git a/packages/riverpod/lib/src/notifier/auto_dispose_family.dart b/packages/riverpod/lib/src/notifier/auto_dispose_family.dart index 02c6f85c3..9ff2976a7 100644 --- a/packages/riverpod/lib/src/notifier/auto_dispose_family.dart +++ b/packages/riverpod/lib/src/notifier/auto_dispose_family.dart @@ -1,6 +1,8 @@ part of '../notifier.dart'; /// {@template riverpod.notifier} +/// +/// {@macro riverpod.notifier_provider_modifier} abstract class AutoDisposeFamilyNotifier extends BuildlessAutoDisposeNotifier { /// {@template riverpod.notifier.family_arg} @@ -17,7 +19,9 @@ abstract class AutoDisposeFamilyNotifier State build(Arg arg); } -/// {@macro riverpod.notifier} +/// {@macro riverpod.notifier_provider} +/// +/// {@macro riverpod.notifier_provider_modifier} typedef AutoDisposeFamilyNotifierProvider< NotifierT extends AutoDisposeFamilyNotifier, T, Arg> = AutoDisposeFamilyNotifierProviderImpl; @@ -30,7 +34,9 @@ typedef AutoDisposeFamilyNotifierProvider< @internal class AutoDisposeFamilyNotifierProviderImpl, T, Arg> extends NotifierProviderBase { - /// {@macro riverpod.notifier} + /// {@macro riverpod.notifier_provider} + /// + /// {@macro riverpod.notifier_provider_modifier} AutoDisposeFamilyNotifierProviderImpl( super._createNotifier, { super.name, diff --git a/packages/riverpod/lib/src/notifier/base.dart b/packages/riverpod/lib/src/notifier/base.dart index 4914a972e..30bbd183c 100644 --- a/packages/riverpod/lib/src/notifier/base.dart +++ b/packages/riverpod/lib/src/notifier/base.dart @@ -64,6 +64,14 @@ abstract class BuildlessNotifier extends NotifierBase { /// The state of [Notifier] is expected to be initialized synchronously. /// For asynchronous initializations, see [AsyncNotifier]. /// {@endtemplate} +/// +/// {@template riverpod.notifier_provider_modifier} +/// When using `autoDispose` or `family`, your notifier type changes. +/// Instead of extending [Notifier], you should extend either: +/// - [AutoDisposeNotifier] for `autoDispose` +/// - [FamilyNotifier] for `family` +/// - [AutoDisposeFamilyNotifier] for `autoDispose.family` +/// {@endtemplate} abstract class Notifier extends BuildlessNotifier { /// {@template riverpod.notifier.build} /// Initialize a [Notifier]. @@ -87,8 +95,12 @@ abstract class NotifierProviderRef implements Ref {} /// {@template riverpod.notifier_provider} /// A Provider which exposes a [Notifier] and listens to it. /// +/// This is equivalent to a [Provider] that exposes ways to modify its state. +/// /// See also [Notifier] for more information. /// {@endtemplate} +/// +/// {@macro riverpod.notifier_provider_modifier} typedef NotifierProvider, T> = NotifierProviderImpl; @@ -101,6 +113,8 @@ typedef NotifierProvider, T> class NotifierProviderImpl, T> extends NotifierProviderBase with AlwaysAliveProviderBase { /// {@macro riverpod.notifier_provider} + /// + /// {@macro riverpod.notifier_provider_modifier} NotifierProviderImpl( super._createNotifier, { super.name, diff --git a/packages/riverpod/lib/src/notifier/family.dart b/packages/riverpod/lib/src/notifier/family.dart index 32bc221e5..9ae4ac516 100644 --- a/packages/riverpod/lib/src/notifier/family.dart +++ b/packages/riverpod/lib/src/notifier/family.dart @@ -1,6 +1,8 @@ part of '../notifier.dart'; -/// {@template riverpod.notifier} +/// {@macro riverpod.notifier} +/// +/// {@macro riverpod.notifier_provider_modifier} abstract class FamilyNotifier extends BuildlessNotifier { /// {@template riverpod.notifier.family_arg} late final Arg arg; diff --git a/packages/riverpod/lib/src/stream_notifier/auto_dispose.dart b/packages/riverpod/lib/src/stream_notifier/auto_dispose.dart index 0346fca33..de681cf17 100644 --- a/packages/riverpod/lib/src/stream_notifier/auto_dispose.dart +++ b/packages/riverpod/lib/src/stream_notifier/auto_dispose.dart @@ -46,7 +46,7 @@ typedef AutoDisposeStreamNotifierProvider< class AutoDisposeStreamNotifierProviderImpl< NotifierT extends AsyncNotifierBase, T> extends StreamNotifierProviderBase with AsyncSelector { - /// {@macro riverpod.notifier} + /// {@macro riverpod.streamNotifier} AutoDisposeStreamNotifierProviderImpl( super._createNotifier, { super.name, diff --git a/packages/riverpod/lib/src/stream_notifier/auto_dispose_family.dart b/packages/riverpod/lib/src/stream_notifier/auto_dispose_family.dart index 05fba015d..843c846af 100644 --- a/packages/riverpod/lib/src/stream_notifier/auto_dispose_family.dart +++ b/packages/riverpod/lib/src/stream_notifier/auto_dispose_family.dart @@ -31,7 +31,7 @@ typedef AutoDisposeFamilyStreamNotifierProvider< class AutoDisposeFamilyStreamNotifierProviderImpl< NotifierT extends AsyncNotifierBase, T, Arg> extends StreamNotifierProviderBase with AsyncSelector { - /// {@macro riverpod.async_notifier_family_provider} + /// {@macro riverpod.streamNotifier} AutoDisposeFamilyStreamNotifierProviderImpl( super._createNotifier, { super.name, diff --git a/packages/riverpod/lib/src/stream_notifier/base.dart b/packages/riverpod/lib/src/stream_notifier/base.dart index 924ba8e9d..d77c78250 100644 --- a/packages/riverpod/lib/src/stream_notifier/base.dart +++ b/packages/riverpod/lib/src/stream_notifier/base.dart @@ -20,8 +20,24 @@ abstract class BuildlessStreamNotifier extends AsyncNotifierBase { } /// {@template riverpod.streamNotifier} -/// A variant of [StreamNotifier] which has [build] creating a [Stream]. -/// {@endtemplate riverpod.streamNotifier} +/// A variant of [AsyncNotifier] which has [build] creating a [Stream]. +/// +/// This can be considered as a [StreamProvider] that can mutate its value over time. +/// +/// The syntax for using this provider is slightly different from the others +/// in that the provider's function doesn't receive a "ref" (and in case +/// of `family`, doesn't receive an argument either). +/// Instead the ref (and argument) are directly accessible in the associated +/// [AsyncNotifier]. +/// +/// This can be considered as a [StreamProvider] that can mutate its value over time. +/// When using `autoDispose` or `family`, your notifier type changes. +/// Instead of extending [StreamNotifier], you should extend either: +/// - [AutoDisposeStreamNotifier] for `autoDispose` +/// - [FamilyStreamNotifier] for `family` +/// - [AutoDisposeFamilyStreamNotifier] for `autoDispose.family` +/// +/// {@endtemplate} abstract class StreamNotifier extends BuildlessStreamNotifier { /// {@template riverpod.asyncnotifier.build} @visibleForOverriding @@ -31,8 +47,7 @@ abstract class StreamNotifier extends BuildlessStreamNotifier { /// {@macro riverpod.providerrefbase} abstract class StreamNotifierProviderRef implements Ref> {} -/// {@template riverpod.async_notifier_provider} -/// {@endtemplate} +/// {@macro riverpod.streamNotifier} typedef StreamNotifierProvider, T> = StreamNotifierProviderImpl; @@ -46,7 +61,7 @@ typedef StreamNotifierProvider, T> class StreamNotifierProviderImpl, T> extends StreamNotifierProviderBase with AlwaysAliveProviderBase>, AlwaysAliveAsyncSelector { - /// {@macro riverpod.async_notifier_provider} + /// {@macro riverpod.streamNotifier} StreamNotifierProviderImpl( super._createNotifier, { super.name, diff --git a/packages/riverpod/lib/src/stream_notifier/family.dart b/packages/riverpod/lib/src/stream_notifier/family.dart index 33b47a6d5..7f21cf8a9 100644 --- a/packages/riverpod/lib/src/stream_notifier/family.dart +++ b/packages/riverpod/lib/src/stream_notifier/family.dart @@ -27,9 +27,7 @@ abstract class FamilyStreamNotifier Stream build(Arg arg); } -/// {@template riverpod.async_notifier_family_provider} -/// The provider for [StreamNotifierProviderFamily]. -/// {@endtemplate} +/// {@macro riverpod.streamNotifier} typedef StreamNotifierFamilyProvider< NotifierT extends FamilyStreamNotifier, T, Arg> = FamilyStreamNotifierProviderImpl; @@ -42,7 +40,7 @@ typedef StreamNotifierFamilyProvider< class FamilyStreamNotifierProviderImpl, T, Arg> extends StreamNotifierProviderBase with AlwaysAliveProviderBase>, AlwaysAliveAsyncSelector { - /// {@macro riverpod.async_notifier_family_provider} + /// {@macro riverpod.streamNotifier} FamilyStreamNotifierProviderImpl( super._createNotifier, { super.name,