diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 000000000..e5b6d8d6a --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,8 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works +with multi-package repos, or single-package repos to help you version and publish your code. You can +find the full documentation for it [in our repository](https://github.com/changesets/changesets) + +We have a quick list of common questions to get you started engaging with this project in +[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 000000000..91b6a9512 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "access": "restricted", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 531e31864..79f67570d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,13 +5,6 @@ on: paths-ignore: - "**.md" - "**.mdx" - push: - branches: - - master - - dev - paths-ignore: - - "**.md" - - "**.mdx" schedule: # runs the CI everyday at 10AM @@ -44,8 +37,6 @@ jobs: - packages/hooks_riverpod - packages/hooks_riverpod/example - packages/riverpod_annotation - # TODO(rrousselGit) update riverpod_cli test setup to be supported by the CI - # - packages/riverpod_cli - packages/riverpod_generator - packages/riverpod_generator/integration/build_yaml # TODO(rrousselGit) update riverpod_graph test setup to be supported by the CI diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml deleted file mode 100644 index a2284df5c..000000000 --- a/.github/workflows/changelog.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Check CHANGELOG.md - -on: - push: - branches: - - master - - dev - pull_request: - - -jobs: - changelog: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3.1.0 - with: - fetch-depth: 2 - - uses: subosito/flutter-action@v2.7.1 - with: - channel: master - - - name: Add pub cache bin to PATH - run: echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH - - name: Add pub cache to PATH - run: echo "PUB_CACHE="$HOME/.pub-cache"" >> $GITHUB_ENV - - - name: Install "semantic_changelog" - run: dart pub global activate -s git https://github.com/rrousselGit/semantic_changelog - - - run: git fetch origin master:refs/remotes/origin/master - - run: semantic_changelog check origin/master \ No newline at end of file diff --git a/.github/workflows/check_generation.yml b/.github/workflows/check_generation.yml index 27f133e58..41fd6e53f 100644 --- a/.github/workflows/check_generation.yml +++ b/.github/workflows/check_generation.yml @@ -2,6 +2,8 @@ name: Check code-generation on: pull_request: + paths-ignore: + - "**.md" push: branches: - master diff --git a/.github/workflows/riverpod_lint.yml b/.github/workflows/riverpod_lint.yml index 4c357c70b..2bee84581 100644 --- a/.github/workflows/riverpod_lint.yml +++ b/.github/workflows/riverpod_lint.yml @@ -4,12 +4,6 @@ on: pull_request: paths-ignore: - "**.md" - push: - branches: - - master - - dev - paths-ignore: - - "**.md" schedule: # runs the CI everyday at 10AM - cron: "0 10 * * *" diff --git a/analysis_options.yaml b/analysis_options.yaml index 5fa2c6604..53072d418 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -18,6 +18,9 @@ analyzer: linter: rules: + # Not an issue if using dartfmt + no_adjacent_strings_in_list: false + # False positive for custom enum-like classes (such as Flutter's "Colors") avoid_classes_with_only_static_members: false @@ -26,9 +29,6 @@ linter: # Low value and lacks a quick fix combinators_ordering: false - - # Low value and high cost to change on all files - eol_at_end_of_file: false # Conflicts with unused variables no_leading_underscores_for_local_identifiers: false @@ -88,6 +88,3 @@ linter: # conflicts with `prefer_relative_imports` always_use_package_imports: false - - # Disabled for now until we have NNBD as it otherwise conflicts with `missing_return` - no_default_cases: false diff --git a/benchmarks/pubspec.yaml b/benchmarks/pubspec.yaml index 701e2aafc..c35a63676 100644 --- a/benchmarks/pubspec.yaml +++ b/benchmarks/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.12.0-0 <4.0.0" + sdk: ">=3.0.0<4.0.0" dependencies: flutter: diff --git a/examples/counter/lib/main.dart b/examples/counter/lib/main.dart index da3b7410e..db7c29ef1 100644 --- a/examples/counter/lib/main.dart +++ b/examples/counter/lib/main.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unreachable_from_main, https://github.com/dart-lang/linter/issues/4652 + import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; diff --git a/examples/counter/lib/main.g.dart b/examples/counter/lib/main.g.dart index a2a619160..de01a3856 100644 --- a/examples/counter/lib/main.g.dart +++ b/examples/counter/lib/main.g.dart @@ -6,24 +6,86 @@ part of 'main.dart'; // RiverpodGenerator // ************************************************************************** -String _$counterHash() => r'4243b34530f53accfd9014a9f0e316fe304ada3e'; - /// Annotating a class by `@riverpod` defines a new shared state for your application, /// accessible using the generated [counterProvider]. /// This class is both responsible for initializing the state (through the [build] method) /// and exposing ways to modify it (cf [increment]). -/// -/// Copied from [Counter]. @ProviderFor(Counter) -final counterProvider = AutoDisposeNotifierProvider.internal( - Counter.new, - name: r'counterProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$counterHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Counter = AutoDisposeNotifier; +const counterProvider = CounterProvider._(); + +/// Annotating a class by `@riverpod` defines a new shared state for your application, +/// accessible using the generated [counterProvider]. +/// This class is both responsible for initializing the state (through the [build] method) +/// and exposing ways to modify it (cf [increment]). +final class CounterProvider extends $NotifierProvider { + /// Annotating a class by `@riverpod` defines a new shared state for your application, + /// accessible using the generated [counterProvider]. + /// This class is both responsible for initializing the state (through the [build] method) + /// and exposing ways to modify it (cf [increment]). + const CounterProvider._( + {super.runNotifierBuildOverride, Counter Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counterProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Counter Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$counterHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Counter create() => _createCb?.call() ?? Counter(); + + @$internal + @override + CounterProvider $copyWithCreate( + Counter Function() create, + ) { + return CounterProvider._(create: create); + } + + @$internal + @override + CounterProvider $copyWithBuild( + int Function( + Ref, + Counter, + ) build, + ) { + return CounterProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$counterHash() => r'4243b34530f53accfd9014a9f0e316fe304ada3e'; + +abstract class _$Counter extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/examples/counter/pubspec_overrides.yaml b/examples/counter/pubspec_overrides.yaml index d2e1fda40..b03880da2 100644 --- a/examples/counter/pubspec_overrides.yaml +++ b/examples/counter/pubspec_overrides.yaml @@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: flutter_riverpod,hooks_riverpod,riverpod,riverpod_lint,riverpod_analyzer_utils +# melos_managed_dependency_overrides: flutter_riverpod,hooks_riverpod,riverpod,riverpod_analyzer_utils,riverpod_lint dependency_overrides: flutter_riverpod: path: ../../packages/flutter_riverpod @@ -8,9 +8,12 @@ dependency_overrides: path: ../../packages/riverpod riverpod_analyzer_utils: path: ../../packages/riverpod_analyzer_utils - riverpod_lint: - path: ../../packages/riverpod_lint riverpod_annotation: path: ../../packages/riverpod_annotation riverpod_generator: path: ../../packages/riverpod_generator + riverpod_lint: + path: ../../packages/riverpod_lint + + # For flutter_test + test_api: ^0.7.4 diff --git a/examples/marvel/analysis_options.yaml b/examples/marvel/analysis_options.yaml index 0b3205522..fd37ac1ff 100644 --- a/examples/marvel/analysis_options.yaml +++ b/examples/marvel/analysis_options.yaml @@ -6,7 +6,3 @@ analyzer: errors: # Some assets are voluntarily gitignored asset_does_not_exist: ignore - # I prefer specifying a parameter on a widget even if they are unused (such as Key) - # for the sake of consistency. - unused_element_parameter: false - unrecognized_error_code: ignore diff --git a/examples/marvel/lib/src/widgets/search_bar.dart b/examples/marvel/lib/src/widgets/search_bar.dart index 5a1be1940..a78989098 100644 --- a/examples/marvel/lib/src/widgets/search_bar.dart +++ b/examples/marvel/lib/src/widgets/search_bar.dart @@ -6,18 +6,17 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../screens/home.dart'; +const _height = 300.0; + class _SearchTheme { const _SearchTheme({ required this.width, - // ignore: unused_element, blocked by https://github.com/dart-lang/linter/issues/3232 - this.height = 300, required this.searchDecoration, required this.iconPadding, required this.searchMargin, }); final double width; - final double height; final BoxDecoration searchDecoration; final EdgeInsets iconPadding; final EdgeInsets searchMargin; @@ -187,7 +186,7 @@ class _SearchHintContainer extends StatelessWidget { Widget build(BuildContext context) { return Container( constraints: BoxConstraints( - maxHeight: theme.height, + maxHeight: _height, ), margin: theme.searchMargin, child: Material( @@ -196,7 +195,7 @@ class _SearchHintContainer extends StatelessWidget { clipBehavior: Clip.hardEdge, child: OverflowBox( alignment: Alignment.topLeft, - maxHeight: theme.height, + maxHeight: _height, maxWidth: 300, child: child, ), diff --git a/examples/marvel/pubspec_overrides.yaml b/examples/marvel/pubspec_overrides.yaml index d2e1fda40..b03880da2 100644 --- a/examples/marvel/pubspec_overrides.yaml +++ b/examples/marvel/pubspec_overrides.yaml @@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: flutter_riverpod,hooks_riverpod,riverpod,riverpod_lint,riverpod_analyzer_utils +# melos_managed_dependency_overrides: flutter_riverpod,hooks_riverpod,riverpod,riverpod_analyzer_utils,riverpod_lint dependency_overrides: flutter_riverpod: path: ../../packages/flutter_riverpod @@ -8,9 +8,12 @@ dependency_overrides: path: ../../packages/riverpod riverpod_analyzer_utils: path: ../../packages/riverpod_analyzer_utils - riverpod_lint: - path: ../../packages/riverpod_lint riverpod_annotation: path: ../../packages/riverpod_annotation riverpod_generator: path: ../../packages/riverpod_generator + riverpod_lint: + path: ../../packages/riverpod_lint + + # For flutter_test + test_api: ^0.7.4 diff --git a/examples/pub/lib/detail.dart b/examples/pub/lib/detail.dart index 628497436..c1b15ff97 100644 --- a/examples/pub/lib/detail.dart +++ b/examples/pub/lib/detail.dart @@ -106,7 +106,7 @@ class PackageDetailPage extends HookConsumerWidget { ref.watch(fetchPackageDetailsProvider(packageName: packageName)); final likedPackages = ref.watch(likedPackagesProvider); - final isLiked = likedPackages.valueOrNull?.contains(packageName) ?? false; + final isLiked = likedPackages.value?.contains(packageName) ?? false; final pendingToggleLike = useState?>(null); final toggleLikeSnapshot = useFuture(pendingToggleLike.value); diff --git a/examples/pub/lib/detail.g.dart b/examples/pub/lib/detail.g.dart index 403bf4184..634aec783 100644 --- a/examples/pub/lib/detail.g.dart +++ b/examples/pub/lib/detail.g.dart @@ -8,375 +8,421 @@ part of 'detail.dart'; // RiverpodGenerator // ************************************************************************** -String _$fetchPackageDetailsHash() => - r'16ad07d6f69412f6d456c6d482f15dc53421df74'; +@ProviderFor(fetchPackageDetails) +const fetchPackageDetailsProvider = FetchPackageDetailsFamily._(); + +final class FetchPackageDetailsProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const FetchPackageDetailsProvider._( + {required FetchPackageDetailsFamily super.from, + required String super.argument, + FutureOr Function( + Ref ref, { + required String packageName, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'fetchPackageDetailsProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); + final FutureOr Function( + Ref ref, { + required String packageName, + })? _createCb; - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } + @override + String debugGetCreateSourceHash() => _$fetchPackageDetailsHash(); - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + @override + String toString() { + return r'fetchPackageDetailsProvider' + '' + '($argument)'; } -} - -/// See also [fetchPackageDetails]. -@ProviderFor(fetchPackageDetails) -const fetchPackageDetailsProvider = FetchPackageDetailsFamily(); -/// See also [fetchPackageDetails]. -class FetchPackageDetailsFamily extends Family> { - /// See also [fetchPackageDetails]. - const FetchPackageDetailsFamily(); - - /// See also [fetchPackageDetails]. - FetchPackageDetailsProvider call({ - required String packageName, - }) { - return FetchPackageDetailsProvider( - packageName: packageName, - ); - } + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); @override - FetchPackageDetailsProvider getProviderOverride( - covariant FetchPackageDetailsProvider provider, + FetchPackageDetailsProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, ) { - return call( - packageName: provider.packageName, - ); + return FetchPackageDetailsProvider._( + argument: argument as String, + from: from! as FetchPackageDetailsFamily, + create: ( + ref, { + required String packageName, + }) => + create(ref)); } - static const Iterable? _dependencies = null; - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; + FutureOr create(Ref ref) { + final _$cb = _createCb ?? fetchPackageDetails; + final argument = this.argument as String; + return _$cb( + ref, + packageName: argument, + ); + } @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + bool operator ==(Object other) { + return other is FetchPackageDetailsProvider && other.argument == argument; + } @override - String? get name => r'fetchPackageDetailsProvider'; + int get hashCode { + return argument.hashCode; + } } -/// See also [fetchPackageDetails]. -class FetchPackageDetailsProvider extends AutoDisposeFutureProvider { - /// See also [fetchPackageDetails]. - FetchPackageDetailsProvider({ - required String packageName, - }) : this._internal( - (ref) => fetchPackageDetails( - ref as FetchPackageDetailsRef, - packageName: packageName, - ), - from: fetchPackageDetailsProvider, +String _$fetchPackageDetailsHash() => + r'16ad07d6f69412f6d456c6d482f15dc53421df74'; + +final class FetchPackageDetailsFamily extends Family { + const FetchPackageDetailsFamily._() + : super( + retry: null, name: r'fetchPackageDetailsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$fetchPackageDetailsHash, - dependencies: FetchPackageDetailsFamily._dependencies, - allTransitiveDependencies: - FetchPackageDetailsFamily._allTransitiveDependencies, - packageName: packageName, + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, ); - FetchPackageDetailsProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.packageName, - }) : super.internal(); + FetchPackageDetailsProvider call({ + required String packageName, + }) => + FetchPackageDetailsProvider._(argument: packageName, from: this); - final String packageName; + @override + String debugGetCreateSourceHash() => _$fetchPackageDetailsHash(); @override + String toString() => r'fetchPackageDetailsProvider'; + + /// {@macro riverpod.override_with} Override overrideWith( - FutureOr Function(FetchPackageDetailsRef provider) create, + FutureOr Function( + Ref ref, + String args, + ) create, ) { - return ProviderOverride( - origin: this, - override: FetchPackageDetailsProvider._internal( - (ref) => create(ref as FetchPackageDetailsRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - packageName: packageName, - ), + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FetchPackageDetailsProvider; + + final argument = provider.argument as String; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, ); } +} + +@ProviderFor(likedPackages) +const likedPackagesProvider = LikedPackagesProvider._(); + +final class LikedPackagesProvider extends $FunctionalProvider< + AsyncValue>, FutureOr>> + with $FutureModifier>, $FutureProvider> { + const LikedPackagesProvider._( + {FutureOr> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'likedPackagesProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr> Function( + Ref ref, + )? _createCb; @override - AutoDisposeFutureProviderElement createElement() { - return _FetchPackageDetailsProviderElement(this); - } + String debugGetCreateSourceHash() => _$likedPackagesHash(); + @$internal @override - bool operator ==(Object other) { - return other is FetchPackageDetailsProvider && - other.packageName == packageName; - } + $FutureProviderElement> $createElement( + $ProviderPointer pointer) => + $FutureProviderElement(this, pointer); @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, packageName.hashCode); + LikedPackagesProvider $copyWithCreate( + FutureOr> Function( + Ref ref, + ) create, + ) { + return LikedPackagesProvider._(create: create); + } - return _SystemHash.finish(hash); + @override + FutureOr> create(Ref ref) { + final _$cb = _createCb ?? likedPackages; + return _$cb(ref); } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FetchPackageDetailsRef on AutoDisposeFutureProviderRef { - /// The parameter `packageName` of this provider. - String get packageName; -} +String _$likedPackagesHash() => r'8debee8d8fa48334d1de21fa9bbf03224265d29d'; -class _FetchPackageDetailsProviderElement - extends AutoDisposeFutureProviderElement - with FetchPackageDetailsRef { - _FetchPackageDetailsProviderElement(super.provider); +@ProviderFor(pubRepository) +const pubRepositoryProvider = PubRepositoryProvider._(); + +final class PubRepositoryProvider + extends $FunctionalProvider + with $Provider { + const PubRepositoryProvider._( + {PubRepository Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'pubRepositoryProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - @override - String get packageName => (origin as FetchPackageDetailsProvider).packageName; -} + final PubRepository Function( + Ref ref, + )? _createCb; -String _$likedPackagesHash() => r'8debee8d8fa48334d1de21fa9bbf03224265d29d'; + @override + String debugGetCreateSourceHash() => _$pubRepositoryHash(); -/// See also [likedPackages]. -@ProviderFor(likedPackages) -final likedPackagesProvider = AutoDisposeFutureProvider>.internal( - likedPackages, - name: r'likedPackagesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$likedPackagesHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef LikedPackagesRef = AutoDisposeFutureProviderRef>; -String _$pubRepositoryHash() => r'fd358feb202d2c34ad507ebf0a40bddbebc8ea98'; + /// {@macro riverpod.override_with_value} + Override overrideWithValue(PubRepository value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } -/// See also [pubRepository]. -@ProviderFor(pubRepository) -final pubRepositoryProvider = AutoDisposeProvider.internal( - pubRepository, - name: r'pubRepositoryProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$pubRepositoryHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef PubRepositoryRef = AutoDisposeProviderRef; -String _$packageMetricsHash() => r'67cd25e50357e6e970d432c1d255085a23b856ac'; + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); -abstract class _$PackageMetrics - extends BuildlessAutoDisposeAsyncNotifier { - late final String packageName; + @override + PubRepositoryProvider $copyWithCreate( + PubRepository Function( + Ref ref, + ) create, + ) { + return PubRepositoryProvider._(create: create); + } - FutureOr build({ - required String packageName, - }); + @override + PubRepository create(Ref ref) { + final _$cb = _createCb ?? pubRepository; + return _$cb(ref); + } } +String _$pubRepositoryHash() => r'fd358feb202d2c34ad507ebf0a40bddbebc8ea98'; + /// A provider that fetches the likes count, popularity score and pub points /// for a given package. /// /// It also exposes utilities to like/unlike a package, assuming the user /// is logged-in. -/// -/// Copied from [PackageMetrics]. @ProviderFor(PackageMetrics) -const packageMetricsProvider = PackageMetricsFamily(); +const packageMetricsProvider = PackageMetricsFamily._(); /// A provider that fetches the likes count, popularity score and pub points /// for a given package. /// /// It also exposes utilities to like/unlike a package, assuming the user /// is logged-in. -/// -/// Copied from [PackageMetrics]. -class PackageMetricsFamily extends Family> { +final class PackageMetricsProvider + extends $AsyncNotifierProvider { /// A provider that fetches the likes count, popularity score and pub points /// for a given package. /// /// It also exposes utilities to like/unlike a package, assuming the user /// is logged-in. - /// - /// Copied from [PackageMetrics]. - const PackageMetricsFamily(); + const PackageMetricsProvider._( + {required PackageMetricsFamily super.from, + required String super.argument, + super.runNotifierBuildOverride, + PackageMetrics Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'packageMetricsProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// A provider that fetches the likes count, popularity score and pub points - /// for a given package. - /// - /// It also exposes utilities to like/unlike a package, assuming the user - /// is logged-in. - /// - /// Copied from [PackageMetrics]. - PackageMetricsProvider call({ - required String packageName, - }) { - return PackageMetricsProvider( - packageName: packageName, - ); + final PackageMetrics Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$packageMetricsHash(); + + @override + String toString() { + return r'packageMetricsProvider' + '' + '($argument)'; } + @$internal + @override + PackageMetrics create() => _createCb?.call() ?? PackageMetrics(); + + @$internal @override - PackageMetricsProvider getProviderOverride( - covariant PackageMetricsProvider provider, + PackageMetricsProvider $copyWithCreate( + PackageMetrics Function() create, ) { - return call( - packageName: provider.packageName, - ); + return PackageMetricsProvider._( + argument: argument as String, + from: from! as PackageMetricsFamily, + create: create); } - static const Iterable? _dependencies = null; - + @$internal @override - Iterable? get dependencies => _dependencies; + PackageMetricsProvider $copyWithBuild( + FutureOr Function( + Ref, + PackageMetrics, + ) build, + ) { + return PackageMetricsProvider._( + argument: argument as String, + from: from! as PackageMetricsFamily, + runNotifierBuildOverride: build); + } - static const Iterable? _allTransitiveDependencies = null; + @$internal + @override + $AsyncNotifierProviderElement + $createElement($ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + bool operator ==(Object other) { + return other is PackageMetricsProvider && other.argument == argument; + } @override - String? get name => r'packageMetricsProvider'; + int get hashCode { + return argument.hashCode; + } } +String _$packageMetricsHash() => r'67cd25e50357e6e970d432c1d255085a23b856ac'; + /// A provider that fetches the likes count, popularity score and pub points /// for a given package. /// /// It also exposes utilities to like/unlike a package, assuming the user /// is logged-in. -/// -/// Copied from [PackageMetrics]. -class PackageMetricsProvider extends AutoDisposeAsyncNotifierProviderImpl< - PackageMetrics, PackageMetricsScore> { +final class PackageMetricsFamily extends Family { + const PackageMetricsFamily._() + : super( + retry: null, + name: r'packageMetricsProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + /// A provider that fetches the likes count, popularity score and pub points /// for a given package. /// /// It also exposes utilities to like/unlike a package, assuming the user /// is logged-in. - /// - /// Copied from [PackageMetrics]. - PackageMetricsProvider({ + PackageMetricsProvider call({ required String packageName, - }) : this._internal( - () => PackageMetrics()..packageName = packageName, - from: packageMetricsProvider, - name: r'packageMetricsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$packageMetricsHash, - dependencies: PackageMetricsFamily._dependencies, - allTransitiveDependencies: - PackageMetricsFamily._allTransitiveDependencies, - packageName: packageName, - ); - - PackageMetricsProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.packageName, - }) : super.internal(); + }) => + PackageMetricsProvider._(argument: packageName, from: this); - final String packageName; + @override + String debugGetCreateSourceHash() => _$packageMetricsHash(); @override - FutureOr runNotifierBuild( - covariant PackageMetrics notifier, + String toString() => r'packageMetricsProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + PackageMetrics Function( + String args, + ) create, ) { - return notifier.build( - packageName: packageName, - ); - } + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as PackageMetricsProvider; - @override - Override overrideWith(PackageMetrics Function() create) { - return ProviderOverride( - origin: this, - override: PackageMetricsProvider._internal( - () => create()..packageName = packageName, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - packageName: packageName, - ), + final argument = provider.argument as String; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, ); } - @override - AutoDisposeAsyncNotifierProviderElement - createElement() { - return _PackageMetricsProviderElement(this); - } + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + FutureOr Function( + Ref ref, PackageMetrics notifier, String argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as PackageMetricsProvider; - @override - bool operator ==(Object other) { - return other is PackageMetricsProvider && other.packageName == packageName; - } + final argument = provider.argument as String; - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, packageName.hashCode); - - return _SystemHash.finish(hash); + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin PackageMetricsRef - on AutoDisposeAsyncNotifierProviderRef { - /// The parameter `packageName` of this provider. - String get packageName; -} - -class _PackageMetricsProviderElement - extends AutoDisposeAsyncNotifierProviderElement with PackageMetricsRef { - _PackageMetricsProviderElement(super.provider); +abstract class _$PackageMetrics extends $AsyncNotifier { + late final _$args = ref.$arg as String; + String get packageName => _$args; + FutureOr build({ + required String packageName, + }); + @$internal @override - String get packageName => (origin as PackageMetricsProvider).packageName; + FutureOr runBuild() => build( + packageName: _$args, + ); } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/examples/pub/lib/search.g.dart b/examples/pub/lib/search.g.dart index f64dd2a38..571727cdf 100644 --- a/examples/pub/lib/search.g.dart +++ b/examples/pub/lib/search.g.dart @@ -8,173 +8,153 @@ part of 'search.dart'; // RiverpodGenerator // ************************************************************************** -String _$fetchPackagesHash() => r'b52d4beb5d9ac53769d76ccd1d81bb005c66edd5'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// See also [fetchPackages]. @ProviderFor(fetchPackages) -const fetchPackagesProvider = FetchPackagesFamily(); - -/// See also [fetchPackages]. -class FetchPackagesFamily extends Family>> { - /// See also [fetchPackages]. - const FetchPackagesFamily(); +const fetchPackagesProvider = FetchPackagesFamily._(); + +final class FetchPackagesProvider extends $FunctionalProvider< + AsyncValue>, FutureOr>> + with $FutureModifier>, $FutureProvider> { + const FetchPackagesProvider._( + {required FetchPackagesFamily super.from, + required ({ + int page, + String search, + }) + super.argument, + FutureOr> Function( + Ref ref, { + required int page, + String search, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'fetchPackagesProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// See also [fetchPackages]. - FetchPackagesProvider call({ + final FutureOr> Function( + Ref ref, { required int page, - String search = '', - }) { - return FetchPackagesProvider( - page: page, - search: search, - ); - } + String search, + })? _createCb; @override - FetchPackagesProvider getProviderOverride( - covariant FetchPackagesProvider provider, - ) { - return call( - page: provider.page, - search: provider.search, - ); - } - - static const Iterable? _dependencies = null; + String debugGetCreateSourceHash() => _$fetchPackagesHash(); @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + String toString() { + return r'fetchPackagesProvider' + '' + '$argument'; + } + @$internal @override - String? get name => r'fetchPackagesProvider'; -} - -/// See also [fetchPackages]. -class FetchPackagesProvider extends AutoDisposeFutureProvider> { - /// See also [fetchPackages]. - FetchPackagesProvider({ - required int page, - String search = '', - }) : this._internal( - (ref) => fetchPackages( - ref as FetchPackagesRef, - page: page, - search: search, - ), - from: fetchPackagesProvider, - name: r'fetchPackagesProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$fetchPackagesHash, - dependencies: FetchPackagesFamily._dependencies, - allTransitiveDependencies: - FetchPackagesFamily._allTransitiveDependencies, - page: page, - search: search, - ); - - FetchPackagesProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.page, - required this.search, - }) : super.internal(); - - final int page; - final String search; + $FutureProviderElement> $createElement( + $ProviderPointer pointer) => + $FutureProviderElement(this, pointer); @override - Override overrideWith( - FutureOr> Function(FetchPackagesRef provider) create, + FetchPackagesProvider $copyWithCreate( + FutureOr> Function( + Ref ref, + ) create, ) { - return ProviderOverride( - origin: this, - override: FetchPackagesProvider._internal( - (ref) => create(ref as FetchPackagesRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - page: page, - search: search, - ), - ); + return FetchPackagesProvider._( + argument: argument as ({ + int page, + String search, + }), + from: from! as FetchPackagesFamily, + create: ( + ref, { + required int page, + String search = '', + }) => + create(ref)); } @override - AutoDisposeFutureProviderElement> createElement() { - return _FetchPackagesProviderElement(this); + FutureOr> create(Ref ref) { + final _$cb = _createCb ?? fetchPackages; + final argument = this.argument as ({ + int page, + String search, + }); + return _$cb( + ref, + page: argument.page, + search: argument.search, + ); } @override bool operator ==(Object other) { - return other is FetchPackagesProvider && - other.page == page && - other.search == search; + return other is FetchPackagesProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, page.hashCode); - hash = _SystemHash.combine(hash, search.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FetchPackagesRef on AutoDisposeFutureProviderRef> { - /// The parameter `page` of this provider. - int get page; +String _$fetchPackagesHash() => r'b52d4beb5d9ac53769d76ccd1d81bb005c66edd5'; - /// The parameter `search` of this provider. - String get search; -} +final class FetchPackagesFamily extends Family { + const FetchPackagesFamily._() + : super( + retry: null, + name: r'fetchPackagesProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); -class _FetchPackagesProviderElement - extends AutoDisposeFutureProviderElement> - with FetchPackagesRef { - _FetchPackagesProviderElement(super.provider); + FetchPackagesProvider call({ + required int page, + String search = '', + }) => + FetchPackagesProvider._(argument: ( + page: page, + search: search, + ), from: this); @override - int get page => (origin as FetchPackagesProvider).page; + String debugGetCreateSourceHash() => _$fetchPackagesHash(); + @override - String get search => (origin as FetchPackagesProvider).search; + String toString() => r'fetchPackagesProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + FutureOr> Function( + Ref ref, + ({ + int page, + String search, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FetchPackagesProvider; + + final argument = provider.argument as ({ + int page, + String search, + }); + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/examples/pub/pubspec_overrides.yaml b/examples/pub/pubspec_overrides.yaml index d2e1fda40..b03880da2 100644 --- a/examples/pub/pubspec_overrides.yaml +++ b/examples/pub/pubspec_overrides.yaml @@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: flutter_riverpod,hooks_riverpod,riverpod,riverpod_lint,riverpod_analyzer_utils +# melos_managed_dependency_overrides: flutter_riverpod,hooks_riverpod,riverpod,riverpod_analyzer_utils,riverpod_lint dependency_overrides: flutter_riverpod: path: ../../packages/flutter_riverpod @@ -8,9 +8,12 @@ dependency_overrides: path: ../../packages/riverpod riverpod_analyzer_utils: path: ../../packages/riverpod_analyzer_utils - riverpod_lint: - path: ../../packages/riverpod_lint riverpod_annotation: path: ../../packages/riverpod_annotation riverpod_generator: path: ../../packages/riverpod_generator + riverpod_lint: + path: ../../packages/riverpod_lint + + # For flutter_test + test_api: ^0.7.4 diff --git a/examples/random_number/pubspec_overrides.yaml b/examples/random_number/pubspec_overrides.yaml index 74695a9c8..020e1f784 100644 --- a/examples/random_number/pubspec_overrides.yaml +++ b/examples/random_number/pubspec_overrides.yaml @@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: flutter_riverpod,riverpod,riverpod_lint,riverpod_analyzer_utils +# melos_managed_dependency_overrides: flutter_riverpod,riverpod,riverpod_analyzer_utils,riverpod_lint dependency_overrides: flutter_riverpod: path: ../../packages/flutter_riverpod @@ -6,9 +6,12 @@ dependency_overrides: path: ../../packages/riverpod riverpod_analyzer_utils: path: ../../packages/riverpod_analyzer_utils - riverpod_lint: - path: ../../packages/riverpod_lint riverpod_annotation: path: ../../packages/riverpod_annotation riverpod_generator: path: ../../packages/riverpod_generator + riverpod_lint: + path: ../../packages/riverpod_lint + + # For flutter_test + test_api: ^0.7.4 diff --git a/examples/stackoverflow/lib/common.dart b/examples/stackoverflow/lib/common.dart index e69985910..fa9380eb9 100644 --- a/examples/stackoverflow/lib/common.dart +++ b/examples/stackoverflow/lib/common.dart @@ -2,6 +2,9 @@ import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'common.g.dart'; final client = Provider((ref) => Dio()); @@ -9,12 +12,15 @@ final client = Provider((ref) => Dio()); /// /// This is unimplemented by default, and will be overridden inside [MaterialApp] /// with the current theme obtained using a [BuildContext]. -final themeProvider = Provider( - (ref) => throw UnimplementedError(), +@Riverpod( // Specifying an empty "dependencies" signals riverpod_lint that this provider - // is scoped. - dependencies: const [], -); + // is scoped. This enables catching places where we need to override + // this provider. + dependencies: [], +) +ThemeData theme(Ref ref) { + throw UnimplementedError(); +} class TimestampParser implements JsonConverter { const TimestampParser(); diff --git a/examples/stackoverflow/lib/common.g.dart b/examples/stackoverflow/lib/common.g.dart new file mode 100644 index 000000000..21effce3f --- /dev/null +++ b/examples/stackoverflow/lib/common.g.dart @@ -0,0 +1,82 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: non_constant_identifier_names, require_trailing_commas + +part of 'common.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +/// A Provider that exposes the current theme. +/// +/// This is unimplemented by default, and will be overridden inside [MaterialApp] +/// with the current theme obtained using a [BuildContext]. +@ProviderFor(theme) +const themeProvider = ThemeProvider._(); + +/// A Provider that exposes the current theme. +/// +/// This is unimplemented by default, and will be overridden inside [MaterialApp] +/// with the current theme obtained using a [BuildContext]. +final class ThemeProvider extends $FunctionalProvider + with $Provider { + /// A Provider that exposes the current theme. + /// + /// This is unimplemented by default, and will be overridden inside [MaterialApp] + /// with the current theme obtained using a [BuildContext]. + const ThemeProvider._( + {ThemeData Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'themeProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final ThemeData Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$themeHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(ThemeData value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ThemeProvider $copyWithCreate( + ThemeData Function( + Ref ref, + ) create, + ) { + return ThemeProvider._(create: create); + } + + @override + ThemeData create(Ref ref) { + final _$cb = _createCb ?? theme; + return _$cb(ref); + } +} + +String _$themeHash() => r'0fea6438c8bee8be98515c10e8e67c2e75c6af46'; + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/examples/stackoverflow/lib/home.dart b/examples/stackoverflow/lib/home.dart index d04df09df..fdfe12b5f 100644 --- a/examples/stackoverflow/lib/home.dart +++ b/examples/stackoverflow/lib/home.dart @@ -2,9 +2,12 @@ import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'question.dart'; +import 'tag.dart'; +@Dependencies([tagTheme]) class MyHomePage extends HookConsumerWidget { const MyHomePage({super.key}); @@ -41,7 +44,7 @@ class MyHomePage extends HookConsumerWidget { itemBuilder: (context, index) { return ProviderScope( overrides: [ - currentQuestion.overrideWithValue( + currentQuestionProvider.overrideWithValue( ref .watch(paginatedQuestionsProvider(index ~/ 50)) .whenData((page) => page.items[index % 50]), diff --git a/examples/stackoverflow/lib/main.dart b/examples/stackoverflow/lib/main.dart index 64e01ed3d..74094df6c 100644 --- a/examples/stackoverflow/lib/main.dart +++ b/examples/stackoverflow/lib/main.dart @@ -25,13 +25,12 @@ class MyApp extends StatelessWidget { /// current theme, without having a BuildContext. themeProvider.overrideWithValue(theme), ], - child: ListTileTheme( - textColor: const Color(0xFFe7e8eb), - child: child!, + child: const ListTileTheme( + textColor: Color(0xFFe7e8eb), + child: MyHomePage(), ), ); }, - home: const MyHomePage(), ); } } diff --git a/examples/stackoverflow/lib/question.dart b/examples/stackoverflow/lib/question.dart index a6a036f9f..a44bd0fda 100644 --- a/examples/stackoverflow/lib/question.dart +++ b/examples/stackoverflow/lib/question.dart @@ -6,6 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:html/parser.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'common.dart'; import 'tag.dart'; @@ -122,20 +123,22 @@ final questionThemeProvider = Provider((ref) { /// /// This is an optional step. Since scoping is a fairly advanced mechanism, /// it's entirely fine to simply pass the [Question] to [QuestionItem] directly. -final currentQuestion = Provider>((ref) { +@Riverpod(dependencies: []) +AsyncValue currentQuestion(Ref ref) { throw UnimplementedError(); -}); +} /// A UI widget rendering a [Question]. /// /// That question will be obtained through [currentQuestion]. As such, it is /// necessary to override that provider before using [QuestionItem]. +@Dependencies([currentQuestion, tagTheme]) class QuestionItem extends HookConsumerWidget { const QuestionItem({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final question = ref.watch(currentQuestion); + final question = ref.watch(currentQuestionProvider); final questionTheme = ref.watch(questionThemeProvider); return question.when( diff --git a/examples/stackoverflow/lib/question.g.dart b/examples/stackoverflow/lib/question.g.dart index 039da7180..11f440f5d 100644 --- a/examples/stackoverflow/lib/question.g.dart +++ b/examples/stackoverflow/lib/question.g.dart @@ -58,3 +58,106 @@ Map _$$QuestionImplToJson(_$QuestionImpl instance) => 'title': instance.title, 'body': instance.body, }; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +/// A scoped provider, exposing the current question used by [QuestionItem]. +/// +/// This is used as a performance optimization to pass a [Question] to +/// [QuestionItem], while still instantiating [QuestionItem] using the `const` +/// keyword. +/// +/// This allows [QuestionItem] to rebuild less often. +/// By doing so, even when using [QuestionItem] in a [ListView], even if new +/// questions are obtained, previously rendered [QuestionItem]s won't rebuild. +/// +/// This is an optional step. Since scoping is a fairly advanced mechanism, +/// it's entirely fine to simply pass the [Question] to [QuestionItem] directly. +@ProviderFor(currentQuestion) +const currentQuestionProvider = CurrentQuestionProvider._(); + +/// A scoped provider, exposing the current question used by [QuestionItem]. +/// +/// This is used as a performance optimization to pass a [Question] to +/// [QuestionItem], while still instantiating [QuestionItem] using the `const` +/// keyword. +/// +/// This allows [QuestionItem] to rebuild less often. +/// By doing so, even when using [QuestionItem] in a [ListView], even if new +/// questions are obtained, previously rendered [QuestionItem]s won't rebuild. +/// +/// This is an optional step. Since scoping is a fairly advanced mechanism, +/// it's entirely fine to simply pass the [Question] to [QuestionItem] directly. +final class CurrentQuestionProvider + extends $FunctionalProvider, AsyncValue> + with $Provider> { + /// A scoped provider, exposing the current question used by [QuestionItem]. + /// + /// This is used as a performance optimization to pass a [Question] to + /// [QuestionItem], while still instantiating [QuestionItem] using the `const` + /// keyword. + /// + /// This allows [QuestionItem] to rebuild less often. + /// By doing so, even when using [QuestionItem] in a [ListView], even if new + /// questions are obtained, previously rendered [QuestionItem]s won't rebuild. + /// + /// This is an optional step. Since scoping is a fairly advanced mechanism, + /// it's entirely fine to simply pass the [Question] to [QuestionItem] directly. + const CurrentQuestionProvider._( + {AsyncValue Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'currentQuestionProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final AsyncValue Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$currentQuestionHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(AsyncValue value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + $ProviderElement> $createElement( + $ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CurrentQuestionProvider $copyWithCreate( + AsyncValue Function( + Ref ref, + ) create, + ) { + return CurrentQuestionProvider._(create: create); + } + + @override + AsyncValue create(Ref ref) { + final _$cb = _createCb ?? currentQuestion; + return _$cb(ref); + } +} + +String _$currentQuestionHash() => r'e9359841a5b980cd7b8c79a0b56cb98878190861'; + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/examples/stackoverflow/lib/tag.dart b/examples/stackoverflow/lib/tag.dart index 7aac44caa..d73659011 100644 --- a/examples/stackoverflow/lib/tag.dart +++ b/examples/stackoverflow/lib/tag.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'common.dart'; +part 'tag.g.dart'; part 'tag.freezed.dart'; @freezed @@ -16,25 +18,24 @@ class TagTheme with _$TagTheme { }) = _TagTheme; } -final tagThemeProvider = Provider( - (ref) { - final theme = ref.watch(themeProvider); - - return TagTheme( - padding: EdgeInsets.symmetric( - horizontal: theme.textTheme.bodyLarge!.fontSize! * 0.5, - vertical: theme.textTheme.bodyLarge!.fontSize! * 0.4, - ), - style: theme.textTheme.bodyMedium!.copyWith( - color: const Color(0xff9cc3db), - ), - borderRadius: BorderRadius.circular(3), - backgroundColor: const Color(0xFF3e4a52), - ); - }, - dependencies: [themeProvider], -); +@Riverpod(dependencies: [theme]) +TagTheme tagTheme(Ref ref) { + final theme = ref.watch(themeProvider); + + return TagTheme( + padding: EdgeInsets.symmetric( + horizontal: theme.textTheme.bodyLarge!.fontSize! * 0.5, + vertical: theme.textTheme.bodyLarge!.fontSize! * 0.4, + ), + style: theme.textTheme.bodyMedium!.copyWith( + color: const Color(0xff9cc3db), + ), + borderRadius: BorderRadius.circular(3), + backgroundColor: const Color(0xFF3e4a52), + ); +} +@Dependencies([tagTheme]) class Tag extends HookConsumerWidget { const Tag({super.key, required this.tag}); diff --git a/examples/stackoverflow/lib/tag.g.dart b/examples/stackoverflow/lib/tag.g.dart new file mode 100644 index 000000000..510f04b06 --- /dev/null +++ b/examples/stackoverflow/lib/tag.g.dart @@ -0,0 +1,74 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: non_constant_identifier_names, require_trailing_commas + +part of 'tag.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +@ProviderFor(tagTheme) +const tagThemeProvider = TagThemeProvider._(); + +final class TagThemeProvider extends $FunctionalProvider + with $Provider { + const TagThemeProvider._( + {TagTheme Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'tagThemeProvider', + isAutoDispose: true, + dependencies: const [themeProvider], + allTransitiveDependencies: const [ + TagThemeProvider.$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = themeProvider; + + final TagTheme Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$tagThemeHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(TagTheme value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + TagThemeProvider $copyWithCreate( + TagTheme Function( + Ref ref, + ) create, + ) { + return TagThemeProvider._(create: create); + } + + @override + TagTheme create(Ref ref) { + final _$cb = _createCb ?? tagTheme; + return _$cb(ref); + } +} + +String _$tagThemeHash() => r'ccf06d5f6b009c601edd44f88bf4f853708c38df'; + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/examples/stackoverflow/pubspec_overrides.yaml b/examples/stackoverflow/pubspec_overrides.yaml index d2e1fda40..b03880da2 100644 --- a/examples/stackoverflow/pubspec_overrides.yaml +++ b/examples/stackoverflow/pubspec_overrides.yaml @@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: flutter_riverpod,hooks_riverpod,riverpod,riverpod_lint,riverpod_analyzer_utils +# melos_managed_dependency_overrides: flutter_riverpod,hooks_riverpod,riverpod,riverpod_analyzer_utils,riverpod_lint dependency_overrides: flutter_riverpod: path: ../../packages/flutter_riverpod @@ -8,9 +8,12 @@ dependency_overrides: path: ../../packages/riverpod riverpod_analyzer_utils: path: ../../packages/riverpod_analyzer_utils - riverpod_lint: - path: ../../packages/riverpod_lint riverpod_annotation: path: ../../packages/riverpod_annotation riverpod_generator: path: ../../packages/riverpod_generator + riverpod_lint: + path: ../../packages/riverpod_lint + + # For flutter_test + test_api: ^0.7.4 diff --git a/examples/todos/lib/main.dart b/examples/todos/lib/main.dart index 3bbcf1688..4f4378cc0 100644 --- a/examples/todos/lib/main.dart +++ b/examples/todos/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:hooks_riverpod/legacy.dart'; import 'todo.dart'; @@ -10,7 +11,7 @@ final activeFilterKey = UniqueKey(); final completedFilterKey = UniqueKey(); final allFilterKey = UniqueKey(); -/// Creates a [TodoList] and initialise it with pre-defined values. +/// Creates a [TodoList] and initialize it with pre-defined values. /// /// We are using [StateNotifierProvider] here as a `List` is a complex /// object, with advanced business logic like how to edit a todo. @@ -35,7 +36,7 @@ final todoListFilter = StateProvider((_) => TodoListFilter.all); /// Even multiple widgets try to read the number of uncompleted todos, /// the value will be computed only once (until the todo-list changes). /// -/// This will also optimise unneeded rebuilds if the todo-list changes, but the +/// This will also optimize unneeded rebuilds if the todo-list changes, but the /// number of uncompleted todos doesn't (such as when editing a todo). final uncompletedTodosCount = Provider((ref) { return ref.watch(todoListProvider).where((todo) => !todo.completed).length; @@ -223,7 +224,10 @@ class Title extends StatelessWidget { /// /// This ensures that when we add/remove/edit todos, only what the /// impacted widgets rebuilds, instead of the entire list of items. -final _currentTodo = Provider((ref) => throw UnimplementedError()); +final _currentTodo = Provider( + dependencies: const [], + (ref) => throw UnimplementedError(), +); /// The widget that that displays the components of an individual Todo Item class TodoItem extends HookConsumerWidget { diff --git a/examples/todos/pubspec_overrides.yaml b/examples/todos/pubspec_overrides.yaml index d2e1fda40..b03880da2 100644 --- a/examples/todos/pubspec_overrides.yaml +++ b/examples/todos/pubspec_overrides.yaml @@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: flutter_riverpod,hooks_riverpod,riverpod,riverpod_lint,riverpod_analyzer_utils +# melos_managed_dependency_overrides: flutter_riverpod,hooks_riverpod,riverpod,riverpod_analyzer_utils,riverpod_lint dependency_overrides: flutter_riverpod: path: ../../packages/flutter_riverpod @@ -8,9 +8,12 @@ dependency_overrides: path: ../../packages/riverpod riverpod_analyzer_utils: path: ../../packages/riverpod_analyzer_utils - riverpod_lint: - path: ../../packages/riverpod_lint riverpod_annotation: path: ../../packages/riverpod_annotation riverpod_generator: path: ../../packages/riverpod_generator + riverpod_lint: + path: ../../packages/riverpod_lint + + # For flutter_test + test_api: ^0.7.4 diff --git a/examples/todos/test/initial_state.png b/examples/todos/test/initial_state.png index ec2108be1..953956c3f 100644 Binary files a/examples/todos/test/initial_state.png and b/examples/todos/test/initial_state.png differ diff --git a/examples/todos/test/new_todo.png b/examples/todos/test/new_todo.png index 85f04e374..9db889397 100644 Binary files a/examples/todos/test/new_todo.png and b/examples/todos/test/new_todo.png differ diff --git a/examples/todos/test/widget_test.dart b/examples/todos/test/widget_test.dart index 28de16a3e..a11c66891 100644 --- a/examples/todos/test/widget_test.dart +++ b/examples/todos/test/widget_test.dart @@ -36,43 +36,40 @@ void main() { matching: find.byType(Checkbox), ); - testWidgets( - 'Render the default todos', - (tester) async { - await tester.pumpWidget(const ProviderScope(child: MyApp())); + testWidgets('Render the default todos', skip: !Platform.isMacOS, + (tester) async { + await tester.pumpWidget(const ProviderScope(child: MyApp())); - expect( - find.descendant(of: firstItem, matching: find.text(firstItemText)), - findsOneWidget, - ); - expect( - tester.widget(firstCheckbox), - isA().having((s) => s.value, 'value', false), - ); - expect( - find.descendant(of: secondItem, matching: find.text(secondItemText)), - findsOneWidget, - ); - expect( - tester.widget(secondCheckbox), - isA().having((s) => s.value, 'value', false), - ); - expect( - find.descendant(of: thirdItem, matching: find.text(thirdItemText)), - findsOneWidget, - ); - expect( - tester.widget(thirdCheckbox), - isA().having((s) => s.value, 'value', false), - ); + expect( + find.descendant(of: firstItem, matching: find.text(firstItemText)), + findsOneWidget, + ); + expect( + tester.widget(firstCheckbox), + isA().having((s) => s.value, 'value', false), + ); + expect( + find.descendant(of: secondItem, matching: find.text(secondItemText)), + findsOneWidget, + ); + expect( + tester.widget(secondCheckbox), + isA().having((s) => s.value, 'value', false), + ); + expect( + find.descendant(of: thirdItem, matching: find.text(thirdItemText)), + findsOneWidget, + ); + expect( + tester.widget(thirdCheckbox), + isA().having((s) => s.value, 'value', false), + ); - await expectLater( - find.byType(MyApp), - matchesGoldenFile('initial_state.png'), - ); - }, - skip: !Platform.isMacOS, - ); + await expectLater( + find.byType(MyApp), + matchesGoldenFile('initial_state.png'), + ); + }); testWidgets('Clicking on checkbox toggles the todo', (tester) async { await tester.pumpWidget(const ProviderScope(child: MyApp())); diff --git a/melos.yaml b/melos.yaml index 660b3bc88..a272f577d 100644 --- a/melos.yaml +++ b/melos.yaml @@ -19,5 +19,7 @@ scripts: flutter pub get generate: - run: melos exec --depends-on=build_runner -- "dart run build_runner build -d" + run: melos exec --depends-on=lint_visitor_generator -- "dart run build_runner build -d" && melos exec --depends-on=build_runner --no-depends-on=lint_visitor_generator -- "dart run build_runner build -d" description: Build all generated files for Dart & Flutter packages in this project. + exclude: + - lint_visitor_generator diff --git a/packages/flutter_riverpod/CHANGELOG.md b/packages/flutter_riverpod/CHANGELOG.md index 1a20b9a17..b405bc9c9 100644 --- a/packages/flutter_riverpod/CHANGELOG.md +++ b/packages/flutter_riverpod/CHANGELOG.md @@ -1,3 +1,47 @@ +## Unreleased build + +- **Breaking**: When a ConsumerWidgets stops being visible (based off `Visibility`), it now temporarily stops listening to providers. +- **Breaking**: Removed all `Ref` subclasses (such `FutureProviderRef`). + Use `Ref` directly instead. + For `FutureProviderRef.future`, migrate to using an `AsyncNotifier`. +- **Breaking**: `ChangeNotifierProvider`, `StateProvider` and `StateNotifierProvider` + are moved out of `package:flutter_riverpod/flutter_riverpod.dart` to + `package:flutter_riverpod/legacy.dart`. +- **Breaking** Some internal utils are no-longer exported. +- Added support for `Ref/ProviderContainer.invalidate(provider, asReload: true)` +- Removed deprecated `@ProviderScope.parent` +- Failing providers are now automatically retried after a delay. + The delay can be optionally configured. + +## 3.0.0-dev.3 - 2023-11-27 + +- Fix "pending timer" issue inside tests when using `ref.keepAlive()`. +- Fix `Ref.invalidate`/`Ref.refresh` not throwing on circular dependency. +- Fix an infinite loop caused by `ref.keepAlive` if the `KeepAliveLink` is immediately closed. +- Fix `container.exists(provider)` on nested containers not checking their + parent containers. + +## 3.0.0-dev.2 - 2023-11-20 + +Fix exceptions when using multiple root `ProviderContainers`/`ProviderScopes`. + +## 3.0.0-dev.1 - 2023-11-20 + +- All notifier properties now throw an error if used after the notifier + has been disposed. +- The error thrown when a notifier property is used inside the constructor + of a notifier has been improved. +- Fix `ProviderObserver.didUpdateProvider` being called with an incorrect + "provider" parameter when the provider is overridden. + +## 3.0.0-dev.0 - 2023-10-29 + +- **Breaking**: `AsyncValue` is now "sealed" and `AsyncData/AsyncLoading/AsyncError` + are "final". This means that it is no-longer possible to subclass + `AsyncValue` or the associated classes. +- **Breaking**: Removed everything marked as "deprecated" +- Bumped minimum Dart SDK to >= 3.0.0-dev + ## 2.6.1 - 2024-10-22 - Added `AsyncNotifier.listenSelf`. It was mistakenly absent from the 2.6.0 release @@ -356,7 +400,7 @@ Riverpod is now stable! - **Breaking** The return value when reading a `StateProvider` changed. Before, doing `ref.read(someStateProvider)` would return the `StateController` instance. Now, this will only return the state of the `StateController`. - This new behaviour matches `StateNotifierProvider`. + This new behavior matches `StateNotifierProvider`. For a simple migration, the old behavior is available by writing `ref.read(someStateProvider.state)`. @@ -684,7 +728,7 @@ Fix an issue where `*Provider.autoDispose` were not able to specify the ### Bug-fixes - fixed a bug where providers were rebuilding even when not listened to -- fixed `ref.listen` now working when downcasting the value of a provider. +- fixed `ref.listen` now working when downcasing the value of a provider. - fixed a bug where disposing a scoped `ProviderContainer` could cause other `ProviderContainer`s to stop working. - fixed an issue where conditionally depending on an "autoDispose" provider @@ -870,7 +914,7 @@ Removed an assert that could cause issues when an application is partially migra ## 0.14.0+1 -- Re-added `StateProvider.overrideWithValue`/`StateProvider.overrideWithProvider` that were unvoluntarily removed. +- Re-added `StateProvider.overrideWithValue`/`StateProvider.overrideWithProvider` that were involuntarily removed. ## 0.14.0 @@ -1289,3 +1333,5 @@ The behavior is the same. Only the syntax changed. ## 0.1.0 Initial release + + diff --git a/packages/flutter_riverpod/example/lib/main.dart b/packages/flutter_riverpod/example/lib/main.dart index d86ad7a20..d0210f8ef 100644 --- a/packages/flutter_riverpod/example/lib/main.dart +++ b/packages/flutter_riverpod/example/lib/main.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; // A Counter example implemented with riverpod diff --git a/packages/flutter_riverpod/example/pubspec.yaml b/packages/flutter_riverpod/example/pubspec.yaml index 945b7287e..a2bf20f97 100644 --- a/packages/flutter_riverpod/example/pubspec.yaml +++ b/packages/flutter_riverpod/example/pubspec.yaml @@ -4,7 +4,7 @@ description: A new Flutter project. publish_to: "none" environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0<4.0.0" dependencies: flutter: diff --git a/packages/flutter_riverpod/lib/flutter_riverpod.dart b/packages/flutter_riverpod/lib/flutter_riverpod.dart index fc6cb403a..85492dde9 100644 --- a/packages/flutter_riverpod/lib/flutter_riverpod.dart +++ b/packages/flutter_riverpod/lib/flutter_riverpod.dart @@ -1,5 +1,6 @@ export 'package:riverpod/riverpod.dart'; -export 'src/change_notifier_provider.dart'; -export 'src/consumer.dart'; -export 'src/framework.dart' hide ProviderScopeState; +export 'src/core.dart' + hide ProviderScopeState, ConsumerBuilder, ConsumerStatefulElement; +// TODO changelog breaking: StateNotifier & co are no-longer exported from pkg:riverpod/riverpod.dart +// Use pkg:riverpod/legacy.dart diff --git a/packages/flutter_riverpod/lib/legacy.dart b/packages/flutter_riverpod/lib/legacy.dart new file mode 100644 index 000000000..29daf07e0 --- /dev/null +++ b/packages/flutter_riverpod/lib/legacy.dart @@ -0,0 +1,3 @@ +export 'package:riverpod/legacy.dart'; + +export 'src/providers/legacy/change_notifier_provider.dart'; diff --git a/packages/flutter_riverpod/lib/src/builders.dart b/packages/flutter_riverpod/lib/src/builders.dart index 242982626..42e60ae7c 100644 --- a/packages/flutter_riverpod/lib/src/builders.dart +++ b/packages/flutter_riverpod/lib/src/builders.dart @@ -11,79 +11,13 @@ // ignore_for_file: invalid_use_of_internal_member import 'package:flutter/foundation.dart'; -import 'internals.dart'; - -/// Builds a [ChangeNotifierProvider]. -class ChangeNotifierProviderBuilder { - /// Builds a [ChangeNotifierProvider]. - const ChangeNotifierProviderBuilder(); +import 'package:meta/meta.dart'; - /// {@template riverpod.autoDispose} - /// Marks the provider as automatically disposed when no longer listened to. - /// - /// Some typical use-cases: - /// - /// - Combined with [StreamProvider], this can be used as a mean to keep - /// the connection with Firebase alive only when truly needed (to reduce costs). - /// - Automatically reset a form state when leaving the screen. - /// - Automatically retry HTTP requests that failed when the user exit and - /// re-enter the screen. - /// - Cancel HTTP requests if the user leaves a screen before the request completed. - /// - /// Marking a provider with `autoDispose` also adds an extra method on `ref`: `keepAlive`. - /// - /// The `keepAlive` function is used to tell Riverpod that the state of the provider - /// should be preserved even if no longer listened to. - /// - /// A use-case would be to set this flag to `true` after an HTTP request have - /// completed: - /// - /// ```dart - /// final myProvider = FutureProvider.autoDispose((ref) async { - /// final response = await httpClient.get(...); - /// ref.keepAlive(); - /// return response; - /// }); - /// ``` - /// - /// This way, if the request failed and the UI leaves the screen then re-enters - /// it, then the request will be performed again. - /// But if the request completed successfully, the state will be preserved - /// and re-entering the screen will not trigger a new request. - /// - /// It can be combined with `ref.onDispose` for more advanced behaviors, such - /// as cancelling pending HTTP requests when the user leaves a screen. - /// For example, modifying our previous snippet and using `dio`, we would have: - /// - /// ```diff - /// final myProvider = FutureProvider.autoDispose((ref) async { - /// + final cancelToken = CancelToken(); - /// + ref.onDispose(() => cancelToken.cancel()); - /// - /// + final response = await dio.get('path', cancelToken: cancelToken); - /// - final response = await dio.get('path'); - /// ref.keepAlive(); - /// return response; - /// }); - /// ``` - /// {@endtemplate} - ChangeNotifierProvider call( - // ignore: deprecated_member_use_from_same_package - Create> create, { - String? name, - Iterable? dependencies, - }) { - return ChangeNotifierProvider( - create, - name: name, - dependencies: dependencies, - ); - } +import 'internals.dart'; - /// {@macro riverpod.autoDispose} - AutoDisposeChangeNotifierProviderBuilder get autoDispose { - return const AutoDisposeChangeNotifierProviderBuilder(); - } +@internal +class ChangeNotifierProviderFamilyBuilder { + const ChangeNotifierProviderFamilyBuilder(); /// {@template riverpod.family} /// A group of providers that builds their value from an external parameter. @@ -294,81 +228,117 @@ class ChangeNotifierProviderBuilder { /// } /// ``` /// {@endtemplate} - ChangeNotifierProviderFamilyBuilder get family { - return const ChangeNotifierProviderFamilyBuilder(); - } -} - -/// Builds a [ChangeNotifierProviderFamily]. -class ChangeNotifierProviderFamilyBuilder { - /// Builds a [ChangeNotifierProviderFamily]. - const ChangeNotifierProviderFamilyBuilder(); - - /// {@macro riverpod.family} - ChangeNotifierProviderFamily - call( - // ignore: deprecated_member_use_from_same_package - FamilyCreate, Arg> create, { + ChangeNotifierProviderFamily + call( + NotifierT Function(Ref ref, ArgT param) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return ChangeNotifierProviderFamily( + return ChangeNotifierProviderFamily( create, name: name, dependencies: dependencies, + retry: retry, ); } - /// {@macro riverpod.autoDispose} - AutoDisposeChangeNotifierProviderFamilyBuilder get autoDispose { - return const AutoDisposeChangeNotifierProviderFamilyBuilder(); - } + /// {@template riverpod.autoDispose} + /// Marks the provider as automatically disposed when no longer listened to. + /// + /// Some typical use-cases: + /// + /// - Combined with [StreamProvider], this can be used as a mean to keep + /// the connection with Firebase alive only when truly needed (to reduce costs). + /// - Automatically reset a form state when leaving the screen. + /// - Automatically retry HTTP requests that failed when the user exit and + /// re-enter the screen. + /// - Cancel HTTP requests if the user leaves a screen before the request completed. + /// + /// Marking a provider with `autoDispose` also adds an extra method on `ref`: `keepAlive`. + /// + /// The `keepAlive` function is used to tell Riverpod that the state of the provider + /// should be preserved even if no longer listened to. + /// + /// A use-case would be to set this flag to `true` after an HTTP request have + /// completed: + /// + /// ```dart + /// final myProvider = FutureProvider.autoDispose((ref) async { + /// final response = await httpClient.get(...); + /// ref.keepAlive(); + /// return response; + /// }); + /// ``` + /// + /// This way, if the request failed and the UI leaves the screen then re-enters + /// it, then the request will be performed again. + /// But if the request completed successfully, the state will be preserved + /// and re-entering the screen will not trigger a new request. + /// + /// It can be combined with `ref.onDispose` for more advanced behaviors, such + /// as cancelling pending HTTP requests when the user leaves a screen. + /// For example, modifying our previous snippet and using `dio`, we would have: + /// + /// ```diff + /// final myProvider = FutureProvider.autoDispose((ref) async { + /// + final cancelToken = CancelToken(); + /// + ref.onDispose(() => cancelToken.cancel()); + /// + /// + final response = await dio.get('path', cancelToken: cancelToken); + /// - final response = await dio.get('path'); + /// ref.keepAlive(); + /// return response; + /// }); + /// ``` + /// {@endtemplate} + AutoDisposeChangeNotifierProviderFamilyBuilder get autoDispose => + const AutoDisposeChangeNotifierProviderFamilyBuilder(); } -/// Builds a [AutoDisposeChangeNotifierProvider]. +@internal class AutoDisposeChangeNotifierProviderBuilder { - /// Builds a [AutoDisposeChangeNotifierProvider]. const AutoDisposeChangeNotifierProviderBuilder(); - /// {@macro riverpod.autoDispose} - AutoDisposeChangeNotifierProvider - call( - // ignore: deprecated_member_use_from_same_package - Create> create, { + /// {@macro riverpod.family} + ChangeNotifierProvider call( + NotifierT Function(Ref ref) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return AutoDisposeChangeNotifierProvider( + return ChangeNotifierProvider( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } /// {@macro riverpod.family} - AutoDisposeChangeNotifierProviderFamilyBuilder get family { - return const AutoDisposeChangeNotifierProviderFamilyBuilder(); - } + AutoDisposeChangeNotifierProviderFamilyBuilder get family => + const AutoDisposeChangeNotifierProviderFamilyBuilder(); } -/// Builds a [AutoDisposeChangeNotifierProviderFamily]. +@internal class AutoDisposeChangeNotifierProviderFamilyBuilder { - /// Builds a [AutoDisposeChangeNotifierProviderFamily]. const AutoDisposeChangeNotifierProviderFamilyBuilder(); /// {@macro riverpod.family} - AutoDisposeChangeNotifierProviderFamily - call( - // ignore: deprecated_member_use_from_same_package - FamilyCreate, Arg> - create, { + ChangeNotifierProviderFamily + call( + NotifierT Function(Ref ref, ArgT param) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return AutoDisposeChangeNotifierProviderFamily( + return ChangeNotifierProviderFamily( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } } diff --git a/packages/flutter_riverpod/lib/src/change_notifier_provider.dart b/packages/flutter_riverpod/lib/src/change_notifier_provider.dart deleted file mode 100644 index b14ad8bce..000000000 --- a/packages/flutter_riverpod/lib/src/change_notifier_provider.dart +++ /dev/null @@ -1,63 +0,0 @@ -// ignore_for_file: invalid_use_of_internal_member - -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; -import 'package:meta/meta.dart'; -// ignore: implementation_imports -import 'package:riverpod/src/internals.dart'; - -import 'builders.dart'; - -part 'change_notifier_provider/auto_dispose.dart'; -part 'change_notifier_provider/base.dart'; - -ProviderElementProxy - _notifier( - _ChangeNotifierProviderBase that, -) { - return ProviderElementProxy( - that, - (element) { - return (element as ChangeNotifierProviderElement) - ._notifierNotifier; - }, - ); -} - -// ignore: subtype_of_sealed_class -/// {@template riverpod.changenotifierprovider} -/// Creates a [ChangeNotifier] and subscribes to it. -/// -/// Note: By using Riverpod, [ChangeNotifier] will no longer be O(N^2) for -/// dispatching notifications, but instead O(N) -/// {@endtemplate} -abstract class _ChangeNotifierProviderBase - extends ProviderBase { - const _ChangeNotifierProviderBase({ - required super.name, - required super.from, - required super.argument, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - }); - - /// Obtains the [ChangeNotifier] associated with this provider, without listening - /// to state changes. - /// - /// This is typically used to invoke methods on a [ChangeNotifier]. For example: - /// - /// ```dart - /// Button( - /// onTap: () => ref.read(changeNotifierProvider.notifier).increment(), - /// ) - /// ``` - /// - /// This listenable will notify its notifiers if the [ChangeNotifier] instance - /// changes. - /// This may happen if the provider is refreshed or one of its dependencies - /// has changes. - ProviderListenable get notifier; - - NotifierT _create(covariant ChangeNotifierProviderElement ref); -} diff --git a/packages/flutter_riverpod/lib/src/change_notifier_provider/auto_dispose.dart b/packages/flutter_riverpod/lib/src/change_notifier_provider/auto_dispose.dart deleted file mode 100644 index e5402af18..000000000 --- a/packages/flutter_riverpod/lib/src/change_notifier_provider/auto_dispose.dart +++ /dev/null @@ -1,145 +0,0 @@ -// ignore_for_file: invalid_use_of_internal_member - -part of '../change_notifier_provider.dart'; - -/// {@macro riverpod.provider_ref_base} -@Deprecated('will be removed in 3.0.0, use Ref instead') -abstract class AutoDisposeChangeNotifierProviderRef< - NotifierT extends ChangeNotifier?> - extends ChangeNotifierProviderRef - implements AutoDisposeRef {} - -// ignore: subtype_of_sealed_class -/// {@macro riverpod.change_notifier_provider} -class AutoDisposeChangeNotifierProvider - extends _ChangeNotifierProviderBase { - /// {@macro riverpod.change_notifier_provider} - AutoDisposeChangeNotifierProvider( - this._createFn, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - AutoDisposeChangeNotifierProvider.internal( - this._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.family} - static const family = AutoDisposeChangeNotifierProviderFamily.new; - - final NotifierT Function( - // ignore: deprecated_member_use_from_same_package - AutoDisposeChangeNotifierProviderRef ref, - ) _createFn; - - @override - NotifierT _create( - // ignore: deprecated_member_use_from_same_package - AutoDisposeChangeNotifierProviderElement ref, - ) { - return _createFn(ref); - } - - @override - // ignore: deprecated_member_use_from_same_package - AutoDisposeChangeNotifierProviderElement createElement() { - // ignore: deprecated_member_use_from_same_package - return AutoDisposeChangeNotifierProviderElement._(this); - } - - @override - late final Refreshable notifier = _notifier(this); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - Create> create, - ) { - return ProviderOverride( - origin: this, - override: AutoDisposeChangeNotifierProvider.internal( - create, - from: from, - argument: argument, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ), - ); - } -} - -/// The element of [AutoDisposeChangeNotifierProvider]. -@Deprecated('will be removed in 3.0.0, use Ref instead') -class AutoDisposeChangeNotifierProviderElement< - NotifierT extends ChangeNotifier?> - extends ChangeNotifierProviderElement - with AutoDisposeProviderElementMixin - implements AutoDisposeChangeNotifierProviderRef { - /// The [ProviderElementBase] for [ChangeNotifier] - @Deprecated('will be removed in 3.0.0, use Ref instead') - AutoDisposeChangeNotifierProviderElement._( - AutoDisposeChangeNotifierProvider super._provider, - ) : super._(); -} - -// ignore: subtype_of_sealed_class -/// The [Family] of [AutoDisposeChangeNotifierProvider]. -class AutoDisposeChangeNotifierProviderFamily - extends AutoDisposeFamilyBase< - // ignore: deprecated_member_use_from_same_package - AutoDisposeChangeNotifierProviderRef, - NotifierT, - Arg, - NotifierT, - AutoDisposeChangeNotifierProvider> { - /// The [Family] of [AutoDisposeChangeNotifierProvider]. - AutoDisposeChangeNotifierProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: AutoDisposeChangeNotifierProvider.internal, - debugGetCreateSourceHash: null, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// {@macro riverpod.override_with} - Override overrideWith( - NotifierT Function( - // ignore: deprecated_member_use_from_same_package - AutoDisposeChangeNotifierProviderRef ref, - Arg arg, - ) create, - ) { - return FamilyOverrideImpl>( - this, - (arg) => AutoDisposeChangeNotifierProvider.internal( - (ref) => create(ref, arg), - from: from, - argument: arg, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ), - ); - } -} diff --git a/packages/flutter_riverpod/lib/src/change_notifier_provider/base.dart b/packages/flutter_riverpod/lib/src/change_notifier_provider/base.dart deleted file mode 100644 index 2e0ab725e..000000000 --- a/packages/flutter_riverpod/lib/src/change_notifier_provider/base.dart +++ /dev/null @@ -1,292 +0,0 @@ -// ignore_for_file: invalid_use_of_internal_member - -part of '../change_notifier_provider.dart'; - -/// {@macro riverpod.provider_ref_base} -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class ChangeNotifierProviderRef - implements Ref { - /// The [ChangeNotifier] currently exposed by this provider. - /// - /// Cannot be accessed while creating the provider. - @Deprecated('will be removed in 3.0.0.') - NotifierT get notifier; -} - -// ignore: subtype_of_sealed_class -/// {@template riverpod.change_notifier_provider} -/// Creates a [ChangeNotifier] and exposes its current state. -/// -/// Combined with [ChangeNotifier], [ChangeNotifierProvider] can be used to manipulate -/// advanced states, that would otherwise be difficult to represent with simpler -/// providers such as [Provider] or [FutureProvider]. -/// -/// For example, you may have a todo-list, where you can add and remove -/// and complete a todo. -/// Using [ChangeNotifier], you could represent such state as: -/// -/// ```dart -/// class TodosNotifier extends ChangeNotifier { -/// List todos = []; -/// -/// void add(Todo todo) { -/// todos.add(todo); -/// notifyListeners(); -/// } -/// -/// void remove(String todoId) { -/// todos.removeWhere((todo) => todo.id == todoId); -/// notifyListeners(); -/// } -/// -/// void toggle(String todoId) { -/// final todo = todos.firstWhere((todo) => todo.id == todoId); -/// todo.completed = !todo.completed; -/// notifyListeners(); -/// } -/// } -/// ``` -/// -/// Which you can then pass to a [ChangeNotifierProvider] like so: -/// -/// ```dart -/// final todosProvider = ChangeNotifierProvider>((ref) => TodosNotifier()); -/// ``` -/// -/// And finally, you can interact with it inside your UI: -/// -/// ```dart -/// Widget build(BuildContext context, WidgetRef ref) { -/// // rebuild the widget when the todo list changes -/// List todos = ref.watch(todosProvider).todos; -/// -/// return ListView( -/// children: [ -/// for (final todo in todos) -/// CheckboxListTile( -/// value: todo.completed, -/// // When tapping on the todo, change its completed status -/// onChanged: (value) => ref.read(todosProvider.notifier).toggle(todo.id), -/// title: Text(todo.description), -/// ), -/// ], -/// ); -/// } -/// ``` -/// {@endtemplate} -class ChangeNotifierProvider - extends _ChangeNotifierProviderBase - with - // ignore: deprecated_member_use - AlwaysAliveProviderBase { - /// {@macro riverpod.change_notifier_provider} - ChangeNotifierProvider( - this._createFn, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - ChangeNotifierProvider.internal( - this._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.autoDispose} - static const autoDispose = AutoDisposeChangeNotifierProviderBuilder(); - - /// {@macro riverpod.family} - static const family = ChangeNotifierProviderFamilyBuilder(); - - final NotifierT Function( - // ignore: deprecated_member_use, deprecated_member_use_from_same_package - ChangeNotifierProviderRef ref, - ) _createFn; - - @override - NotifierT _create(ChangeNotifierProviderElement ref) { - return _createFn(ref); - } - - @override - ChangeNotifierProviderElement createElement() { - return ChangeNotifierProviderElement._(this); - } - - @override - // ignore: deprecated_member_use, deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable notifier = - _notifier(this); - - /// {@template riverpod.override_with} - /// Override the provider with a new initialization function. - /// - /// This will also disable the auto-scoping mechanism, meaning that if the - /// overridden provider specified `dependencies`, it will have no effect. - /// - /// The override must not specify a `dependencies`. - /// - /// Some common use-cases are: - /// - testing, by replacing a service with a fake implementation, or to reach - /// a very specific state easily. - /// - multiple environments, by changing the implementation of a class - /// based on the platform or other parameters. - /// - /// This function should be used in combination with `ProviderScope.overrides` - /// or `ProviderContainer.overrides`: - /// - /// ```dart - /// final myService = Provider((ref) => MyService()); - /// - /// runApp( - /// ProviderScope( - /// overrides: [ - /// // Replace the implementation of the provider with a different one - /// myService.overrideWithProvider((ref) { - /// ref.watch('other'); - /// return MyFakeService(), - /// })), - /// ], - /// child: MyApp(), - /// ), - /// ); - /// ``` - /// {@endtemplate} - Override overrideWith( - // ignore: deprecated_member_use, deprecated_member_use_from_same_package - Create> create, - ) { - return ProviderOverride( - origin: this, - override: ChangeNotifierProvider.internal( - create, - from: from, - argument: argument, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ), - ); - } -} - -/// The element of [ChangeNotifierProvider]. -class ChangeNotifierProviderElement - extends ProviderElementBase - // ignore: deprecated_member_use, deprecated_member_use_from_same_package - implements - // ignore: deprecated_member_use, deprecated_member_use_from_same_package - ChangeNotifierProviderRef { - ChangeNotifierProviderElement._( - _ChangeNotifierProviderBase super._provider, - ); - - @override - NotifierT get notifier => _notifierNotifier.value; - final _notifierNotifier = ProxyElementValueNotifier(); - - void Function()? _removeListener; - - @override - void create({required bool didChangeDependency}) { - final provider = this.provider as _ChangeNotifierProviderBase; - - final notifierResult = - _notifierNotifier.result = Result.guard(() => provider._create(this)); - - // TODO test requireState, as ref.read(p) is expected to throw if notifier creation failed - final notifier = notifierResult.requireState; - - setState(notifier); - - if (notifier != null) { - void listener() => setState(notifier); - notifier.addListener(listener); - _removeListener = () => notifier.removeListener(listener); - } - } - - @override - bool updateShouldNotify(NotifierT previous, NotifierT next) => true; - - @override - void runOnDispose() { - super.runOnDispose(); - - _removeListener?.call(); - _removeListener = null; - - final notifier = _notifierNotifier.result?.stateOrNull; - if (notifier != null) { - // TODO test ChangeNotifier.dispose is guarded - runGuarded(notifier.dispose); - } - _notifierNotifier.result = null; - } - - @override - void visitChildren({ - required void Function(ProviderElementBase element) elementVisitor, - required void Function(ProxyElementValueNotifier element) - notifierVisitor, - }) { - super.visitChildren( - elementVisitor: elementVisitor, - notifierVisitor: notifierVisitor, - ); - notifierVisitor(_notifierNotifier); - } -} - -// ignore: subtype_of_sealed_class -/// The [Family] of [ChangeNotifierProvider]. -class ChangeNotifierProviderFamily - // ignore: deprecated_member_use, deprecated_member_use_from_same_package - extends FamilyBase, NotifierT, Arg, - NotifierT, ChangeNotifierProvider> { - /// The [Family] of [ChangeNotifierProvider]. - ChangeNotifierProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: ChangeNotifierProvider.internal, - debugGetCreateSourceHash: null, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use, deprecated_member_use_from_same_package - NotifierT Function(ChangeNotifierProviderRef ref, Arg arg) - create, - ) { - return FamilyOverrideImpl>( - this, - (arg) => ChangeNotifierProvider.internal( - (ref) => create(ref, arg), - from: from, - argument: arg, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ), - ); - } -} diff --git a/packages/flutter_riverpod/lib/src/consumer.dart b/packages/flutter_riverpod/lib/src/consumer.dart deleted file mode 100644 index 6fe0c9068..000000000 --- a/packages/flutter_riverpod/lib/src/consumer.dart +++ /dev/null @@ -1,686 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:meta/meta.dart'; - -import 'internals.dart'; - -/// An object that allows widgets to interact with providers. -abstract class WidgetRef { - /// The [BuildContext] of the widget associated to this [WidgetRef]. - /// - /// This is strictly identical to the [BuildContext] passed to [ConsumerWidget.build]. - BuildContext get context; - - /// Returns the value exposed by a provider and rebuild the widget when that - /// value changes. - /// - /// See also: - /// - /// - [ProviderListenable.select], which allows a widget to filter rebuilds by - /// observing only the selected properties. - /// - [listen], to react to changes on a provider, such as for showing modals. - T watch(ProviderListenable provider); - - /// Determines whether a provider is initialized or not. - /// - /// Writing logic that conditionally depends on the existence of a provider - /// is generally unsafe and should be avoided. - /// The problem is that once the provider gets initialized, logic that - /// depends on the existence or not of a provider won't be rerun; possibly - /// causing your state to get out of date. - /// - /// But it can be useful in some cases, such as to avoid re-fetching an - /// object if a different network request already obtained it: - /// - /// ```dart - /// final fetchItemList = FutureProvider>(...); - /// - /// final fetchItem = FutureProvider.autoDispose.family((ref, id) async { - /// if (ref.exists(fetchItemList)) { - /// // If `fetchItemList` is initialized, we look into its state - /// // and return the already obtained item. - /// final itemFromItemList = await ref.watch( - /// fetchItemList.selectAsync((items) => items.firstWhereOrNull((item) => item.id == id)), - /// ); - /// if (itemFromItemList != null) return itemFromItemList; - /// } - /// - /// // If `fetchItemList` is not initialized, perform a network request for - /// // "id" separately - /// - /// final json = await http.get('api/items/$id'); - /// return Item.fromJson(json); - /// }); - /// ``` - bool exists(ProviderBase provider); - - /// Listen to a provider and call `listener` whenever its value changes, - /// without having to take care of removing the listener. - /// - /// The [listen] method should exclusively be used within the `build` method - /// of a widget: - /// - /// ```dart - /// Consumer( - /// builder: (context, ref, child) { - /// ref.listen(counterProvider, (prev, next) { - /// print('counter changed $next'); - /// }); - /// }, - /// ) - /// ``` - /// - /// When used inside `build`, listeners will automatically be removed - /// if a widget rebuilds and stops listening to a provider. - /// - /// For listening to a provider from outside `build`, consider using [listenManual] instead. - /// - /// This is useful for showing modals or other imperative logic. - void listen( - ProviderListenable provider, - void Function(T? previous, T next) listener, { - void Function(Object error, StackTrace stackTrace)? onError, - }); - - /// Listen to a provider and call `listener` whenever its value changes. - /// - /// As opposed to [listen], [listenManual] is not safe to use within the `build` - /// method of a widget. - /// Instead, [listenManual] is designed to be used inside [State.initState] or - /// other [State] lifecycles. - /// - /// [listenManual] returns a [ProviderSubscription] which can be used to stop - /// listening to the provider, or to read the current value exposed by - /// the provider. - /// - /// It is not necessary to call [ProviderSubscription.close] inside [State.dispose]. - /// When the widget that calls [listenManual] is disposed, the subscription - /// will be disposed automatically. - ProviderSubscription listenManual( - ProviderListenable provider, - void Function(T? previous, T next) listener, { - void Function(Object error, StackTrace stackTrace)? onError, - bool fireImmediately, - }); - - /// Reads a provider without listening to it. - /// - /// **AVOID** calling [read] inside build if the value is used only for events: - /// - /// ```dart - /// Widget build(BuildContext context) { - /// // counter is used only for the onPressed of RaisedButton - /// final counter = ref.read(counterProvider); - /// - /// return RaisedButton( - /// onPressed: () => counter.increment(), - /// ); - /// } - /// ``` - /// - /// While this code is not bugged in itself, this is an anti-pattern. - /// It could easily lead to bugs in the future after refactoring the widget - /// to use `counter` for other things, but forget to change [read] into [Consumer]/`ref.watch(`. - /// - /// **CONSIDER** calling [read] inside event handlers: - /// - /// ```dart - /// Widget build(BuildContext context) { - /// return RaisedButton( - /// onPressed: () { - /// // as performant as the previous solution, but resilient to refactoring - /// ref.read(counterProvider).increment(), - /// }, - /// ); - /// } - /// ``` - /// - /// This has the same efficiency as the previous anti-pattern, but does not - /// suffer from the drawback of being brittle. - /// - /// **AVOID** using [read] for creating widgets with a value that never changes - /// - /// ```dart - /// Widget build(BuildContext context) { - /// // using read because we only use a value that never changes. - /// final model = ref.read(modelProvider); - /// - /// return Text('${model.valueThatNeverChanges}'); - /// } - /// ``` - /// - /// While the idea of not rebuilding the widget if unnecessary is good, - /// this should not be done with [read]. - /// Relying on [read] for optimisations is very brittle and dependent - /// on an implementation detail. - /// - /// **CONSIDER** using [Provider] or `select` for filtering unwanted rebuilds: - /// - /// ```dart - /// Widget build(BuildContext context) { - /// // Using select to listen only to the value that used - /// final valueThatNeverChanges = ref.watch(modelProvider.select((model) { - /// return model.valueThatNeverChanges; - /// })); - /// - /// return Text('$valueThatNeverChanges'); - /// } - /// ``` - /// - /// While more verbose than [read], using [Provider]/`select` is a lot safer. - /// It does not rely on implementation details on `Model`, and it makes - /// impossible to have a bug where our UI does not refresh. - T read(ProviderListenable provider); - - /// Forces a provider to re-evaluate its state immediately, and return the created value. - /// - /// Writing: - /// - /// ```dart - /// final newValue = ref.refresh(provider); - /// ``` - /// - /// is strictly identical to doing: - /// - /// ```dart - /// ref.invalidate(provider); - /// final newValue = ref.read(provider); - /// ``` - /// - /// If you do not care about the return value of [refresh], use [invalidate] instead. - /// Doing so has the benefit of: - /// - making the invalidation logic more resilient by avoiding multiple - /// refreshes at once. - /// - possibly avoids recomputing a provider if it isn't - /// needed immediately. - /// - /// This method is useful for features like "pull to refresh" or "retry on error", - /// to restart a specific provider. - /// - /// For example, a pull-to-refresh may be implemented by combining - /// [FutureProvider] and a `RefreshIndicator`: - /// - /// ```dart - /// final productsProvider = FutureProvider((ref) async { - /// final response = await httpClient.get('https://host.com/products'); - /// return Products.fromJson(response.data); - /// }); - /// - /// class Example extends ConsumerWidget { - /// @override - /// Widget build(BuildContext context, WidgetRef ref) { - /// final Products products = ref.watch(productsProvider); - /// - /// return RefreshIndicator( - /// onRefresh: () => ref.refresh(productsProvider.future), - /// child: ListView( - /// children: [ - /// for (final product in products.items) ProductItem(product: product), - /// ], - /// ), - /// ); - /// } - /// } - /// ``` - @useResult - State refresh(Refreshable provider); - - /// Invalidates the state of the provider, causing it to refresh. - /// - /// As opposed to [refresh], the refresh is not immediate and is instead - /// delayed to the next read or next frame. - /// - /// Calling [invalidate] multiple times will refresh the provider only - /// once. - /// - /// Calling [invalidate] will cause the provider to be disposed immediately. - /// - /// If used on a provider which is not initialized, this method will have no effect. - void invalidate(ProviderOrFamily provider); -} - -/// A function that can also listen to providers -/// -/// See also [Consumer] -typedef ConsumerBuilder = Widget Function( - BuildContext context, - WidgetRef ref, - Widget? child, -); - -/// {@template riverpod.consumer} -/// Build a widget tree while listening to providers. -/// -/// [Consumer] can be used to listen to providers inside a [StatefulWidget] -/// or to rebuild as few widgets as possible when a provider updates. -/// -/// As an example, consider: -/// -/// ```dart -/// final helloWorldProvider = Provider((_) => 'Hello world'); -/// ``` -/// -/// We can then use [Consumer] to listen to `helloWorldProvider` inside a -/// [StatefulWidget] like so: -/// -/// ```dart -/// class Example extends StatefulWidget { -/// @override -/// _ExampleState createState() => _ExampleState(); -/// } -/// -/// class _ExampleState extends State { -/// @override -/// Widget build(BuildContext context) { -/// return Consumer( -/// builder: (context, ref, child) { -/// final value = ref.watch(helloWorldProvider); -/// return Text(value); // Hello world -/// }, -/// ); -/// } -/// } -/// ``` -/// -/// **Note** -/// You can watch as many providers inside [Consumer] as you want to: -/// -/// ```dart -/// Consumer( -/// builder: (context, ref, child) { -/// final value = ref.watch(someProvider); -/// final another = ref.watch(anotherProvider); -/// ... -/// }, -/// ); -/// ``` -/// -/// ## Performance optimizations -/// -/// If your `builder` function contains a subtree that does not depend on the -/// animation, it is more efficient to build that subtree once instead of -/// rebuilding it on every provider update. -/// -/// If you pass the pre-built subtree as the `child` parameter, the -/// Consumer will pass it back to your builder function so that you -/// can incorporate it into your build. -/// -/// Using this pre-built child is entirely optional, but can improve -/// performance significantly in some cases and is therefore a good practice. -/// -/// This sample shows how you could use a [Consumer] -/// -/// ```dart -/// final counterProvider = StateProvider((ref) => 0); -/// -/// class MyHomePage extends ConsumerWidget { -/// MyHomePage({Key? key, required this.title}) : super(key: key); -/// final String title; -/// -/// @override -/// Widget build(BuildContext context, WidgetRef ref) { -/// return Scaffold( -/// appBar: AppBar( -/// title: Text(title) -/// ), -/// body: Center( -/// child: Column( -/// mainAxisAlignment: MainAxisAlignment.center, -/// children: [ -/// Text('You have pushed the button this many times:'), -/// Consumer( -/// builder: (BuildContext context, WidgetRef ref, Widget? child) { -/// // This builder will only get called when the counterProvider -/// // is updated. -/// final count = ref.watch(counterProvider); -/// -/// return Row( -/// mainAxisAlignment: MainAxisAlignment.spaceEvenly, -/// children: [ -/// Text('$count'), -/// child!, -/// ], -/// ); -/// }, -/// // The child parameter is most helpful if the child is -/// // expensive to build and does not depend on the value from -/// // the notifier. -/// child: Text('Good job!'), -/// ) -/// ], -/// ), -/// ), -/// floatingActionButton: FloatingActionButton( -/// child: Icon(Icons.plus_one), -/// onPressed: () => ref.read(counterProvider.notifier).state++, -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// See also: -/// -/// * [ConsumerWidget], a base-class for widgets that wants to listen to providers. -/// {@endtemplate} -@sealed -class Consumer extends ConsumerWidget { - /// {@macro riverpod.consumer} - const Consumer({super.key, required ConsumerBuilder builder, Widget? child}) - : _child = child, - _builder = builder; - - final ConsumerBuilder _builder; - final Widget? _child; - - @override - Widget build(BuildContext context, WidgetRef ref) { - return _builder(context, ref, _child); - } -} - -/// {@template riverpod.consumerwidget} -/// A [StatelessWidget] that can listen to providers. -/// -/// Using [ConsumerWidget], this allows the widget tree to listen to changes on -/// provider, so that the UI automatically updates when needed. -/// -/// Do not modify any state or start any http request inside [build]. -/// -/// As a usage example, consider: -/// -/// ```dart -/// final helloWorldProvider = Provider((_) => 'Hello world'); -/// ``` -/// -/// We can then subclass [ConsumerWidget] to listen to `helloWorldProvider` like so: -/// -/// ```dart -/// class Example extends ConsumerWidget { -/// const Example({Key? key}): super(key: key); -/// -/// @override -/// Widget build(BuildContext context, WidgetRef ref) { -/// final value = ref.watch(helloWorldProvider); -/// return Text(value); // Hello world -/// } -/// } -/// ``` -/// -/// **Note** -/// You can watch as many providers inside [build] as you want to: -/// -/// ```dart -/// @override -/// Widget build(BuildContext context, WidgetRef ref) { -/// final value = ref.watch(someProvider); -/// final another = ref.watch(anotherProvider); -/// return Text(value); // Hello world -/// } -/// ``` -/// -/// For reading providers inside a [StatefulWidget] or for performance -/// optimizations, see [Consumer]. -/// {@endtemplate} -abstract class ConsumerWidget extends ConsumerStatefulWidget { - /// {@macro riverpod.consumerwidget} - const ConsumerWidget({super.key}); - - /// Describes the part of the user interface represented by this widget. - /// - /// The framework calls this method when this widget is inserted into the tree - /// in a given [BuildContext] and when the dependencies of this widget change - /// (e.g., an [InheritedWidget] referenced by this widget changes). This - /// method can potentially be called in every frame and should not have any side - /// effects beyond building a widget. - /// - /// The framework replaces the subtree below this widget with the widget - /// returned by this method, either by updating the existing subtree or by - /// removing the subtree and inflating a new subtree, depending on whether the - /// widget returned by this method can update the root of the existing - /// subtree, as determined by calling [Widget.canUpdate]. - /// - /// Typically implementations return a newly created constellation of widgets - /// that are configured with information from this widget's constructor and - /// from the given [BuildContext]. - /// - /// The given [BuildContext] contains information about the location in the - /// tree at which this widget is being built. For example, the context - /// provides the set of inherited widgets for this location in the tree. A - /// given widget might be built with multiple different [BuildContext] - /// arguments over time if the widget is moved around the tree or if the - /// widget is inserted into the tree in multiple places at once. - /// - /// The implementation of this method must only depend on: - /// - /// * the fields of the widget, which themselves must not change over time, - /// and - /// * any ambient state obtained from the `context` using - /// [BuildContext.dependOnInheritedWidgetOfExactType]. - /// - /// If a widget's [build] method is to depend on anything else, use a - /// [StatefulWidget] instead. - /// - /// See also: - /// - /// * [StatelessWidget], which contains the discussion on performance considerations. - Widget build(BuildContext context, WidgetRef ref); - - @override - // ignore: library_private_types_in_public_api - _ConsumerState createState() => _ConsumerState(); -} - -class _ConsumerState extends ConsumerState { - @override - Widget build(BuildContext context) { - return widget.build(context, ref); - } -} - -/// A [StatefulWidget] that can read providers. -abstract class ConsumerStatefulWidget extends StatefulWidget { - /// A [StatefulWidget] that can read providers. - const ConsumerStatefulWidget({super.key}); - - @override - // ignore: no_logic_in_create_state - ConsumerState createState(); - - @override - ConsumerStatefulElement createElement() { - return ConsumerStatefulElement(this); - } -} - -/// A [State] that has access to a [WidgetRef] through [ref], allowing -/// it to read providers. -abstract class ConsumerState - extends State { - /// An object that allows widgets to interact with providers. - late final WidgetRef ref = context as WidgetRef; -} - -/// The [Element] for a [ConsumerStatefulWidget] -class ConsumerStatefulElement extends StatefulElement implements WidgetRef { - /// The [Element] for a [ConsumerStatefulWidget] - ConsumerStatefulElement(ConsumerStatefulWidget super.widget); - - late ProviderContainer _container = ProviderScope.containerOf(this); - var _dependencies = - , ProviderSubscription>{}; - Map, ProviderSubscription>? - _oldDependencies; - final _listeners = >[]; - List<_ListenManual>? _manualListeners; - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - final newContainer = ProviderScope.containerOf(this); - if (_container != newContainer) { - _container = newContainer; - for (final dependency in _dependencies.values) { - dependency.close(); - } - _dependencies.clear(); - } - } - - @override - Widget build() { - // TODO disallow didChangeDependencies - try { - _oldDependencies = _dependencies; - for (var i = 0; i < _listeners.length; i++) { - _listeners[i].close(); - } - _listeners.clear(); - _dependencies = {}; - return super.build(); - } finally { - for (final dep in _oldDependencies!.values) { - dep.close(); - } - _oldDependencies = null; - } - } - - void _assertNotDisposed() { - if (!context.mounted) { - throw StateError('Cannot use "ref" after the widget was disposed.'); - } - } - - @override - Res watch(ProviderListenable target) { - _assertNotDisposed(); - return _dependencies.putIfAbsent(target, () { - final oldDependency = _oldDependencies?.remove(target); - - if (oldDependency != null) { - return oldDependency; - } - - return _container.listen( - target, - (_, __) => markNeedsBuild(), - ); - }).read() as Res; - } - - @override - void unmount() { - /// Calling `super.unmount()` will call `dispose` on the state - /// And [ListenManual] subscriptions should be closed after `dispose` - super.unmount(); - - for (final dependency in _dependencies.values) { - dependency.close(); - } - for (var i = 0; i < _listeners.length; i++) { - _listeners[i].close(); - } - final manualListeners = _manualListeners?.toList(); - if (manualListeners != null) { - for (final listener in manualListeners) { - listener.close(); - } - _manualListeners = null; - } - } - - @override - void listen( - ProviderListenable provider, - void Function(T? previous, T value) listener, { - void Function(Object error, StackTrace stackTrace)? onError, - }) { - _assertNotDisposed(); - assert( - debugDoingBuild, - 'ref.listen can only be used within the build method of a ConsumerWidget', - ); - - // We can't implement a fireImmediately flag because we wouldn't know - // which listen call was preserved between widget rebuild, and we wouldn't - // want to call the listener on every rebuild. - final sub = _container.listen(provider, listener, onError: onError); - _listeners.add(sub); - } - - @override - bool exists(ProviderBase provider) { - _assertNotDisposed(); - return ProviderScope.containerOf(this, listen: false).exists(provider); - } - - @override - T read(ProviderListenable provider) { - _assertNotDisposed(); - return ProviderScope.containerOf(this, listen: false).read(provider); - } - - @override - State refresh(Refreshable provider) { - _assertNotDisposed(); - return ProviderScope.containerOf(this, listen: false).refresh(provider); - } - - @override - void invalidate(ProviderOrFamily provider) { - _assertNotDisposed(); - _container.invalidate(provider); - } - - @override - ProviderSubscription listenManual( - ProviderListenable provider, - void Function(T? previous, T next) listener, { - void Function(Object error, StackTrace stackTrace)? onError, - bool fireImmediately = false, - }) { - _assertNotDisposed(); - final listeners = _manualListeners ??= []; - - // Reading the container using "listen:false" to guarantee that this can - // be used inside initState. - final container = ProviderScope.containerOf(this, listen: false); - - final sub = _ListenManual( - // TODO somehow pass "this" instead for the devtool's sake - container, - container.listen( - provider, - listener, - onError: onError, - fireImmediately: fireImmediately, - ), - this, - ); - listeners.add(sub); - - return sub; - } - - @override - BuildContext get context => this; -} - -class _ListenManual extends ProviderSubscription { - _ListenManual(super.source, this._subscription, this._element); - - final ProviderSubscription _subscription; - final ConsumerStatefulElement _element; - - @override - void close() { - if (!closed) { - _subscription.close(); - _element._manualListeners?.remove(this); - } - super.close(); - } - - @override - T read() => _subscription.read(); -} diff --git a/packages/flutter_riverpod/lib/src/core.dart b/packages/flutter_riverpod/lib/src/core.dart new file mode 100644 index 000000000..203e43195 --- /dev/null +++ b/packages/flutter_riverpod/lib/src/core.dart @@ -0,0 +1,10 @@ +import 'package:flutter/foundation.dart' hide describeIdentity; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:meta/meta.dart'; + +import './internals.dart'; + +part 'core/consumer.dart'; +part 'core/provider_scope.dart'; +part 'core/widget_ref.dart'; diff --git a/packages/flutter_riverpod/lib/src/core/consumer.dart b/packages/flutter_riverpod/lib/src/core/consumer.dart new file mode 100644 index 000000000..b02275a89 --- /dev/null +++ b/packages/flutter_riverpod/lib/src/core/consumer.dart @@ -0,0 +1,557 @@ +part of '../core.dart'; + +/// A function that can also listen to providers +/// +/// See also [Consumer] +@internal +typedef ConsumerBuilder = Widget Function( + BuildContext context, + WidgetRef ref, + Widget? child, +); + +/// {@template riverpod.consumer} +/// Build a widget tree while listening to providers. +/// +/// [Consumer]'s main use-case is for reducing the number of rebuilt widgets. +/// when a provider changes. +/// +/// As an example, consider: +/// +/// ```dart +/// @riverpod +/// Future fetchUser(Ref ref) async { +/// // ... +/// } +/// ``` +/// +/// Normally, we would use a [ConsumerWidget] as followed: +/// +/// ```dart +/// class Example extends ConsumerWidget { +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// return Scaffold( +/// appBar: AppBar(title: Text('User')), +/// body: switch (ref.watch(userProvider) { +/// AsyncValue(:final value?) => Text(value.name), +/// AsyncValue(hasError: true) => Text('Error'), +/// _ => CircularProgressIndicator(), +/// }, +/// } +/// } +/// ``` +/// +/// However, this would rebuild the entire `Scaffold` when the user changes. +/// If we are looking to reduce this, have two options: +/// - Extract the `body` into a separate [ConsumerWidget]. Then only the `body` will rebuild. +/// This is the recommended approach, but is a bit more verbose. +/// - Use [Consumer] to only rebuild the `body` when the user changes. +/// This is less recommended, but avoids creating a new widget. +/// +/// Using [Consumer], the resulting code would look like: +/// ```dart +/// class Example extends StatelessWidget { +/// @override +/// Widget build(BuildContext context) { +/// return Scaffold( +/// appBar: AppBar(title: Text('User')), +/// body: Consumer( +/// builder: (context, ref, child) { +/// return switch (ref.watch(userProvider) { +/// AsyncValue(:final value?) => Text(value.name), +/// AsyncValue(hasError: true) => Text('Error'), +/// _ => CircularProgressIndicator(), +/// }; +/// }), +/// ); +/// } +/// } +/// ``` +/// +/// ## Performance considerations +/// +/// To optimize performance by avoiding unnecessary network requests and +/// pausing unused streams, [Consumer] will temporarily stop listening to +/// providers when the widget stops being visible. +/// +/// This is determined using [Visibility.of], and will invoke +/// [ProviderSubscription.pause] on all currently active subscriptions. +/// +/// See also: +/// +/// * [ConsumerWidget], a base-class for widgets that wants to listen to providers. +/// {@endtemplate} +@sealed +class Consumer extends ConsumerWidget { + /// {@macro riverpod.consumer} + const Consumer({super.key, required this.builder, this.child}); + + /// The builder that will be called when the provider is updated. + /// + /// The `child` parameter will be the same as [child] if specified, or null otherwise. + /// + /// **Note** + /// You can watch as many providers inside [Consumer] as you want to: + /// ```dart + /// Consumer( + /// builder: (context, ref, child) { + /// final value = ref.watch(someProvider); + /// final another = ref.watch(anotherProvider); + /// ... + /// }, + /// ); + /// ``` + /// + /// See also [child]. + final ConsumerBuilder builder; + + /// The [child] parameter is an optional parameter for the sole purpose of + /// further performance optimizations. + /// + /// If your `builder` function contains a subtree that does not depend on the + /// animation, it is more efficient to build that subtree once instead of + /// rebuilding it on every provider update. + /// + /// If you pass the pre-built subtree as the `child` parameter, the + /// Consumer will pass it back to your builder function so that you + /// can incorporate it into your build. + /// + /// Using this pre-built child is entirely optional, but can improve + /// performance significantly in some cases and is therefore a good practice. + /// + /// This sample shows how you could use a [Consumer] + /// + /// ```dart + /// final counterProvider = StateProvider((ref) => 0); + /// + /// class MyHomePage extends ConsumerWidget { + /// MyHomePage({Key? key, required this.title}) : super(key: key); + /// final String title; + /// + /// @override + /// Widget build(BuildContext context, WidgetRef ref) { + /// return Scaffold( + /// appBar: AppBar( + /// title: Text(title) + /// ), + /// body: Center( + /// child: Column( + /// mainAxisAlignment: MainAxisAlignment.center, + /// children: [ + /// Text('You have pushed the button this many times:'), + /// Consumer( + /// builder: (BuildContext context, WidgetRef ref, Widget? child) { + /// // This builder will only get called when the counterProvider + /// // is updated. + /// final count = ref.watch(counterProvider); + /// + /// return Row( + /// mainAxisAlignment: MainAxisAlignment.spaceEvenly, + /// children: [ + /// Text('$count'), + /// child!, + /// ], + /// ); + /// }, + /// // The child parameter is most helpful if the child is + /// // expensive to build and does not depend on the value from + /// // the notifier. + /// child: Text('Good job!'), + /// ) + /// ], + /// ), + /// ), + /// floatingActionButton: FloatingActionButton( + /// child: Icon(Icons.plus_one), + /// onPressed: () => ref.read(counterProvider.notifier).state++, + /// ), + /// ); + /// } + /// } + /// ``` + final Widget? child; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return builder(context, ref, child); + } +} + +/// {@template riverpod.consumer_widget} +/// The equivalent of a [StatelessWidget] that can listen to providers. +/// +/// Using [ConsumerWidget], this allows the widget tree to listen to changes on +/// provider, so that the UI automatically updates when needed. +/// +/// Do not modify any state or start any http request inside [build]. +/// +/// As a usage example, consider: +/// +/// ```dart +/// final helloWorldProvider = Provider((_) => 'Hello world'); +/// ``` +/// +/// We can then subclass [ConsumerWidget] to listen to `helloWorldProvider` like so: +/// +/// ```dart +/// class Example extends ConsumerWidget { +/// const Example({Key? key}): super(key: key); +/// +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// final value = ref.watch(helloWorldProvider); +/// return Text(value); // Hello world +/// } +/// } +/// ``` +/// +/// **Note** +/// You can watch as many providers inside [build] as you want to: +/// +/// ```dart +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// final value = ref.watch(someProvider); +/// final another = ref.watch(anotherProvider); +/// return Text(value); // Hello world +/// } +/// ``` +/// +/// See also: +/// - [ConsumerStatefulWidget], for a [StatefulWidget] variant. +/// - [Consumer], to help reducing the number of rebuilt widgets without making +/// a new widget. +/// {@endtemplate} +abstract class ConsumerWidget extends ConsumerStatefulWidget { + /// {@macro riverpod.consumer_widget} + const ConsumerWidget({super.key}); + + /// Describes the part of the user interface represented by this widget. + /// + /// The framework calls this method when this widget is inserted into the tree + /// in a given [BuildContext] and when the dependencies of this widget change + /// (e.g., an [InheritedWidget] referenced by this widget changes). This + /// method can potentially be called in every frame and should not have any side + /// effects beyond building a widget. + /// + /// The framework replaces the subtree below this widget with the widget + /// returned by this method, either by updating the existing subtree or by + /// removing the subtree and inflating a new subtree, depending on whether the + /// widget returned by this method can update the root of the existing + /// subtree, as determined by calling [Widget.canUpdate]. + /// + /// Typically implementations return a newly created constellation of widgets + /// that are configured with information from this widget's constructor and + /// from the given [BuildContext]. + /// + /// The given [BuildContext] contains information about the location in the + /// tree at which this widget is being built. For example, the context + /// provides the set of inherited widgets for this location in the tree. A + /// given widget might be built with multiple different [BuildContext] + /// arguments over time if the widget is moved around the tree or if the + /// widget is inserted into the tree in multiple places at once. + /// + /// The implementation of this method must only depend on: + /// + /// * the fields of the widget, which themselves must not change over time, + /// and + /// * any ambient state obtained from the `context` using + /// [BuildContext.dependOnInheritedWidgetOfExactType]. + /// + /// If a widget's [build] method is to depend on anything else, use a + /// [StatefulWidget] instead. + /// + /// See also: + /// + /// * [StatelessWidget], which contains the discussion on performance considerations. + Widget build(BuildContext context, WidgetRef ref); + + @override + // ignore: library_private_types_in_public_api + _ConsumerState createState() => _ConsumerState(); +} + +class _ConsumerState extends ConsumerState { + @override + Widget build(BuildContext context) { + return widget.build(context, ref); + } +} + +/// A [StatefulWidget] that has a [State] capable of reading providers. +/// +/// This is used exactly like a [StatefulWidget], but with a [State] that must +/// subclass [ConsumerState] : +/// +/// ```dart +/// class MyConsumer extends ConsumerStatefulWidget { +/// const MyConsumer({Key? key}): super(key: key); +/// +/// @override +/// ConsumerState createState() => _MyConsumerState(); +/// } +/// +/// class _MyConsumerState extends ConsumerState { +/// @override +/// void initState() { +/// // All State life-cycles can be used +/// super.initState(); +/// } +/// +/// @override +/// Widget build(BuildContext context) { +/// // "ref" is a property of ConsumerState and can be used to read providers +/// ref.watch(someProvider); +/// } +/// } +/// ``` +abstract class ConsumerStatefulWidget extends StatefulWidget { + /// A [StatefulWidget] that can read providers. + const ConsumerStatefulWidget({super.key}); + + @override + ConsumerState createState(); + + @override + StatefulElement createElement() => ConsumerStatefulElement(this); +} + +/// The [State] for a [ConsumerStatefulWidget]. +/// +/// It has all the life-cycles if a normal [State], with the only difference +/// being that it has a [ref] property. +/// +/// It must be used in conjunction with a [ConsumerStatefulWidget] : +/// +/// ```dart +/// class MyConsumer extends ConsumerStatefulWidget { +/// const MyConsumer({Key? key}): super(key: key); +/// +/// @override +/// ConsumerState createState() => _MyConsumerState(); +/// } +/// +/// class _MyConsumerState extends ConsumerState { +/// @override +/// void initState() { +/// // All State life-cycles can be used +/// super.initState(); +/// } +/// +/// @override +/// Widget build(BuildContext context) { +/// // "ref" is a property of ConsumerState and can be used to read providers +/// ref.watch(someProvider); +/// } +/// } +/// ``` +abstract class ConsumerState + extends State { + /// {@macro flutter_riverpod.widget_ref} + late final WidgetRef ref = context as WidgetRef; +} + +/// The [Element] for a [ConsumerStatefulWidget] +@internal +class ConsumerStatefulElement extends StatefulElement implements WidgetRef { + /// The [Element] for a [ConsumerStatefulWidget] + ConsumerStatefulElement(ConsumerStatefulWidget super.widget); + + @override + BuildContext get context => this; + + late ProviderContainer _container = ProviderScope.containerOf(this); + var _dependencies = + , ProviderSubscription>{}; + Map, ProviderSubscription>? + _oldDependencies; + final _listeners = >[]; + List>? _manualListeners; + bool? _visible; + + Iterable get _allSubscriptions sync* { + yield* _dependencies.values; + yield* _listeners; + if (_manualListeners != null) { + yield* _manualListeners!; + } + } + + void _applyVisibility(ProviderSubscription sub) { + if (_visible == false) sub.pause(); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + final newContainer = ProviderScope.containerOf(this); + if (_container != newContainer) { + _container = newContainer; + for (final dependency in _dependencies.values) { + dependency.close(); + } + _dependencies.clear(); + } + } + + @override + Widget build() { + final visible = Visibility.of(context); + if (visible != _visible) { + _visible = visible; + for (final sub in _allSubscriptions) { + if (visible) { + sub.resume(); + } else { + sub.pause(); + } + } + } + + try { + _oldDependencies = _dependencies; + for (var i = 0; i < _listeners.length; i++) { + _listeners[i].close(); + } + _listeners.clear(); + _dependencies = {}; + return super.build(); + } finally { + for (final dep in _oldDependencies!.values) { + dep.close(); + } + _oldDependencies = null; + } + } + + void _assertNotDisposed() { + if (!context.mounted) { + throw StateError('Cannot use "ref" after the widget was disposed.'); + } + } + + @override + Res watch(ProviderListenable target) { + _assertNotDisposed(); + return _dependencies.putIfAbsent(target, () { + final oldDependency = _oldDependencies?.remove(target); + + if (oldDependency != null) { + return oldDependency; + } + + final sub = _container.listen( + target, + (_, __) => markNeedsBuild(), + ); + _applyVisibility(sub); + return sub; + }).read() as Res; + } + + @override + void unmount() { + /// Calling `super.unmount()` will call `dispose` on the state + /// And [ListenManual] subscriptions should be closed after `dispose` + super.unmount(); + + for (final dependency in _dependencies.values) { + dependency.close(); + } + for (var i = 0; i < _listeners.length; i++) { + _listeners[i].close(); + } + final manualListeners = _manualListeners?.toList(); + if (manualListeners != null) { + for (final listener in manualListeners) { + listener.close(); + } + _manualListeners = null; + } + } + + @override + void listen( + ProviderListenable provider, + void Function(T? previous, T value) listener, { + void Function(Object error, StackTrace stackTrace)? onError, + }) { + _assertNotDisposed(); + assert( + debugDoingBuild, + 'ref.listen can only be used within the build method of a ConsumerWidget', + ); + + // We can't implement a fireImmediately flag because we wouldn't know + // which listen call was preserved between widget rebuild, and we wouldn't + // want to call the listener on every rebuild. + final sub = _container.listen(provider, listener, onError: onError); + _applyVisibility(sub); + _listeners.add(sub); + } + + @override + bool exists(ProviderBase provider) { + _assertNotDisposed(); + return ProviderScope.containerOf(this, listen: false).exists(provider); + } + + @override + T read(ProviderListenable provider) { + _assertNotDisposed(); + return ProviderScope.containerOf(this, listen: false).read(provider); + } + + @override + State refresh(Refreshable provider) { + _assertNotDisposed(); + return ProviderScope.containerOf(this, listen: false).refresh(provider); + } + + @override + void invalidate( + ProviderOrFamily provider, { + bool asReload = false, + }) { + _assertNotDisposed(); + _container.invalidate(provider, asReload: asReload); + } + + @override + ProviderSubscription listenManual( + ProviderListenable provider, + void Function(T? previous, T next) listener, { + void Function(Object error, StackTrace stackTrace)? onError, + bool fireImmediately = false, + }) { + _assertNotDisposed(); + final listeners = _manualListeners ??= []; + + // Reading the container using "listen:false" to guarantee that this can + // be used inside initState. + final container = ProviderScope.containerOf(this, listen: false); + + final innerSubscription = container.listen( + provider, + listener, + onError: onError, + fireImmediately: fireImmediately, + // ignore: invalid_use_of_internal_member, from riverpod + ) as ProviderSubscriptionWithOrigin; + + // ignore: invalid_use_of_internal_member, from riverpod + late final ProviderSubscriptionView sub; + sub = ProviderSubscriptionView( + innerSubscription: innerSubscription, + listener: (prev, next) {}, + onError: (error, stackTrace) {}, + onClose: () => _manualListeners?.remove(sub), + read: innerSubscription.read, + ); + _applyVisibility(sub); + listeners.add(sub); + + return sub; + } +} diff --git a/packages/flutter_riverpod/lib/src/framework.dart b/packages/flutter_riverpod/lib/src/core/provider_scope.dart similarity index 69% rename from packages/flutter_riverpod/lib/src/framework.dart rename to packages/flutter_riverpod/lib/src/core/provider_scope.dart index 2889afbb6..3437360d3 100644 --- a/packages/flutter_riverpod/lib/src/framework.dart +++ b/packages/flutter_riverpod/lib/src/core/provider_scope.dart @@ -1,12 +1,5 @@ -// ignore_for_file: invalid_use_of_internal_member, deprecated_member_use_from_same_package - -import 'package:flutter/foundation.dart' hide describeIdentity; -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:flutter/widgets.dart'; -import 'package:meta/meta.dart'; - -import 'internals.dart'; +// ignore_for_file: invalid_use_of_internal_member +part of '../core.dart'; /// {@template riverpod.provider_scope} /// A widget that stores the state of providers. @@ -76,17 +69,13 @@ import 'internals.dart'; /// - [UncontrolledProviderScope], which exposes a [ProviderContainer] to the widget /// tree without managing its life-cycles. /// {@endtemplate} -@sealed class ProviderScope extends StatefulWidget { /// {@macro riverpod.provider_scope} const ProviderScope({ super.key, this.overrides = const [], this.observers, - @Deprecated( - 'Will be removed in 3.0.0. See https://github.com/rrousselGit/riverpod/issues/3261#issuecomment-1973514033', - ) - this.parent, + this.retry, required this.child, }); @@ -113,44 +102,35 @@ class ProviderScope extends StatefulWidget { return scope.container; } - /// Explicitly override the parent [ProviderContainer] that this [ProviderScope] - /// would be a descendant of. - /// - /// A common use-case is to allow modals to access scoped providers, as they - /// would otherwise be unable to since they would be in a different branch - /// of the widget tree. - /// - /// That can be achieved with: - /// - /// ```dart - /// ElevatedButton( - /// onTap: () { - /// final container = ProviderScope.containerOf(context); - /// showDialog( - /// context: context, - /// builder: (context) { - /// return ProviderScope(parent: container, child: MyModal()); - /// }, - /// ); - /// }, - /// child: Text('show modal'), - /// ) - /// ``` - /// + /// The default retry logic used by providers associated to this container. /// - /// The [parent] variable must never change. - @Deprecated( - 'Will be removed in 3.0.0. See https://github.com/rrousselGit/riverpod/issues/3261#issuecomment-1973514033', - ) - final ProviderContainer? parent; + /// The default implementation: + /// - has unlimited retries + /// - starts with a delay of 200ms + /// - doubles the delay on each retry up to 6.4 seconds + /// - retries all failures + final Retry? retry; /// The part of the widget tree that can use Riverpod and has overridden providers. final Widget child; - /// The listeners that subscribes to changes on providers stored on this [ProviderScope]. + /// The listeners that subscribe to changes on providers stored on this [ProviderScope]. + /// + /// See [ProviderObserver] for more information. final List? observers; /// Information on how to override a provider/family. + /// + /// This can be used either for: + /// - testing, such as to mock a provider + /// - dependency injection, to avoid having to pass a value to many + /// widgets in the widget tree. + /// - performance optimization: By using this to inject values to widgets + /// using `ref` inside of their constructor, widgets may be able to use + /// `const` constructors, which can improve performance. + /// + /// **Note**: Overrides only apply to this [ProviderScope] and its descendants. + /// Ancestors of this [ProviderScope] will not be affected by the overrides. final List overrides; @override @@ -158,13 +138,10 @@ class ProviderScope extends StatefulWidget { } /// Do not use: The [State] of [ProviderScope] -@visibleForTesting -@sealed @internal -class ProviderScopeState extends State { +final class ProviderScopeState extends State { /// The [ProviderContainer] exposed to [ProviderScope.child]. @visibleForTesting - // ignore: diagnostic_describe_all_properties late final ProviderContainer container; ProviderContainer? _debugParentOwner; var _dirty = false; @@ -174,80 +151,46 @@ class ProviderScopeState extends State { super.initState(); final parent = _getParent(); - assert( - () { - _debugParentOwner = parent; - return true; - }(), - '', - ); + if (kDebugMode) { + _debugParentOwner = parent; + } container = ProviderContainer( parent: parent, overrides: widget.overrides, observers: widget.observers, - // TODO How to report to FlutterError? - // onError: (dynamic error, stack) { - // FlutterError.reportError( - // FlutterErrorDetails( - // library: 'flutter_provider', - // exception: error, - // stack: stack, - // ), - // ); - // }, + retry: widget.retry, ); } ProviderContainer? _getParent() { - if (widget.parent != null) { - return widget.parent; - } else { - final scope = context - .getElementForInheritedWidgetOfExactType() - ?.widget as UncontrolledProviderScope?; + final scope = context + .getElementForInheritedWidgetOfExactType() + ?.widget as UncontrolledProviderScope?; - return scope?.container; - } + return scope?.container; } @override void didUpdateWidget(ProviderScope oldWidget) { super.didUpdateWidget(oldWidget); _dirty = true; + } - if (oldWidget.parent != widget.parent) { - FlutterError.reportError( - FlutterErrorDetails( - library: 'flutter_riverpod', - exception: UnsupportedError( - 'Changing ProviderScope.parent is not supported', - ), - context: ErrorDescription('while rebuilding ProviderScope'), - ), + void _debugAssertParentDidNotChange() { + final parent = _getParent(); + + if (parent != _debugParentOwner) { + throw UnsupportedError( + 'ProviderScope was rebuilt with a different ProviderScope ancestor', ); } } @override Widget build(BuildContext context) { - assert( - () { - if (widget.parent != null) { - // didUpdateWidget already takes care of widget.parent change - return true; - } - final parent = _getParent(); - - if (parent != _debugParentOwner) { - throw UnsupportedError( - 'ProviderScope was rebuilt with a different ProviderScope ancestor', - ); - } - return true; - }(), - '', - ); + if (kDebugMode) _debugAssertParentDidNotChange(); + if (_dirty) { _dirty = false; container.updateOverrides(widget.overrides); @@ -271,8 +214,7 @@ class ProviderScopeState extends State { /// /// This is what makes `ref.watch`/`Consumer`/`ref.read` work. /// {@endtemplate} -@sealed -class UncontrolledProviderScope extends InheritedWidget { +final class UncontrolledProviderScope extends InheritedWidget { /// {@macro riverpod.UncontrolledProviderScope} const UncontrolledProviderScope({ super.key, @@ -318,13 +260,9 @@ class _UncontrolledProviderScopeElement extends InheritedElement { @override void reassemble() { super.reassemble(); - assert( - () { - _containerOf(widget).debugReassemble(); - return true; - }(), - '', - ); + if (kDebugMode) { + _containerOf(widget).debugReassemble(); + } } void _flutterVsync(void Function() task) { diff --git a/packages/flutter_riverpod/lib/src/core/widget_ref.dart b/packages/flutter_riverpod/lib/src/core/widget_ref.dart new file mode 100644 index 000000000..213c7614f --- /dev/null +++ b/packages/flutter_riverpod/lib/src/core/widget_ref.dart @@ -0,0 +1,386 @@ +part of '../core.dart'; + +/// {@template flutter_riverpod.widget_ref} +/// An object that allows widgets to interact with providers. +/// +/// [WidgetRef]s are typically obtained by using [ConsumerWidget] or its variants: +/// +/// ```dart +/// class Example extends ConsumerWidget { +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// // We now have a "ref" +/// } +/// } +/// ``` +/// +/// Once we have a [WidgetRef], we can use its various methods to interact with +/// providers. +/// The most common use-case is to use [watch] inside the `build` method of our +/// widgets. This will enable our UI to update whenever the state of a provider +/// changes: +/// +/// ```dart +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// final count = ref.watch(counterProvider); +/// // The text will automatically update whenever `counterProvider` emits a new value +/// return Text('$count'); +/// } +/// ``` +/// +/// **Note**: +/// Using a [WidgetRef] is equivalent to writing UI logic. +/// As such, [WidgetRef]s should not leave the widget layer. If you need to +/// interact with providers outside of the widget layer, consider using +/// a [Ref] instead. +/// +/// +/// {@endtemplate} +abstract class WidgetRef { + /// The [BuildContext] of the widget associated to this [WidgetRef]. + /// + /// This is strictly identical to the [BuildContext] passed to [ConsumerWidget.build]. + BuildContext get context; + + /// Returns the value exposed by a provider and rebuild the widget when that + /// value changes. + /// + /// This method should only be used at the "root" of the `build` method of a widget. + /// + /// **Good**: Use [watch] inside the `build` method. + /// ```dart + /// class Example extends ConsumerWidget { + /// @override + /// Widget build(BuildContext context, WidgetRef ref) { + /// // Correct, we are inside the build method and at its root. + /// final count = ref.watch(counterProvider); + /// } + /// } + /// ``` + /// **Good**: It is accepted to use [watch] at the root of "builders" too. + /// ```dart + /// class Example extends ConsumerWidget { + /// @override + /// Widget build(BuildContext context, WidgetRef ref) { + /// return ListView.builder( + /// itemBuilder: (context) { + /// // This is accepted, as we are at the root of a "builder" + /// final count = ref.watch(counterProvider); + /// }, + /// ); + /// } + /// } + /// ``` + /// + /// **Bad**: Don't use [watch] outside of the `build` method. + /// ```dart + /// class Example extends ConsumerStatefulWidget { + /// @override + /// ExampleState createState() => ExampleState(); + /// } + /// + /// class ExampleState extends ConsumerState { + /// @override + /// void initState() { + /// super.initState(); + /// // Incorrect, we are not inside the build method. + /// final count = ref.watch(counterProvider); + /// } + /// } + /// ``` + /// + /// **Bad**: Don't use [watch] inside event handles withing `build` method. + /// ```dart + /// class Example extends ConsumerWidget { + /// @override + /// Widget build(BuildContext context, WidgetRef ref) { + /// return ElevatedButton( + /// onTap: () { + /// // Incorrect, we are inside the build method, but neither at its + /// // root, nor inside a "builder". + /// final count = ref.watch(counterProvider); + /// } + /// ); + /// } + /// } + /// ``` + /// + /// See also: + /// + /// - [ProviderListenable.select], which allows a widget to filter rebuilds by + /// observing only the selected properties. + /// - [listen], to react to changes on a provider, such as for showing modals. + T watch(ProviderListenable provider); + + /// Determines whether a provider is initialized or not. + /// + /// Writing logic that conditionally depends on the existence of a provider + /// is generally unsafe and should be avoided. + /// The problem is that once the provider gets initialized, logic that + /// depends on the existence or not of a provider won't be rerun; possibly + /// causing your state to get out of date. + /// + /// But it can be useful in some cases, such as to avoid re-fetching an + /// object if a different network request already obtained it: + /// + /// ```dart + /// final fetchItemList = FutureProvider>(...); + /// + /// final fetchItem = FutureProvider.autoDispose.family((ref, id) async { + /// if (ref.exists(fetchItemList)) { + /// // If `fetchItemList` is initialized, we look into its state + /// // and return the already obtained item. + /// final itemFromItemList = await ref.watch( + /// fetchItemList.selectAsync((items) => items.firstWhereOrNull((item) => item.id == id)), + /// ); + /// if (itemFromItemList != null) return itemFromItemList; + /// } + /// + /// // If `fetchItemList` is not initialized, perform a network request for + /// // "id" separately + /// + /// final json = await http.get('api/items/$id'); + /// return Item.fromJson(json); + /// }); + /// ``` + bool exists(ProviderBase provider); + + /// Listen to a provider and call `listener` whenever its value changes, + /// without having to take care of removing the listener. + /// + /// The [listen] method should exclusively be used at the root of the `build`: + /// + /// **Good**: Use [listen] inside the `build` method. + /// ```dart + /// class Example extends ConsumerWidget { + /// @override + /// Widget build(BuildContext context, WidgetRef ref) { + /// // Correct, we are inside the build method and at its root. + /// ref.listen(counterProvider, (prev, next) {}); + /// } + /// } + /// ``` + /// + /// **Bad**: Do not use [listen] inside builders. + /// ```dart + /// class Example extends ConsumerWidget { + /// @override + /// Widget build(BuildContext context, WidgetRef ref) { + /// return ListView.builder( + /// itemBuilder: (context) { + /// // This is accepted, as we are at the root of a "builder" + /// ref.listen(counterProvider, (prev, next) {}); + /// }, + /// ); + /// } + /// } + /// ``` + /// + /// **Bad**: Don't use [listen] outside of the `build` method. + /// ```dart + /// class Example extends ConsumerStatefulWidget { + /// @override + /// ExampleState createState() => ExampleState(); + /// } + /// + /// class ExampleState extends ConsumerState { + /// @override + /// void initState() { + /// super.initState(); + /// // Incorrect, we are not inside the build method. + /// ref.listen(counterProvider, (prev, next) {}); + /// } + /// } + /// ``` + /// + /// **Bad**: Don't use [listen] inside event handles withing `build` method. + /// ```dart + /// class Example extends ConsumerWidget { + /// @override + /// Widget build(BuildContext context, WidgetRef ref) { + /// return ElevatedButton( + /// onTap: () { + /// // Incorrect, we are inside the build method, but neither at its + /// // root, nor inside a "builder". + /// ref.listen(counterProvider, (prev, next) {}); + /// } + /// ); + /// } + /// } + /// ``` + /// + /// **Note**: + /// Listeners will automatically be removed if a widget rebuilds and stops + /// listening to a provider. + /// + /// See also: + /// - [listenManual], for listening to a provider from outside `build`. + /// - [watch], to listen to providers in a declarative manner. + /// - [read], to read a provider without listening to it. + /// + /// This is useful for showing modals or other imperative logic. + void listen( + ProviderListenable provider, + void Function(T? previous, T next) listener, { + void Function(Object error, StackTrace stackTrace)? onError, + }); + + /// Listen to a provider and call `listener` whenever its value changes. + /// + /// As opposed to [listen], [listenManual] is not safe to use within the `build` + /// method of a widget. + /// Instead, [listenManual] is designed to be used inside [State.initState] or + /// other [State] lifecycle. + /// + /// [listenManual] returns a [ProviderSubscription] which can be used to stop + /// listening to the provider, or to read the current value exposed by + /// the provider. + /// + /// It is not necessary to call [ProviderSubscription.close] inside [State.dispose]. + /// When the widget that calls [listenManual] is disposed, the subscription + /// will be disposed automatically. + ProviderSubscription listenManual( + ProviderListenable provider, + void Function(T? previous, T next) listener, { + void Function(Object error, StackTrace stackTrace)? onError, + bool fireImmediately, + }); + + /// Reads a provider without listening to it. + /// + /// **AVOID** calling [read] inside build if the value is used only for events: + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // counter is used only for the onPressed of RaisedButton + /// final counter = ref.read(counterProvider); + /// + /// return RaisedButton( + /// onPressed: () => counter.increment(), + /// ); + /// } + /// ``` + /// + /// While this code is not bugged in itself, this is an anti-pattern. + /// It could easily lead to bugs in the future after refactoring the widget + /// to use `counter` for other things, but forget to change [read] into [Consumer]/`ref.watch(`. + /// + /// **CONSIDER** calling [read] inside event handlers: + /// + /// ```dart + /// Widget build(BuildContext context) { + /// return RaisedButton( + /// onPressed: () { + /// // as performant as the previous solution, but resilient to refactoring + /// ref.read(counterProvider).increment(), + /// }, + /// ); + /// } + /// ``` + /// + /// This has the same efficiency as the previous anti-pattern, but does not + /// suffer from the drawback of being brittle. + /// + /// **AVOID** using [read] for creating widgets with a value that never changes + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // using read because we only use a value that never changes. + /// final model = ref.read(modelProvider); + /// + /// return Text('${model.valueThatNeverChanges}'); + /// } + /// ``` + /// + /// While the idea of not rebuilding the widget if unnecessary is good, + /// this should not be done with [read]. + /// Relying on [read] for optimizations is very brittle and dependent + /// on an implementation detail. + /// + /// **CONSIDER** using [Provider] or `select` for filtering unwanted rebuilds: + /// + /// ```dart + /// Widget build(BuildContext context) { + /// // Using select to listen only to the value that used + /// final valueThatNeverChanges = ref.watch(modelProvider.select((model) { + /// return model.valueThatNeverChanges; + /// })); + /// + /// return Text('$valueThatNeverChanges'); + /// } + /// ``` + /// + /// While more verbose than [read], using [Provider]/`select` is a lot safer. + /// It does not rely on implementation details on `Model`, and it makes + /// impossible to have a bug where our UI does not refresh. + T read(ProviderListenable provider); + + /// Forces a provider to re-evaluate its state immediately, and return the created value. + /// + /// Using [refresh] is strictly identical to using [invalidate] followed by [read] : + /// + /// ```dart + /// ref.invalidate(provider); + /// final newValue = ref.read(provider); + /// ``` + /// + /// If you do not care about the return value of [refresh], use [invalidate] instead. + /// Doing so has the benefits of: + /// - making the invalidation logic more resilient by avoiding multiple + /// refreshes at once. + /// - possibly avoiding recomputing a provider if it isn't needed immediately. + /// + /// This method is useful for features like "pull to refresh" or "retry on error", + /// to restart a specific provider. + /// + /// For example, a pull-to-refresh may be implemented by combining + /// [FutureProvider] and a [RefreshIndicator] : + /// + /// ```dart + /// final productsProvider = FutureProvider((ref) async { + /// final response = await httpClient.get('https://host.com/products'); + /// return Products.fromJson(response.data); + /// }); + /// + /// class Example extends ConsumerWidget { + /// @override + /// Widget build(BuildContext context, WidgetRef ref) { + /// final Products products = ref.watch(productsProvider); + /// + /// return RefreshIndicator( + /// onRefresh: () => ref.refresh(productsProvider.future), + /// child: ListView( + /// children: [ + /// for (final product in products.items) ProductItem(product: product), + /// ], + /// ), + /// ); + /// } + /// } + /// ``` + @useResult + State refresh(Refreshable provider); + + /// Invalidates the state of the provider, causing it to refresh. + /// + /// As opposed to [refresh], the refresh is not immediate and is instead + /// delayed to the next read or next frame. + /// + /// Calling [invalidate] multiple times will refresh the provider only + /// once. + /// Calling [invalidate] will cause the provider to be disposed immediately. + /// + /// - [asReload] (false by default) can be optionally passed to tell + /// Riverpod to clear the state before refreshing it. + /// This is only useful for asynchronous providers, as by default, + /// [AsyncValue] keeps a reference on state during loading states. + /// Using [asReload] will disable this behavior and count as a + /// "hard refresh". + /// + /// If used on a provider which is not initialized, this method will have no effect. + void invalidate( + ProviderOrFamily provider, { + bool asReload = false, + }); +} diff --git a/packages/flutter_riverpod/lib/src/internals.dart b/packages/flutter_riverpod/lib/src/internals.dart index 8609c050b..93b81e119 100644 --- a/packages/flutter_riverpod/lib/src/internals.dart +++ b/packages/flutter_riverpod/lib/src/internals.dart @@ -1,4 +1,4 @@ export 'package:riverpod/src/internals.dart'; -export 'change_notifier_provider.dart'; -export 'consumer.dart'; -export 'framework.dart'; + +export './core.dart'; +export 'providers/legacy/change_notifier_provider.dart'; diff --git a/packages/flutter_riverpod/lib/src/providers/legacy/change_notifier_provider.dart b/packages/flutter_riverpod/lib/src/providers/legacy/change_notifier_provider.dart new file mode 100644 index 000000000..b9fcf133b --- /dev/null +++ b/packages/flutter_riverpod/lib/src/providers/legacy/change_notifier_provider.dart @@ -0,0 +1,257 @@ +// ignore_for_file: invalid_use_of_internal_member + +import 'package:flutter/foundation.dart'; +import 'package:meta/meta.dart'; +// ignore: implementation_imports +import 'package:riverpod/src/internals.dart'; + +import '../../builders.dart'; + +ProviderElementProxy + _notifier( + ChangeNotifierProvider that, +) { + return ProviderElementProxy( + that, + (element) { + return (element as ChangeNotifierProviderElement) + ._notifierNotifier; + }, + ); +} + +/// Creates a [ChangeNotifier] and exposes its current state. +/// +/// Combined with [ChangeNotifier], [ChangeNotifierProvider] can be used to manipulate +/// advanced states, that would otherwise be difficult to represent with simpler +/// providers such as [Provider] or [FutureProvider]. +/// +/// For example, you may have a todo-list, where you can add and remove +/// and complete a todo. +/// Using [ChangeNotifier], you could represent such state as: +/// +/// ```dart +/// class TodosNotifier extends ChangeNotifier { +/// List todos = []; +/// +/// void add(Todo todo) { +/// todos.add(todo); +/// notifyListeners(); +/// } +/// +/// void remove(String todoId) { +/// todos.removeWhere((todo) => todo.id == todoId); +/// notifyListeners(); +/// } +/// +/// void toggle(String todoId) { +/// final todo = todos.firstWhere((todo) => todo.id == todoId); +/// todo.completed = !todo.completed; +/// notifyListeners(); +/// } +/// } +/// ``` +/// +/// Which you can then pass to a [ChangeNotifierProvider] like so: +/// +/// ```dart +/// final todosProvider = ChangeNotifierProvider>((ref) => TodosNotifier()); +/// ``` +/// +/// And finally, you can interact with it inside your UI: +/// +/// ```dart +/// Widget build(BuildContext context, WidgetRef ref) { +/// // rebuild the widget when the todo list changes +/// List todos = ref.watch(todosProvider).todos; +/// +/// return ListView( +/// children: [ +/// for (final todo in todos) +/// CheckboxListTile( +/// value: todo.completed, +/// // When tapping on the todo, change its completed status +/// onChanged: (value) => ref.read(todosProvider.notifier).toggle(todo.id), +/// title: Text(todo.description), +/// ), +/// ], +/// ); +/// } +/// ``` +final class ChangeNotifierProvider + extends $FunctionalProvider + with LegacyProviderMixin { + /// {@macro riverpod.change_notifier_provider} + ChangeNotifierProvider( + this._createFn, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + from: null, + argument: null, + ); + + /// An implementation detail of Riverpod + @internal + const ChangeNotifierProvider.internal( + this._createFn, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.isAutoDispose, + super.from, + super.argument, + super.retry, + }); + + /// {@macro riverpod.autoDispose} + static const autoDispose = AutoDisposeChangeNotifierProviderBuilder(); + + /// {@macro riverpod.family} + static const family = ChangeNotifierProviderFamilyBuilder(); + + /// Obtains the [ChangeNotifier] associated with this provider, without listening + /// to state changes. + /// + /// This is typically used to invoke methods on a [ChangeNotifier]. For example: + /// + /// ```dart + /// Button( + /// onTap: () => ref.read(changeNotifierProvider.notifier).increment(), + /// ) + /// ``` + /// + /// This listenable will notify its notifiers if the [ChangeNotifier] instance + /// changes. + /// This may happen if the provider is refreshed or one of its dependencies + /// has changes. + Refreshable get notifier => _notifier(this); + + final NotifierT Function(Ref ref) _createFn; + + @internal + @override + ChangeNotifierProviderElement $createElement( + $ProviderPointer pointer, + ) { + return ChangeNotifierProviderElement._(this, pointer); + } + + @mustBeOverridden + @visibleForOverriding + @override + ChangeNotifierProvider $copyWithCreate( + Create create, + ) { + return ChangeNotifierProvider.internal( + create, + name: name, + dependencies: dependencies, + isAutoDispose: isAutoDispose, + from: from, + argument: argument, + allTransitiveDependencies: allTransitiveDependencies, + ); + } +} + +/// The element of [ChangeNotifierProvider]. +class ChangeNotifierProviderElement + extends ProviderElement { + ChangeNotifierProviderElement._(this.provider, super.pointer); + + @override + final ChangeNotifierProvider provider; + + final _notifierNotifier = $ElementLense(); + + void Function()? _removeListener; + + @override + WhenComplete create(Ref ref, {required bool didChangeDependency}) { + final notifierResult = _notifierNotifier.result = $Result.guard( + () => provider._createFn(ref), + ); + + final notifier = notifierResult.requireState; + + setStateResult(ResultData(notifier)); + + if (notifier != null) { + void listener() => setStateResult(ResultData(notifier)); + notifier.addListener(listener); + _removeListener = () => notifier.removeListener(listener); + } + + return null; + } + + @override + bool updateShouldNotify(NotifierT previous, NotifierT next) => true; + + @override + void runOnDispose() { + super.runOnDispose(); + + _removeListener?.call(); + _removeListener = null; + + final notifier = _notifierNotifier.result?.stateOrNull; + if (notifier != null) { + runGuarded(notifier.dispose); + } + _notifierNotifier.result = null; + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + listenableVisitor(_notifierNotifier); + } +} + +/// The [Family] of [ChangeNotifierProvider]. +class ChangeNotifierProviderFamily + extends FunctionalFamily> { + /// The [Family] of [ChangeNotifierProvider]. + ChangeNotifierProviderFamily( + super._createFn, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + providerFactory: ChangeNotifierProvider.internal, + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + ); + + @override + Override overrideWith( + NotifierT Function(Ref ref, Arg arg) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ChangeNotifierProvider; + + return ChangeNotifierProvider.internal( + (ref) => create(ref, provider.argument as Arg), + from: provider.from, + argument: provider.argument, + isAutoDispose: provider.isAutoDispose, + dependencies: null, + allTransitiveDependencies: null, + name: null, + ).$createElement(pointer); + }, + ); + } +} diff --git a/packages/flutter_riverpod/pubspec.yaml b/packages/flutter_riverpod/pubspec.yaml index 1058adff5..313841192 100644 --- a/packages/flutter_riverpod/pubspec.yaml +++ b/packages/flutter_riverpod/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_riverpod description: > A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze. -version: 2.6.1 +version: 3.0.0-dev.3 homepage: https://riverpod.dev repository: https://github.com/rrousselGit/riverpod issue_tracker: https://github.com/rrousselGit/riverpod/issues @@ -10,7 +10,7 @@ funding: - https://github.com/sponsors/rrousselGit/ environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0+0.0-dev <4.0.0" flutter: ">=3.0.0" dependencies: @@ -18,7 +18,7 @@ dependencies: flutter: sdk: flutter meta: ^1.4.0 - riverpod: 2.6.1 + riverpod: 3.0.0-dev.3 state_notifier: ">=0.7.2 <2.0.0" dev_dependencies: diff --git a/packages/flutter_riverpod/test/analysis_options.yaml b/packages/flutter_riverpod/test/analysis_options.yaml index 38c1cf1a8..99904038d 100644 --- a/packages/flutter_riverpod/test/analysis_options.yaml +++ b/packages/flutter_riverpod/test/analysis_options.yaml @@ -1,5 +1,10 @@ -import: "../../analysis_options.yaml" - +include: ../../../analysis_options.yaml analyzer: errors: - deprecated_member_use_from_same_package: false + unused_local_variable: ignore + +linter: + rules: + # Some tests may want to explicitly create an unused variable without type inference, + # for the sake of testing that that two types are assignable + omit_local_variable_types: false diff --git a/packages/flutter_riverpod/test/auto_dispose_change_notifier_provider_test.dart b/packages/flutter_riverpod/test/auto_dispose_change_notifier_provider_test.dart index 18d990ba6..6c9b9ac67 100644 --- a/packages/flutter_riverpod/test/auto_dispose_change_notifier_provider_test.dart +++ b/packages/flutter_riverpod/test/auto_dispose_change_notifier_provider_test.dart @@ -1,5 +1,6 @@ import 'package:flutter/widgets.dart' hide Listener; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; @@ -7,7 +8,7 @@ import 'utils.dart'; void main() { test('auto-dispose notifier when stop listening', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final onDispose = OnDisposeMock(); final provider = ChangeNotifierProvider.autoDispose((ref) { ref.onDispose(onDispose.call); @@ -30,7 +31,7 @@ void main() { }); test('family', () { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = ChangeNotifierProvider.autoDispose .family, int>((ref, value) { return ValueNotifier(value); @@ -69,7 +70,7 @@ void main() { final provider = ChangeNotifierProvider.autoDispose((ref) { return ref.watch(dep) == 0 ? notifier : notifier2; }); - final container = createContainer(); + final container = ProviderContainer.test(); addTearDown(container.dispose); var callCount = 0; @@ -137,55 +138,6 @@ void main() { expect(notifier.mounted, isFalse); }); - - // test( - // 'overrideWithValue listens to the notifier, support notifier change, and does not dispose of the notifier', - // () async { - // final provider = ChangeNotifierProvider.autoDispose((_) => TestNotifier()); - // final notifier = TestNotifier(); - // final notifier2 = TestNotifier(); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(notifier), - // ]); - // addTearDown(container.dispose); - - // var callCount = 0; - // final sub = container.listen(provider, (_, __) => callCount++); - // final notifierSub = container.listen(provider.notifier, (_, __) {}); - - // expect(sub.read(), notifier); - // expect(callCount, 0); - // expect(notifierSub.read(), notifier); - // expect(notifier.hasListeners, true); - - // notifier.count++; - - // await container.pump(); - // expect(callCount, 1); - - // container.updateOverrides([ - // provider.overrideWithValue(notifier2), - // ]); - - // await container.pump(); - // expect(callCount, 2); - // expect(notifier.hasListeners, false); - // expect(notifier2.hasListeners, true); - // expect(notifier.mounted, true); - // expect(notifierSub.read(), notifier2); - - // notifier2.count++; - - // await container.pump(); - // expect(callCount, 3); - - // container.dispose(); - - // expect(callCount, 3); - // expect(notifier2.hasListeners, false); - // expect(notifier2.mounted, true); - // expect(notifier.mounted, true); - // }); } class OnDisposeMock extends Mock { @@ -196,7 +148,6 @@ class TestNotifier extends ChangeNotifier { bool mounted = true; @override - // ignore: unnecessary_overrides bool get hasListeners => super.hasListeners; int _count = 0; diff --git a/packages/flutter_riverpod/test/consumer_listen_test.dart b/packages/flutter_riverpod/test/consumer_listen_test.dart index f9530e9f5..76bc0ccfd 100644 --- a/packages/flutter_riverpod/test/consumer_listen_test.dart +++ b/packages/flutter_riverpod/test/consumer_listen_test.dart @@ -1,5 +1,8 @@ +// ignore_for_file: invalid_use_of_internal_member + import 'package:flutter/material.dart' hide Listener; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; @@ -11,7 +14,7 @@ void main() { final dep = StateProvider((ref) => 0); final provider = Provider((ref) => ref.watch(dep)); - final container = createContainer(); + final container = ProviderContainer.test(); final listener = Listener(); await tester.pumpWidget( @@ -47,7 +50,7 @@ void main() { }); testWidgets('can mark parents as dirty during onChange', (tester) async { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = StateProvider((ref) => 0); final onChange = Listener(); @@ -79,7 +82,7 @@ void main() { testWidgets('calls onChange synchronously if possible', (tester) async { final provider = StateProvider((ref) => 0); final onChange = Listener(); - final container = createContainer(); + final container = ProviderContainer.test(); await tester.pumpWidget( UncontrolledProviderScope( @@ -111,7 +114,7 @@ void main() { final provider = StateProvider((ref) => 0); final isEven = Provider((ref) => ref.watch(provider).isEven); final onChange = Listener(); - final container = createContainer(); + final container = ProviderContainer.test(); await tester.pumpWidget( UncontrolledProviderScope( @@ -140,7 +143,7 @@ void main() { testWidgets('closes the subscription on dispose', (tester) async { final provider = StateProvider((ref) => 0); final onChange = Listener(); - final container = createContainer(); + final container = ProviderContainer.test(); await tester.pumpWidget( UncontrolledProviderScope( @@ -154,16 +157,19 @@ void main() { ), ); - expect(container.readProviderElement(provider).hasListeners, true); + expect(container.readProviderElement(provider).hasNonWeakListeners, true); await tester.pumpWidget(Container()); - expect(container.readProviderElement(provider).hasListeners, false); + expect( + container.readProviderElement(provider).hasNonWeakListeners, + false, + ); }); testWidgets('closes the subscription on provider change', (tester) async { final provider = StateProvider.family((ref, _) => 0); - final container = createContainer(); + final container = ProviderContainer.test(); await tester.pumpWidget( UncontrolledProviderScope( @@ -177,8 +183,14 @@ void main() { ), ); - expect(container.readProviderElement(provider(0)).hasListeners, true); - expect(container.readProviderElement(provider(1)).hasListeners, false); + expect( + container.readProviderElement(provider(0)).hasNonWeakListeners, + true, + ); + expect( + container.readProviderElement(provider(1)).hasNonWeakListeners, + false, + ); await tester.pumpWidget( UncontrolledProviderScope( @@ -192,14 +204,20 @@ void main() { ), ); - expect(container.readProviderElement(provider(0)).hasListeners, false); - expect(container.readProviderElement(provider(1)).hasListeners, true); + expect( + container.readProviderElement(provider(0)).hasNonWeakListeners, + false, + ); + expect( + container.readProviderElement(provider(1)).hasNonWeakListeners, + true, + ); }); testWidgets('listen to the new provider on provider change', (tester) async { final provider = StateProvider.family((ref, _) => 0); - final container = createContainer(); + final container = ProviderContainer.test(); final onChange = Listener(); await tester.pumpWidget( @@ -239,10 +257,10 @@ void main() { testWidgets('supports Changing the ProviderContainer', (tester) async { final provider = Provider((ref) => 0); final onChange = Listener(); - final container = createContainer( + final container = ProviderContainer.test( overrides: [provider.overrideWithValue(0)], ); - final container2 = createContainer( + final container2 = ProviderContainer.test( overrides: [provider.overrideWithValue(0)], ); @@ -285,7 +303,7 @@ void main() { testWidgets('supports overriding Providers', (tester) async { final provider = Provider((ref) => 0); final onChange = Listener(); - final container = createContainer( + final container = ProviderContainer.test( overrides: [provider.overrideWithValue(42)], ); diff --git a/packages/flutter_riverpod/test/framework_test.dart b/packages/flutter_riverpod/test/framework_test.dart index f2bffa093..e7b6f15c9 100644 --- a/packages/flutter_riverpod/test/framework_test.dart +++ b/packages/flutter_riverpod/test/framework_test.dart @@ -154,54 +154,6 @@ void main() { verifyOnly(listener, listener(0, 1)); }); - testWidgets('ProviderScope can receive a custom parent', (tester) async { - final provider = Provider((ref) => 0); - - final container = createContainer( - overrides: [provider.overrideWithValue(42)], - ); - - await tester.pumpWidget( - ProviderScope( - // ignore: deprecated_member_use_from_same_package - parent: container, - child: Consumer( - builder: (context, ref, _) { - return Text( - '${ref.watch(provider)}', - textDirection: TextDirection.ltr, - ); - }, - ), - ), - ); - - expect(find.text('42'), findsOneWidget); - }); - - testWidgets('ProviderScope.parent cannot change', (tester) async { - final container = createContainer(); - final container2 = createContainer(); - - await tester.pumpWidget( - ProviderScope( - // ignore: deprecated_member_use_from_same_package - parent: container, - child: Container(), - ), - ); - - await tester.pumpWidget( - ProviderScope( - // ignore: deprecated_member_use_from_same_package - parent: container2, - child: Container(), - ), - ); - - expect(tester.takeException(), isUnsupportedError); - }); - testWidgets('ref.read works with providers that returns null', (tester) async { final nullProvider = Provider((ref) => null); @@ -241,7 +193,7 @@ void main() { testWidgets('ref.read obtains the nearest Provider possible', (tester) async { late WidgetRef ref; - final provider = Provider((watch) => 42); + final provider = Provider((watch) => 42, dependencies: const []); await tester.pumpWidget( ProviderScope( @@ -269,7 +221,7 @@ void main() { }; final provider = StateProvider((ref) => 0); - final container = createContainer(); + final container = ProviderContainer.test(); // using runZonedGuarded as StateNotifier will emit an handleUncaughtError // if a listener threw @@ -294,7 +246,7 @@ void main() { testWidgets('ref.watch within a build method can flush providers', (tester) async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateProvider((ref) => 0); final provider = Provider((ref) => ref.watch(dep)); @@ -328,8 +280,8 @@ void main() { testWidgets('UncontrolledProviderScope gracefully handles vsync', (tester) async { - final container = createContainer(); - final container2 = createContainer(parent: container); + final container = ProviderContainer.test(); + final container2 = ProviderContainer.test(parent: container); expect(container.scheduler.flutterVsyncs, isEmpty); @@ -415,7 +367,7 @@ void main() { testWidgets( 'UncontrolledProviderScope gracefully handles debugCanModifyProviders', (tester) async { - final container = createContainer(); + final container = ProviderContainer.test(); expect(debugCanModifyProviders, null); @@ -481,29 +433,6 @@ void main() { expect(ref.refresh(provider), null); }); - // testWidgets('ProviderScope allows specifying a ProviderContainer', - // (tester) async { - // final provider = FutureProvider((ref) async => 42); - // late WidgetRef ref; - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // await tester.pumpWidget( - // UncontrolledProviderScope( - // container: container, - // child: Consumer( - // builder: (context, r, _) { - // ref = r; - // return Container(); - // }, - // ), - // ), - // ); - - // expect(ref.read(provider), const AsyncValue.data(42)); - // }); - testWidgets('AlwaysAliveProviderBase.read(context) inside initState', (tester) async { final provider = Provider((_) => 42); @@ -511,7 +440,7 @@ void main() { await tester.pumpWidget( ProviderScope( - child: InitState( + child: _InitState( initState: (context, ref) => result = ref.read(provider), ), ), @@ -806,7 +735,7 @@ void main() { return 0; }); - final container = createContainer(); + final container = ProviderContainer.test(); final key = GlobalKey(); await tester.pumpWidget( @@ -831,17 +760,18 @@ void main() { expect(find.text('1'), findsOneWidget); - // ignore: unawaited_futures - key.currentState!.pushReplacement( - PageRouteBuilder( - pageBuilder: (_, __, ___) { - return Consumer( - builder: (context, ref, _) { - final count = ref.watch(counterProvider); - return Text('new $count'); - }, - ); - }, + unawaited( + key.currentState!.pushReplacement( + PageRouteBuilder( + pageBuilder: (_, __, ___) { + return Consumer( + builder: (context, ref, _) { + final count = ref.watch(counterProvider); + return Text('new $count'); + }, + ); + }, + ), ), ); @@ -853,18 +783,16 @@ void main() { }); } -class InitState extends ConsumerStatefulWidget { - const InitState({super.key, required this.initState}); +class _InitState extends ConsumerStatefulWidget { + const _InitState({required this.initState}); - // ignore: diagnostic_describe_all_properties final void Function(BuildContext context, WidgetRef ref) initState; @override - // ignore: library_private_types_in_public_api _InitStateState createState() => _InitStateState(); } -class _InitStateState extends ConsumerState { +class _InitStateState extends ConsumerState<_InitState> { @override void initState() { super.initState(); diff --git a/packages/flutter_riverpod/test/future_provider_test.dart b/packages/flutter_riverpod/test/future_provider_test.dart index f0fb81628..e57ada5f1 100644 --- a/packages/flutter_riverpod/test/future_provider_test.dart +++ b/packages/flutter_riverpod/test/future_provider_test.dart @@ -5,12 +5,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'utils.dart'; - void main() { test('SynchronousFuture', () { final futureProvider = FutureProvider((_) => SynchronousFuture(42)); - final container = createContainer(); + final container = ProviderContainer.test(); expect(container.read(futureProvider), const AsyncValue.data(42)); }); @@ -128,218 +126,6 @@ void main() { await Future.value(); }); - group('overrideWithValue', () { - // var callCount = 0; - // final futureProvider = FutureProvider((s) async { - // callCount++; - // return 42; - // }); - - // Future? future; - // var completed = false; - // final proxy = Provider( - // (ref) { - // final first = ref.watch(futureProvider.future); - // future = first - // ..then( - // (value) => completed = true, - // onError: (dynamic _) => completed = true, - // ); - // return ''; - // }, - // ); - - // setUp(() { - // callCount = 0; - // completed = false; - // future = null; - // }); - - // final child = Directionality( - // textDirection: TextDirection.ltr, - // child: Consumer(builder: (c, ref, _) { - // ref.watch(proxy); - // return ref.watch(futureProvider).when( - // data: (data) => Text(data.toString()), - // loading: () => const Text('loading'), - // error: (err, stack) { - // return const Text('error'); - // }, - // ); - // }), - // ); - - // testWidgets('no-op if completed and rebuild with same value', - // (tester) async { - // await tester.pumpWidget( - // ProviderScope( - // overrides: [ - // futureProvider.overrideWithValue(const AsyncValue.data(42)), - // ], - // child: child, - // ), - // ); - - // expect(completed, true); - // await expectLater(future, completion(42)); - - // await tester.pumpWidget( - // ProviderScope( - // overrides: [ - // futureProvider.overrideWithValue(const AsyncValue.data(42)), - // ], - // child: child, - // ), - // ); - // }); - - // testWidgets( - // 'FutureProviderDependency.future completes on rebuild with data', - // (tester) async { - // await tester.pumpWidget( - // ProviderScope( - // overrides: [ - // futureProvider.overrideWithValue(const AsyncValue.loading()), - // ], - // child: child, - // ), - // ); - - // // make sure the future doesn't just complete in one frame - // await Future.value(); - - // expect(completed, false); - // expect(future, isNotNull); - - // await tester.pumpWidget( - // ProviderScope( - // overrides: [ - // futureProvider.overrideWithValue(const AsyncValue.data(42)), - // ], - // child: child, - // ), - // ); - - // expect(completed, true); - // await expectLater(future, completion(42)); - // }); - - // testWidgets( - // 'FutureProviderDependency.future completes on rebuild with error', - // (tester) async { - // await tester.pumpWidget( - // ProviderScope( - // overrides: [ - // futureProvider.overrideWithValue(const AsyncValue.loading()), - // ], - // child: child, - // ), - // ); - - // // make sure the future doesn't just complete in one frame - // await Future.value(); - - // expect(completed, false); - // expect(future, isNotNull); - - // final error = Error(); - // await tester.pumpWidget( - // ProviderScope( - // overrides: [ - // futureProvider.overrideWithValue(AsyncValue.error(error)), - // ], - // child: child, - // ), - // ); - - // expect(completed, true); - // await expectLater(future, throwsA(error)); - // }); - - // testWidgets('FutureProviderDependency.future loading to loading is no-op', - // (tester) async { - // await tester.pumpWidget( - // ProviderScope( - // overrides: [ - // futureProvider.overrideWithValue(const AsyncValue.loading()), - // ], - // child: child, - // ), - // ); - - // expect(completed, false); - // expect(future, isNotNull); - - // await tester.pumpWidget( - // ProviderScope( - // overrides: [ - // futureProvider.overrideWithValue(const AsyncValue.loading()), - // ], - // child: child, - // ), - // ); - - // // make sure the future doesn't just complete in one frame - // await Future.value(); - - // expect(completed, false); - // expect(future, isNotNull); - // }); - - // testWidgets('Initial build as loading', (tester) async { - // await tester.pumpWidget( - // ProviderScope( - // overrides: [ - // futureProvider.overrideWithValue(const AsyncValue.loading()), - // ], - // child: child, - // ), - // ); - - // expect(callCount, 0); - // expect(find.text('loading'), findsOneWidget); - - // expect(completed, false); - // expect(future, isNotNull); - // }); - - // testWidgets('Initial build as value', (tester) async { - // await tester.pumpWidget( - // ProviderScope( - // overrides: [ - // futureProvider.overrideWithValue(const AsyncValue.data(42)), - // ], - // child: child, - // ), - // ); - - // expect(callCount, 0); - // expect(find.text('42'), findsOneWidget); - - // expect(completed, true); - // await expectLater(future, completion(42)); - // }); - - // testWidgets('Initial build as error', (tester) async { - // final error = Error(); - - // await tester.pumpWidget( - // ProviderScope( - // overrides: [ - // futureProvider.overrideWithValue(AsyncValue.error(error)), - // ], - // child: child, - // ), - // ); - - // expect(callCount, 0); - // expect(find.text('error'), findsOneWidget); - - // expect(completed, true); - // await expectLater(future, throwsA(error)); - // }); - }); - testWidgets('FutureProvider into FutureProviderFamily', (tester) async { final futureProvider = FutureProvider((_) async => 42); diff --git a/packages/flutter_riverpod/test/listen_test.dart b/packages/flutter_riverpod/test/listen_test.dart index 3c56c9696..5b0bb4b59 100644 --- a/packages/flutter_riverpod/test/listen_test.dart +++ b/packages/flutter_riverpod/test/listen_test.dart @@ -1,7 +1,8 @@ import 'dart:async'; import 'package:flutter/material.dart' hide Listener; -import 'package:flutter_riverpod/flutter_riverpod.dart' hide ErrorListener; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; @@ -154,7 +155,7 @@ void main() { group('WidgetRef.listen', () { testWidgets('expose previous and new value on change', (tester) async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateNotifierProvider, int>( (ref) => StateController(0), ); @@ -180,7 +181,7 @@ void main() { testWidgets( 'when using selectors, `previous` is the latest notification instead of latest event', (tester) async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateNotifierProvider, int>( (ref) => StateController(0), ); diff --git a/packages/flutter_riverpod/test/provider_container_test.dart b/packages/flutter_riverpod/test/provider_container_test.dart new file mode 100644 index 000000000..71478bb5a --- /dev/null +++ b/packages/flutter_riverpod/test/provider_container_test.dart @@ -0,0 +1,21 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('ProviderContainer', () { + testWidgets('Does not cause Timer issue when used in widget tests', + (tester) async { + final root = ProviderContainer.test(); + final container = ProviderContainer.test(parent: root); + final provider = Provider((ref) => 0); + + root.read(provider); + root.invalidate(provider); + container.read(provider); + container.invalidate(provider); + + // Needed due to https://github.com/flutter/flutter/issues/144472 + root.dispose(); + }); + }); +} diff --git a/packages/flutter_riverpod/test/provider_scope_test.dart b/packages/flutter_riverpod/test/provider_scope_test.dart new file mode 100644 index 000000000..9396aa694 --- /dev/null +++ b/packages/flutter_riverpod/test/provider_scope_test.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('ProviderScope', () { + group('retry', () { + testWidgets('passes the value to the ProviderContainer', (tester) async { + Duration? retry(int count, Object error) => Duration.zero; + + await tester.pumpWidget( + ProviderScope(retry: retry, child: Container()), + ); + + final element = tester.element(find.byType(Container)); + final container = ProviderScope.containerOf(element); + + expect(container.retry, retry); + }); + + testWidgets('works in widget tests', (tester) async { + await tester.pumpWidget( + ProviderScope( + retry: (retryCount, error) => const Duration(milliseconds: 10), + child: Container(), + ), + ); + var buildCount = 0; + final provider = Provider((ref) { + buildCount++; + throw UnimplementedError(); + }); + + final element = tester.element(find.byType(Container)); + final container = ProviderScope.containerOf(element); + + container.listen(provider, (a, b) {}, onError: (err, stack) {}); + + expect(buildCount, 1); + + await tester.pump(const Duration(milliseconds: 10)); + + expect(buildCount, 2); + }); + }); + }); +} diff --git a/packages/flutter_riverpod/test/provider_test.dart b/packages/flutter_riverpod/test/provider_test.dart index 6f5791522..202eadeaf 100644 --- a/packages/flutter_riverpod/test/provider_test.dart +++ b/packages/flutter_riverpod/test/provider_test.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; @@ -18,14 +19,10 @@ void main() { Consumer( builder: (context, ref, _) { - // ignore: omit_local_variable_types, unused_local_variable, prefer_final_locals - int providerValue = ref.read(provider); - // ignore: omit_local_variable_types, unused_local_variable, prefer_final_locals - AsyncValue futureProviderValue = ref.read(futureProvider); - // ignore: omit_local_variable_types, unused_local_variable, prefer_final_locals - AsyncValue streamProviderValue = ref.read(streamProvider); - // ignore: omit_local_variable_types, unused_local_variable, prefer_final_locals - ValueNotifier changeNotifierProviderValue = + final int providerValue = ref.read(provider); + final AsyncValue futureProviderValue = ref.read(futureProvider); + final AsyncValue streamProviderValue = ref.read(streamProvider); + final ValueNotifier changeNotifierProviderValue = ref.read(changeNotifierProvider); return Container(); @@ -295,7 +292,6 @@ void main() { // These check the type safety Ref? ref; - // ignore: omit_local_variable_types final Provider provider1 = Provider((r) { final first = r.watch(provider); ref = r; diff --git a/packages/flutter_riverpod/test/providers/change_notifier/auto_dispose_change_notifier_provider_test.dart b/packages/flutter_riverpod/test/providers/change_notifier/auto_dispose_change_notifier_provider_test.dart index 470a633fe..9c15be3cf 100644 --- a/packages/flutter_riverpod/test/providers/change_notifier/auto_dispose_change_notifier_provider_test.dart +++ b/packages/flutter_riverpod/test/providers/change_notifier/auto_dispose_change_notifier_provider_test.dart @@ -3,14 +3,11 @@ import 'package:flutter/widgets.dart' hide Listener; import 'package:flutter_riverpod/src/internals.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; - -import '../../utils.dart'; void main() { group('ChangeNotifierProvider.autoDispose', () { test('support null ChangeNotifier', () { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = ChangeNotifierProvider.autoDispose?>( (ref) => null, ); @@ -21,28 +18,12 @@ void main() { container.dispose(); }); - test('can read and set current ChangeNotifier', () async { - final container = createContainer(); - final listener = Listener>(); - late AutoDisposeChangeNotifierProviderRef> ref; - final provider = - ChangeNotifierProvider.autoDispose>((r) { - ref = r; - return ValueNotifier(0); - }); - - container.listen(provider, listener.call); - - verifyZeroInteractions(listener); - expect(ref.notifier.value, 0); - }); - test('can refresh .notifier', () async { var initialValue = 1; final provider = ChangeNotifierProvider.autoDispose>( (ref) => ValueNotifier(initialValue), ); - final container = createContainer(); + final container = ProviderContainer.test(); container.listen(provider.notifier, (prev, value) {}); @@ -57,7 +38,7 @@ void main() { test('can be refreshed', () async { var result = ValueNotifier(0); - final container = createContainer(); + final container = ProviderContainer.test(); final provider = ChangeNotifierProvider.autoDispose((ref) => result); expect(container.read(provider), result); @@ -72,56 +53,35 @@ void main() { group('scoping an override overrides all the associated subproviders', () { test('when passing the provider itself', () { - final provider = - ChangeNotifierProvider.autoDispose((ref) => ValueNotifier(0)); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = ChangeNotifierProvider.autoDispose( + (ref) => ValueNotifier(0), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = + ProviderContainer.test(parent: root, overrides: [provider]); expect(container.read(provider.notifier).value, 0); expect(container.read(provider).value, 0); expect( container.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), ]), ); expect(root.getAllProviderElements(), isEmpty); }); - // test('when using provider.overrideWithValue', () { - // final provider = - // ChangeNotifierProvider.autoDispose((ref) => ValueNotifier(0)); - // final root = createContainer(); - // final container = createContainer(parent: root, overrides: [ - // provider.overrideWithValue(ValueNotifier(42)), - // ]); - - // expect(container.read(provider.notifier).value, 42); - // expect(container.read(provider).value, 42); - // expect( - // container.getAllProviderElements(), - // unorderedEquals([ - // isA>() - // .having((e) => e.origin, 'origin', provider), - // isA>() - // .having((e) => e.origin, 'origin', provider.notifier) - // ]), - // ); - // expect(root.getAllProviderElements(), isEmpty); - // }); - - test('when using provider.overrideWithProvider', () { - final provider = - ChangeNotifierProvider.autoDispose((ref) => ValueNotifier(0)); - final root = createContainer(); - final container = createContainer( + test('when using provider.overrideWith', () { + final provider = ChangeNotifierProvider.autoDispose( + (ref) => ValueNotifier(0), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ - // ignore: deprecated_member_use - provider.overrideWithProvider( - ChangeNotifierProvider.autoDispose((ref) => ValueNotifier(42)), - ), + provider.overrideWith((ref) => ValueNotifier(42)), ], ); @@ -130,8 +90,7 @@ void main() { expect( container.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), ]), ); expect(root.getAllProviderElements(), isEmpty); @@ -139,13 +98,13 @@ void main() { }); test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider((ref) => 0, dependencies: const []); final provider = ChangeNotifierProvider.autoDispose( (ref) => ValueNotifier(ref.watch(dep)), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); diff --git a/packages/flutter_riverpod/test/providers/change_notifier/change_notifier_provider_test.dart b/packages/flutter_riverpod/test/providers/change_notifier/change_notifier_provider_test.dart index 036c5e172..0585c526a 100644 --- a/packages/flutter_riverpod/test/providers/change_notifier/change_notifier_provider_test.dart +++ b/packages/flutter_riverpod/test/providers/change_notifier/change_notifier_provider_test.dart @@ -1,13 +1,33 @@ // ignore_for_file: invalid_use_of_internal_member, avoid_types_on_closure_parameters, deprecated_member_use_from_same_package, deprecated_member_use +import 'dart:async'; + import 'package:flutter/widgets.dart' hide Listener; import 'package:flutter_riverpod/src/internals.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; import '../../utils.dart'; void main() { + test('Guards ChangeNotifier.dispose', () { + final notifier = DelegateNotifier( + onDispose: () => throw StateError('called'), + ); + final container = ProviderContainer.test(); + final provider = ChangeNotifierProvider((_) => notifier); + + container.read(provider); + + final errors = []; + + runZonedGuarded( + () => container.invalidate(provider), + (error, stack) => errors.add(error), + ); + + expect(errors, [isStateError]); + }); + test('supports overrideWith', () { final provider = ChangeNotifierProvider>((ref) => ValueNotifier(0)); @@ -15,16 +35,10 @@ void main() { (ref) => ValueNotifier(0), ); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - provider.overrideWith( - (ChangeNotifierProviderRef> ref) => - ValueNotifier(42), - ), - autoDispose.overrideWith( - (AutoDisposeChangeNotifierProviderRef> ref) => - ValueNotifier(84), - ), + provider.overrideWith((ref) => ValueNotifier(42)), + autoDispose.overrideWith((ref) => ValueNotifier(84)), ], ); @@ -40,19 +54,11 @@ void main() { ChangeNotifierProvider.autoDispose.family, int>( (ref, arg) => ValueNotifier('0 $arg'), ); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - family.overrideWith( - (ChangeNotifierProviderRef> ref, int arg) => - ValueNotifier('42 $arg'), - ), - autoDisposeFamily.overrideWith( - ( - AutoDisposeChangeNotifierProviderRef> ref, - int arg, - ) => - ValueNotifier('84 $arg'), - ), + family.overrideWith((ref, int arg) => ValueNotifier('42 $arg')), + autoDisposeFamily + .overrideWith((ref, int arg) => ValueNotifier('84 $arg')), ], ); @@ -60,25 +66,8 @@ void main() { expect(container.read(autoDisposeFamily(10)).value, '84 10'); }); - test('ref.listenSelf listens to state changes', () { - final listener = Listener>(); - final container = createContainer(); - final provider = ChangeNotifierProvider>((ref) { - ref.listenSelf(listener.call); - return ValueNotifier(0); - }); - - final notifier = container.read(provider); - - verifyOnly(listener, listener(null, notifier)); - - container.read(provider.notifier).value++; - - verifyOnly(listener, listener(notifier, notifier)); - }); - test('support null ChangeNotifier', () { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = ChangeNotifierProvider?>((ref) => null); expect(container.read(provider), null); @@ -87,27 +76,12 @@ void main() { container.dispose(); }); - test('can read and set current ChangeNotifier', () async { - final container = createContainer(); - final listener = Listener>(); - late ChangeNotifierProviderRef> ref; - final provider = ChangeNotifierProvider>((r) { - ref = r; - return ValueNotifier(0); - }); - - container.listen(provider, listener.call); - - verifyZeroInteractions(listener); - expect(ref.notifier.value, 0); - }); - test('can refresh .notifier', () async { var initialValue = 1; final provider = ChangeNotifierProvider>( (ref) => ValueNotifier(initialValue), ); - final container = createContainer(); + final container = ProviderContainer.test(); expect(container.read(provider).value, 1); expect(container.read(provider.notifier).value, 1); @@ -120,7 +94,7 @@ void main() { test('can be refreshed', () async { var result = ValueNotifier(0); - final container = createContainer(); + final container = ProviderContainer.test(); final provider = ChangeNotifierProvider((ref) => result); expect(container.read(provider), result); @@ -134,7 +108,7 @@ void main() { }); test('pass the notifier as previous value when notifying listeners', () { - final container = createContainer(); + final container = ProviderContainer.test(); final notifier = ValueNotifier(0); final provider = ChangeNotifierProvider((ref) => notifier); final listener = Listener>(); @@ -150,52 +124,35 @@ void main() { group('scoping an override overrides all the associated subproviders', () { test('when passing the provider itself', () { - final provider = ChangeNotifierProvider((ref) => ValueNotifier(0)); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = ChangeNotifierProvider( + (ref) => ValueNotifier(0), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = + ProviderContainer.test(parent: root, overrides: [provider]); expect(container.read(provider.notifier).value, 0); expect(container.read(provider).value, 0); expect( container.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), ]), ); expect(root.getAllProviderElements(), isEmpty); }); - // test('when using provider.overrideWithValue', () { - // final provider = ChangeNotifierProvider((ref) => ValueNotifier(0)); - // final root = createContainer(); - // final container = createContainer(parent: root, overrides: [ - // provider.overrideWithValue(ValueNotifier(42)), - // ]); - - // expect(container.read(provider.notifier).value, 42); - // expect(container.read(provider).value, 42); - // expect( - // container.getAllProviderElements(), - // unorderedEquals([ - // isA>() - // .having((e) => e.origin, 'origin', provider), - // isA>() - // .having((e) => e.origin, 'origin', provider.notifier) - // ]), - // ); - // expect(root.getAllProviderElements(), isEmpty); - // }); - - test('when using provider.overrideWithProvider', () { - final provider = ChangeNotifierProvider((ref) => ValueNotifier(0)); - final root = createContainer(); - final container = createContainer( + test('when using provider.overrideWith', () { + final provider = ChangeNotifierProvider( + (ref) => ValueNotifier(0), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ - provider.overrideWithProvider( - ChangeNotifierProvider((ref) => ValueNotifier(42)), - ), + provider.overrideWith((ref) => ValueNotifier(42)), ], ); @@ -204,39 +161,16 @@ void main() { expect( container.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), ]), ); expect(root.getAllProviderElements(), isEmpty); }); }); - // test('overriding with value listens to the ChangeNotifier', () { - // final provider = ChangeNotifierProvider((ref) => ValueNotifier(0)); - // final notifier = ValueNotifier(42); - // final listener = Listener(); - - // final container = createContainer( - // overrides: [provider.overrideWithValue(notifier)], - // ); - - // container.listen>( - // provider, - // (prev, value) => listener(prev?.value, value.value), - // ); - - // expect(container.read(provider).value, 42); - // expect(container.read(provider.notifier).value, 42); - - // notifier.value = 21; - - // verifyOnly(listener, listener(21, 21)); - // }); - test('refresh recreates the ChangeNotifier', () { final provider = ChangeNotifierProvider((ref) => ValueNotifier(0)); - final container = createContainer(); + final container = ProviderContainer.test(); container.read(provider).value = 42; @@ -247,7 +181,7 @@ void main() { }); test('family', () { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = ChangeNotifierProvider.family, int>((ref, value) { return ValueNotifier(value); @@ -312,7 +246,7 @@ void main() { final provider = ChangeNotifierProvider((ref) { return ref.watch(dep) == 0 ? notifier : notifier2; }); - final container = createContainer(); + final container = ProviderContainer.test(); addTearDown(container.dispose); var callCount = 0; @@ -338,65 +272,14 @@ void main() { expect(callCount, 1); }); - // test( - // 'overrideWithValue listens to the notifier, support notifier change, and does not dispose of the notifier', - // () async { - // final provider = ChangeNotifierProvider((_) { - // return TestNotifier('a'); - // }); - // final notifier = TestNotifier('b'); - // final notifier2 = TestNotifier('c'); - // final container = createContainer(overrides: [ - // provider.overrideWithValue(notifier), - // ]); - // addTearDown(container.dispose); - - // var callCount = 0; - // final sub = container.listen(provider, (_, __) => callCount++); - // final notifierSub = container.listen(provider.notifier, (_, __) {}); - - // expect(sub.read(), notifier); - // expect(callCount, 0); - // expect(notifierSub.read(), notifier); - // expect(notifier.hasListeners, true); - - // notifier.count++; - - // await container.pump(); - // expect(callCount, 1); - - // container.updateOverrides([ - // provider.overrideWithValue(notifier2), - // ]); - - // await container.pump(); - // expect(callCount, 2); - // expect(notifier.hasListeners, false); - // expect(notifier2.hasListeners, true); - // expect(notifier.mounted, true); - // expect(notifierSub.read(), notifier2); - - // notifier2.count++; - - // await container.pump(); - // expect(callCount, 3); - - // container.dispose(); - - // expect(callCount, 3); - // expect(notifier2.hasListeners, false); - // expect(notifier2.mounted, true); - // expect(notifier.mounted, true); - // }); - test('ChangeNotifier can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider((ref) => 0, dependencies: const []); final provider = ChangeNotifierProvider( (ref) => ValueNotifier(ref.watch(dep)), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -407,15 +290,15 @@ void main() { expect(root.getAllProviderElements(), isEmpty); }); - test('overrideWithProvider preserves the state across update', () async { + test('overrideWith preserves the state across update', () async { final provider = ChangeNotifierProvider((_) { return TestNotifier(); }); final notifier = TestNotifier(); final notifier2 = TestNotifier(); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - provider.overrideWithProvider(ChangeNotifierProvider((_) => notifier)), + provider.overrideWith((_) => notifier), ], ); addTearDown(container.dispose); @@ -434,7 +317,7 @@ void main() { expect(callCount, 1); container.updateOverrides([ - provider.overrideWithProvider(ChangeNotifierProvider((_) => notifier2)), + provider.overrideWith((_) => notifier2), ]); await container.pump(); @@ -485,3 +368,15 @@ class TestNotifier extends ChangeNotifier { return 'TestNotifier($debugLabel)'; } } + +class DelegateNotifier extends ChangeNotifier { + DelegateNotifier({this.onDispose}); + + final void Function()? onDispose; + + @override + void dispose() { + onDispose?.call(); + super.dispose(); + } +} diff --git a/packages/flutter_riverpod/test/providers/change_notifier/family_auto_dispose_change_notifier_provider_test.dart b/packages/flutter_riverpod/test/providers/change_notifier/family_auto_dispose_change_notifier_provider_test.dart index 9d495c413..30798bf1d 100644 --- a/packages/flutter_riverpod/test/providers/change_notifier/family_auto_dispose_change_notifier_provider_test.dart +++ b/packages/flutter_riverpod/test/providers/change_notifier/family_auto_dispose_change_notifier_provider_test.dart @@ -4,8 +4,6 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_riverpod/src/internals.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../../utils.dart'; - void main() { group('ChangeNotifierProvider.autoDispose.family', () { test('specifies `from` and `argument` for related providers', () { @@ -19,7 +17,7 @@ void main() { }); test('support null ChangeNotifier', () { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = ChangeNotifierProvider.family.autoDispose?, int>( (ref, _) => null, @@ -33,17 +31,21 @@ void main() { group('scoping an override overrides all the associated subproviders', () { test('when passing the provider itself', () async { - final provider = ChangeNotifierProvider.autoDispose - .family, int>((ref, _) => ValueNotifier(0)); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = + ChangeNotifierProvider.autoDispose.family, int>( + (ref, _) => ValueNotifier(0), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = + ProviderContainer.test(parent: root, overrides: [provider]); expect(container.read(provider(0).notifier).value, 0); expect(container.read(provider(0)).value, 0); expect( container.getAllProviderElementsInOrder(), unorderedEquals([ - isA>() + isA() .having((e) => e.origin, 'origin', provider(0)), ]), ); @@ -51,14 +53,14 @@ void main() { }); test('ChangeNotifier can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider((ref) => 0, dependencies: const []); final provider = ChangeNotifierProvider.autoDispose.family, int>( (ref, i) => ValueNotifier(ref.watch(dep) + i), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -69,18 +71,17 @@ void main() { expect(root.getAllProviderElements(), isEmpty); }); - test('when using provider.overrideWithProvider', () async { - final provider = ChangeNotifierProvider.autoDispose - .family, int>((ref, _) => ValueNotifier(0)); - final root = createContainer(); - final container = createContainer( + test('when using provider.overrideWith', () async { + final provider = + ChangeNotifierProvider.autoDispose.family, int>( + (ref, _) => ValueNotifier(0), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ - provider.overrideWithProvider( - (value) => ChangeNotifierProvider.autoDispose( - (ref) => ValueNotifier(42), - ), - ), + provider.overrideWith((ref, value) => ValueNotifier(42)), ], ); @@ -90,7 +91,7 @@ void main() { expect( container.getAllProviderElementsInOrder(), unorderedEquals([ - isA>() + isA() .having((e) => e.origin, 'origin', provider(0)), ]), ); diff --git a/packages/flutter_riverpod/test/providers/change_notifier/family_change_notifier_provider_test.dart b/packages/flutter_riverpod/test/providers/change_notifier/family_change_notifier_provider_test.dart index 8a028db84..097f9a3d1 100644 --- a/packages/flutter_riverpod/test/providers/change_notifier/family_change_notifier_provider_test.dart +++ b/packages/flutter_riverpod/test/providers/change_notifier/family_change_notifier_provider_test.dart @@ -4,8 +4,6 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_riverpod/src/internals.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../../utils.dart'; - void main() { group('ChangeNotifierProvider.family', () { test('specifies `from` and `argument` for related providers', () { @@ -18,7 +16,7 @@ void main() { }); test('support null ChangeNotifier', () { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = ChangeNotifierProvider.family?, int>( (ref, _) => null, ); @@ -33,16 +31,18 @@ void main() { test('when passing the provider itself', () async { final provider = ChangeNotifierProvider.family, int>( (ref, _) => ValueNotifier(0), + dependencies: const [], ); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final root = ProviderContainer.test(); + final container = + ProviderContainer.test(parent: root, overrides: [provider]); expect(container.read(provider(0).notifier).value, 0); expect(container.read(provider(0)).value, 0); expect( container.getAllProviderElementsInOrder(), unorderedEquals([ - isA>() + isA() .having((e) => e.origin, 'origin', provider(0)), ]), ); @@ -51,13 +51,13 @@ void main() { }); test('ChangeNotifier can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider((ref) => 0, dependencies: const []); final provider = ChangeNotifierProvider.family, int>( (ref, i) => ValueNotifier(ref.watch(dep) + i), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -68,17 +68,16 @@ void main() { expect(root.getAllProviderElements(), isEmpty); }); - test('when using provider.overrideWithProvider', () async { + test('when using provider.overrideWith', () async { final provider = ChangeNotifierProvider.family, int>( (ref, _) => ValueNotifier(0), + dependencies: const [], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ - provider.overrideWithProvider( - (value) => ChangeNotifierProvider((ref) => ValueNotifier(42)), - ), + provider.overrideWith((ref, value) => ValueNotifier(42)), ], ); @@ -88,8 +87,7 @@ void main() { expect( container.getAllProviderElementsInOrder(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider(0)), + isA().having((e) => e.origin, 'origin', provider(0)), ]), ); }); @@ -99,12 +97,9 @@ void main() { ChangeNotifierProvider.family, int>((ref, value) { return ValueNotifier(value); }); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - provider.overrideWithProvider( - (value) => - ChangeNotifierProvider((ref) => ValueNotifier(value * 2)), - ), + provider.overrideWith((ref, value) => ValueNotifier(value * 2)), ], ); diff --git a/packages/flutter_riverpod/test/consumer_test.dart b/packages/flutter_riverpod/test/src/core/consumer_test.dart similarity index 75% rename from packages/flutter_riverpod/test/consumer_test.dart rename to packages/flutter_riverpod/test/src/core/consumer_test.dart index 5a5804096..5ffbd24ff 100644 --- a/packages/flutter_riverpod/test/consumer_test.dart +++ b/packages/flutter_riverpod/test/src/core/consumer_test.dart @@ -1,11 +1,229 @@ +// ignore_for_file: invalid_use_of_internal_member + +import 'dart:async'; + import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/src/internals.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:riverpod/legacy.dart'; -import 'utils.dart'; +class SimpleVisibility extends StatelessWidget { + const SimpleVisibility({ + super.key, + required this.visible, + required this.child, + }); + + final bool visible; + final Widget child; + + @override + Widget build(BuildContext context) { + return Visibility( + visible: visible, + maintainState: true, + maintainAnimation: true, + maintainSize: true, + maintainInteractivity: true, + child: child, + ); + } +} void main() { + group('_ListenManual', () { + testWidgets('handles pause/resume', (tester) async { + late WidgetRef ref; + late ProviderSubscription sub; + final provider = Provider((ref) => 0); + + await tester.pumpWidget( + ProviderScope( + child: Consumer( + builder: (context, r, child) { + ref = r; + sub = ref.listenManual( + provider, + (_, __) {}, + ); + return const SizedBox(); + }, + ), + ), + ); + + final container = ProviderScope.containerOf(ref.context); + final element = container.readProviderElement(provider); + + expect(element.isActive, true); + + sub.pause(); + + expect(element.isActive, false); + + sub.resume(); + + expect(element.isActive, true); + }); + }); + + group('Handles Visibility', () { + testWidgets( + 'when adding a listener, initializes pause state based on visibility', + (tester) async { + final providerForVisible = Provider((ref) => 0); + + await tester.pumpWidget( + ProviderScope( + child: Column( + children: [ + Consumer( + builder: (c, ref, _) { + ref.listen(providerForVisible, (_, __) {}); + ref.watch(providerForVisible); + ref.listenManual(providerForVisible, (_, __) {}); + + return const SizedBox(); + }, + ), + SimpleVisibility( + visible: false, + child: Consumer( + builder: (c, ref, _) { + ref.listen(_provider, (_, __) {}); + ref.watch(_provider); + ref.listenManual(_provider, (_, __) {}); + + return const SizedBox(); + }, + ), + ), + ], + ), + ), + ); + + final container = ProviderScope.containerOf( + tester.element(find.byType(Column)), + ); + final visibleElement = container.readProviderElement(providerForVisible); + final hiddenElement = container.readProviderElement(_provider); + + expect( + hiddenElement.dependents, + everyElement( + isA() + .having((e) => e.isPaused, 'isPaused', true), + ), + ); + expect( + visibleElement.dependents, + everyElement( + isA() + .having((e) => e.isPaused, 'isPaused', false), + ), + ); + }); + + testWidgets( + 'listenManual inside life-cycles before didChangeDependencies ' + 'on non-visible widgets does not paused twice', (tester) async { + late ProviderSubscription sub; + final widget = CallbackConsumerWidget( + initState: (context, ref) { + sub = ref.listenManual(_provider, (_, __) {}); + }, + ); + + await tester.pumpWidget( + ProviderScope( + child: SimpleVisibility(visible: false, child: widget), + ), + ); + + sub.resume(); + + expect(sub.isPaused, false); + }); + + testWidgets('when visibility changes, pause/resume listeners', + (tester) async { + late WidgetRef ref; + final widget = Consumer( + builder: (c, r, _) { + ref = r; + + return const SizedBox(); + }, + ); + + await tester.pumpWidget( + ProviderScope( + child: SimpleVisibility(visible: true, child: widget), + ), + ); + + final sub = ref.listenManual(_provider, (_, __) {}); + + await tester.pumpWidget( + ProviderScope( + child: SimpleVisibility(visible: false, child: widget), + ), + ); + + expect(sub.isPaused, true); + + await tester.pumpWidget( + ProviderScope( + child: SimpleVisibility(visible: true, child: widget), + ), + ); + + expect(sub.isPaused, false); + }); + + testWidgets( + 'when a dependency changes but visibility does not, do not pause/resume listeners', + (tester) async { + late WidgetRef ref; + final widget = Consumer( + builder: (context, r, _) { + // Subscribe to Theme + Theme.of(context); + ref = r; + + return const SizedBox(); + }, + ); + + await tester.pumpWidget( + ProviderScope( + child: Theme( + data: ThemeData.dark(), + child: SimpleVisibility(visible: false, child: widget), + ), + ), + ); + + final sub = ref.listenManual(_provider, (_, __) {}); + + await tester.pumpWidget( + ProviderScope( + child: Theme( + data: ThemeData.light(), + child: SimpleVisibility(visible: false, child: widget), + ), + ), + ); + + expect(sub.isPaused, true); + + sub.resume(); + + expect(sub.isPaused, false); + }); + }); + testWidgets('Riverpod test', (tester) async { // Regression test for https://github.com/rrousselGit/riverpod/pull/3156 @@ -190,8 +408,7 @@ void main() { expect(find.byType(Container), findsOneWidget); expect(buildCount, 1); - // ignore: unawaited_futures - tester.binding.reassembleApplication(); + unawaited(tester.binding.reassembleApplication()); await tester.pump(); expect(find.byType(Container), findsOneWidget); @@ -245,8 +462,8 @@ void main() { }); expect(buildCount, 1); - expect(familyState0.hasListeners, true); - expect(familyState1.hasListeners, false); + expect(familyState0.hasNonWeakListeners, true); + expect(familyState1.hasNonWeakListeners, false); expect(find.text('0 0'), findsOneWidget); notifier0.increment(); @@ -266,8 +483,8 @@ void main() { expect(buildCount, 3); expect(find.text('1 43'), findsOneWidget); - expect(familyState1.hasListeners, true); - expect(familyState0.hasListeners, true); + expect(familyState1.hasNonWeakListeners, true); + expect(familyState0.hasNonWeakListeners, true); notifier1.increment(); await tester.pump(); @@ -326,8 +543,8 @@ void main() { }); expect(buildCount, 1); - expect(familyState0.hasListeners, true); - expect(familyState1.hasListeners, false); + expect(familyState0.hasNonWeakListeners, true); + expect(familyState1.hasNonWeakListeners, false); expect(find.text('0'), findsOneWidget); notifier0.increment(); @@ -347,8 +564,8 @@ void main() { expect(buildCount, 3); expect(find.text('43'), findsOneWidget); - expect(familyState1.hasListeners, true); - expect(familyState0.hasListeners, false); + expect(familyState1.hasNonWeakListeners, true); + expect(familyState0.hasNonWeakListeners, false); notifier1.increment(); await tester.pump(); @@ -452,7 +669,7 @@ void main() { final provider = StateNotifierProvider((_) => notifier); final computed = Provider((ref) => !ref.watch(provider).isNegative); var buildCount = 0; - final container = createContainer(); + final container = ProviderContainer.test(); await tester.pumpWidget( UncontrolledProviderScope( @@ -478,105 +695,19 @@ void main() { expect(find.text('isPositive true'), findsOneWidget); expect(buildCount, 1); - notifier.value = -10; + notifier.state = -10; await tester.pump(); expect(find.text('isPositive false'), findsOneWidget); expect(buildCount, 2); - notifier.value = -5; + notifier.state = -5; await tester.pump(); expect(find.text('isPositive false'), findsOneWidget); expect(buildCount, 2); }); - // testWidgets('remove listener when changing container', (tester) async { - // final notifier = TestNotifier(); - // final provider = StateNotifierProvider((_) { - // return notifier; - // }, name: 'provider'); - // final notifier2 = TestNotifier(42); - // const firstOwnerKey = Key('first'); - // const secondOwnerKey = Key('second'); - // final key = GlobalKey(); - - // final consumer = Consumer( - // builder: (context, ref, _) { - // final value = ref.watch(provider); - // return Text('$value', textDirection: TextDirection.ltr); - // }, - // key: key); - - // await tester.pumpWidget( - // Column( - // children: [ - // ProviderScope( - // key: firstOwnerKey, - // child: consumer, - // ), - // ProviderScope( - // key: secondOwnerKey, - // overrides: [ - // provider.overrideWithValue(notifier2), - // ], - // child: Container(), - // ), - // ], - // ), - // ); - - // final owner1 = tester // - // .firstState(find.byKey(firstOwnerKey)) - // .container; - - // final state1 = owner1 - // .getAllProviderElements() - // .firstWhere((s) => s.provider == provider); - - // expect(state1.hasListeners, true); - // expect(find.text('0'), findsOneWidget); - - // await tester.pumpWidget( - // Column( - // children: [ - // ProviderScope( - // key: firstOwnerKey, - // child: Container(), - // ), - // ProviderScope( - // key: secondOwnerKey, - // overrides: [ - // provider.overrideWithValue(notifier2), - // ], - // child: consumer, - // ), - // ], - // ), - // ); - - // final container2 = tester // - // .firstState(find.byKey(secondOwnerKey)) - // .container; - - // final state2 = container2 - // .getAllProviderElements() - // .firstWhere((s) => s.provider is StateNotifierProvider); - - // expect(find.text('0'), findsNothing); - // expect(find.text('42'), findsOneWidget); - // expect(state1.hasListeners, false); - // expect(state2.hasListeners, true); - - // notifier2.increment(); - // await tester.pump(); - - // expect(find.text('0'), findsNothing); - // expect(find.text('43'), findsOneWidget); - // expect(state1.hasListeners, false); - // expect(state2.hasListeners, true); - // }); - testWidgets('remove listener when destroying the consumer', (tester) async { final notifier = TestNotifier(); final provider = StateNotifierProvider((_) => notifier); @@ -600,7 +731,7 @@ void main() { .getAllProviderElements() .firstWhere((s) => s.provider == provider); - expect(state.hasListeners, true); + expect(state.hasNonWeakListeners, true); expect(find.text('0'), findsOneWidget); await tester.pumpWidget( @@ -609,7 +740,7 @@ void main() { ), ); - expect(state.hasListeners, false); + expect(state.hasNonWeakListeners, false); }); testWidgets('Multiple providers', (tester) async { @@ -760,8 +891,8 @@ class TestNotifier extends StateNotifier { void increment() => state++; - // ignore: avoid_setters_without_getters - set value(int value) => state = value; + @override + int get state; } final _provider = Provider((ref) => 'hello world'); diff --git a/packages/flutter_riverpod/test/utils.dart b/packages/flutter_riverpod/test/utils.dart index 41996fea7..4d11a9102 100644 --- a/packages/flutter_riverpod/test/utils.dart +++ b/packages/flutter_riverpod/test/utils.dart @@ -1,27 +1,13 @@ import 'dart:async'; -import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; class ErrorListener extends Mock { void call(Object? error, StackTrace? stackTrace); } -ProviderContainer createContainer({ - ProviderContainer? parent, - List overrides = const [], - List? observers, -}) { - final container = ProviderContainer( - parent: parent, - overrides: overrides, - observers: observers, - ); - addTearDown(container.dispose); - return container; -} - class Counter extends StateNotifier { Counter() : super(0); diff --git a/packages/flutter_riverpod/test/widget_ref_test.dart b/packages/flutter_riverpod/test/widget_ref_test.dart new file mode 100644 index 000000000..3bf409389 --- /dev/null +++ b/packages/flutter_riverpod/test/widget_ref_test.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('WidgetRef', () { + group('invalidate', () { + testWidgets('supports asReload', (tester) async { + final provider = FutureProvider((r) async => 0); + + await tester.pumpWidget( + ProviderScope( + child: Consumer( + builder: (context, ref, _) => Container(), + ), + ), + ); + + final ref = tester.firstElement(find.byType(Consumer)) as WidgetRef; + + await ref.read(provider.future); + expect(ref.read(provider), const AsyncValue.data(0)); + + ref.invalidate(provider, asReload: true); + + expect( + ref.read(provider), + isA>().having((e) => e.value, 'value', 0), + ); + }); + }); + }); +} diff --git a/packages/hooks_riverpod/CHANGELOG.md b/packages/hooks_riverpod/CHANGELOG.md index fc0fd9e6a..0cd6a306f 100644 --- a/packages/hooks_riverpod/CHANGELOG.md +++ b/packages/hooks_riverpod/CHANGELOG.md @@ -1,3 +1,40 @@ +## Unreleased build + +- **Breaking**: `ChangeNotifierProvider`, `StateProvider` and `StateNotifierProvider` + are moved out of `package:hooks_riverpod/hooks_riverpod.dart` to + `package:hooks_riverpod/legacy.dart`. +- Failing providers are now automatically retried after a delay. + The delay can be optionally configured. + +## 3.0.0-dev.3 - 2023-11-27 + +- Fix "pending timer" issue inside tests when using `ref.keepAlive()`. +- Fix `Ref.invalidate`/`Ref.refresh` not throwing on circular dependency. +- Fix an infinite loop caused by `ref.keepAlive` if the `KeepAliveLink` is immediately closed. +- Fix `container.exists(provider)` on nested containers not checking their + parent containers. + +## 3.0.0-dev.2 - 2023-11-20 + +Fix exceptions when using multiple root `ProviderContainers`/`ProviderScopes`. + +## 3.0.0-dev.1 - 2023-11-20 + +- All notifier properties now throw an error if used after the notifier + has been disposed. +- The error thrown when a notifier property is used inside the constructor + of a notifier has been improved. +- Fix `ProviderObserver.didUpdateProvider` being called with an incorrect + "provider" parameter when the provider is overridden. + +## 3.0.0-dev.0 - 2023-10-29 + +- **Breaking**: `AsyncValue` is now "sealed" and `AsyncData/AsyncLoading/AsyncError` + are "final". This means that it is no-longer possible to subclass + `AsyncValue` or the associated classes. +- **Breaking**: Removed everything marked as "deprecated" +- Bumped minimum Dart SDK to >= 3.0.0-dev + ## 2.6.1 - 2024-10-22 - Added `AsyncNotifier.listenSelf`. It was mistakenly absent from the 2.6.0 release @@ -835,7 +872,7 @@ Fix an issue where `*Provider.autoDispose` were not able to specify the ### Bug-fixes - fixed a bug where providers were rebuilding even when not listened to -- fixed `ref.listen` now working when downcasting the value of a provider. +- fixed `ref.listen` now working when downcasing the value of a provider. - fixed a bug where disposing a scoped `ProviderContainer` could cause other `ProviderContainer`s to stop working. - fixed an issue where conditionally depending on an "autoDispose" provider @@ -1484,3 +1521,5 @@ The behavior is the same. Only the syntax changed. ## 0.1.0 Initial release + + diff --git a/packages/hooks_riverpod/example/lib/main.dart b/packages/hooks_riverpod/example/lib/main.dart index 6b63ad5d1..072f143a9 100644 --- a/packages/hooks_riverpod/example/lib/main.dart +++ b/packages/hooks_riverpod/example/lib/main.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:hooks_riverpod/legacy.dart'; void main() { runApp( @@ -32,7 +33,7 @@ class Counter extends StateNotifier { } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); + const MyApp({super.key}); @override Widget build(BuildContext context) { @@ -43,7 +44,7 @@ class MyApp extends StatelessWidget { } class MyHomePage extends HookConsumerWidget { - const MyHomePage({Key? key}) : super(key: key); + const MyHomePage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -52,7 +53,7 @@ class MyHomePage extends HookConsumerWidget { title: const Text('Riverpod counter example'), ), body: Center( - // HookConsumer is a builder widget that allows you to read providers and utilise hooks. + // HookConsumer is a builder widget that allows you to read providers and utilize hooks. child: HookConsumer( builder: (context, ref, _) { final count = ref.watch(counterProvider); diff --git a/packages/hooks_riverpod/example/pubspec.yaml b/packages/hooks_riverpod/example/pubspec.yaml index 8223d8af7..9b3ff0a6a 100644 --- a/packages/hooks_riverpod/example/pubspec.yaml +++ b/packages/hooks_riverpod/example/pubspec.yaml @@ -4,7 +4,7 @@ description: A new Flutter project. publish_to: "none" environment: - sdk: ">=2.12.0-0 <4.0.0" + sdk: ">=3.0.0<4.0.0" flutter: ">=1.17.0" dependencies: diff --git a/packages/hooks_riverpod/lib/legacy.dart b/packages/hooks_riverpod/lib/legacy.dart new file mode 100644 index 000000000..8bad64e00 --- /dev/null +++ b/packages/hooks_riverpod/lib/legacy.dart @@ -0,0 +1 @@ +export 'package:flutter_riverpod/legacy.dart'; diff --git a/packages/hooks_riverpod/lib/src/consumer.dart b/packages/hooks_riverpod/lib/src/consumer.dart index 5bd6b0d99..d35974c17 100644 --- a/packages/hooks_riverpod/lib/src/consumer.dart +++ b/packages/hooks_riverpod/lib/src/consumer.dart @@ -2,13 +2,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'internals.dart'; -/// {@template hooks_riverpod.hookconsumer.hookconsumerwidget} +/// {@template hooks_riverpod.hook_consumer.hook_consumer_widget} /// A widget that can both use hooks and listen to providers. /// -/// If you do not need hooks, you can use [Consumer]. +/// If you do not need hooks, you can use [ConsumerWidget] and its variants. /// {@endtemplate} abstract class HookConsumerWidget extends ConsumerWidget { - /// {@macro hooks_riverpod.hookconsumer.hookconsumerwidget} + /// {@macro hooks_riverpod.hook_consumer.hook_consumer_widget} const HookConsumerWidget({super.key}); @override @@ -16,20 +16,21 @@ abstract class HookConsumerWidget extends ConsumerWidget { _HookConsumerElement createElement() => _HookConsumerElement(this); } -// ignore: invalid_use_of_visible_for_testing_member +// ignore: invalid_use_of_internal_member class _HookConsumerElement extends ConsumerStatefulElement with HookElement { _HookConsumerElement(HookConsumerWidget super.widget); } -/// {@macro hooks_riverpod.hookconsumer.hookconsumerwidget} +/// {@macro hooks_riverpod.hook_consumer.hook_consumer_widget} class HookConsumer extends HookConsumerWidget { - /// {@macro hooks_riverpod.hookconsumer.hookconsumerwidget} + /// {@macro hooks_riverpod.hook_consumer.hook_consumer_widget} const HookConsumer({super.key, required this.builder, this.child}); /// A function that builds a widget. /// /// Can both listen to providers and use hooks. + // ignore: invalid_use_of_internal_member final ConsumerBuilder builder; /// An optional child widget that will be passed to [builder]. @@ -55,6 +56,7 @@ abstract class StatefulHookConsumerWidget extends ConsumerStatefulWidget { _StatefulHookConsumerElement(this); } +// ignore: invalid_use_of_internal_member class _StatefulHookConsumerElement extends ConsumerStatefulElement with // ignore: invalid_use_of_visible_for_testing_member diff --git a/packages/hooks_riverpod/pubspec.yaml b/packages/hooks_riverpod/pubspec.yaml index 4c51f85ba..4f7ea2acb 100644 --- a/packages/hooks_riverpod/pubspec.yaml +++ b/packages/hooks_riverpod/pubspec.yaml @@ -2,7 +2,7 @@ name: hooks_riverpod description: > A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze. -version: 2.6.1 +version: 3.0.0-dev.3 homepage: https://riverpod.dev repository: https://github.com/rrousselGit/riverpod issue_tracker: https://github.com/rrousselGit/riverpod/issues @@ -10,7 +10,7 @@ funding: - https://github.com/sponsors/rrousselGit/ environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0+0.0-dev <4.0.0" flutter: ">=3.0.0" dependencies: @@ -18,8 +18,8 @@ dependencies: flutter: sdk: flutter flutter_hooks: '>=0.18.0 <0.22.0' - flutter_riverpod: 2.6.1 - riverpod: 2.6.1 + flutter_riverpod: 3.0.0-dev.3 + riverpod: 3.0.0-dev.3 state_notifier: ">=0.7.2 <2.0.0" dev_dependencies: diff --git a/packages/hooks_riverpod/test/internal_test.dart b/packages/hooks_riverpod/test/internal_test.dart index de7b5de04..756abced8 100644 --- a/packages/hooks_riverpod/test/internal_test.dart +++ b/packages/hooks_riverpod/test/internal_test.dart @@ -1,57 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:hooks_riverpod/legacy.dart'; void main() { - // testWidgets('ref.watch supports changing the selected provider', - // (tester) async { - // final notifier1 = Counter(); - // final provider1 = StateNotifierProvider((_) => notifier1); - // final notifier2 = Counter(42); - // final provider2 = StateNotifierProvider((_) => notifier2); - // var buildCount = 0; - // var selectCount = 0; - - // Widget build(StateNotifierProvider provider) { - // return ProviderScope( - // child: HookConsumer(builder: (c, ref, child) { - // buildCount++; - // final value = ref.watch(provider.select((value) { - // selectCount++; - // return value > 5; - // })); - // return Text('$value', textDirection: TextDirection.ltr); - // }), - // ); - // } - - // await tester.pumpWidget(build(provider1)); - - // expect(find.text('false'), findsOneWidget); - // expect(buildCount, 1); - // expect(selectCount, 1); - - // await tester.pumpWidget(build(provider2)); - - // expect(find.text('true'), findsOneWidget); - // expect(buildCount, 2); - // expect(selectCount, 2); - - // notifier1.state = 42; - // await tester.pump(); - - // expect(find.text('true'), findsOneWidget); - // expect(buildCount, 2); - // expect(selectCount, 2); - - // notifier2.state = 0; - // await tester.pump(); - - // expect(find.text('false'), findsOneWidget); - // expect(buildCount, 3); - // expect(selectCount, 4); - // }); - testWidgets('ref.watch supports changing the provider', (tester) async { final notifier1 = Counter(); final provider1 = StateNotifierProvider((_) => notifier1); @@ -93,173 +45,8 @@ void main() { expect(find.text('43'), findsOneWidget); expect(buildCount, 3); }); - - group('ref.watch(provider.select)', () { - // testWidgets('simple flow', (tester) async { - // final notifier = Counter(); - // final provider = StateNotifierProvider((_) => notifier); - // final selector = SelectorSpy(); - // var buildCount = 0; - // Object? lastSelectedValue; - - // await tester.pumpWidget( - // ProviderScope( - // child: HookConsumer(builder: (c, ref, child) { - // buildCount++; - // lastSelectedValue = ref.watch(provider.select((value) { - // selector(value); - // return value.isNegative; - // })); - // return Container(); - // }), - // ), - // ); - - // expect(lastSelectedValue, false); - // expect(buildCount, 1); - // verify(selector(0)).called(1); - // verifyNoMoreInteractions(selector); - - // notifier.increment(); - - // verifyNoMoreInteractions(selector); - - // await tester.pump(); - - // expect(lastSelectedValue, false); - // expect(buildCount, 1); - // verify(selector(1)).called(1); - // verifyNoMoreInteractions(selector); - // }); - - // testWidgets('recompute value when changing selector', (tester) async { - // final notifier = Counter(); - // final provider = StateNotifierProvider((_) => notifier); - // final selector = SelectorSpy(); - // String? value2; - // final build = BuildSpy(); - // when(build()).thenAnswer((_) => value2 = 'foo'); - // Object? lastSelectedValue; - - // await tester.pumpWidget( - // ProviderScope(child: HookConsumer(builder: (c, ref, child) { - // build(); - // lastSelectedValue = ref.watch(provider.select((value) { - // selector('$value $value2'); - // return '$value $value2'; - // })); - // return Container(); - // })), - // ); - - // expect(lastSelectedValue, '0 foo'); - // verifyInOrder([ - // build(), - // selector('0 foo'), - // ]); - // verifyNoMoreInteractions(selector); - // verifyNoMoreInteractions(build); - - // notifier.increment(); - // when(build()).thenAnswer((_) => value2 = 'bar'); - - // await tester.pump(); - - // expect(lastSelectedValue, '1 bar'); - // verifyInOrder([ - // selector('1 foo'), - // build(), - // selector('1 bar'), - // ]); - // verifyNoMoreInteractions(selector); - // verifyNoMoreInteractions(build); - // }); - - // testWidgets('stop calling selectors after one cause rebuild', - // (tester) async { - // final notifier = Counter(); - // final provider = StateNotifierProvider((_) => notifier); - // bool? lastSelectedValue; - // final selector = SelectorSpy(); - // int? lastSelectedValue2; - // final selector2 = SelectorSpy(); - // Object? lastSelectedValue3; - // final selector3 = SelectorSpy(); - // final build = BuildSpy(); - - // await tester.pumpWidget( - // ProviderScope(child: HookConsumer(builder: (c, ref, child) { - // build(); - // lastSelectedValue = ref.watch(provider.select((value) { - // selector(value); - // return value.isNegative; - // })); - // lastSelectedValue2 = ref.watch(provider.select((value) { - // selector2(value); - // return value; - // })); - // lastSelectedValue3 = ref.watch(provider.select((value) { - // selector3(value); - // return value; - // })); - // return Container(); - // })), - // ); - - // verifyInOrder([ - // build(), - // selector(0), - // selector2(0), - // selector3(0), - // ]); - // verifyNoMoreInteractions(build); - // verifyNoMoreInteractions(selector); - // verifyNoMoreInteractions(selector2); - // verifyNoMoreInteractions(selector3); - // expect(lastSelectedValue, false); - // expect(lastSelectedValue2, 0); - // expect(lastSelectedValue3, 0); - - // notifier.increment(); - - // verifyNoMoreInteractions(build); - // verifyNoMoreInteractions(selector); - // verifyNoMoreInteractions(selector2); - // verifyNoMoreInteractions(selector3); - - // await tester.pump(); - - // verifyInOrder([ - // selector(1), - // selector2(1), - // build(), - // selector(1), - // selector2(1), - // selector3(1), - // ]); - // verifyNoMoreInteractions(build); - // verifyNoMoreInteractions(selector); - // verifyNoMoreInteractions(selector2); - // verifyNoMoreInteractions(selector3); - // expect(lastSelectedValue, false); - // expect(lastSelectedValue2, 1); - // expect(lastSelectedValue3, 1); - // }); - }); } -// class SelectorSpy extends Mock { -// void call(T value); -// } - -// class BuildSpy extends Mock { -// void call(); -// } - -// class MockCreateState extends Mock { -// void call(); -// } - class Counter extends StateNotifier { Counter([super.initialValue = 0]); diff --git a/packages/lint_visitor_generator/LICENSE b/packages/lint_visitor_generator/LICENSE new file mode 100644 index 000000000..62492abf7 --- /dev/null +++ b/packages/lint_visitor_generator/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Remi Rousselet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/packages/lint_visitor_generator/README.md b/packages/lint_visitor_generator/README.md new file mode 100644 index 000000000..1845aa6de --- /dev/null +++ b/packages/lint_visitor_generator/README.md @@ -0,0 +1 @@ +Internal tooling to generate `riverpod_analyzer_utils`'s `RiverpodAstVisitor` (and variants). diff --git a/packages/lint_visitor_generator/build.yaml b/packages/lint_visitor_generator/build.yaml new file mode 100644 index 000000000..cabd94bb8 --- /dev/null +++ b/packages/lint_visitor_generator/build.yaml @@ -0,0 +1,21 @@ +targets: + $default: + builders: + lint_visitor_generator: + enabled: true + generate_for: + include: + - "**/*.dart" + source_gen|combining_builder: + options: + ignore_for_file: + - "type=lint" + +builders: + lint_visitor_generator: + import: "package:lint_visitor_generator/builder.dart" + builder_factories: ["lintVisitorGenerator"] + build_extensions: { ".dart": [".lint_visitor_generator.g.part"] } + auto_apply: dependents + build_to: cache + applies_builders: ["source_gen|combining_builder"] \ No newline at end of file diff --git a/packages/lint_visitor_generator/lib/builder.dart b/packages/lint_visitor_generator/lib/builder.dart new file mode 100644 index 000000000..d72e149bb --- /dev/null +++ b/packages/lint_visitor_generator/lib/builder.dart @@ -0,0 +1,224 @@ +// ignore_for_file: require_trailing_commas + +import 'package:analyzer/dart/element/element.dart'; +import 'package:build/build.dart'; +import 'package:collection/collection.dart'; +import 'package:source_gen/source_gen.dart' hide TypeChecker; + +/// Builds generators for `build_runner` to run +Builder lintVisitorGenerator(BuilderOptions options) { + return SharedPartBuilder( + [_LintVisitorGenerator()], + 'lint_visitor_generator', + ); +} + +extension on String { + String get plural { + if (endsWith('y')) return '${substring(0, length - 1)}ies'; + if (endsWith('s')) return '${this}List'; + return '${this}s'; + } +} + +class _LintVisitorGenerator extends Generator { + @override + String generate(LibraryReader library, BuildStep buildStep) { + final buffer = StringBuffer(); + if (library.element.name == 'nodes') { + _writeRiverpodAstVisitor(library, buffer); + } + + if (buffer.isNotEmpty) { + buffer.writeln('// ignore_for_file: type=lint'); + } + + return buffer.toString(); + } + + void _writeRiverpodAstVisitor(LibraryReader library, StringBuffer buffer) { + final allAst = library.element.topLevelElements + .whereType() + .where((e) => e.metadata.firstOrNull?.toSource() == '@_ast') + .expand((extension) { + final constraint = extension.extendedType; + + return extension.accessors + .map( + (e) => ( + constraint: constraint.element!.name!, + type: e.returnType.element!.name!, + name: e.name, + ), + ) + .where((e) => !e.name.startsWith('_cache')); + }).toList(); + + final byConstraint = <({ + String type, + String name, + }), + List<({String type, String name})>>{}; + for (final ast in allAst) { + byConstraint.putIfAbsent(( + type: ast.constraint, + name: ast.constraint == 'AstNode' ? 'Node' : ast.constraint, + ), () => []).add((type: ast.type, name: ast.name)); + } + + buffer.writeln(''' +mixin RiverpodAstVisitor { + ${allAst.map((e) => 'void visit${e.type}(${e.type} node) {}').join('\n')} +} + +abstract class RecursiveRiverpodAstVisitor + extends GeneralizingAstVisitor + with RiverpodAstVisitor { + ${byConstraint.entries.map((e) => ''' +@override +void visit${e.key.name}(${e.key.type} node) { + ${e.value.map((e) => ''' + if (node.${e.name} case final value?) { + visit${e.type}(value); + return; + } + ''').join('\n')} + + super.visit${e.key.name}(node); +}''').join('\n')} + + ${allAst.map((e) => ''' + void visit${e.type}(${e.type} node) { + super.visit${e.constraint == 'AstNode' ? 'Node' : e.constraint}(node.node); + } + ''').join('\n')} + +} + +abstract class SimpleRiverpodAstVisitor + extends RecursiveRiverpodAstVisitor { + @override + void visitNode(AstNode node) {} +} + +abstract class UnimplementedRiverpodAstVisitor + extends SimpleRiverpodAstVisitor { + ${allAst.map((e) => 'void visit${e.type}(${e.type} node) => throw UnimplementedError();').join('\n')} +} + +@internal +class CollectionRiverpodAst extends SimpleRiverpodAstVisitor { + final Map> riverpodAst = {}; + List? _pendingList; + +${byConstraint.keys.map((e) => ''' + @override + void visit${e.name}( + ${e.type} node, + ) { + final list = riverpodAst.putIfAbsent('${e.type}', () => []); + final previousList = list; + _pendingList = list; + super.visit${e.name}(node); + _pendingList = previousList; + } +''').join('\n')} + +${allAst.map((e) => ''' + void visit${e.type}(${e.type} node) { + _pendingList!.add(node); + } +''').join('\n')} +} + +@internal +class RiverpodAnalysisResult extends RecursiveRiverpodAstVisitor { + final List errors = []; + + ${allAst.map((e) => ''' + final ${e.type.lowerFirst.plural} = <${e.type}>[]; + @override + void visit${e.type}( + ${e.type} node, + ) { + super.visit${e.type}(node); + ${e.type.lowerFirst.plural}.add(node); + } +''').join('\n')} +} + +class RiverpodAstRegistry { + static final _cache = Expando>>(); + + void run(AstNode node) { + final previousErrorReporter = errorReporter; + try { + final errors = _cache.upsert( + node, + () => [], + ); + + final visitor = _RiverpodAstRegistryVisitor(this); + errorReporter = errors.add; + + node.accept(visitor); + for (final error in errors) { + visitor._runSubscriptions(error, _onRiverpodAnalysisError); + } + } finally { + errorReporter = previousErrorReporter; + } + } + + final _onRiverpodAnalysisError = []; + void addRiverpodAnalysisError( + void Function(RiverpodAnalysisError node) cb, + ) { + _onRiverpodAnalysisError.add(cb); + } + + ${allAst.map((e) => ''' + final _on${e.type} = []; + void add${e.type}(void Function(${e.type} node) cb) { + _on${e.type}.add(cb); + } +''').join('\n')} +} + +class _RiverpodAstRegistryVisitor extends RecursiveRiverpodAstVisitor { + _RiverpodAstRegistryVisitor(this._registry); + + final RiverpodAstRegistry _registry; + + void _runSubscriptions( + R value, + List subscriptions, + ) { + for (final sub in subscriptions) { + try { + sub(value); + } catch (e, stack) { + Zone.current.handleUncaughtError(e, stack); + } + } + } + + ${allAst.map((e) => ''' + @override + void visit${e.type}(${e.type} node) { + super.visit${e.type}(node); + _runSubscriptions( + node, + _registry._on${e.type}, + ); + } + +''').join('\n')} +} +'''); + } +} + +extension on String { + String get lowerFirst => substring(0, 1).toLowerCase() + substring(1); +} diff --git a/packages/lint_visitor_generator/lib/src/type_checker.dart b/packages/lint_visitor_generator/lib/src/type_checker.dart new file mode 100644 index 000000000..6ec046641 --- /dev/null +++ b/packages/lint_visitor_generator/lib/src/type_checker.dart @@ -0,0 +1,476 @@ +// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Code imported from source_gen + +import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/constant/value.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:meta/meta.dart'; +import 'package:path/path.dart' as p; +import 'package:source_span/source_span.dart'; + +/// An abstraction around doing static type checking at compile/build time. +abstract class TypeChecker { + const TypeChecker._(); + + /// Creates a new [TypeChecker] that delegates to other [checkers]. + /// + /// This implementation will return `true` for type checks if _any_ of the + /// provided type checkers return true, which is useful for deprecating an + /// API: + /// ```dart + /// const $Foo = const TypeChecker.fromRuntime(Foo); + /// const $Bar = const TypeChecker.fromRuntime(Bar); + /// + /// // Used until $Foo is deleted. + /// const $FooOrBar = const TypeChecker.forAny(const [$Foo, $Bar]); + /// ``` + const factory TypeChecker.any(Iterable checkers) = _AnyChecker; + + /// Creates a new [TypeChecker] that delegates to other [checkers]. + /// + /// This implementation will return `true` if **all** the checkers match. + /// Checkers will be checked in order. + const factory TypeChecker.every(Iterable checkers) = + _EveryChecker; + + /// Create a new [TypeChecker] backed by a static [type]. + const factory TypeChecker.fromStatic(DartType type) = _LibraryTypeChecker; + + /// Checks that the element comes from a specific package. + const factory TypeChecker.fromPackage(String packageName) = _PackageChecker; + + /// Checks that the element has a specific name, and optionally checks that it + /// is defined from a specific package. + /// + /// This is similar to [TypeChecker.fromUrl] but does not rely on exactly where + /// the definition of the element comes from. + /// The downside is that if somehow a package exposes two elements with the + /// same name, there could be a conflict. + const factory TypeChecker.fromName( + String name, { + String? packageName, + }) = _NamedChecker; + + /// Create a new [TypeChecker] backed by a library [url]. + /// + /// Example of referring to a `LinkedHashMap` from `dart:collection`: + /// ```dart + /// const linkedHashMap = const TypeChecker.fromUrl( + /// 'dart:collection#LinkedHashMap', + /// ); + /// ``` + /// + /// **NOTE**: This is considered a more _brittle_ way of determining the type + /// because it relies on knowing the _absolute_ path (i.e. after resolved + /// `export` directives). You should ideally only use `fromUrl` when you know + /// the full path (likely you own/control the package) or it is in a stable + /// package like in the `dart:` SDK. + const factory TypeChecker.fromUrl(dynamic url) = _UriTypeChecker; + + /// Returns the first constant annotating [element] assignable to this type. + /// + /// Otherwise returns `null`. + /// + /// Throws on unresolved annotations unless [throwOnUnresolved] is `false`. + DartObject? firstAnnotationOf( + Element element, { + bool throwOnUnresolved = true, + }) { + if (element.metadata.isEmpty) { + return null; + } + final results = + annotationsOf(element, throwOnUnresolved: throwOnUnresolved); + return results.isEmpty ? null : results.first; + } + + /// Returns if a constant annotating [element] is assignable to this type. + /// + /// Throws on unresolved annotations unless [throwOnUnresolved] is `false`. + bool hasAnnotationOf(Element element, {bool throwOnUnresolved = true}) => + firstAnnotationOf(element, throwOnUnresolved: throwOnUnresolved) != null; + + /// Returns the first constant annotating [element] that is exactly this type. + /// + /// Throws [UnresolvedAnnotationException] on unresolved annotations unless + /// [throwOnUnresolved] is explicitly set to `false` (default is `true`). + DartObject? firstAnnotationOfExact( + Element element, { + bool throwOnUnresolved = true, + }) { + if (element.metadata.isEmpty) { + return null; + } + final results = + annotationsOfExact(element, throwOnUnresolved: throwOnUnresolved); + return results.isEmpty ? null : results.first; + } + + DartObject? _computeConstantValue( + Element element, + int annotationIndex, { + bool throwOnUnresolved = true, + }) { + final annotation = element.metadata[annotationIndex]; + final result = annotation.computeConstantValue(); + if (result == null && throwOnUnresolved) { + throw UnresolvedAnnotationException._from(element, annotationIndex); + } + return result; + } + + /// Returns annotating constants on [element] assignable to this type. + /// + /// Throws [UnresolvedAnnotationException] on unresolved annotations unless + /// [throwOnUnresolved] is explicitly set to `false` (default is `true`). + Iterable annotationsOf( + Element element, { + bool throwOnUnresolved = true, + }) => + _annotationsWhere( + element, + isAssignableFromType, + throwOnUnresolved: throwOnUnresolved, + ); + + Iterable _annotationsWhere( + Element element, + bool Function(DartType) predicate, { + bool throwOnUnresolved = true, + }) sync* { + for (var i = 0; i < element.metadata.length; i++) { + final value = _computeConstantValue( + element, + i, + throwOnUnresolved: throwOnUnresolved, + ); + if (value?.type != null && predicate(value!.type!)) { + yield value; + } + } + } + + /// Returns annotating constants on [element] of exactly this type. + /// + /// Throws [UnresolvedAnnotationException] on unresolved annotations unless + /// [throwOnUnresolved] is explicitly set to `false` (default is `true`). + Iterable annotationsOfExact( + Element element, { + bool throwOnUnresolved = true, + }) => + _annotationsWhere( + element, + isExactlyType, + throwOnUnresolved: throwOnUnresolved, + ); + + /// Returns `true` if the type of [element] can be assigned to this type. + bool isAssignableFrom(Element element) => + isExactly(element) || + (element is ClassElement && element.allSupertypes.any(isExactlyType)); + + /// Returns `true` if [staticType] can be assigned to this type. + // ignore: avoid_bool_literals_in_conditional_expressions + bool isAssignableFromType(DartType staticType) => staticType.element == null + ? false + : isAssignableFrom(staticType.element!); + + /// Returns `true` if representing the exact same class as [element]. + bool isExactly(Element element); + + /// Returns `true` if representing the exact same type as [staticType]. + bool isExactlyType(DartType staticType) { + final element = staticType.element; + return element != null && isExactly(element); + } + + /// Returns `true` if representing a super class of [element]. + /// + /// This check only takes into account the *extends* hierarchy. If you wish + /// to check mixins and interfaces, use [isAssignableFrom]. + bool isSuperOf(Element element) { + if (element is ClassElement) { + var theSuper = element.supertype; + + do { + if (isExactlyType(theSuper!)) { + return true; + } + + theSuper = theSuper.superclass; + } while (theSuper != null); + } + + return false; + } + + /// Returns `true` if representing a super type of [staticType]. + /// + /// This only takes into account the *extends* hierarchy. If you wish + /// to check mixins and interfaces, use [isAssignableFromType]. + bool isSuperTypeOf(DartType staticType) => isSuperOf(staticType.element!); +} + +// Checks a static type against another static type; +class _LibraryTypeChecker extends TypeChecker { + const _LibraryTypeChecker(this._type) : super._(); + + final DartType _type; + + @override + bool isExactly(Element element) => + element is ClassElement && element == _type.element; + + @override + String toString() => _urlOfElement(_type.element!); +} + +@immutable +class _PackageChecker extends TypeChecker { + const _PackageChecker(this._packageName) : super._(); + + final String _packageName; + + @override + bool isExactly(Element element) { + final elementLibraryIdentifier = element.library?.identifier; + + return elementLibraryIdentifier != null && + elementLibraryIdentifier.startsWith('package:$_packageName/'); + } + + @override + bool operator ==(Object o) { + return o is _PackageChecker && o._packageName == _packageName; + } + + @override + int get hashCode => Object.hash(runtimeType, _packageName); + + @override + String toString() => _packageName; +} + +@immutable +class _NamedChecker extends TypeChecker { + const _NamedChecker(this._name, {this.packageName}) : super._(); + + final String _name; + final String? packageName; + + @override + bool isExactly(Element element) { + if (element.name != _name) return false; + + // No packageName specified, ignoring it. + if (packageName == null) return true; + + final checker = _PackageChecker(packageName!); + return checker.isExactly(element); + } + + @override + bool operator ==(Object o) { + return o is _NamedChecker && + o._name == _name && + o.packageName == packageName; + } + + @override + int get hashCode => Object.hash(runtimeType, _name, packageName); + + @override + String toString() => '$packageName#$_name'; +} + +// Checks a runtime type against an Uri and Symbol. +@immutable +class _UriTypeChecker extends TypeChecker { + const _UriTypeChecker(dynamic url) + : _url = '$url', + super._(); + + // Precomputed cache of String --> Uri. + static final _cache = Expando(); + + final String _url; + + /// Url as a [Uri] object, lazily constructed. + Uri get uri => _cache[this] ??= _normalizeUrl(Uri.parse(_url)); + + /// Returns whether this type represents the same as [url]. + bool hasSameUrl(dynamic url) => + uri.toString() == + (url is String ? url : _normalizeUrl(url as Uri).toString()); + + @override + bool isExactly(Element element) => hasSameUrl(_urlOfElement(element)); + + @override + bool operator ==(Object o) => o is _UriTypeChecker && o._url == _url; + + @override + int get hashCode => _url.hashCode; + + @override + String toString() => '$uri'; +} + +class _AnyChecker extends TypeChecker { + const _AnyChecker(this._checkers) : super._(); + + final Iterable _checkers; + + @override + bool isExactly(Element element) => _checkers.any((c) => c.isExactly(element)); +} + +class _EveryChecker extends TypeChecker { + const _EveryChecker(this._checkers) : super._(); + + final Iterable _checkers; + + @override + bool isExactly(Element element) { + return _checkers.every((c) => c.isExactly(element)); + } +} + +/// Exception thrown when [TypeChecker] fails to resolve a metadata annotation. +/// +/// Methods such as [TypeChecker.firstAnnotationOf] may throw this exception +/// when one or more annotations are not resolvable. This is usually a sign that +/// something was misspelled, an import is missing, or a dependency was not +/// defined (for build systems such as Bazel). +class UnresolvedAnnotationException implements Exception { + /// Creates an exception from an annotation ([annotationIndex]) that was not + /// resolvable while traversing [Element.metadata] on [annotatedElement]. + factory UnresolvedAnnotationException._from( + Element annotatedElement, + int annotationIndex, + ) { + final sourceSpan = _findSpan(annotatedElement, annotationIndex); + return UnresolvedAnnotationException._(annotatedElement, sourceSpan); + } + + const UnresolvedAnnotationException._( + this.annotatedElement, + this.annotationSource, + ); + + static SourceSpan? _findSpan( + Element annotatedElement, + int annotationIndex, + ) { + final parsedLibrary = annotatedElement.session! + .getParsedLibraryByElement(annotatedElement.library!) + as ParsedLibraryResult; + final declaration = parsedLibrary.getElementDeclaration(annotatedElement); + if (declaration == null) { + return null; + } + final node = declaration.node; + final List metadata; + if (node is AnnotatedNode) { + metadata = node.metadata; + } else if (node is FormalParameter) { + metadata = node.metadata; + } else { + throw StateError( + 'Unhandled Annotated AST node type: ${node.runtimeType}', + ); + } + final annotation = metadata[annotationIndex]; + final start = annotation.offset; + final end = start + annotation.length; + final parsedUnit = declaration.parsedUnit!; + return SourceSpan( + SourceLocation(start, sourceUrl: parsedUnit.uri), + SourceLocation(end, sourceUrl: parsedUnit.uri), + parsedUnit.content.substring(start, end), + ); + } + + /// Element that was annotated with something we could not resolve. + final Element annotatedElement; + + /// Source span of the annotation that was not resolved. + /// + /// May be `null` if the import library was not found. + final SourceSpan? annotationSource; + + @override + String toString() { + final message = 'Could not resolve annotation for `$annotatedElement`.'; + if (annotationSource != null) { + return annotationSource!.message(message); + } + return message; + } +} + +/// Returns a URL representing [element]. +String _urlOfElement(Element element) => element.kind == ElementKind.DYNAMIC + ? 'dart:core#dynamic' + : element.kind == ElementKind.NEVER + ? 'dart:core#Never' + // using librarySource.uri – in case the element is in a part + : _normalizeUrl(element.librarySource!.uri) + .replace(fragment: element.name) + .toString(); + +Uri _normalizeUrl(Uri url) { + switch (url.scheme) { + case 'dart': + return _normalizeDartUrl(url); + case 'package': + return _packageToAssetUrl(url); + case 'file': + return _fileToAssetUrl(url); + default: + return url; + } +} + +/// Make `dart:`-type URLs look like a user-knowable path. +/// +/// Some internal dart: URLs are something like `dart:core/map.dart`. +/// +/// This isn't a user-knowable path, so we strip out extra path segments +/// and only expose `dart:core`. +Uri _normalizeDartUrl(Uri url) => url.pathSegments.isNotEmpty + ? url.replace(pathSegments: url.pathSegments.take(1)) + : url; + +Uri _fileToAssetUrl(Uri url) { + if (!p.isWithin(p.url.current, url.path)) return url; + + return Uri( + scheme: 'asset', + path: p.join('', p.relative(url.path)), + ); +} + +/// Returns a `package:` URL converted to a `asset:` URL. +/// +/// This makes internal comparison logic much easier, but still allows users +/// to define assets in terms of `package:`, which is something that makes more +/// sense to most. +/// +/// For example, this transforms `package:source_gen/source_gen.dart` into: +/// `asset:source_gen/lib/source_gen.dart`. +Uri _packageToAssetUrl(Uri url) => url.scheme == 'package' + ? url.replace( + scheme: 'asset', + pathSegments: [ + url.pathSegments.first, + 'lib', + ...url.pathSegments.skip(1), + ], + ) + : url; diff --git a/packages/lint_visitor_generator/pubspec.yaml b/packages/lint_visitor_generator/pubspec.yaml new file mode 100644 index 000000000..c8995dee6 --- /dev/null +++ b/packages/lint_visitor_generator/pubspec.yaml @@ -0,0 +1,20 @@ +name: lint_visitor_generator +publish_to: none + +environment: + sdk: ">=3.0.0 <4.0.0" + +dependencies: + analyzer: ^7.0.0 + build: ^2.3.1 + build_config: ^1.1.0 + collection: ^1.17.1 + meta: ^1.7.0 + path: ^1.9.0 + source_gen: ^2.0.0 + source_span: ^1.10.0 + +dev_dependencies: + build_runner: ^2.2.0 + build_test: ^2.1.5 + source_gen_test: ^1.0.4 diff --git a/packages/riverpod/CHANGELOG.md b/packages/riverpod/CHANGELOG.md index e9f681672..1a2f3acb5 100644 --- a/packages/riverpod/CHANGELOG.md +++ b/packages/riverpod/CHANGELOG.md @@ -1,3 +1,93 @@ +## Unreleased build + +- **Breaking**: ProviderObserver methods have been updated to take a `ProviderObserverContext` parameter. + This replaces the old `provider`+`container` parameters, and contains extra + information. +- **Breaking**: It is now a runtime exception to "scope" a provider + that is not specifying `dependencies`. +- **Breaking**: Removed all `Ref` subclasses (such `FutureProviderRef`). + Use `Ref` directly instead. + For `FutureProviderRef.future`, migrate to using an `AsyncNotifier`. +- **Breaking** All ref and notifier methods besides "mounted" now throw if used after getting disposed. +- **Breaking**: `StateProvider` and `StateNotifierProvider` + are moved out of `package:flutter_riverpod/flutter_riverpod.dart` to + `package:flutter_riverpod/legacy.dart`. +- **Breaking** Some internal utils are no-longer exported. +- **Breaking** `AsyncValue.value` now returns `null` during errors. +- **Breaking** removed `AsyncValue.valueOrNull` (use `.value` instead). +- `Stream/FutureProvider.overrideWithValue` was added back. +- **Breaking**: `Notifier` and variants are now recreated whenever the provider + rebuilds. This enables using `Ref.mounted` to check dispose. +- **Breaking**: `StreamProvider` now pauses its `StreamSubscription` when + the provider is not actively listened. +- **Breaking**: Calling ref.watch multiple times calls ref.onListen every-times. +- **Breaking**: A provider is now considered "paused" if all + of its listeners are also paused. So if a provider `A` is watched _only_ by a provider `B`, and `B` is currently unused, + then `A` will be paused. +- **Breaking**: When an asynchronous provider rebuilds, it doesn't immediately stops + listening to its previous providers. Instead, those subscriptions are removed when the rebuild completes. + This impacts how "auto-dispose" behaves. See https://github.com/rrousselGit/riverpod/issues/1253 +- Fix `StreamProvider` not cancelling the `StreamSubscription` if the stream is never emitted any value. +- All `Ref` life-cycles (such as `Ref.onDispose`) and `Notifier.listenSelf` + now return a function to remove the listener. +- Added methods to `ProviderObserver` for listening to "mutations". + Mutations are a new code-generation-only feature. See riverpod_generator's changelog + for more information. +- Added `Ref.listen(..., weak: true)`. + When specifying `weak: true`, the listener will not cause the provider to be + initialized. This is useful when wanting to react to changes to a provider, + but not trigger a network request if not necessary. +- `AsyncValue` now has an optional `progress` field. + This can be set by providers to allow the UI to show a custom progress logic. +- An error is now thrown when trying to override a provider twice in the same + `ProviderContainer`. +- Disposing a `ProviderContainer` now disposes of all of its sub `ProviderContainers` too. +- Added `ProviderSubscription.pause()`/`.resume()`. + This enables temporarily stopping the subscription to a provider, without it possibly loosing its state when using `autoDispose`. +- Added `ProviderContainer.test()`. This is a custom constructor for testing + purpose. It is meant to replace the `createContainer` utility. +- Added `NotifierProvider.overrideWithBuild`, to override `Notifier.build` without + overriding methods of the notifier. +- `Ref.mounted` has been added. It can now be used to check if a provider + was disposed. +- When a provider is rebuilt, a new `Ref` is now created. This avoids + issues where an old build of a provider is still performing work. +- Updated `AsyncValue` documentations to use pattern matching. +- Added support for `Ref/ProviderContainer.invalidate(provider, asReload: true)` +- Failing providers are now automatically retried after a delay. + The delay can be optionally configured. +- Fixed a bug when overriding a specific provider of a `family`, combined with `dependencies: [family]` + +## 3.0.0-dev.3 - 2023-11-27 + +- Fix "pending timer" issue inside tests when using `ref.keepAlive()`. +- Fix `Ref.invalidate`/`Ref.refresh` not throwing on circular dependency. +- Fix an infinite loop caused by `ref.keepAlive` if the `KeepAliveLink` is + immediately closed. +- Fix `container.exists(provider)` on nested containers not checking their + parent containers. + +## 3.0.0-dev.2 - 2023-11-20 + +Fix exceptions when using multiple root `ProviderContainers`/`ProviderScopes`. + +## 3.0.0-dev.1 - 2023-11-20 + +- All notifier properties now throw an error if used after the notifier + has been disposed. +- The error thrown when a notifier property is used inside the constructor + of a notifier has been improved. +- Fix `ProviderObserver.didUpdateProvider` being called with an incorrect + "provider" parameter when the provider is overridden. + +## 3.0.0-dev.0 - 2023-10-29 + +- **Breaking**: `AsyncValue` is now "sealed" and `AsyncData/AsyncLoading/AsyncError` + are "final". This means that it is no-longer possible to subclass + `AsyncValue` or the associated classes. +- **Breaking**: Removed everything marked as "deprecated" +- Bumped minimum Dart SDK to >= 3.0.0-dev + ## 2.6.1 - 2024-10-22 - Added `AsyncNotifier.listenSelf`. It was mistakenly absent from the 2.6.0 release @@ -55,10 +145,6 @@ Fix exceptions when using multiple root `ProviderContainers`/`ProviderScopes`. `ProviderObserver.providerDidFail`. - Fix exception when a `ProviderScope` is rebuilt with a different `key`. -## 2.4.5 - 2023-10-28 - -- Support assigning `AsyncValue` to `AsyncNotifier.state` - ## 2.4.4 - 2023-10-15 - Update the documentation of `provider.argument` to match the behavior of @@ -473,10 +559,10 @@ Riverpod is now stable! `ProviderContainer.debugProviderElements` are removed. You can now instead use `ProviderContainer.getAllProviderElements`. - Increased minimum SDK version to 2.14.0 -- **Breaking** The return value when reading a `StateProvider` changed. Before, - doing `ref.read(someStateProvider)` would return the `StateController` - instance. Now, this will only return the state of the `StateController`. This - new behaviour matches `StateNotifierProvider`. +- **Breaking** The return value when reading a `StateProvider` changed. + Before, doing `ref.read(someStateProvider)` would return the `StateController` instance. + Now, this will only return the state of the `StateController`. + This new behavior matches `StateNotifierProvider`. For a simple migration, the old behavior is available by writing `ref.read(someStateProvider.state)`. @@ -775,7 +861,7 @@ Fix an issue where `*Provider.autoDispose` were not able to specify the ### Bug-fixes - fixed a bug where providers were rebuilding even when not being listened to -- fixed `ref.listen` now working when downcasting the value of a provider. +- fixed `ref.listen` now working when downcasing the value of a provider. - fixed a bug where disposing a scoped `ProviderContainer` could cause other `ProviderContainer`s to stop working. - fixed an issue where conditionally depending on an "autoDispose" provider may @@ -932,7 +1018,7 @@ migrated to null safety. - Re-added `StateProvider.overrideWithValue`/`StateProvider.overrideWithProvider` that - were unvoluntarily removed. + were involuntarily removed. ## 0.14.0 @@ -1290,3 +1376,5 @@ The behavior is the same. Only the syntax changed. ## 0.1.0 Initial release + + diff --git a/packages/riverpod/analysis_options.yaml b/packages/riverpod/analysis_options.yaml new file mode 100644 index 000000000..f82ccf8e2 --- /dev/null +++ b/packages/riverpod/analysis_options.yaml @@ -0,0 +1,5 @@ +include: ../../analysis_options.yaml +linter: + rules: + ## temporary disable the public_member_api_docs rule + public_member_api_docs: false \ No newline at end of file diff --git a/packages/riverpod/example/pubspec.yaml b/packages/riverpod/example/pubspec.yaml index e5135ee35..fc3fef851 100644 --- a/packages/riverpod/example/pubspec.yaml +++ b/packages/riverpod/example/pubspec.yaml @@ -2,7 +2,7 @@ name: riverpod_example publish_to: "none" environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0<4.0.0" dependencies: crypto: ^3.0.0 diff --git a/packages/riverpod/lib/legacy.dart b/packages/riverpod/lib/legacy.dart new file mode 100644 index 000000000..f12166f77 --- /dev/null +++ b/packages/riverpod/lib/legacy.dart @@ -0,0 +1,5 @@ +export 'package:state_notifier/state_notifier.dart' show StateNotifier; + +export 'src/providers/legacy/state_controller.dart'; +export 'src/providers/legacy/state_notifier_provider.dart'; +export 'src/providers/legacy/state_provider.dart'; diff --git a/packages/riverpod/lib/riverpod.dart b/packages/riverpod/lib/riverpod.dart index 6f848c3b7..8c6f97aa6 100644 --- a/packages/riverpod/lib/riverpod.dart +++ b/packages/riverpod/lib/riverpod.dart @@ -1,72 +1,69 @@ -export 'package:state_notifier/state_notifier.dart' hide Listener, LocatorMixin; - -export 'src/async_notifier.dart' - hide - AsyncNotifierProviderImpl, - AutoDisposeAsyncNotifierProviderImpl, - AutoDisposeFamilyAsyncNotifierProviderImpl, - FamilyAsyncNotifierProviderImpl, - AsyncNotifierBase, - AsyncNotifierProviderBase, - CancelAsyncSubscription, - BuildlessAsyncNotifier, - BuildlessAutoDisposeAsyncNotifier, - FutureHandlerProviderElementMixin, - FamilyStreamNotifierProviderImpl, - StreamNotifierProviderImpl, - AutoDisposeStreamNotifierProviderImpl, - AutoDisposeFamilyStreamNotifierProviderImpl, - StreamNotifierProviderBase, - BuildlessAutoDisposeStreamNotifier, - BuildlessStreamNotifier; - -export 'src/common.dart' hide AsyncTransition; - +export 'src/core/async_value.dart' hide AsyncTransition; export 'src/framework.dart' hide ProviderScheduler, + Retry, debugCanModifyProviders, Vsync, - ValueProviderElement, - ValueProvider, + $ValueProvider, FamilyCreate, - AsyncSelector, - FamilyBase, - FamilyOverrideImpl, - AutoDisposeProviderElementMixin, - FamilyOverride, - NotifierFamilyBase, + FunctionalFamily, + WhenComplete, + $FamilyOverride, + ClassFamily, SetupFamilyOverride, SetupOverride, - AutoDisposeNotifierFamilyBase, - ProviderOverride, - AutoDisposeFamilyBase, - AlwaysAliveAsyncSelector, - handleFireImmediately, - DebugGetCreateSourceHash, - ProviderNotifierCreate, - ProviderCreate, + $ProviderOverride, + ClassProviderFactory, + FunctionalProviderFactory, + $RefArg, computeAllTransitiveDependencies, Create, Node, ProviderElementProxy, - OnError; + OnError, + ProviderContainerTest, + TransitiveFamilyOverride, + TransitiveProviderOverride, + $ProviderPointer, + UnmountedRefException, + ProviderPointerManager, + ProviderDirectory, + $AsyncClassModifier, + $FutureModifier, + ProviderElement, + ClassBaseX, + AsyncSubscription, + FutureModifierElement, + RunNotifierBuild, + ProviderListenableWithOrigin, + $FunctionalProvider, + ProviderStateSubscription, + ProviderSubscriptionImpl, + ProviderSubscriptionWithOrigin, + ProviderSubscriptionView, + $ClassProvider, + LegacyProviderMixin, + ClassProviderElement, + alreadyInitializedError, + uninitializedElementError, + shortHash, + describeIdentity, + CircularDependencyError, + $AsyncValueProvider; -export 'src/future_provider.dart'; +export 'src/providers/async_notifier.dart' + hide $AsyncNotifier, $AsyncNotifierProvider, $AsyncNotifierProviderElement; -export 'src/notifier.dart' +export 'src/providers/future_provider.dart' + hide $FutureProviderElement, $FutureProvider; +export 'src/providers/notifier.dart' + hide $Notifier, $NotifierProvider, $NotifierProviderElement; +export 'src/providers/provider.dart' hide $ProviderElement, $Provider; +export 'src/providers/stream_notifier.dart' hide - NotifierBase, - NotifierProviderBase, - AutoDisposeFamilyNotifierProviderImpl, - AutoDisposeNotifierProviderImpl, - FamilyNotifierProviderImpl, - NotifierProviderImpl, - BuildlessAutoDisposeNotifier, - BuildlessNotifier; - -export 'src/provider.dart' hide InternalProvider; -export 'src/state_controller.dart'; -export 'src/state_notifier_provider.dart'; -export 'src/state_provider.dart'; -export 'src/stream_provider.dart'; + $StreamNotifier, + $StreamNotifierProvider, + $StreamNotifierProviderElement; +export 'src/providers/stream_provider.dart' + hide $StreamProviderElement, $StreamProvider; diff --git a/packages/riverpod/lib/src/async_notifier.dart b/packages/riverpod/lib/src/async_notifier.dart deleted file mode 100644 index f5174a3c6..000000000 --- a/packages/riverpod/lib/src/async_notifier.dart +++ /dev/null @@ -1,220 +0,0 @@ -import 'dart:async'; - -import 'package:meta/meta.dart'; - -import 'builders.dart'; -import 'common.dart'; -import 'framework.dart'; -import 'future_provider.dart' show FutureProvider; -import 'listenable.dart'; -import 'notifier.dart'; -import 'pragma.dart'; -import 'result.dart'; -import 'run_guarded.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'; -part 'stream_notifier/base.dart'; -part 'stream_notifier/family.dart'; - -/// A base class for [AsyncNotifier]. -/// -/// Not meant for public consumption. -@internal -abstract class AsyncNotifierBase { - AsyncNotifierProviderElementBase, State> - get _element; - - void _setElement(ProviderElementBase> element); - - /// {@macro notifier.listen} - void listenSelf( - void Function(AsyncValue? previous, AsyncValue next) - listener, { - void Function(Object error, StackTrace stackTrace)? onError, - }) { - _element.listenSelf(listener, onError: onError); - } - - /// 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 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 [AsyncNotifier.build] to be re-executed. - @visibleForTesting - @protected - AsyncValue get state { - _element.flush(); - // ignore: invalid_use_of_protected_member - return _element.requireState; - } - - @visibleForTesting - @protected - set state(AsyncValue newState) { - _element.state = newState; - } - - /// The [Ref] from the provider associated with this [AsyncNotifier]. - @protected - Ref> get ref; - - /// {@template riverpod.async_notifier.future} - /// Obtains a [Future] that resolves with the first [state] value that is not - /// [AsyncLoading]. - /// - /// This future will not necessarily wait for [AsyncNotifier.build] to complete. - /// If [state] is modified before [AsyncNotifier.build] completes, then [future] - /// will resolve with that new [state] value. - /// - /// 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} - @visibleForTesting - @protected - Future get future { - _element.flush(); - return _element.futureNotifier.value; - } - - /// 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. - @visibleForTesting - @protected - Future update( - FutureOr Function(State) cb, { - FutureOr Function(Object err, StackTrace stackTrace)? onError, - }) async { - // TODO cancel on rebuild? - - final newState = await future.then(cb, onError: onError); - state = AsyncData(newState); - return newState; - } - - /// A method invoked when the state exposed by this [AsyncNotifier] changes. - /// - /// 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. - /// - /// 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. - /// - /// 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( - previous, - next, - ); - } -} - -ProviderElementProxy, NotifierT> - _asyncNotifier, T>( - AsyncNotifierProviderBase that, -) { - return ProviderElementProxy, NotifierT>( - that, - (element) { - return (element as AsyncNotifierProviderElement) - ._notifierNotifier; - }, - ); -} - -ProviderElementProxy, Future> _asyncFuture( - AsyncNotifierProviderBase, T> that, -) { - return ProviderElementProxy, Future>( - that, - (element) { - return (element as AsyncNotifierProviderElement, T>) - .futureNotifier; - }, - ); -} - -/// A base class for [AsyncNotifierProvider] -/// -/// Not meant for public consumption -@visibleForTesting -@internal -abstract class AsyncNotifierProviderBase, - T> extends ProviderBase> { - /// A base class for [AsyncNotifierProvider] - /// - /// Not meant for public consumption - const AsyncNotifierProviderBase( - this._createNotifier, { - required super.name, - required super.from, - required super.argument, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - }); - - /// Obtains the [AsyncNotifier] associated with this provider, without listening - /// to state changes. - /// - /// This is typically used to invoke methods on a [AsyncNotifier]. For example: - /// - /// ```dart - /// Button( - /// onTap: () => ref.read(stateNotifierProvider.notifier).increment(), - /// ) - /// ``` - /// - /// This listenable will notify its notifiers if the [AsyncNotifier] instance - /// changes. - /// This may happen if the provider is refreshed or one of its dependencies - /// has changes. - Refreshable get notifier; - - /// {@macro riverpod.async_notifier.future} - /// - /// Listening to this using [Ref.watch] will rebuild the widget/provider - /// when the [AsyncNotifier] emits a new value. - /// This will then return a new [Future] that resoles with the latest "state". - Refreshable> get future; - - final NotifierT Function() _createNotifier; - - /// Runs the `build` method of a notifier. - /// - /// This is an implementation detail for differentiating [AsyncNotifier.build] - /// from [FamilyAsyncNotifier.build]. - FutureOr runNotifierBuild(AsyncNotifierBase notifier); -} diff --git a/packages/riverpod/lib/src/async_notifier/auto_dispose.dart b/packages/riverpod/lib/src/async_notifier/auto_dispose.dart deleted file mode 100644 index c42657201..000000000 --- a/packages/riverpod/lib/src/async_notifier/auto_dispose.dart +++ /dev/null @@ -1,134 +0,0 @@ -part of '../async_notifier.dart'; - -/// A [AutoDisposeAsyncNotifier] base class shared between family and non-family notifiers. -/// -/// Not meant for public consumption outside of riverpod_generator -@internal -abstract class BuildlessAutoDisposeAsyncNotifier - extends AsyncNotifierBase { - @override - late final AutoDisposeAsyncNotifierProviderElement, - State> _element; - - @override - void _setElement(ProviderElementBase> element) { - _element = element as AutoDisposeAsyncNotifierProviderElement< - AsyncNotifierBase, State>; - } - - @override - // ignore: deprecated_member_use_from_same_package - AutoDisposeAsyncNotifierProviderRef get ref => _element; -} - -/// {@macro riverpod.async_notifier_provider} -/// -/// {@macro riverpod.async_notifier_provider_modifier} -abstract class AutoDisposeAsyncNotifier - extends BuildlessAutoDisposeAsyncNotifier { - /// {@macro riverpod.async_notifier.build} - @visibleForOverriding - FutureOr build(); -} - -/// {@macro riverpod.provider_ref_base} -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class AutoDisposeAsyncNotifierProviderRef - implements - AsyncNotifierProviderRef, - // ignore: deprecated_member_use_from_same_package - AutoDisposeRef> {} - -/// {@macro riverpod.async_notifier_provider} -/// -/// {@macro riverpod.async_notifier_provider_modifier} -typedef AutoDisposeAsyncNotifierProvider< - NotifierT extends AutoDisposeAsyncNotifier, T> - = AutoDisposeAsyncNotifierProviderImpl; - -/// The implementation of [AutoDisposeAsyncNotifierProvider] but with loosened type constraints -/// that can be shared with [AsyncNotifierProvider]. -/// -/// This enables tests to execute on both [AutoDisposeAsyncNotifierProvider] and -/// [AsyncNotifierProvider] at the same time. -@internal -class AutoDisposeAsyncNotifierProviderImpl< - NotifierT extends AsyncNotifierBase, - T> extends AsyncNotifierProviderBase with AsyncSelector { - /// {@macro riverpod.notifier} - AutoDisposeAsyncNotifierProviderImpl( - super._createNotifier, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - AutoDisposeAsyncNotifierProviderImpl.internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.family} - static const family = AutoDisposeAsyncNotifierProviderFamily.new; - - @override - late final Refreshable notifier = - _asyncNotifier(this); - - @override - late final Refreshable> future = _asyncFuture(this); - - @override - AutoDisposeAsyncNotifierProviderElement createElement() { - return AutoDisposeAsyncNotifierProviderElement(this); - } - - @override - @mustBeOverridden - FutureOr runNotifierBuild(AsyncNotifierBase notifier) { - return (notifier as AutoDisposeAsyncNotifier).build(); - } - - /// {@macro riverpod.override_with} - @mustBeOverridden - Override overrideWith(NotifierT Function() create) { - return ProviderOverride( - origin: this, - override: AutoDisposeAsyncNotifierProviderImpl.internal( - create, - from: from, - argument: argument, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} - -/// The element of [AutoDisposeAsyncNotifierProvider]. -class AutoDisposeAsyncNotifierProviderElement< - NotifierT extends AsyncNotifierBase, - T> extends AsyncNotifierProviderElement - with - AutoDisposeProviderElementMixin> - implements - // ignore: deprecated_member_use_from_same_package - AutoDisposeAsyncNotifierProviderRef { - /// The [ProviderElementBase] for [AsyncNotifierProvider] - @internal - AutoDisposeAsyncNotifierProviderElement(super._provider) : super(); -} diff --git a/packages/riverpod/lib/src/async_notifier/auto_dispose_family.dart b/packages/riverpod/lib/src/async_notifier/auto_dispose_family.dart deleted file mode 100644 index e728d1218..000000000 --- a/packages/riverpod/lib/src/async_notifier/auto_dispose_family.dart +++ /dev/null @@ -1,123 +0,0 @@ -part of '../async_notifier.dart'; - -/// {@macro riverpod.async_notifier_provider} -/// -/// {@macro riverpod.async_notifier_provider_modifier} -abstract class AutoDisposeFamilyAsyncNotifier - extends BuildlessAutoDisposeAsyncNotifier { - /// {@macro riverpod.notifier.family_arg} - late final Arg arg; - - @override - void _setElement(ProviderElementBase> element) { - super._setElement(element); - arg = element.origin.argument as Arg; - } - - /// {@macro riverpod.async_notifier.build} - @visibleForOverriding - FutureOr build(Arg arg); -} - -/// {@macro riverpod.async_notifier_provider} -/// -/// {@macro riverpod.async_notifier_provider_modifier} -typedef AutoDisposeFamilyAsyncNotifierProvider< - NotifierT extends AutoDisposeFamilyAsyncNotifier, T, Arg> - = AutoDisposeFamilyAsyncNotifierProviderImpl; - -/// The implementation of [AutoDisposeAsyncNotifierProvider] but with loosened type constraints -/// that can be shared with [AsyncNotifierProvider]. -/// -/// This enables tests to execute on both [AutoDisposeAsyncNotifierProvider] and -/// [AsyncNotifierProvider] at the same time. -@internal -class AutoDisposeFamilyAsyncNotifierProviderImpl< - NotifierT extends AsyncNotifierBase, - T, - Arg> extends AsyncNotifierProviderBase with AsyncSelector { - /// {@macro riverpod.async_notifier_family_provider} - AutoDisposeFamilyAsyncNotifierProviderImpl( - super._createNotifier, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - AutoDisposeFamilyAsyncNotifierProviderImpl.internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - @override - late final Refreshable notifier = - _asyncNotifier(this); - - @override - late final Refreshable> future = _asyncFuture(this); - - @override - AutoDisposeAsyncNotifierProviderElement createElement() { - return AutoDisposeAsyncNotifierProviderElement(this); - } - - @override - FutureOr runNotifierBuild( - covariant AutoDisposeFamilyAsyncNotifier notifier, - ) { - return notifier.build(notifier.arg); - } -} - -/// The [Family] of [AsyncNotifierProvider]. -class AutoDisposeAsyncNotifierProviderFamily< - NotifierT extends AutoDisposeFamilyAsyncNotifier, T, Arg> - extends AutoDisposeNotifierFamilyBase< - // ignore: deprecated_member_use_from_same_package - AutoDisposeAsyncNotifierProviderRef, - AsyncValue, - Arg, - NotifierT, - AutoDisposeFamilyAsyncNotifierProvider> { - /// The [Family] of [AutoDisposeAsyncNotifierProvider]. - AutoDisposeAsyncNotifierProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: AutoDisposeFamilyAsyncNotifierProvider.internal, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - debugGetCreateSourceHash: null, - ); - - /// {@macro riverpod.override_with} - Override overrideWith(NotifierT Function() create) { - return FamilyOverrideImpl, Arg, - AutoDisposeFamilyAsyncNotifierProvider>( - this, - (arg) => - AutoDisposeFamilyAsyncNotifierProvider.internal( - create, - from: from, - argument: arg, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} diff --git a/packages/riverpod/lib/src/async_notifier/base.dart b/packages/riverpod/lib/src/async_notifier/base.dart deleted file mode 100644 index d9fba83e4..000000000 --- a/packages/riverpod/lib/src/async_notifier/base.dart +++ /dev/null @@ -1,597 +0,0 @@ -part of '../async_notifier.dart'; - -/// A [AsyncNotifier] base class shared between family and non-family notifiers. -/// -/// Not meant for public consumption outside of riverpod_generator -@internal -abstract class BuildlessAsyncNotifier extends AsyncNotifierBase { - @override - late final AsyncNotifierProviderElement, State> - _element; - - @override - void _setElement(ProviderElementBase> element) { - _element = element - as AsyncNotifierProviderElement, State>; - } - - @override - // ignore: deprecated_member_use_from_same_package - AsyncNotifierProviderRef get ref => _element; -} - -/// {@template riverpod.async_notifier} -/// 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". -/// And the [AsyncNotifier] could expose methods such as "setName", -/// to allow changing the current user name. -/// - 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} -abstract class AsyncNotifier extends BuildlessAsyncNotifier { - /// {@template riverpod.async_notifier.build} - /// Initialize an [AsyncNotifier]. - /// - /// It is safe to use [Ref.watch] or [Ref.listen] inside this method. - /// - /// If a dependency of this [AsyncNotifier] (when using [Ref.watch]) changes, - /// then [build] will be re-executed. On the other hand, the [AsyncNotifier] - /// will **not** be recreated. Its instance will be preserved between - /// executions of [build]. - /// - /// If this method throws or returns a future that fails, the error - /// will be caught and an [AsyncError] will be emitted. - /// {@endtemplate} - @visibleForOverriding - FutureOr build(); -} - -/// {@macro riverpod.provider_ref_base} -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class AsyncNotifierProviderRef implements Ref> {} - -/// {@template riverpod.async_notifier_provider} -/// A provider which creates and listens 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; - -/// The implementation of [AsyncNotifierProvider] but with loosened type constraints -/// that can be shared with [AutoDisposeAsyncNotifierProvider]. -/// -/// This enables tests to execute on both [AsyncNotifierProvider] and -/// [AutoDisposeAsyncNotifierProvider] at the same time. -@visibleForTesting -@internal -class AsyncNotifierProviderImpl, T> - extends AsyncNotifierProviderBase - with - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderBase>, - AlwaysAliveAsyncSelector { - /// {@macro riverpod.async_notifier_provider} - /// - /// {@macro riverpod.async_notifier_provider_modifier} - AsyncNotifierProviderImpl( - super._createNotifier, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - AsyncNotifierProviderImpl.internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.autoDispose} - static const autoDispose = AutoDisposeAsyncNotifierProviderBuilder(); - - /// {@macro riverpod.family} - static const family = AsyncNotifierProviderFamilyBuilder(); - - @override - // ignore: deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable notifier = - _asyncNotifier(this); - - @override - // ignore: deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable> future = _asyncFuture(this); - - @override - AsyncNotifierProviderElement createElement() { - return AsyncNotifierProviderElement(this); - } - - @override - @mustBeOverridden - FutureOr runNotifierBuild(AsyncNotifierBase notifier) { - return (notifier as AsyncNotifier).build(); - } - - /// {@macro riverpod.override_with} - @mustBeOverridden - Override overrideWith(NotifierT Function() create) { - return ProviderOverride( - origin: this, - override: AsyncNotifierProviderImpl.internal( - create, - from: from, - argument: argument, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ), - ); - } -} - -/// Internal typedef for cancelling the subscription to an async operation -@internal -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]. - static bool handleUpdateShouldNotify( - AsyncValue previous, - AsyncValue next, - ) { - final wasLoading = previous.isLoading; - final isLoading = next.isLoading; - - if (wasLoading || isLoading) return wasLoading != isLoading; - - return true; - } - - /// An internal function used to obtain the private [futureNotifier] from the mixin - static ProxyElementValueNotifier> futureNotifierOf( - FutureHandlerProviderElementMixin handler, - ) { - return handler.futureNotifier; - } - - /// An observable for [FutureProvider.future]. - @internal - final futureNotifier = ProxyElementValueNotifier>(); - Completer? _futureCompleter; - Future? _lastFuture; - CancelAsyncSubscription? _lastFutureSub; - CancelAsyncSubscription? _cancelSubscription; - - /// Handles manual state change (as opposed to automatic state change from - /// listening to the [Future]). - @protected - AsyncValue get state => requireState; - - @protected - set state(AsyncValue newState) { - // TODO assert Notifier isn't disposed - newState.map( - loading: _onLoading, - error: onError, - data: onData, - ); - } - - @override - bool updateShouldNotify(AsyncValue previous, AsyncValue next) { - return FutureHandlerProviderElementMixin.handleUpdateShouldNotify( - previous, - next, - ); - } - - void _onLoading(AsyncLoading value, {bool seamless = false}) { - asyncTransition(value, seamless: seamless); - if (_futureCompleter == null) { - final completer = _futureCompleter = Completer(); - futureNotifier.result = ResultData(completer.future); - } - } - - /// Life-cycle for when an error from the provider's "build" method is received. - /// - /// Might be invoked after the element is disposed in the case where `provider.future` - /// has yet to complete. - @internal - void onError(AsyncError value, {bool seamless = false}) { - if (mounted) { - asyncTransition(value, seamless: seamless); - - for (final observer in container.observers) { - runQuaternaryGuarded( - observer.providerDidFail, - provider, - value.error, - value.stackTrace, - container, - ); - } - } - - final completer = _futureCompleter; - if (completer != null) { - completer - // TODO test ignore - ..future.ignore() - ..completeError( - value.error, - value.stackTrace, - ); - _futureCompleter = null; - // TODO SynchronousFuture.error - } else if (mounted) { - futureNotifier.result = Result.data( - // TODO test ignore - Future.error( - value.error, - value.stackTrace, - )..ignore(), - ); - } - } - - /// Life-cycle for when a data from the provider's "build" method is received. - /// - /// Might be invoked after the element is disposed in the case where `provider.future` - /// has yet to complete. - @internal - void onData(AsyncData value, {bool seamless = false}) { - if (mounted) { - asyncTransition(value, seamless: seamless); - } - - final completer = _futureCompleter; - if (completer != null) { - completer.complete(value.value); - _futureCompleter = null; - } else if (mounted) { - futureNotifier.result = Result.data(Future.value(value.value)); - } - } - - /// Listens to a [Stream] and convert it into an [AsyncValue]. - @preferInline - @internal - void handleStream( - Stream Function() create, { - required bool didChangeDependency, - }) { - _handleAsync(didChangeDependency: didChangeDependency, ({ - required data, - required done, - required error, - required last, - }) { - final rawStream = create(); - final stream = rawStream.isBroadcast - ? rawStream - : rawStream.asBroadcastStream(onCancel: (sub) => sub.cancel()); - - stream.lastCancelable(last, orElseError: _missingLastValueError); - - final sub = stream.listen(data, onError: error, onDone: done); - return sub.cancel; - }); - } - - StateError _missingLastValueError() { - return StateError( - 'The provider $origin was disposed during loading state, ' - 'yet no value could be emitted.', - ); - } - - /// Listens to a [Future] and convert it into an [AsyncValue]. - @preferInline - @internal - void handleFuture( - FutureOr Function() create, { - required bool didChangeDependency, - }) { - _handleAsync(didChangeDependency: didChangeDependency, ({ - required data, - required done, - required error, - required last, - }) { - final futureOr = create(); - if (futureOr is! Future) { - data(futureOr); - done(); - return null; - } - // Received a Future - - var running = true; - void cancel() { - running = false; - } - - futureOr.then( - (value) { - if (!running) return; - data(value); - done(); - }, - // ignore: avoid_types_on_closure_parameters - onError: (Object err, StackTrace stackTrace) { - if (!running) return; - error(err, stackTrace); - done(); - }, - ); - - last(futureOr, cancel); - - return cancel; - }); - } - - /// Listens to a [Future] and transforms it into an [AsyncValue]. - void _handleAsync( - // Stream Function({required void Function(T) fireImmediately}) create, - CancelAsyncSubscription? Function({ - required void Function(T) data, - required void Function(Object, StackTrace) error, - required void Function() done, - required void Function(Future, CancelAsyncSubscription) last, - }) listen, { - required bool didChangeDependency, - }) { - _onLoading(AsyncLoading(), seamless: !didChangeDependency); - - try { - final sub = _cancelSubscription = listen( - data: (value) { - onData(AsyncData(value), seamless: !didChangeDependency); - }, - error: (error, stack) { - onError(AsyncError(error, stack), seamless: !didChangeDependency); - }, - last: (last, sub) { - assert(_lastFuture == null, 'bad state'); - assert(_lastFutureSub == null, 'bad state'); - _lastFuture = last; - _lastFutureSub = sub; - }, - done: () { - _lastFutureSub?.call(); - _lastFutureSub = null; - _lastFuture = null; - }, - ); - assert( - sub == null || _lastFuture != null, - 'An async operation is pending but the state for provider.future was not initialized.', - ); - - // TODO test build throws -> provider emits AsyncError synchronously & .future emits Future.error - // TODO test build resolves with error -> emits AsyncError & .future emits Future.error - // TODO test build emits value -> .future emits value & provider emits AsyncData - } catch (error, stackTrace) { - onError( - AsyncError(error, stackTrace), - seamless: !didChangeDependency, - ); - } - } - - @override - @internal - void runOnDispose() { - // Stops listening to the previous async operation - _lastFutureSub?.call(); - _lastFutureSub = null; - _lastFuture = null; - _cancelSubscription?.call(); - _cancelSubscription = null; - super.runOnDispose(); - } - - @override - void dispose() { - final completer = _futureCompleter; - if (completer != null) { - // Whatever happens after this, the error is emitted post dispose of the provider. - // So the error doesn't matter anymore. - completer.future.ignore(); - - final lastFuture = _lastFuture; - if (lastFuture != null) { - // The completer will be completed by the while loop in handleStream - - final cancelSubscription = _cancelSubscription; - if (cancelSubscription != null) { - completer.future - .then( - (_) {}, - // ignore: avoid_types_on_closure_parameters - onError: (Object _) {}, - ) - .whenComplete(cancelSubscription); - } - - // Prevent super.dispose from cancelling the subscription on the "last" - // stream value, so that it can be sent to `provider.future`. - _lastFuture = null; - _lastFutureSub = null; - _cancelSubscription = null; - } else { - // The listened stream completed during a "loading" state. - completer.completeError( - _missingLastValueError(), - StackTrace.current, - ); - } - } - super.dispose(); - } - - @override - void visitChildren({ - required void Function(ProviderElementBase element) elementVisitor, - required void Function(ProxyElementValueNotifier element) notifierVisitor, - }) { - super.visitChildren( - elementVisitor: elementVisitor, - notifierVisitor: notifierVisitor, - ); - notifierVisitor(futureNotifier); - } -} - -/// The element of [AsyncNotifierProvider]. -abstract class AsyncNotifierProviderElementBase< - NotifierT extends AsyncNotifierBase, - T> extends ProviderElementBase> - with FutureHandlerProviderElementMixin { - /// The element of [AsyncNotifierProvider]. - @internal - AsyncNotifierProviderElementBase(super._provider); - - final _notifierNotifier = ProxyElementValueNotifier(); - - @override - void visitChildren({ - required void Function(ProviderElementBase element) elementVisitor, - required void Function(ProxyElementValueNotifier element) notifierVisitor, - }) { - super.visitChildren( - elementVisitor: elementVisitor, - notifierVisitor: notifierVisitor, - ); - notifierVisitor(_notifierNotifier); - } - - @override - bool updateShouldNotify(AsyncValue previous, AsyncValue next) { - return _notifierNotifier.result?.stateOrNull - ?.updateShouldNotify(previous, next) ?? - true; - } -} - -/// The element of [AsyncNotifierProvider]. -class AsyncNotifierProviderElement, T> - extends AsyncNotifierProviderElementBase - implements - // ignore: deprecated_member_use_from_same_package - AsyncNotifierProviderRef { - /// The element of [AsyncNotifierProvider]. - @internal - AsyncNotifierProviderElement( - AsyncNotifierProviderBase super._provider, - ); - - @override - void create({required bool didChangeDependency}) { - final provider = this.provider as AsyncNotifierProviderBase; - - final notifierResult = _notifierNotifier.result ??= Result.guard(() { - return provider._createNotifier().._setElement(this); - }); - - // TODO test notifier constructor throws -> provider emits AsyncError - // TODO test notifier constructor throws -> .notifier rethrows the error - // TODO test notifier constructor throws -> .future emits Future.error - notifierResult.when( - error: (error, stackTrace) { - onError(AsyncError(error, stackTrace), seamless: !didChangeDependency); - }, - data: (notifier) { - handleFuture( - () => provider.runNotifierBuild(notifier), - didChangeDependency: didChangeDependency, - ); - }, - ); - } -} - -extension on Stream { - void lastCancelable( - void Function(Future, CancelAsyncSubscription) last, { - required Object Function() orElseError, - }) { - late StreamSubscription subscription; - final completer = Completer(); - - Result? result; - subscription = listen( - (event) => result = Result.data(event), - // ignore: avoid_types_on_closure_parameters - onError: (Object error, StackTrace stackTrace) { - result = Result.error(error, stackTrace); - }, - onDone: () { - if (result != null) { - result!.map( - data: (result) => completer.complete(result.state), - error: (result) { - // TODO: should this be reported to the zone? - completer.future.ignore(); - completer.completeError(result.error, result.stackTrace); - }, - ); - } else { - // The error happens after the associated provider is disposed. - // As such, it's normally never read. Reporting this error as uncaught - // would cause too many false-positives. And the edge-cases that - // do reach this error will throw anyway - completer.future.ignore(); - - completer.completeError( - orElseError(), - StackTrace.current, - ); - } - }, - ); - - last(completer.future, subscription.cancel); - } -} diff --git a/packages/riverpod/lib/src/async_notifier/family.dart b/packages/riverpod/lib/src/async_notifier/family.dart deleted file mode 100644 index ed62db16e..000000000 --- a/packages/riverpod/lib/src/async_notifier/family.dart +++ /dev/null @@ -1,134 +0,0 @@ -part of '../async_notifier.dart'; - -/// {@macro riverpod.async_notifier} -/// -/// {@macro riverpod.async_notifier_provider_modifier} -abstract class FamilyAsyncNotifier - extends BuildlessAsyncNotifier { - /// {@template riverpod.notifier.family_arg} - /// The argument that was passed to this family. - /// - /// For example, when doing: - /// - /// ```dart - /// ref.watch(provider(0)); - /// ``` - /// - /// then [arg] will be `0`. - /// {@endtemplate} - late final Arg arg; - - @override - void _setElement(ProviderElementBase> element) { - super._setElement(element); - arg = element.origin.argument as Arg; - } - - /// {@macro riverpod.async_notifier.build} - @visibleForOverriding - FutureOr build(Arg arg); -} - -/// {@macro riverpod.async_notifier_provider} -/// -/// {@macro riverpod.async_notifier_provider_modifier} -typedef AsyncNotifierFamilyProvider< - NotifierT extends FamilyAsyncNotifier, T, Arg> - = FamilyAsyncNotifierProviderImpl; - -/// An internal implementation of [AsyncNotifierFamilyProvider] for testing purpose. -/// -/// Not meant for public consumption. -@visibleForTesting -@internal -class FamilyAsyncNotifierProviderImpl, T, - Arg> extends AsyncNotifierProviderBase - with - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderBase>, - AlwaysAliveAsyncSelector { - /// {@macro riverpod.async_notifier_family_provider} - FamilyAsyncNotifierProviderImpl( - super._createNotifier, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - FamilyAsyncNotifierProviderImpl.internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.autoDispose} - static const autoDispose = AutoDisposeAsyncNotifierProviderFamily.new; - - @override - // ignore: deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable notifier = - _asyncNotifier(this); - - @override - // ignore: deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable> future = _asyncFuture(this); - - @override - AsyncNotifierProviderElement createElement() { - return AsyncNotifierProviderElement(this); - } - - @override - FutureOr runNotifierBuild( - covariant FamilyAsyncNotifier notifier, - ) { - return notifier.build(notifier.arg); - } -} - -/// The [Family] of [AsyncNotifierProvider]. -class AsyncNotifierProviderFamily, - T, Arg> - // ignore: deprecated_member_use_from_same_package - extends NotifierFamilyBase, AsyncValue, Arg, - NotifierT, AsyncNotifierFamilyProvider> { - /// The [Family] of [AsyncNotifierProvider]. - AsyncNotifierProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: AsyncNotifierFamilyProvider.internal, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - debugGetCreateSourceHash: null, - ); - - /// {@macro riverpod.override_with} - Override overrideWith(NotifierT Function() create) { - return FamilyOverrideImpl, Arg, - AsyncNotifierFamilyProvider>( - this, - (arg) => AsyncNotifierFamilyProvider.internal( - create, - from: from, - argument: arg, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} diff --git a/packages/riverpod/lib/src/builders.dart b/packages/riverpod/lib/src/builder.dart similarity index 52% rename from packages/riverpod/lib/src/builders.dart rename to packages/riverpod/lib/src/builder.dart index cb5070ee2..219444bc6 100644 --- a/packages/riverpod/lib/src/builders.dart +++ b/packages/riverpod/lib/src/builder.dart @@ -9,381 +9,15 @@ // generate_providers import 'dart:async'; + +import 'package:meta/meta.dart'; import 'package:state_notifier/state_notifier.dart'; import 'internals.dart'; -/// Builds a [AsyncNotifierProvider]. -class AsyncNotifierProviderBuilder { - /// Builds a [AsyncNotifierProvider]. - const AsyncNotifierProviderBuilder(); - - /// {@macro riverpod.autoDispose} - AsyncNotifierProvider - call, T>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return AsyncNotifierProvider( - create, - name: name, - dependencies: dependencies, - ); - } - - /// {@macro riverpod.autoDispose} - AutoDisposeAsyncNotifierProviderBuilder get autoDispose { - return const AutoDisposeAsyncNotifierProviderBuilder(); - } - - /// {@macro riverpod.family} - AsyncNotifierProviderFamilyBuilder get family { - return const AsyncNotifierProviderFamilyBuilder(); - } -} - -/// Builds a [AsyncNotifierProviderFamily]. -class AsyncNotifierProviderFamilyBuilder { - /// Builds a [AsyncNotifierProviderFamily]. - const AsyncNotifierProviderFamilyBuilder(); - - /// {@macro riverpod.family} - AsyncNotifierProviderFamily - call, T, Arg>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return AsyncNotifierProviderFamily( - create, - name: name, - dependencies: dependencies, - ); - } - - /// {@macro riverpod.autoDispose} - AutoDisposeAsyncNotifierProviderFamilyBuilder get autoDispose { - return const AutoDisposeAsyncNotifierProviderFamilyBuilder(); - } -} - -/// Builds a [AutoDisposeAsyncNotifierProvider]. -class AutoDisposeAsyncNotifierProviderBuilder { - /// Builds a [AutoDisposeAsyncNotifierProvider]. - const AutoDisposeAsyncNotifierProviderBuilder(); - - /// {@macro riverpod.autoDispose} - AutoDisposeAsyncNotifierProvider - call, T>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return AutoDisposeAsyncNotifierProvider( - create, - name: name, - dependencies: dependencies, - ); - } - - /// {@macro riverpod.family} - AutoDisposeAsyncNotifierProviderFamilyBuilder get family { - return const AutoDisposeAsyncNotifierProviderFamilyBuilder(); - } -} - -/// Builds a [AutoDisposeAsyncNotifierProviderFamily]. -class AutoDisposeAsyncNotifierProviderFamilyBuilder { - /// Builds a [AutoDisposeAsyncNotifierProviderFamily]. - const AutoDisposeAsyncNotifierProviderFamilyBuilder(); - - /// {@macro riverpod.family} - AutoDisposeAsyncNotifierProviderFamily - call, T, Arg>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return AutoDisposeAsyncNotifierProviderFamily( - create, - name: name, - dependencies: dependencies, - ); - } -} - -/// Builds a [NotifierProvider]. -class NotifierProviderBuilder { - /// Builds a [NotifierProvider]. - const NotifierProviderBuilder(); - - /// {@macro riverpod.autoDispose} - NotifierProvider - call, State>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return NotifierProvider( - create, - name: name, - dependencies: dependencies, - ); - } - - /// {@macro riverpod.autoDispose} - AutoDisposeNotifierProviderBuilder get autoDispose { - return const AutoDisposeNotifierProviderBuilder(); - } - - /// {@macro riverpod.family} - NotifierProviderFamilyBuilder get family { - return const NotifierProviderFamilyBuilder(); - } -} - -/// Builds a [NotifierProviderFamily]. -class NotifierProviderFamilyBuilder { - /// Builds a [NotifierProviderFamily]. - const NotifierProviderFamilyBuilder(); - - /// {@macro riverpod.family} - NotifierProviderFamily - call, State, Arg>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return NotifierProviderFamily( - create, - name: name, - dependencies: dependencies, - ); - } - - /// {@macro riverpod.autoDispose} - AutoDisposeNotifierProviderFamilyBuilder get autoDispose { - return const AutoDisposeNotifierProviderFamilyBuilder(); - } -} - -/// Builds a [AutoDisposeNotifierProvider]. -class AutoDisposeNotifierProviderBuilder { - /// Builds a [AutoDisposeNotifierProvider]. - const AutoDisposeNotifierProviderBuilder(); - - /// {@macro riverpod.autoDispose} - AutoDisposeNotifierProvider - call, State>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return AutoDisposeNotifierProvider( - create, - name: name, - dependencies: dependencies, - ); - } - - /// {@macro riverpod.family} - AutoDisposeNotifierProviderFamilyBuilder get family { - return const AutoDisposeNotifierProviderFamilyBuilder(); - } -} - -/// Builds a [AutoDisposeNotifierProviderFamily]. -class AutoDisposeNotifierProviderFamilyBuilder { - /// Builds a [AutoDisposeNotifierProviderFamily]. - const AutoDisposeNotifierProviderFamilyBuilder(); - - /// {@macro riverpod.family} - AutoDisposeNotifierProviderFamily - call, State, Arg>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return AutoDisposeNotifierProviderFamily( - create, - name: name, - dependencies: dependencies, - ); - } -} - -/// Builds a [StreamNotifierProvider]. -class StreamNotifierProviderBuilder { - /// Builds a [StreamNotifierProvider]. - const StreamNotifierProviderBuilder(); - - /// {@macro riverpod.autoDispose} - StreamNotifierProvider - call, T>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return StreamNotifierProvider( - create, - name: name, - dependencies: dependencies, - ); - } - - /// {@macro riverpod.autoDispose} - AutoDisposeStreamNotifierProviderBuilder get autoDispose { - return const AutoDisposeStreamNotifierProviderBuilder(); - } - - /// {@macro riverpod.family} - StreamNotifierProviderFamilyBuilder get family { - return const StreamNotifierProviderFamilyBuilder(); - } -} - -/// Builds a [StreamNotifierProviderFamily]. -class StreamNotifierProviderFamilyBuilder { - /// Builds a [StreamNotifierProviderFamily]. - const StreamNotifierProviderFamilyBuilder(); - - /// {@macro riverpod.family} - StreamNotifierProviderFamily - call, T, Arg>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return StreamNotifierProviderFamily( - create, - name: name, - dependencies: dependencies, - ); - } - - /// {@macro riverpod.autoDispose} - AutoDisposeStreamNotifierProviderFamilyBuilder get autoDispose { - return const AutoDisposeStreamNotifierProviderFamilyBuilder(); - } -} - -/// Builds a [AutoDisposeStreamNotifierProvider]. -class AutoDisposeStreamNotifierProviderBuilder { - /// Builds a [AutoDisposeStreamNotifierProvider]. - const AutoDisposeStreamNotifierProviderBuilder(); - - /// {@macro riverpod.autoDispose} - AutoDisposeStreamNotifierProvider - call, T>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return AutoDisposeStreamNotifierProvider( - create, - name: name, - dependencies: dependencies, - ); - } - - /// {@macro riverpod.family} - AutoDisposeStreamNotifierProviderFamilyBuilder get family { - return const AutoDisposeStreamNotifierProviderFamilyBuilder(); - } -} - -/// Builds a [AutoDisposeStreamNotifierProviderFamily]. -class AutoDisposeStreamNotifierProviderFamilyBuilder { - /// Builds a [AutoDisposeStreamNotifierProviderFamily]. - const AutoDisposeStreamNotifierProviderFamilyBuilder(); - - /// {@macro riverpod.family} - AutoDisposeStreamNotifierProviderFamily - call, T, Arg>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return AutoDisposeStreamNotifierProviderFamily( - create, - name: name, - dependencies: dependencies, - ); - } -} - -/// Builds a [StateProvider]. -class StateProviderBuilder { - /// Builds a [StateProvider]. - const StateProviderBuilder(); - - /// {@template riverpod.autoDispose} - /// Marks the provider as automatically disposed when no longer listened to. - /// - /// Some typical use-cases: - /// - /// - Combined with [StreamProvider], this can be used as a mean to keep - /// the connection with Firebase alive only when truly needed (to reduce costs). - /// - Automatically reset a form state when leaving the screen. - /// - Automatically retry HTTP requests that failed when the user exit and - /// re-enter the screen. - /// - Cancel HTTP requests if the user leaves a screen before the request completed. - /// - /// Marking a provider with `autoDispose` also adds an extra method on `ref`: `keepAlive`. - /// - /// The `keepAlive` function is used to tell Riverpod that the state of the provider - /// should be preserved even if no longer listened to. - /// - /// A use-case would be to set this flag to `true` after an HTTP request have - /// completed: - /// - /// ```dart - /// final myProvider = FutureProvider.autoDispose((ref) async { - /// final response = await httpClient.get(...); - /// ref.keepAlive(); - /// return response; - /// }); - /// ``` - /// - /// This way, if the request failed and the UI leaves the screen then re-enters - /// it, then the request will be performed again. - /// But if the request completed successfully, the state will be preserved - /// and re-entering the screen will not trigger a new request. - /// - /// It can be combined with `ref.onDispose` for more advanced behaviors, such - /// as cancelling pending HTTP requests when the user leaves a screen. - /// For example, modifying our previous snippet and using `dio`, we would have: - /// - /// ```diff - /// final myProvider = FutureProvider.autoDispose((ref) async { - /// + final cancelToken = CancelToken(); - /// + ref.onDispose(() => cancelToken.cancel()); - /// - /// + final response = await dio.get('path', cancelToken: cancelToken); - /// - final response = await dio.get('path'); - /// ref.keepAlive(); - /// return response; - /// }); - /// ``` - /// {@endtemplate} - StateProvider call( - // ignore: deprecated_member_use_from_same_package - Create> create, { - String? name, - Iterable? dependencies, - }) { - return StateProvider( - create, - name: name, - dependencies: dependencies, - ); - } - - /// {@macro riverpod.autoDispose} - AutoDisposeStateProviderBuilder get autoDispose { - return const AutoDisposeStateProviderBuilder(); - } +@internal +class StateProviderFamilyBuilder { + const StateProviderFamilyBuilder(); /// {@template riverpod.family} /// A group of providers that builds their value from an external parameter. @@ -594,487 +228,617 @@ class StateProviderBuilder { /// } /// ``` /// {@endtemplate} - StateProviderFamilyBuilder get family { - return const StateProviderFamilyBuilder(); + StateProviderFamily call( + StateT Function(Ref ref, ArgT param) create, { + String? name, + Iterable? dependencies, + Retry? retry, + }) { + return StateProviderFamily( + create, + name: name, + dependencies: dependencies, + retry: retry, + ); } + + /// {@template riverpod.autoDispose} + /// Marks the provider as automatically disposed when no longer listened to. + /// + /// Some typical use-cases: + /// + /// - Combined with [StreamProvider], this can be used as a mean to keep + /// the connection with Firebase alive only when truly needed (to reduce costs). + /// - Automatically reset a form state when leaving the screen. + /// - Automatically retry HTTP requests that failed when the user exit and + /// re-enter the screen. + /// - Cancel HTTP requests if the user leaves a screen before the request completed. + /// + /// Marking a provider with `autoDispose` also adds an extra method on `ref`: `keepAlive`. + /// + /// The `keepAlive` function is used to tell Riverpod that the state of the provider + /// should be preserved even if no longer listened to. + /// + /// A use-case would be to set this flag to `true` after an HTTP request have + /// completed: + /// + /// ```dart + /// final myProvider = FutureProvider.autoDispose((ref) async { + /// final response = await httpClient.get(...); + /// ref.keepAlive(); + /// return response; + /// }); + /// ``` + /// + /// This way, if the request failed and the UI leaves the screen then re-enters + /// it, then the request will be performed again. + /// But if the request completed successfully, the state will be preserved + /// and re-entering the screen will not trigger a new request. + /// + /// It can be combined with `ref.onDispose` for more advanced behaviors, such + /// as cancelling pending HTTP requests when the user leaves a screen. + /// For example, modifying our previous snippet and using `dio`, we would have: + /// + /// ```diff + /// final myProvider = FutureProvider.autoDispose((ref) async { + /// + final cancelToken = CancelToken(); + /// + ref.onDispose(() => cancelToken.cancel()); + /// + /// + final response = await dio.get('path', cancelToken: cancelToken); + /// - final response = await dio.get('path'); + /// ref.keepAlive(); + /// return response; + /// }); + /// ``` + /// {@endtemplate} + AutoDisposeStateProviderFamilyBuilder get autoDispose => + const AutoDisposeStateProviderFamilyBuilder(); } -/// Builds a [StateProviderFamily]. -class StateProviderFamilyBuilder { - /// Builds a [StateProviderFamily]. - const StateProviderFamilyBuilder(); +@internal +class AutoDisposeStateProviderBuilder { + const AutoDisposeStateProviderBuilder(); /// {@macro riverpod.family} - StateProviderFamily call( - // ignore: deprecated_member_use_from_same_package - FamilyCreate, Arg> create, { + StateProvider call( + StateT Function(Ref ref) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return StateProviderFamily( + return StateProvider( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } - /// {@macro riverpod.autoDispose} - AutoDisposeStateProviderFamilyBuilder get autoDispose { - return const AutoDisposeStateProviderFamilyBuilder(); - } + /// {@macro riverpod.family} + AutoDisposeStateProviderFamilyBuilder get family => + const AutoDisposeStateProviderFamilyBuilder(); } -/// Builds a [StateNotifierProvider]. -class StateNotifierProviderBuilder { - /// Builds a [StateNotifierProvider]. - const StateNotifierProviderBuilder(); +@internal +class AutoDisposeStateProviderFamilyBuilder { + const AutoDisposeStateProviderFamilyBuilder(); - /// {@macro riverpod.autoDispose} - StateNotifierProvider - call, State>( - // ignore: deprecated_member_use_from_same_package - Create> create, { + /// {@macro riverpod.family} + StateProviderFamily call( + StateT Function(Ref ref, ArgT param) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return StateNotifierProvider( + return StateProviderFamily( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } - - /// {@macro riverpod.autoDispose} - AutoDisposeStateNotifierProviderBuilder get autoDispose { - return const AutoDisposeStateNotifierProviderBuilder(); - } - - /// {@macro riverpod.family} - StateNotifierProviderFamilyBuilder get family { - return const StateNotifierProviderFamilyBuilder(); - } } -/// Builds a [StateNotifierProviderFamily]. +@internal class StateNotifierProviderFamilyBuilder { - /// Builds a [StateNotifierProviderFamily]. const StateNotifierProviderFamilyBuilder(); /// {@macro riverpod.family} - StateNotifierProviderFamily - call, State, Arg>( - // ignore: deprecated_member_use_from_same_package - FamilyCreate, Arg> - create, { + StateNotifierProviderFamily + call, StateT, ArgT>( + NotifierT Function(Ref ref, ArgT param) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return StateNotifierProviderFamily( + return StateNotifierProviderFamily( create, name: name, dependencies: dependencies, + retry: retry, ); } /// {@macro riverpod.autoDispose} - AutoDisposeStateNotifierProviderFamilyBuilder get autoDispose { - return const AutoDisposeStateNotifierProviderFamilyBuilder(); - } + AutoDisposeStateNotifierProviderFamilyBuilder get autoDispose => + const AutoDisposeStateNotifierProviderFamilyBuilder(); } -/// Builds a [Provider]. -class ProviderBuilder { - /// Builds a [Provider]. - const ProviderBuilder(); +@internal +class AutoDisposeStateNotifierProviderBuilder { + const AutoDisposeStateNotifierProviderBuilder(); - /// {@macro riverpod.autoDispose} - Provider call( - // ignore: deprecated_member_use_from_same_package - Create> create, { + /// {@macro riverpod.family} + StateNotifierProvider + call, StateT>( + NotifierT Function(Ref ref) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return Provider( + return StateNotifierProvider( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } - /// {@macro riverpod.autoDispose} - AutoDisposeProviderBuilder get autoDispose { - return const AutoDisposeProviderBuilder(); - } + /// {@macro riverpod.family} + AutoDisposeStateNotifierProviderFamilyBuilder get family => + const AutoDisposeStateNotifierProviderFamilyBuilder(); +} + +@internal +class AutoDisposeStateNotifierProviderFamilyBuilder { + const AutoDisposeStateNotifierProviderFamilyBuilder(); /// {@macro riverpod.family} - ProviderFamilyBuilder get family { - return const ProviderFamilyBuilder(); + StateNotifierProviderFamily + call, StateT, ArgT>( + NotifierT Function(Ref ref, ArgT param) create, { + String? name, + Iterable? dependencies, + Retry? retry, + }) { + return StateNotifierProviderFamily( + create, + name: name, + isAutoDispose: true, + dependencies: dependencies, + retry: retry, + ); } } -/// Builds a [ProviderFamily]. +@internal class ProviderFamilyBuilder { - /// Builds a [ProviderFamily]. const ProviderFamilyBuilder(); /// {@macro riverpod.family} - ProviderFamily call( - // ignore: deprecated_member_use_from_same_package - FamilyCreate, Arg> create, { + ProviderFamily call( + StateT Function(Ref ref, ArgT param) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return ProviderFamily( + return ProviderFamily( create, name: name, dependencies: dependencies, + retry: retry, ); } /// {@macro riverpod.autoDispose} - AutoDisposeProviderFamilyBuilder get autoDispose { - return const AutoDisposeProviderFamilyBuilder(); - } + AutoDisposeProviderFamilyBuilder get autoDispose => + const AutoDisposeProviderFamilyBuilder(); } -/// Builds a [FutureProvider]. -class FutureProviderBuilder { - /// Builds a [FutureProvider]. - const FutureProviderBuilder(); +@internal +class AutoDisposeProviderBuilder { + const AutoDisposeProviderBuilder(); - /// {@macro riverpod.autoDispose} - FutureProvider call( - // ignore: deprecated_member_use_from_same_package - Create, FutureProviderRef> create, { + /// {@macro riverpod.family} + Provider call( + StateT Function(Ref ref) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return FutureProvider( + return Provider( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } - /// {@macro riverpod.autoDispose} - AutoDisposeFutureProviderBuilder get autoDispose { - return const AutoDisposeFutureProviderBuilder(); - } + /// {@macro riverpod.family} + AutoDisposeProviderFamilyBuilder get family => + const AutoDisposeProviderFamilyBuilder(); +} + +@internal +class AutoDisposeProviderFamilyBuilder { + const AutoDisposeProviderFamilyBuilder(); /// {@macro riverpod.family} - FutureProviderFamilyBuilder get family { - return const FutureProviderFamilyBuilder(); + ProviderFamily call( + StateT Function(Ref ref, ArgT param) create, { + String? name, + Iterable? dependencies, + Retry? retry, + }) { + return ProviderFamily( + create, + name: name, + isAutoDispose: true, + dependencies: dependencies, + retry: retry, + ); } } -/// Builds a [FutureProviderFamily]. +@internal class FutureProviderFamilyBuilder { - /// Builds a [FutureProviderFamily]. const FutureProviderFamilyBuilder(); /// {@macro riverpod.family} - FutureProviderFamily call( - // ignore: deprecated_member_use_from_same_package - FamilyCreate, FutureProviderRef, Arg> create, { + FutureProviderFamily call( + FutureOr Function(Ref ref, ArgT param) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return FutureProviderFamily( + return FutureProviderFamily( create, name: name, dependencies: dependencies, + retry: retry, ); } /// {@macro riverpod.autoDispose} - AutoDisposeFutureProviderFamilyBuilder get autoDispose { - return const AutoDisposeFutureProviderFamilyBuilder(); - } + AutoDisposeFutureProviderFamilyBuilder get autoDispose => + const AutoDisposeFutureProviderFamilyBuilder(); } -/// Builds a [StreamProvider]. -class StreamProviderBuilder { - /// Builds a [StreamProvider]. - const StreamProviderBuilder(); +@internal +class AutoDisposeFutureProviderBuilder { + const AutoDisposeFutureProviderBuilder(); - /// {@macro riverpod.autoDispose} - StreamProvider call( - // ignore: deprecated_member_use_from_same_package - Create, StreamProviderRef> create, { + /// {@macro riverpod.family} + FutureProvider call( + FutureOr Function(Ref ref) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return StreamProvider( + return FutureProvider( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } - /// {@macro riverpod.autoDispose} - AutoDisposeStreamProviderBuilder get autoDispose { - return const AutoDisposeStreamProviderBuilder(); - } + /// {@macro riverpod.family} + AutoDisposeFutureProviderFamilyBuilder get family => + const AutoDisposeFutureProviderFamilyBuilder(); +} + +@internal +class AutoDisposeFutureProviderFamilyBuilder { + const AutoDisposeFutureProviderFamilyBuilder(); /// {@macro riverpod.family} - StreamProviderFamilyBuilder get family { - return const StreamProviderFamilyBuilder(); + FutureProviderFamily call( + FutureOr Function(Ref ref, ArgT param) create, { + String? name, + Iterable? dependencies, + Retry? retry, + }) { + return FutureProviderFamily( + create, + name: name, + isAutoDispose: true, + dependencies: dependencies, + retry: retry, + ); } } -/// Builds a [StreamProviderFamily]. +@internal class StreamProviderFamilyBuilder { - /// Builds a [StreamProviderFamily]. const StreamProviderFamilyBuilder(); /// {@macro riverpod.family} - StreamProviderFamily call( - // ignore: deprecated_member_use_from_same_package - FamilyCreate, StreamProviderRef, Arg> create, { + StreamProviderFamily call( + Stream Function(Ref ref, ArgT param) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return StreamProviderFamily( + return StreamProviderFamily( create, name: name, dependencies: dependencies, + retry: retry, ); } /// {@macro riverpod.autoDispose} - AutoDisposeStreamProviderFamilyBuilder get autoDispose { - return const AutoDisposeStreamProviderFamilyBuilder(); - } + AutoDisposeStreamProviderFamilyBuilder get autoDispose => + const AutoDisposeStreamProviderFamilyBuilder(); } -/// Builds a [AutoDisposeStateProvider]. -class AutoDisposeStateProviderBuilder { - /// Builds a [AutoDisposeStateProvider]. - const AutoDisposeStateProviderBuilder(); +@internal +class AutoDisposeStreamProviderBuilder { + const AutoDisposeStreamProviderBuilder(); - /// {@macro riverpod.autoDispose} - AutoDisposeStateProvider call( - // ignore: deprecated_member_use_from_same_package - Create> create, { + /// {@macro riverpod.family} + StreamProvider call( + Stream Function(Ref ref) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return AutoDisposeStateProvider( + return StreamProvider( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } /// {@macro riverpod.family} - AutoDisposeStateProviderFamilyBuilder get family { - return const AutoDisposeStateProviderFamilyBuilder(); - } + AutoDisposeStreamProviderFamilyBuilder get family => + const AutoDisposeStreamProviderFamilyBuilder(); } -/// Builds a [AutoDisposeStateProviderFamily]. -class AutoDisposeStateProviderFamilyBuilder { - /// Builds a [AutoDisposeStateProviderFamily]. - const AutoDisposeStateProviderFamilyBuilder(); +@internal +class AutoDisposeStreamProviderFamilyBuilder { + const AutoDisposeStreamProviderFamilyBuilder(); /// {@macro riverpod.family} - AutoDisposeStateProviderFamily call( - // ignore: deprecated_member_use_from_same_package - FamilyCreate, Arg> create, { + StreamProviderFamily call( + Stream Function(Ref ref, ArgT param) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return AutoDisposeStateProviderFamily( + return StreamProviderFamily( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } } -/// Builds a [AutoDisposeStateNotifierProvider]. -class AutoDisposeStateNotifierProviderBuilder { - /// Builds a [AutoDisposeStateNotifierProvider]. - const AutoDisposeStateNotifierProviderBuilder(); +@internal +class AutoDisposeNotifierProviderBuilder { + const AutoDisposeNotifierProviderBuilder(); /// {@macro riverpod.autoDispose} - AutoDisposeStateNotifierProvider - call, State>( - // ignore: deprecated_member_use_from_same_package - Create> - create, { + NotifierProvider + call, StateT>( + NotifierT Function() create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return AutoDisposeStateNotifierProvider( + return NotifierProvider( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } /// {@macro riverpod.family} - AutoDisposeStateNotifierProviderFamilyBuilder get family { - return const AutoDisposeStateNotifierProviderFamilyBuilder(); - } + AutoDisposeNotifierProviderFamilyBuilder get family => + const AutoDisposeNotifierProviderFamilyBuilder(); } -/// Builds a [AutoDisposeStateNotifierProviderFamily]. -class AutoDisposeStateNotifierProviderFamilyBuilder { - /// Builds a [AutoDisposeStateNotifierProviderFamily]. - const AutoDisposeStateNotifierProviderFamilyBuilder(); +@internal +class NotifierProviderFamilyBuilder { + const NotifierProviderFamilyBuilder(); - /// {@macro riverpod.family} - AutoDisposeStateNotifierProviderFamily - call, State, Arg>( - // ignore: deprecated_member_use_from_same_package - FamilyCreate, - Arg> - create, { + /// {@macro riverpod.autoDispose} + NotifierProviderFamily + call, StateT, ArgT>( + NotifierT Function() create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return AutoDisposeStateNotifierProviderFamily( + return NotifierProviderFamily.internal( create, name: name, dependencies: dependencies, + retry: retry, ); } + + /// {@macro riverpod.autoDispose} + AutoDisposeNotifierProviderFamilyBuilder get autoDispose => + const AutoDisposeNotifierProviderFamilyBuilder(); } -/// Builds a [AutoDisposeProvider]. -class AutoDisposeProviderBuilder { - /// Builds a [AutoDisposeProvider]. - const AutoDisposeProviderBuilder(); +@internal +class AutoDisposeNotifierProviderFamilyBuilder { + const AutoDisposeNotifierProviderFamilyBuilder(); /// {@macro riverpod.autoDispose} - AutoDisposeProvider call( - // ignore: deprecated_member_use_from_same_package - Create> create, { + NotifierProviderFamily + call, StateT, ArgT>( + NotifierT Function() create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return AutoDisposeProvider( + return NotifierProviderFamily.internal( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } - - /// {@macro riverpod.family} - AutoDisposeProviderFamilyBuilder get family { - return const AutoDisposeProviderFamilyBuilder(); - } } -/// Builds a [AutoDisposeProviderFamily]. -class AutoDisposeProviderFamilyBuilder { - /// Builds a [AutoDisposeProviderFamily]. - const AutoDisposeProviderFamilyBuilder(); +@internal +class AutoDisposeStreamNotifierProviderBuilder { + const AutoDisposeStreamNotifierProviderBuilder(); - /// {@macro riverpod.family} - AutoDisposeProviderFamily call( - // ignore: deprecated_member_use_from_same_package - FamilyCreate, Arg> create, { + /// {@macro riverpod.autoDispose} + StreamNotifierProvider + call, StateT>( + NotifierT Function() create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return AutoDisposeProviderFamily( + return StreamNotifierProvider( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } + + /// {@macro riverpod.family} + AutoDisposeStreamNotifierProviderFamilyBuilder get family => + const AutoDisposeStreamNotifierProviderFamilyBuilder(); } -/// Builds a [AutoDisposeFutureProvider]. -class AutoDisposeFutureProviderBuilder { - /// Builds a [AutoDisposeFutureProvider]. - const AutoDisposeFutureProviderBuilder(); +@internal +class StreamNotifierProviderFamilyBuilder { + const StreamNotifierProviderFamilyBuilder(); /// {@macro riverpod.autoDispose} - AutoDisposeFutureProvider call( - // ignore: deprecated_member_use_from_same_package - Create, AutoDisposeFutureProviderRef> create, { + StreamNotifierProviderFamily + call, StateT, ArgT>( + NotifierT Function() create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return AutoDisposeFutureProvider( + return StreamNotifierProviderFamily.internal( create, name: name, dependencies: dependencies, + retry: retry, ); } - /// {@macro riverpod.family} - AutoDisposeFutureProviderFamilyBuilder get family { - return const AutoDisposeFutureProviderFamilyBuilder(); - } + /// {@macro riverpod.autoDispose} + AutoDisposeStreamNotifierProviderFamilyBuilder get autoDispose => + const AutoDisposeStreamNotifierProviderFamilyBuilder(); } -/// Builds a [AutoDisposeFutureProviderFamily]. -class AutoDisposeFutureProviderFamilyBuilder { - /// Builds a [AutoDisposeFutureProviderFamily]. - const AutoDisposeFutureProviderFamilyBuilder(); +@internal +class AutoDisposeStreamNotifierProviderFamilyBuilder { + const AutoDisposeStreamNotifierProviderFamilyBuilder(); - /// {@macro riverpod.family} - AutoDisposeFutureProviderFamily call( - // ignore: deprecated_member_use_from_same_package - FamilyCreate, AutoDisposeFutureProviderRef, Arg> - create, { + /// {@macro riverpod.autoDispose} + StreamNotifierProviderFamily + call, StateT, ArgT>( + NotifierT Function() create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return AutoDisposeFutureProviderFamily( + return StreamNotifierProviderFamily.internal( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } } -/// Builds a [AutoDisposeStreamProvider]. -class AutoDisposeStreamProviderBuilder { - /// Builds a [AutoDisposeStreamProvider]. - const AutoDisposeStreamProviderBuilder(); +@internal +class AutoDisposeAsyncNotifierProviderBuilder { + const AutoDisposeAsyncNotifierProviderBuilder(); /// {@macro riverpod.autoDispose} - AutoDisposeStreamProvider call( - // ignore: deprecated_member_use_from_same_package - Create, AutoDisposeStreamProviderRef> create, { + AsyncNotifierProvider + call, StateT>( + NotifierT Function() create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return AutoDisposeStreamProvider( + return AsyncNotifierProvider( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } /// {@macro riverpod.family} - AutoDisposeStreamProviderFamilyBuilder get family { - return const AutoDisposeStreamProviderFamilyBuilder(); + AutoDisposeAsyncNotifierProviderFamilyBuilder get family => + const AutoDisposeAsyncNotifierProviderFamilyBuilder(); +} + +@internal +class AsyncNotifierProviderFamilyBuilder { + const AsyncNotifierProviderFamilyBuilder(); + + /// {@macro riverpod.autoDispose} + AsyncNotifierProviderFamily + call, StateT, ArgT>( + NotifierT Function() create, { + String? name, + Iterable? dependencies, + Retry? retry, + }) { + return AsyncNotifierProviderFamily.internal( + create, + name: name, + dependencies: dependencies, + retry: retry, + ); } + + /// {@macro riverpod.autoDispose} + AutoDisposeAsyncNotifierProviderFamilyBuilder get autoDispose => + const AutoDisposeAsyncNotifierProviderFamilyBuilder(); } -/// Builds a [AutoDisposeStreamProviderFamily]. -class AutoDisposeStreamProviderFamilyBuilder { - /// Builds a [AutoDisposeStreamProviderFamily]. - const AutoDisposeStreamProviderFamilyBuilder(); +@internal +class AutoDisposeAsyncNotifierProviderFamilyBuilder { + const AutoDisposeAsyncNotifierProviderFamilyBuilder(); - /// {@macro riverpod.family} - AutoDisposeStreamProviderFamily call( - // ignore: deprecated_member_use_from_same_package - FamilyCreate, AutoDisposeStreamProviderRef, Arg> - create, { + /// {@macro riverpod.autoDispose} + AsyncNotifierProviderFamily + call, StateT, ArgT>( + NotifierT Function() create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return AutoDisposeStreamProviderFamily( + return AsyncNotifierProviderFamily.internal( create, name: name, + isAutoDispose: true, dependencies: dependencies, + retry: retry, ); } } diff --git a/packages/riverpod/lib/src/common/env.dart b/packages/riverpod/lib/src/common/env.dart index eb60eadff..6a012603a 100644 --- a/packages/riverpod/lib/src/common/env.dart +++ b/packages/riverpod/lib/src/common/env.dart @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: do_not_use_environment, comment_references +// ignore_for_file: do_not_use_environment + +import 'package:meta/meta.dart'; /// A constant that is true if the application was compiled in release mode. /// @@ -21,6 +23,7 @@ /// /// * [kDebugMode], which is true in debug builds. /// * [kProfileMode], which is true in profile builds. +@internal const bool kReleaseMode = bool.fromEnvironment('dart.vm.product'); /// A constant that is true if the application was compiled in profile mode. @@ -36,6 +39,7 @@ const bool kReleaseMode = bool.fromEnvironment('dart.vm.product'); /// /// * [kDebugMode], which is true in debug builds. /// * [kReleaseMode], which is true in release builds. +@internal const bool kProfileMode = bool.fromEnvironment('dart.vm.profile'); /// A constant that is true if the application was compiled in debug mode. @@ -60,23 +64,5 @@ const bool kProfileMode = bool.fromEnvironment('dart.vm.profile'); /// /// * [kReleaseMode], which is true in release builds. /// * [kProfileMode], which is true in profile builds. +@internal const bool kDebugMode = !kReleaseMode && !kProfileMode; - -/// The epsilon of tolerable double precision error. -/// -/// This is used in various places in the framework to allow for floating point -/// precision loss in calculations. Differences below this threshold are safe to -/// disregard. -const double precisionErrorTolerance = 1e-10; - -/// A constant that is true if the application was compiled to run on the web. -/// -/// See also: -/// -/// * [defaultTargetPlatform], which is used by themes to find out which -/// platform the application is running on (or, in the case of a web app, -/// which platform the application's browser is running in). Can be overridden -/// in tests with [debugDefaultTargetPlatformOverride]. -/// * [dart:io.Platform], a way to find out the browser's platform that is not -/// overridable in tests. -const bool kIsWeb = bool.fromEnvironment('dart.library.js_util'); diff --git a/packages/riverpod/lib/src/listenable.dart b/packages/riverpod/lib/src/common/listenable.dart similarity index 86% rename from packages/riverpod/lib/src/listenable.dart rename to packages/riverpod/lib/src/common/listenable.dart index 607e19da9..b2e03522a 100644 --- a/packages/riverpod/lib/src/listenable.dart +++ b/packages/riverpod/lib/src/common/listenable.dart @@ -1,7 +1,8 @@ import 'package:meta/meta.dart'; -import 'framework.dart' show ProviderElementBase; -import 'internals.dart' show OnError; +import '../framework.dart' show ProviderElement; +import '../internals.dart' show OnError; +import 'env.dart'; import 'pragma.dart'; import 'result.dart'; @@ -14,11 +15,11 @@ class _Listener { final void Function()? onDependencyMayHaveChanged; } -/// A listenable object used by [ProviderElementBase] as a mean to subscribe +/// A listenable object used by [ProviderElement] as a mean to subscribe /// to subsets of the state exposed by a provider. @internal @optionalTypeArgs -class ProxyElementValueNotifier extends _ValueListenable { +class $ElementLense extends _ValueListenable { /// Directly obtain the value exposed, gratefully handling cases where /// [result] is null or in error state. T get value { @@ -32,25 +33,26 @@ class ProxyElementValueNotifier extends _ValueListenable { /// The state associated with this notifier. /// /// Modifying this property will notify listeners. - Result? get result => _result; - Result? _result; - set result(Result? value) { + $Result? get result => _result; + $Result? _result; + set result($Result? value) { final previous = _result; _result = value; - value?.when( - data: (newValue) => _notifyValue(previous?.stateOrNull, newValue), - error: _notifyError, - ); - } - /// Updates the [result] of this [ProxyElementValueNotifier] without invoking listeners. - // ignore: use_setters_to_change_properties, non_constant_identifier_names - void UNSAFE_setResultWithoutNotifyingListeners(Result? value) { - _result = value; + switch (value) { + case null: + break; + case ResultData(): + _notifyValue(previous?.stateOrNull, value.state); + case ResultError(): + _notifyError(value.error, value.stackTrace); + } } } class _ValueListenable { + void Function()? onCancel; + int _count = 0; // The _listeners is intentionally set to a fixed-length _GrowableList instead // of const []. @@ -68,6 +70,12 @@ class _ValueListenable { int _reentrantlyRemovedListeners = 0; bool _debugDisposed = false; + /// The accumulated skipped notification while it was locked by [lockNotification]. + ({ + ({T? prev, T next})? data, + ({Object error, StackTrace stack})? error, + })? _skippedNotification; + static bool debugAssertNotDisposed(_ValueListenable notifier) { assert( !notifier._debugDisposed, @@ -93,11 +101,28 @@ class _ValueListenable { /// [_notifyListeners]; and similarly, by overriding [_removeListener], checking /// if [hasListeners] is false after calling `super.removeListener()`, and if /// so, stopping that same work. - @protected bool get hasListeners { return _count > 0; } + void lockNotification() { + assert(_skippedNotification == null, ''); + _skippedNotification = (data: null, error: null); + } + + void unlockNotification() { + final notification = _skippedNotification; + if (notification != null) { + _skippedNotification = null; + + if (notification.data case final data?) { + _notifyValue(data.prev, data.next); + } else if (notification.error case final error?) { + _notifyError(error.error, error.stack); + } + } + } + /// Register a closure to be called when the object changes. /// /// If the given closure is already registered, an additional instance is @@ -218,6 +243,9 @@ class _ValueListenable { break; } } + + final onCancel = this.onCancel; + if (!hasListeners && onCancel != null) onCancel(); } /// Discards any resources used by the object. After this is called, the @@ -231,15 +259,11 @@ class _ValueListenable { /// listeners or not immediately before disposal. @mustCallSuper void dispose() { - assert( - () { - _debugDisposed = true; - return true; - }(), - '', - ); + assert(!_debugDisposed, ''); + if (kDebugMode) _debugDisposed = true; _listeners = _emptyListeners(); _count = 0; + lockNotification(); } /// Call all the registered listeners. @@ -332,10 +356,32 @@ class _ValueListenable { } void _notifyValue(T? prev, T next) { + if (_skippedNotification != null) { + _skippedNotification = ( + error: null, + data: ( + prev: _skippedNotification?.data?.prev ?? prev, + next: next, + ), + ); + return; + } + _notifyListeners((listener) => listener.onValue(prev, next)); } void _notifyError(Object err, StackTrace stack) { + if (_skippedNotification != null) { + _skippedNotification = ( + error: ( + error: err, + stack: stack, + ), + data: null, + ); + return; + } + _notifyListeners((listener) => listener.onError?.call(err, stack)); } diff --git a/packages/riverpod/lib/src/pragma.dart b/packages/riverpod/lib/src/common/pragma.dart similarity index 100% rename from packages/riverpod/lib/src/pragma.dart rename to packages/riverpod/lib/src/common/pragma.dart diff --git a/packages/riverpod/lib/src/result.dart b/packages/riverpod/lib/src/common/result.dart similarity index 52% rename from packages/riverpod/lib/src/result.dart rename to packages/riverpod/lib/src/common/result.dart index fba7523f4..535be3d77 100644 --- a/packages/riverpod/lib/src/result.dart +++ b/packages/riverpod/lib/src/common/result.dart @@ -3,24 +3,24 @@ import 'package:meta/meta.dart'; /// A T|Error union type. @immutable @internal -abstract class Result { +sealed class $Result { /// The data case // coverage:ignore-start - factory Result.data(State state) = ResultData; + factory $Result.data(State state) = ResultData; // coverage:ignore-end /// The error case // coverage:ignore-start - factory Result.error(Object error, StackTrace stackTrace) = ResultError; + factory $Result.error(Object error, StackTrace stackTrace) = ResultError; // coverage:ignore-end /// Automatically catches errors into a [ResultError] and convert successful /// values into a [ResultData]. - static Result guard(State Function() cb) { + static $Result guard(State Function() cb) { try { - return Result.data(cb()); + return $Result.data(cb()); } catch (err, stack) { - return Result.error(err, stack); + return $Result.error(err, stack); } } @@ -32,25 +32,11 @@ abstract class Result { /// The state if this is a [ResultData], throws otherwise. State get requireState; - - // TODO remove when migrating to Dart 3 - /// Returns the result of calling [data] if this is a [ResultData] or [error] - R map({ - required R Function(ResultData data) data, - required R Function(ResultError) error, - }); - - // TODO remove when migrating to Dart 3 - /// Returns the result of calling [data] if this is a [ResultData] or [error] - R when({ - required R Function(State data) data, - required R Function(Object error, StackTrace stackTrace) error, - }); } /// The data case @internal -class ResultData implements Result { +class ResultData implements $Result { /// The data case ResultData(this.state); @@ -66,22 +52,6 @@ class ResultData implements Result { @override State get requireState => state; - @override - R map({ - required R Function(ResultData data) data, - required R Function(ResultError) error, - }) { - return data(this); - } - - @override - R when({ - required R Function(State data) data, - required R Function(Object error, StackTrace stackTrace) error, - }) { - return data(state); - } - @override bool operator ==(Object other) => other is ResultData && @@ -94,7 +64,7 @@ class ResultData implements Result { /// The error case @internal -class ResultError implements Result { +class ResultError implements $Result { /// The error case ResultError(this.error, this.stackTrace); @@ -113,22 +83,6 @@ class ResultError implements Result { @override State get requireState => Error.throwWithStackTrace(error, stackTrace); - @override - R map({ - required R Function(ResultData data) data, - required R Function(ResultError) error, - }) { - return error(this); - } - - @override - R when({ - required R Function(State data) data, - required R Function(Object error, StackTrace stackTrace) error, - }) { - return error(this.error, stackTrace); - } - @override bool operator ==(Object other) => other is ResultError && diff --git a/packages/riverpod/lib/src/run_guarded.dart b/packages/riverpod/lib/src/common/run_guarded.dart similarity index 100% rename from packages/riverpod/lib/src/run_guarded.dart rename to packages/riverpod/lib/src/common/run_guarded.dart diff --git a/packages/riverpod/lib/src/stack_trace.dart b/packages/riverpod/lib/src/common/stack_trace.dart similarity index 100% rename from packages/riverpod/lib/src/stack_trace.dart rename to packages/riverpod/lib/src/common/stack_trace.dart diff --git a/packages/riverpod/lib/src/common/tenable.dart b/packages/riverpod/lib/src/common/tenable.dart new file mode 100644 index 000000000..890887d07 --- /dev/null +++ b/packages/riverpod/lib/src/common/tenable.dart @@ -0,0 +1,104 @@ +import 'dart:async'; + +import 'package:meta/meta.dart'; + +/// A Future-like class that can emit synchronously. +/// +/// This is similar to [FutureOr], but can hold errors. +@internal +abstract class Tenable { + const Tenable._(); + + const factory Tenable.value(T value) = _TenableValue; + const factory Tenable.error(Object error, StackTrace stacktrace) = + _TenableError; + factory Tenable.fromFuture(Future future) = _TenableFromFuture; + factory Tenable.guardSync(T Function() cb) { + try { + return Tenable.value(cb()); + } catch (err, stackTrace) { + return Tenable.error(err, stackTrace); + } + } + factory Tenable.guardTenable(Tenable Function() cb) { + try { + return cb(); + } catch (err, stackTrace) { + return Tenable.error(err, stackTrace); + } + } + factory Tenable.fromFutureOr(FutureOr Function() cb) { + try { + final futureOr = cb(); + if (futureOr is Future) { + return Tenable.fromFuture(futureOr); + } else { + return Tenable.value(futureOr); + } + } catch (err, stackTrace) { + return Tenable.error(err, stackTrace); + } + } + + Tenable then( + FutureOr Function(T value) cb, { + FutureOr Function(Object error, StackTrace stack)? onError, + }); + + Tenable whenComplete(FutureOr Function() cb) { + return then( + (value) => Tenable.fromFutureOr(cb), + onError: (err, stackTrace) => Tenable.fromFutureOr(cb), + ); + } +} + +class _TenableValue extends Tenable { + const _TenableValue(this.value) : super._(); + + final T value; + + @override + Tenable then( + FutureOr Function(T value) cb, { + FutureOr Function(Object error, StackTrace stack)? onError, + }) { + return Tenable.fromFutureOr(() => cb(value)); + } +} + +class _TenableError extends Tenable { + const _TenableError(this.error, this.stackTrace) : super._(); + + final Object error; + final StackTrace stackTrace; + + @override + Tenable then( + FutureOr Function(T value) cb, { + FutureOr Function(Object error, StackTrace stack)? onError, + }) { + if (onError == null) return Tenable.error(error, stackTrace); + + return Tenable.fromFutureOr(() => onError(error, stackTrace)); + } +} + +class _TenableFromFuture extends Tenable { + const _TenableFromFuture(this.future) : super._(); + + final Future future; + + @override + Tenable then( + FutureOr Function(T value) cb, { + FutureOr Function(Object error, StackTrace stack)? onError, + }) { + return Tenable.fromFuture( + future.then( + cb, + onError: onError, + ), + ); + } +} diff --git a/packages/riverpod/lib/src/common.dart b/packages/riverpod/lib/src/core/async_value.dart similarity index 73% rename from packages/riverpod/lib/src/common.dart rename to packages/riverpod/lib/src/core/async_value.dart index b0a1735c6..2e24a1ee8 100644 --- a/packages/riverpod/lib/src/common.dart +++ b/packages/riverpod/lib/src/core/async_value.dart @@ -1,35 +1,13 @@ import 'package:meta/meta.dart'; -import 'framework.dart'; -import 'future_provider.dart' show FutureProvider; -import 'stack_trace.dart'; -import 'stream_provider.dart' show StreamProvider; +import '../common/stack_trace.dart'; +import '../framework.dart'; +import '../providers/future_provider.dart' show FutureProvider; +import '../providers/stream_provider.dart' show StreamProvider; -/// An extension for [asyncTransition]. @internal -extension AsyncTransition on ProviderElementBase> { - /// Internal utility for transitioning an [AsyncValue] after a provider refresh. - /// - /// [seamless] controls how the previous state is preserved: - /// - seamless:true => import previous state and skip loading - /// - seamless:false => import previous state and prefer loading - void asyncTransition( - AsyncValue newState, { - required bool seamless, - }) { -// ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member - final previous = getState()?.requireState; - - if (previous == null) { -// ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member - setState(newState); - } else { -// ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member - setState( - newState._cast().copyWithPrevious(previous, isRefresh: seamless), - ); - } - } +extension AsyncTransition on AsyncValue { + AsyncValue cast() => _cast(); } /// A utility for safely manipulating asynchronous data. @@ -37,10 +15,8 @@ extension AsyncTransition on ProviderElementBase> { /// By using [AsyncValue], you are guaranteed that you cannot forget to /// handle the loading/error state of an asynchronous operation. /// -/// It also exposes some utilities to nicely convert an [AsyncValue] to -/// a different object. -/// For example, a Flutter Widget may use [when] to convert an [AsyncValue] -/// into either a progress indicator, an error screen, or to show the data: +/// [AsyncValue] is a sealed class, and is designed to be used with +/// pattern matching to handle the different states: /// /// ```dart /// /// A provider that asynchronously exposes the current user @@ -53,30 +29,31 @@ extension AsyncTransition on ProviderElementBase> { /// Widget build(BuildContext context, WidgetRef ref) { /// final AsyncValue user = ref.watch(userProvider); /// -/// return user.when( -/// loading: () => CircularProgressIndicator(), -/// error: (error, stack) => Text('Oops, something unexpected happened'), -/// data: (user) => Text('Hello ${user.name}'), +/// return switch (user) { +/// AsyncValue(hasError: true) => Text('Oops, something unexpected happened'), +/// AsyncValue(:final value, hasValue: true) => Text('Hello ${value.name}'), +/// _ => CircularProgressIndicator(), /// ); /// } /// } /// ``` /// /// If a consumer of an [AsyncValue] does not care about the loading/error -/// state, consider using [value]/[valueOrNull] to read the state: +/// state, consider using [requireValue] to read the state: /// /// ```dart /// Widget build(BuildContext context, WidgetRef ref) { -/// // Reading .value will be throw during error and return null on "loading" states. -/// final User user = ref.watch(userProvider).value; -/// -/// // Reading .value will be throw both on loading and error states. -/// final User user2 = ref.watch(userProvider).requiredValue; +/// // Reading .requiredValue will be throw both on loading and error states. +/// final User user = ref.watch(userProvider).requiredValue; /// /// ... /// } /// ``` /// +/// By using [requireValue], we get an immediate access to the value. At the same +/// time, if we made a mistake and the value is not available, we will get an +/// exception. This is a good thing because it will help us to spot problem. +/// /// See also: /// /// - [FutureProvider], [StreamProvider] which transforms a [Future] into @@ -84,26 +61,26 @@ extension AsyncTransition on ProviderElementBase> { /// - [AsyncValue.guard], to simplify transforming a [Future] into an [AsyncValue]. @sealed @immutable -abstract class AsyncValue { +sealed class AsyncValue { const AsyncValue._(); - /// {@template asyncvalue.data} + /// {@template async_value.data} /// Creates an [AsyncValue] with a data. /// {@endtemplate} // coverage:ignore-start - const factory AsyncValue.data(T value) = AsyncData; + const factory AsyncValue.data(StateT value) = AsyncData; // coverage:ignore-end - /// {@template asyncvalue.loading} + /// {@template async_value.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; + const factory AsyncValue.loading({num progress}) = AsyncLoading; // coverage:ignore-end - /// {@template asyncvalue.error_ctor} + /// {@template async_value.error_ctor} /// Creates an [AsyncValue] in the error state. /// /// _I don't have a [StackTrace], what can I do?_ @@ -115,7 +92,7 @@ abstract class AsyncValue { /// {@endtemplate} // coverage:ignore-start const factory AsyncValue.error(Object error, StackTrace stackTrace) = - AsyncError; + AsyncError; // coverage:ignore-end /// Transforms a [Future] that may fail into something that is safe to read. @@ -200,21 +177,37 @@ abstract class AsyncValue { /// to also be true. bool get hasValue; + /// The current progress of the asynchronous operation. + /// + /// This value must be between 0 and 1. + /// + /// By default, the progress will always be `null`. + /// Providers can set this manually as such: + /// + /// ```dart + /// @riverpod + /// Future example(Ref ref) async { + /// ref.state = AsyncLoading(progress: 0); + /// + /// await something(); + /// + /// ref.state = AsyncLoading(progress: 1); + /// } + /// ``` + num? get progress; + /// 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. + /// If currently in error/loading state, will return the previous value. + /// If there is no previous value available, `null` will be returned. /// /// If you do not want to return previous value during loading/error states, - /// consider using [unwrapPrevious] with [valueOrNull]: + /// consider using [unwrapPrevious]: /// /// ```dart - /// ref.watch(provider).unwrapPrevious().valueOrNull; + /// ref.watch(provider).unwrapPrevious().value; /// ``` - /// - /// This will return null during loading/error states. - T? get value; + StateT? get value; /// The [error]. Object? get error; @@ -222,19 +215,270 @@ abstract class AsyncValue { /// The stacktrace of [error]. StackTrace? get stackTrace; - /// Casts the [AsyncValue] to a different type. - AsyncValue _cast(); + String get _displayString; + + /// 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. + StateT get requireValue { + if (hasValue) return value as StateT; + if (hasError) { + throwErrorWithCombinedStackTrace(error!, stackTrace!); + } + + throw StateError( + 'Tried to call `requireValue` on an `AsyncValue` that has no value: $this', + ); + } + + /// Whether the associated provider was forced to recompute even though + /// 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]. + /// + /// If a provider rebuilds because one of its dependencies changes (using [Ref.watch]), + /// then [isRefreshing] will be false, and instead [isReloading] will be true. + bool get isRefreshing => + isLoading && (hasValue || hasError) && this is! AsyncLoading; + + /// 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 changed (using [Ref.watch]), + /// then [isReloading] will be true. + /// If a provider rebuilds only due to [Ref.invalidate]/[Ref.refresh], then + /// [isReloading] will be false (and [isRefreshing] will be true). + /// + /// See also [isRefreshing] for manual provider rebuild. + bool get isReloading => (hasValue || hasError) && this is AsyncLoading; + + /// Whether [error] is not null. + /// + /// 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] + /// is an [AsyncLoading]/[AsyncError]. + /// + /// Note that an [AsyncData] may still be in loading/error state, such + /// as during a pull-to-refresh. + AsyncData? get asData { + return map( + data: (d) => d, + error: (e) => null, + loading: (l) => null, + ); + } + + /// Upcast [AsyncValue] into an [AsyncError], or return null if the [AsyncValue] + /// is an [AsyncLoading]/[AsyncData]. + /// + /// Note that an [AsyncError] may still be in loading state, such + /// as during a pull-to-refresh. + AsyncError? get asError => map( + data: (_) => null, + error: (e) => e, + loading: (_) => null, + ); /// Perform some action based on the current state of the [AsyncValue]. /// /// This allows reading the content of an [AsyncValue] in a type-safe way, /// without potentially ignoring to handle a case. R map({ - required R Function(AsyncData data) data, - required R Function(AsyncError error) error, - required R Function(AsyncLoading loading) loading, + required R Function(AsyncData data) data, + required R Function(AsyncError error) error, + required R Function(AsyncLoading loading) loading, }); + /// Casts the [AsyncValue] to a different type. + AsyncValue _cast(); + + /// Shorthand for [when] to handle only the `data` case. + /// + /// For loading/error cases, creates a new [AsyncValue] with the corresponding + /// generic type while preserving the error/stacktrace. + AsyncValue whenData(R Function(StateT value) cb) { + return map( + data: (d) { + try { + return AsyncData._( + cb(d.value), + isLoading: d.isLoading, + error: d.error, + stackTrace: d.stackTrace, + progress: d.progress, + ); + } catch (err, stack) { + return AsyncError._( + err, + stackTrace: stack, + isLoading: d.isLoading, + value: null, + hasValue: false, + progress: d.progress, + ); + } + }, + error: (e) => AsyncError._( + e.error, + stackTrace: e.stackTrace, + isLoading: e.isLoading, + value: null, + hasValue: false, + progress: e.progress, + ), + loading: (l) => AsyncLoading(progress: progress), + ); + } + + /// Switch-case over the state of the [AsyncValue] while purposefully not handling + /// some cases. + /// + /// If [AsyncValue] was in a case that is not handled, will return [orElse]. + /// + /// {@macro async_value.skip_flags} + R maybeWhen({ + bool skipLoadingOnReload = false, + bool skipLoadingOnRefresh = true, + bool skipError = false, + R Function(StateT data)? data, + R Function(Object error, StackTrace stackTrace)? error, + R Function()? loading, + required R Function() orElse, + }) { + return when( + skipError: skipError, + skipLoadingOnRefresh: skipLoadingOnRefresh, + skipLoadingOnReload: skipLoadingOnReload, + data: data ?? (_) => orElse(), + error: error ?? (err, stack) => orElse(), + loading: loading ?? () => orElse(), + ); + } + + /// Performs an action based on the state of the [AsyncValue]. + /// + /// All cases are required, which allows returning a non-nullable value. + /// + /// {@template async_value.skip_flags} + /// By default, [when] skips "loading" states if triggered by a [Ref.refresh] + /// or [Ref.invalidate] (but does not skip loading states if triggered by [Ref.watch]). + /// + /// In the event that an [AsyncValue] is in multiple states at once (such as + /// when reloading a provider or emitting an error after a valid data), + /// [when] offers various flags to customize whether it should call + /// [loading]/[error]/[data] : + /// + /// - [skipLoadingOnReload] (false by default) customizes whether [loading] + /// should be invoked if a provider rebuilds because of [Ref.watch]. + /// In that situation, [when] will try to invoke either [error]/[data] + /// with the previous state. + /// + /// - [skipLoadingOnRefresh] (true by default) controls whether [loading] + /// should be invoked if a provider rebuilds because of [Ref.refresh] + /// or [Ref.invalidate]. + /// In that situation, [when] will try to invoke either [error]/[data] + /// with the previous state. + /// + /// - [skipError] (false by default) decides whether to invoke [data] instead + /// of [error] if a previous [value] is available. + /// {@endtemplate} + R when({ + bool skipLoadingOnReload = false, + bool skipLoadingOnRefresh = true, + bool skipError = false, + required R Function(StateT data) data, + required R Function(Object error, StackTrace stackTrace) error, + required R Function() loading, + }) { + if (isLoading) { + bool skip; + if (isRefreshing) { + skip = skipLoadingOnRefresh; + } else if (isReloading) { + skip = skipLoadingOnReload; + } else { + skip = false; + } + if (!skip) return loading(); + } + + if (hasError && (!hasValue || !skipError)) { + return error(this.error!, stackTrace!); + } + + return data(requireValue); + } + + /// Perform actions conditionally based on the state of the [AsyncValue]. + /// + /// Returns null if [AsyncValue] was in a state that was not handled. + /// This is similar to [maybeWhen] where `orElse` returns null. + /// + /// {@macro async_value.skip_flags} + R? whenOrNull({ + bool skipLoadingOnReload = false, + bool skipLoadingOnRefresh = true, + bool skipError = false, + R? Function(StateT data)? data, + R? Function(Object error, StackTrace stackTrace)? error, + R? Function()? loading, + }) { + return when( + skipError: skipError, + skipLoadingOnRefresh: skipLoadingOnRefresh, + skipLoadingOnReload: skipLoadingOnReload, + data: data ?? (_) => null, + error: error ?? (err, stack) => null, + loading: loading ?? () => null, + ); + } + + /// Perform some actions based on the state of the [AsyncValue], or call orElse + /// if the current state was not tested. + R maybeMap({ + R Function(AsyncData data)? data, + R Function(AsyncError error)? error, + R Function(AsyncLoading loading)? loading, + required R Function() orElse, + }) { + return map( + data: (d) { + if (data != null) return data(d); + return orElse(); + }, + error: (d) { + if (error != null) return error(d); + return orElse(); + }, + loading: (d) { + if (loading != null) return loading(d); + return orElse(); + }, + ); + } + + /// Perform some actions based on the state of the [AsyncValue], or return null + /// if the current state wasn't tested. + R? mapOrNull({ + R? Function(AsyncData data)? data, + R? Function(AsyncError error)? error, + R? Function(AsyncLoading loading)? loading, + }) { + return map( + data: (d) => data?.call(d), + error: (d) => error?.call(d), + loading: (d) => loading?.call(d), + ); + } + /// Clone an [AsyncValue], merging it with [previous]. /// /// When doing so, the resulting [AsyncValue] can contain the information @@ -247,24 +491,24 @@ abstract class AsyncValue { /// or instead by [Ref.watch] (if false). /// This changes the default behavior of [when] and sets the [isReloading]/ /// [isRefreshing] flags accordingly. - AsyncValue copyWithPrevious( - AsyncValue previous, { + AsyncValue copyWithPrevious( + AsyncValue previous, { bool isRefresh = true, }); /// The opposite of [copyWithPrevious], reverting to the raw [AsyncValue] /// with no information on the previous state. - AsyncValue unwrapPrevious() { + AsyncValue unwrapPrevious() { return map( data: (d) { - if (d.isLoading) return AsyncLoading(); + if (d.isLoading) return AsyncLoading(progress: progress); return AsyncData(d.value); }, error: (e) { - if (e.isLoading) return AsyncLoading(); + if (e.isLoading) return AsyncLoading(progress: progress); return AsyncError(e.error, e.stackTrace); }, - loading: (l) => AsyncLoading(), + loading: (l) => AsyncLoading(progress: progress), ); } @@ -272,6 +516,7 @@ abstract class AsyncValue { String toString() { final content = [ if (isLoading && this is! AsyncLoading) 'isLoading: $isLoading', + if (progress case final progress?) 'progress: $progress', if (hasValue) 'value: $value', if (hasError) ...[ 'error: $error', @@ -279,18 +524,19 @@ abstract class AsyncValue { ], ].join(', '); - return '$runtimeType($content)'; + return '$_displayString<$StateT>($content)'; } @override bool operator ==(Object other) { return runtimeType == other.runtimeType && - other is AsyncValue && + other is AsyncValue && other.isLoading == isLoading && other.hasValue == hasValue && other.error == error && other.stackTrace == stackTrace && - other.valueOrNull == valueOrNull; + other.progress == progress && + other.value == value; } @override @@ -298,21 +544,23 @@ abstract class AsyncValue { runtimeType, isLoading, hasValue, - valueOrNull, + value, error, stackTrace, + progress, ); } -/// {@macro asyncvalue.data} -class AsyncData extends AsyncValue { - /// {@macro asyncvalue.data} - const AsyncData(T value) +/// {@macro async_value.data} +final class AsyncData extends AsyncValue { + /// {@macro async_value.data} + const AsyncData(StateT value) : this._( value, isLoading: false, error: null, stackTrace: null, + progress: null, ); const AsyncData._( @@ -320,14 +568,22 @@ class AsyncData extends AsyncValue { required this.isLoading, required this.error, required this.stackTrace, + required this.progress, }) : super._(); @override - final T value; + String get _displayString => 'AsyncData'; @override bool get hasValue => true; + @override + final num? progress; + + @override + final StateT value; + + @override @override final bool isLoading; @@ -339,16 +595,16 @@ class AsyncData extends AsyncValue { @override R map({ - required R Function(AsyncData data) data, - required R Function(AsyncError error) error, - required R Function(AsyncLoading loading) loading, + required R Function(AsyncData data) data, + required R Function(AsyncError error) error, + required R Function(AsyncLoading loading) loading, }) { return data(this); } @override - AsyncData copyWithPrevious( - AsyncValue previous, { + AsyncData copyWithPrevious( + AsyncValue previous, { bool isRefresh = true, }) { return this; @@ -356,24 +612,29 @@ class AsyncData extends AsyncValue { @override AsyncValue _cast() { - if (T == R) return this as AsyncValue; + if (StateT == R) return this as AsyncValue; return AsyncData._( value as R, isLoading: isLoading, error: error, stackTrace: stackTrace, + progress: progress, ); } } -/// {@macro asyncvalue.loading} -class AsyncLoading extends AsyncValue { - /// {@macro asyncvalue.loading} - const AsyncLoading() +/// {@macro async_value.loading} +final class AsyncLoading extends AsyncValue { + /// {@macro async_value.loading} + const AsyncLoading({this.progress}) : hasValue = false, value = null, error = null, stackTrace = null, + assert( + progress == null || (progress >= 0 && progress <= 1), + 'progress must be between 0 and 1', + ), super._(); const AsyncLoading._({ @@ -381,16 +642,23 @@ class AsyncLoading extends AsyncValue { required this.value, required this.error, required this.stackTrace, + required this.progress, }) : super._(); @override bool get isLoading => true; + @override + String get _displayString => 'AsyncLoading'; + @override final bool hasValue; @override - final T? value; + final num? progress; + + @override + final StateT? value; @override final Object? error; @@ -400,27 +668,28 @@ class AsyncLoading extends AsyncValue { @override AsyncValue _cast() { - if (T == R) return this as AsyncValue; + if (StateT == R) return this as AsyncValue; return AsyncLoading._( hasValue: hasValue, value: value as R?, error: error, stackTrace: stackTrace, + progress: progress, ); } @override R map({ - required R Function(AsyncData data) data, - required R Function(AsyncError error) error, - required R Function(AsyncLoading loading) loading, + required R Function(AsyncData data) data, + required R Function(AsyncError error) error, + required R Function(AsyncLoading loading) loading, }) { return loading(this); } @override - AsyncValue copyWithPrevious( - AsyncValue previous, { + AsyncValue copyWithPrevious( + AsyncValue previous, { bool isRefresh = true, }) { if (isRefresh) { @@ -430,13 +699,15 @@ class AsyncLoading extends AsyncValue { isLoading: true, error: d.error, stackTrace: d.stackTrace, + progress: progress, ), error: (e) => AsyncError._( e.error, isLoading: true, - value: e.valueOrNull, + value: e.value, stackTrace: e.stackTrace, hasValue: e.hasValue, + progress: progress, ), loading: (_) => this, ); @@ -444,15 +715,17 @@ class AsyncLoading extends AsyncValue { return previous.map( data: (d) => AsyncLoading._( hasValue: true, - value: d.valueOrNull, + value: d.value, error: d.error, stackTrace: d.stackTrace, + progress: progress, ), error: (e) => AsyncLoading._( hasValue: e.hasValue, - value: e.valueOrNull, + value: e.value, error: e.error, stackTrace: e.stackTrace, + progress: progress, ), loading: (e) => e, ); @@ -460,9 +733,9 @@ class AsyncLoading extends AsyncValue { } } -/// {@macro asyncvalue.error_ctor} -class AsyncError extends AsyncValue { - /// {@macro asyncvalue.error_ctor} +/// {@macro async_value.error_ctor} +final class AsyncError extends AsyncValue { + /// {@macro async_value.error_ctor} const AsyncError(Object error, StackTrace stackTrace) : this._( error, @@ -470,16 +743,23 @@ class AsyncError extends AsyncValue { isLoading: false, hasValue: false, value: null, + progress: null, ); const AsyncError._( this.error, { required this.stackTrace, - required T? value, + required this.value, required this.hasValue, required this.isLoading, - }) : _value = value, - super._(); + required this.progress, + }) : super._(); + + @override + String get _displayString => 'AsyncError'; + + @override + final num? progress; @override final bool isLoading; @@ -487,15 +767,8 @@ class AsyncError extends AsyncValue { @override final bool hasValue; - final T? _value; - @override - T? get value { - if (!hasValue) { - throwErrorWithCombinedStackTrace(error, stackTrace); - } - return _value; - } + final StateT? value; @override final Object error; @@ -505,302 +778,38 @@ class AsyncError extends AsyncValue { @override AsyncValue _cast() { - if (T == R) return this as AsyncValue; + if (StateT == R) return this as AsyncValue; return AsyncError._( error, stackTrace: stackTrace, isLoading: isLoading, - value: _value as R?, + value: value as R?, hasValue: hasValue, + progress: progress, ); } @override R map({ - required R Function(AsyncData data) data, - required R Function(AsyncError error) error, - required R Function(AsyncLoading loading) loading, + required R Function(AsyncData data) data, + required R Function(AsyncError error) error, + required R Function(AsyncLoading loading) loading, }) { return error(this); } @override - AsyncError copyWithPrevious( - AsyncValue previous, { + AsyncError copyWithPrevious( + AsyncValue previous, { bool isRefresh = true, }) { return AsyncError._( error, stackTrace: stackTrace, isLoading: isLoading, - value: previous.valueOrNull, + value: previous.value, hasValue: previous.hasValue, - ); - } -} - -/// An extension that adds methods like [when] to an [AsyncValue]. -extension AsyncValueX on AsyncValue { - /// 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 { - if (hasValue) return value as T; - if (hasError) { - throwErrorWithCombinedStackTrace(error!, stackTrace!); - } - - throw StateError( - 'Tried to call `requireValue` on an `AsyncValue` that has no value: $this', - ); - } - - /// Return the value or previous value if in loading/error state. - /// - /// If there is no previous value, null will be returned during loading/error state. - /// - /// 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] : - /// - /// ```dart - /// ref.watch(provider).unwrapPrevious()?.valueOrNull; - /// ``` - T? get valueOrNull { - if (hasValue) return value; - return null; - } - - /// Whether the associated provider was forced to recompute even though - /// 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]. - /// - /// If a provider rebuilds because one of its dependencies changes (using [Ref.watch]), - /// then [isRefreshing] will be false, and instead [isReloading] will be true. - bool get isRefreshing => - isLoading && (hasValue || hasError) && this is! AsyncLoading; - - /// 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 changed (using [Ref.watch]), - /// then [isReloading] will be true. - /// If a provider rebuilds only due to [Ref.invalidate]/[Ref.refresh], then - /// [isReloading] will be false (and [isRefreshing] will be true). - /// - /// See also [isRefreshing] for manual provider rebuild. - bool get isReloading => (hasValue || hasError) && this is AsyncLoading; - - /// Whether [error] is not null. - /// - /// 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] - /// is an [AsyncLoading]/[AsyncError]. - /// - /// Note that an [AsyncData] may still be in loading/error state, such - /// as during a pull-to-refresh. - AsyncData? get asData { - return map( - data: (d) => d, - error: (e) => null, - loading: (l) => null, - ); - } - - /// Upcast [AsyncValue] into an [AsyncError], or return null if the [AsyncValue] - /// is an [AsyncLoading]/[AsyncData]. - /// - /// Note that an [AsyncError] may still be in loading state, such - /// as during a pull-to-refresh. - AsyncError? get asError => map( - data: (_) => null, - error: (e) => e, - loading: (_) => null, - ); - - /// Shorthand for [when] to handle only the `data` case. - /// - /// For loading/error cases, creates a new [AsyncValue] with the corresponding - /// generic type while preserving the error/stacktrace. - AsyncValue whenData(R Function(T value) cb) { - return map( - data: (d) { - try { - return AsyncData._( - cb(d.value), - isLoading: d.isLoading, - error: d.error, - stackTrace: d.stackTrace, - ); - } catch (err, stack) { - return AsyncError._( - err, - stackTrace: stack, - isLoading: d.isLoading, - value: null, - hasValue: false, - ); - } - }, - error: (e) => AsyncError._( - e.error, - stackTrace: e.stackTrace, - isLoading: e.isLoading, - value: null, - hasValue: false, - ), - loading: (l) => AsyncLoading(), - ); - } - - /// Switch-case over the state of the [AsyncValue] while purposefully not handling - /// some cases. - /// - /// If [AsyncValue] was in a case that is not handled, will return [orElse]. - /// - /// {@macro asyncvalue.skip_flags} - R maybeWhen({ - bool skipLoadingOnReload = false, - bool skipLoadingOnRefresh = true, - bool skipError = false, - R Function(T data)? data, - R Function(Object error, StackTrace stackTrace)? error, - R Function()? loading, - required R Function() orElse, - }) { - return when( - skipError: skipError, - skipLoadingOnRefresh: skipLoadingOnRefresh, - skipLoadingOnReload: skipLoadingOnReload, - data: data ?? (_) => orElse(), - error: error ?? (err, stack) => orElse(), - loading: loading ?? () => orElse(), - ); - } - - /// Performs an action based on the state of the [AsyncValue]. - /// - /// All cases are required, which allows returning a non-nullable value. - /// - /// {@template asyncvalue.skip_flags} - /// By default, [when] skips "loading" states if triggered by a [Ref.refresh] - /// or [Ref.invalidate] (but does not skip loading states if triggered by [Ref.watch]). - /// - /// In the event that an [AsyncValue] is in multiple states at once (such as - /// when reloading a provider or emitting an error after a valid data), - /// [when] offers various flags to customize whether it should call - /// [loading]/[error]/[data] : - /// - /// - [skipLoadingOnReload] (false by default) customizes whether [loading] - /// should be invoked if a provider rebuilds because of [Ref.watch]. - /// In that situation, [when] will try to invoke either [error]/[data] - /// with the previous state. - /// - /// - [skipLoadingOnRefresh] (true by default) controls whether [loading] - /// should be invoked if a provider rebuilds because of [Ref.refresh] - /// or [Ref.invalidate]. - /// In that situation, [when] will try to invoke either [error]/[data] - /// with the previous state. - /// - /// - [skipError] (false by default) decides whether to invoke [data] instead - /// of [error] if a previous [value] is available. - /// {@endtemplate} - R when({ - bool skipLoadingOnReload = false, - bool skipLoadingOnRefresh = true, - bool skipError = false, - required R Function(T data) data, - required R Function(Object error, StackTrace stackTrace) error, - required R Function() loading, - }) { - if (isLoading) { - bool skip; - if (isRefreshing) { - skip = skipLoadingOnRefresh; - } else if (isReloading) { - skip = skipLoadingOnReload; - } else { - skip = false; - } - if (!skip) return loading(); - } - - if (hasError && (!hasValue || !skipError)) { - return error(this.error!, stackTrace!); - } - - return data(requireValue); - } - - /// Perform actions conditionally based on the state of the [AsyncValue]. - /// - /// Returns null if [AsyncValue] was in a state that was not handled. - /// This is similar to [maybeWhen] where `orElse` returns null. - /// - /// {@macro asyncvalue.skip_flags} - R? whenOrNull({ - bool skipLoadingOnReload = false, - bool skipLoadingOnRefresh = true, - bool skipError = false, - R? Function(T data)? data, - R? Function(Object error, StackTrace stackTrace)? error, - R? Function()? loading, - }) { - return when( - skipError: skipError, - skipLoadingOnRefresh: skipLoadingOnRefresh, - skipLoadingOnReload: skipLoadingOnReload, - data: data ?? (_) => null, - error: error ?? (err, stack) => null, - loading: loading ?? () => null, - ); - } - - /// Perform some actions based on the state of the [AsyncValue], or call orElse - /// if the current state was not tested. - R maybeMap({ - R Function(AsyncData data)? data, - R Function(AsyncError error)? error, - R Function(AsyncLoading loading)? loading, - required R Function() orElse, - }) { - return map( - data: (d) { - if (data != null) return data(d); - return orElse(); - }, - error: (d) { - if (error != null) return error(d); - return orElse(); - }, - loading: (d) { - if (loading != null) return loading(d); - return orElse(); - }, - ); - } - - /// Perform some actions based on the state of the [AsyncValue], or return null - /// if the current state wasn't tested. - R? mapOrNull({ - R? Function(AsyncData data)? data, - R? Function(AsyncError error)? error, - R? Function(AsyncLoading loading)? loading, - }) { - return map( - data: (d) => data?.call(d), - error: (d) => error?.call(d), - loading: (d) => loading?.call(d), + progress: progress, ); } } diff --git a/packages/riverpod/lib/src/core/element.dart b/packages/riverpod/lib/src/core/element.dart new file mode 100644 index 000000000..2452448d5 --- /dev/null +++ b/packages/riverpod/lib/src/core/element.dart @@ -0,0 +1,911 @@ +part of '../framework.dart'; + +/// {@template riverpod.refreshable} +/// An interface for provider expressions that can be passed to `ref.refresh` +/// +/// This differentiates: +/// +/// ```dart +/// ref.watch(provider); +/// ref.watch(provider.future); +/// ``` +/// +/// from: +/// +/// ```dart +/// ref.watch(provider.select((value) => value)); +/// ``` +/// {@endtemplate} +sealed class Refreshable implements ProviderListenable {} + +mixin _ProviderRefreshable + implements Refreshable, ProviderListenableWithOrigin { + ProviderBase get provider; +} + +/// A debug utility used by `flutter_riverpod`/`hooks_riverpod` to check +/// if it is safe to modify a provider. +/// +/// This corresponds to all the widgets that a [Provider] is associated with. +@internal +void Function()? debugCanModifyProviders; + +@internal +typedef WhenComplete = void Function(void Function() cb)?; + +/// {@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 one of its dependencies changes. +/// +/// This class is not meant to be used directly and is an implementation detail +/// of providers. +/// Do not use. +/// {@endtemplate} +@internal +@optionalTypeArgs +abstract class ProviderElement implements Node { + /// {@macro riverpod.provider_element_base} + ProviderElement(this.pointer); + + static Duration? _defaultRetry(int retryCount, Object error) { + return Duration( + milliseconds: math.min(200 * math.pow(2, retryCount).toInt(), 6400), + ); + } + + static ProviderElement? _debugCurrentlyBuildingElement; + + /// The last result of [ProviderBase.debugGetCreateSourceHash]. + /// + /// Available only in debug mode. + String? _debugCurrentCreateHash; + var _debugSkipNotifyListenersAsserts = false; + + /// The provider associated with this [ProviderElement], before applying overrides. + ProviderBase get origin => pointer.origin as ProviderBase; + + /// The provider associated with this [ProviderElement], after applying overrides. + ProviderBase get provider; + + /// The [$ProviderPointer] associated with this [ProviderElement]. + final $ProviderPointer pointer; + + /// The [ProviderContainer] that owns this [ProviderElement]. + ProviderContainer get container => pointer.targetContainer; + + // ignore: library_private_types_in_public_api, not public + _Ref? ref; + + /// Whether this [ProviderElement] is actively in use. + /// + /// A provider is considered not used if: + /// - it has no listeners + /// - all of its listeners are "weak" (i.e. created with `listen(weak: true)`) + /// + /// See also [mayNeedDispose], called when [isActive] may have changed. + bool get isActive => (_listenerCount - _pausedActiveSubscriptionCount) > 0; + + int get _listenerCount => dependents?.length ?? 0; + + var _pausedActiveSubscriptionCount = 0; + var _didCancelOnce = false; + + /// Whether this [ProviderElement] is currently listened to or not. + /// + /// This maps to listeners added with `listen` and `watch`, + /// excluding `listen(weak: true)`. + bool get hasNonWeakListeners => _listenerCount > 0; + + List? _inactiveSubscriptions; + @visibleForTesting + List? subscriptions; + @visibleForTesting + List? dependents; + + /// "listen(weak: true)" pointing to this provider. + /// + /// Those subscriptions are separate from [ProviderElement.dependents] for a few reasons: + /// - They do not count towards [ProviderElement.isActive]. + /// - They may be reused between two instances of a [ProviderElement]. + @visibleForTesting + final weakDependents = []; + + bool _mustRecomputeState = false; + bool _dependencyMayHaveChanged = false; + bool _didChangeDependency = false; + + var _retryCount = 0; + Timer? _pendingRetryTimer; + + /// Whether the assert that prevents [requireState] from returning + /// if the state was not set before is enabled. + @visibleForOverriding + bool get debugAssertDidSetStateEnabled => true; + + bool _debugDidSetState = false; + bool _didBuild = false; + var _didMount = false; + + /* STATE */ + $Result? _stateResult; + + /// The current state of the provider. + /// + /// Obtains the current state, or null if the provider has yet to initialize. + /// + /// The returned object will contain error information, if any. + /// This function does not cause the provider to rebuild if it somehow was + /// outdated. + /// + /// This is not meant for public consumption. Instead, public API should use + /// [readSelf]. + $Result? get stateResult => _stateResult; + + /// Returns the currently exposed by a provider + /// + /// May throw if the provider threw when creating the exposed value. + StateT readSelf() { + flush(); + + return requireState; + } + + /// Update the exposed value of a provider and notify its listeners. + /// + /// Listeners will only be notified if [updateShouldNotify] + /// returns true. + /// + /// This API is not meant for public consumption. Instead if a [Ref] needs + /// to expose a way to update the state, the practice is to expose a getter/setter. + void setStateResult($Result newState) { + if (kDebugMode) _debugDidSetState = true; + + final previousResult = stateResult; + final result = _stateResult = newState; + + if (_didBuild) { + _notifyListeners(result, previousResult); + } + } + + /// Read the current value of a provider and: + /// + /// - if in error state, rethrow the error + /// - if the provider is not initialized, gracefully handle the error. + /// + /// This is not meant for public consumption. Instead, public API should use + /// [readSelf]. + StateT get requireState { + const uninitializedError = ''' +Tried to read the state of an uninitialized provider. +This could mean a few things: +- You have a circular dependency, and your provider end-up depending on itself. +- You read "ref.state", but no value was set beforehand. +'''; + + if (kDebugMode) { + if (debugAssertDidSetStateEnabled && !_debugDidSetState) { + throw StateError(uninitializedError); + } + } + + final state = stateResult; + if (state == null) throw StateError(uninitializedError); + + return switch (state) { + ResultError() => throwErrorWithCombinedStackTrace( + state.error, + state.stackTrace, + ), + ResultData() => state.state, + }; + } + + /// Called when a provider is rebuilt. Used for providers to not notify their + /// listeners if the exposed value did not change. + bool updateShouldNotify(StateT previous, StateT next); + + /* /STATE */ + + /// A life-cycle executed when a hot-reload is performed. + /// + /// This is equivalent to Flutter's `State.reassemble`. + /// + /// This life-cycle is used to check for change in [ProviderBase.debugGetCreateSourceHash], + /// and invalidate the provider state on change. + void debugReassemble() { + final previousHash = _debugCurrentCreateHash; + _debugCurrentCreateHash = provider.debugGetCreateSourceHash(); + + if (previousHash != _debugCurrentCreateHash) { + invalidateSelf(asReload: false); + } + } + + /// Called the first time a provider is obtained. + void mount() { + if (kDebugMode) { + _debugCurrentCreateHash = provider.debugGetCreateSourceHash(); + } + + final ref = this.ref = _Ref(this); + buildState(ref); + + _notifyListeners( + _stateResult!, + null, + isMount: true, + checkUpdateShouldNotify: false, + ); + } + + /// Called when the override of a provider changes. + /// + /// See also: + /// - `overrideWithValue`, which relies on [update] to handle + /// the scenario where the value changed. + @visibleForOverriding + void update(ProviderBase newProvider) {} + + /// Initialize a provider and track dependencies used during the initialization. + /// + /// After a provider is initialized, this function takes care of unsubscribing + /// to dependencies that are no-longer used. + void _performBuild() { + runOnDispose(); + final ref = this.ref = _Ref(this); + final previousStateResult = _stateResult; + + if (kDebugMode) _debugDidSetState = false; + + visitListenables((listenable) { + listenable.lockNotification(); + }); + + buildState(ref); + + visitListenables((listenable) { + listenable.unlockNotification(); + }); + + if (!identical(_stateResult, previousStateResult)) { + // Asserts would otherwise prevent a provider rebuild from updating + // other providers + if (kDebugMode) _debugSkipNotifyListenersAsserts = true; + + _notifyListeners(_stateResult!, previousStateResult); + + if (kDebugMode) _debugSkipNotifyListenersAsserts = false; + } + } + + /// Initialize a provider. + /// + /// This function **must** call [setStateResult] or throw (or both). + /// + /// Exceptions within this function will be caught and set the provider in error + /// state. Then, reading this provider will rethrow the thrown exception. + /// + /// - [didChangeDependency] can be used to differentiate a rebuild caused + /// by [Ref.watch] from one caused by [Ref.refresh]/[Ref.invalidate]. + @visibleForOverriding + WhenComplete create( + // ignore: library_private_types_in_public_api, not public + _Ref ref, { + required bool didChangeDependency, + }); + + /// A utility for re-initializing a provider when needed. + /// + /// Calling [flush] will only re-initialize the provider if it needs to rerun. + /// This can involve: + /// - a previous call to [Ref.invalidateSelf] + /// - a dependency of the provider has changed (such as when using [Ref.watch]). + /// + /// This is not meant for public consumption. Public API should hide + /// [flush] from users, such that they don't need to care about invoking this function. + void flush() { + if (!_didMount) { + _didMount = true; + mount(); + } + + _maybeRebuildDependencies(); + if (_mustRecomputeState) { + _mustRecomputeState = false; + _performBuild(); + } + } + + /// Iterates over the dependencies of this provider, calling [flush] on them too. + /// + /// This work is only performed if a dependency has notified that it might + /// need to be re-executed. + void _maybeRebuildDependencies() { + if (!_dependencyMayHaveChanged) return; + + _dependencyMayHaveChanged = false; + + visitAncestors( + (element) => element.flush(), + ); + } + + /// Invokes [create] and handles errors. + @internal + void buildState( + // ignore: library_private_types_in_public_api, not public + _Ref ref, + ) { + if (_didChangeDependency) _retryCount = 0; + + ProviderElement? debugPreviouslyBuildingElement; + final previousDidChangeDependency = _didChangeDependency; + _didChangeDependency = false; + if (kDebugMode) { + debugPreviouslyBuildingElement = _debugCurrentlyBuildingElement; + _debugCurrentlyBuildingElement = this; + } + + _didBuild = false; + try { + final whenComplete = create( + ref, + didChangeDependency: previousDidChangeDependency, + ) ?? + (cb) => cb(); + + whenComplete(_didCompleteInitialization); + } catch (err, stack) { + if (kDebugMode) _debugDidSetState = true; + + _stateResult = $Result.error(err, stack); + triggerRetry(err); + } finally { + _didBuild = true; + if (kDebugMode) { + _debugCurrentlyBuildingElement = debugPreviouslyBuildingElement; + } + + assert( + stateResult != null, + 'Bad state, the provider did not initialize. Did "create" forget to set the state?', + ); + } + } + + @protected + void triggerRetry(Object error) { + // Don't start retry if the provider was disposed + if (ref == null) return; + + final retry = origin.retry ?? container.retry ?? _defaultRetry; + + // Capture exceptions. On error, stop retrying if the retry + // function failed + runGuarded(() { + final duration = retry(_retryCount, error); + if (duration == null) return; + + _pendingRetryTimer = Timer(duration, () { + _pendingRetryTimer = null; + _retryCount++; + invalidateSelf(asReload: false); + }); + }); + } + + void _debugAssertNotificationAllowed() { + if (_debugSkipNotifyListenersAsserts) return; + + assert( + _debugCurrentlyBuildingElement == null || + _debugCurrentlyBuildingElement == this, + ''' +Providers are not allowed to modify other providers during their initialization. + +The provider ${_debugCurrentlyBuildingElement!.origin} modified $origin while building. +''', + ); + + debugCanModifyProviders?.call(); + } + + void invalidateSelf({required bool asReload}) { + if (asReload) _didChangeDependency = true; + if (_mustRecomputeState) return; + + _mustRecomputeState = true; + runOnDispose(); + mayNeedDispose(); + container.scheduler.scheduleProviderRefresh(this); + + // We don't call this._markDependencyMayHaveChanged here because we voluntarily + // do not want to set the _dependencyMayHaveChanged flag to true. + // Since the dependency is known to have changed, there is no reason to try + // and "flush" it, as it will already get rebuilt. + visitChildren((element) { + element._markDependencyMayHaveChanged(); + element.visitListenables( + (notifier) => notifier.notifyDependencyMayHaveChanged(), + ); + }); + visitListenables((notifier) => notifier.notifyDependencyMayHaveChanged()); + } + + MutationContext? _currentMutationContext() => + Zone.current[mutationZoneKey] as MutationContext?; + + ProviderObserverContext _currentObserverContext() { + return ProviderObserverContext( + origin, + container, + mutation: _currentMutationContext(), + ); + } + + void _notifyListeners( + $Result newState, + $Result? previousStateResult, { + bool checkUpdateShouldNotify = true, + bool isMount = false, + }) { + if (kDebugMode && !isMount) _debugAssertNotificationAllowed(); + + final previousState = previousStateResult?.stateOrNull; + + // listenSelf listeners do not respect updateShouldNotify + switch (newState) { + case final ResultData newState: + final onChangeSelfListeners = ref?._onChangeSelfListeners; + if (onChangeSelfListeners != null) { + for (var i = 0; i < onChangeSelfListeners.length; i++) { + Zone.current.runBinaryGuarded( + onChangeSelfListeners[i], + previousState, + newState.state, + ); + } + } + case final ResultError newState: + final onErrorSelfListeners = ref?._onErrorSelfListeners; + if (onErrorSelfListeners != null) { + for (var i = 0; i < onErrorSelfListeners.length; i++) { + Zone.current.runBinaryGuarded( + onErrorSelfListeners[i], + newState.error, + newState.stackTrace, + ); + } + } + } + + if (checkUpdateShouldNotify && + previousStateResult != null && + previousStateResult.hasState && + newState.hasState && + !updateShouldNotify( + previousState as StateT, + newState.requireState, + )) { + return; + } + + final listeners = [...weakDependents, if (!isMount) ...?dependents]; + switch (newState) { + case final ResultData newState: + for (var i = 0; i < listeners.length; i++) { + final listener = listeners[i]; + if (listener.closed) continue; + + Zone.current.runBinaryGuarded( + listener._onOriginData, + previousState, + newState.state, + ); + } + case final ResultError newState: + for (var i = 0; i < listeners.length; i++) { + final listener = listeners[i]; + if (listener.closed) continue; + + Zone.current.runBinaryGuarded( + listener._onOriginError, + newState.error, + newState.stackTrace, + ); + } + } + + for (final observer in container.observers) { + if (isMount) { + runBinaryGuarded( + observer.didAddProvider, + _currentObserverContext(), + newState.stateOrNull, + ); + } else { + runTernaryGuarded( + observer.didUpdateProvider, + _currentObserverContext(), + previousState, + newState.stateOrNull, + ); + } + } + + for (final observer in container.observers) { + if (newState is ResultError) { + runTernaryGuarded( + observer.providerDidFail, + _currentObserverContext(), + newState.error, + newState.stackTrace, + ); + } + } + } + + void _markDependencyMayHaveChanged() { + if (_dependencyMayHaveChanged) return; + + _dependencyMayHaveChanged = true; + + visitChildren( + (element) { + element._markDependencyMayHaveChanged(); + element.visitListenables( + (notifier) => notifier.notifyDependencyMayHaveChanged(), + ); + }, + ); + visitListenables((notifier) => notifier.notifyDependencyMayHaveChanged()); + } + + @override + ProviderElement readProviderElement(ProviderBase provider) { + return container.readProviderElement(provider); + } + + ProviderSubscription listen( + ProviderListenable listenable, + void Function(T? previous, T value) listener, { + bool weak = false, + void Function(Object error, StackTrace stackTrace)? onError, + bool fireImmediately = false, + // Not part of the public "Ref" API + void Function()? onDependencyMayHaveChanged, + }) { + final ref = this.ref!; + ref._throwIfInvalidUsage(); + + final sub = listenable.addListener( + this, + listener, + fireImmediately: fireImmediately, + onError: onError, + weak: weak, + onDependencyMayHaveChanged: onDependencyMayHaveChanged, + ); + + switch (sub) { + case final ProviderSubscriptionImpl sub: + sub._listenedElement.addDependentSubscription(sub); + } + + if (kDebugMode) ref._debugAssertCanDependOn(listenable); + + return sub; + } + + void onCancel() { + subscriptions?.forEach((sub) { + switch (sub) { + case ProviderSubscriptionImpl(): + sub._listenedElement.onSubscriptionPause(sub); + } + }); + } + + void onResume() { + subscriptions?.forEach((sub) { + switch (sub) { + case ProviderSubscriptionImpl(): + sub._listenedElement.onSubscriptionResume(sub); + } + }); + } + + void addDependentSubscription(ProviderSubscriptionImpl sub) { + _onChangeSubscription(sub, () { + if (sub.weak) { + weakDependents.add(sub); + } else { + final dependents = this.dependents ??= []; + dependents.add(sub); + } + + if (sub.source case final ProviderElement element) { + final subs = element.subscriptions ??= []; + subs.add(sub); + } + }); + } + + void removeDependentSubscription(ProviderSubscription sub) { + _onChangeSubscription(sub, () { + // If the subscription is paused, internally resume it to decrement any + // associated state. + // We don't want to call onResume though. + _notifyResumeListeners = false; + while (sub.isPaused) { + sub.resume(); + } + _notifyResumeListeners = true; + + if (sub.weak) { + weakDependents.remove(sub); + } else { + dependents?.remove(sub); + } + + if (sub.source case final ProviderElement element) { + element.subscriptions?.remove(sub); + } + }); + } + + var _notifyResumeListeners = true; + void _onChangeSubscription(ProviderSubscription sub, void Function() apply) { + final wasActive = isActive; + final previousListenerCount = _listenerCount; + apply(); + + switch ((wasActive: wasActive, isActive: isActive)) { + case (wasActive: false, isActive: true) when _didCancelOnce: + if (_notifyResumeListeners) { + ref?._onResumeListeners?.forEach(runGuarded); + } + onResume(); + + case (wasActive: true, isActive: false): + _didCancelOnce = true; + ref?._onCancelListeners?.forEach(runGuarded); + onCancel(); + + default: + // No state change, so do nothing + } + + if (_listenerCount < previousListenerCount) { + if (ref?._onRemoveListeners case final listeners?) { + listeners.forEach(runGuarded); + } + mayNeedDispose(); + } else if (_listenerCount > previousListenerCount) { + if (ref?._onAddListeners case final listeners?) { + listeners.forEach(runGuarded); + } + } + } + + void onSubscriptionPause(ProviderSubscription sub) { + // Weak listeners are not counted towards isActive, so we don't want to change + // _pausedActiveSubscriptionCount + if (sub.weak) return; + + _onChangeSubscription(sub, () => _pausedActiveSubscriptionCount++); + } + + void onSubscriptionResume(ProviderSubscription sub) { + // Weak listeners are not counted towards isActive, so we don't want to change + // _pausedActiveSubscriptionCount + if (sub.weak) return; + + _onChangeSubscription(sub, () { + _pausedActiveSubscriptionCount = math.max( + 0, + _pausedActiveSubscriptionCount - 1, + ); + }); + } + + /// A life-cycle called by subclasses of [ProviderElement] when + /// their provider's `build` method completes. + void _didCompleteInitialization() { + // Close inactive subscriptions + if (_inactiveSubscriptions case final subs?) { + _closeSubscriptions(subs); + _inactiveSubscriptions = null; + } + } + + /// Life-cycle for when a listener is removed. + void mayNeedDispose() { + if (provider.isAutoDispose) { + final links = ref?._keepAliveLinks; + + if (!isActive && (links == null || links.isEmpty)) { + container.scheduler.scheduleProviderDispose(this); + } + } + } + + void _closeSubscriptions(List subscriptions) { + final subs = subscriptions.toList(); + for (var i = 0; i < subs.length; i++) { + subs[i].close(); + } + } + + void _pauseSubscriptions(List subscriptions) { + for (var i = 0; i < subscriptions.length; i++) { + subscriptions[i].pause(); + } + } + + /// Executes the [Ref.onDispose] listeners previously registered, then clear + /// the list of listeners. + @protected + @visibleForOverriding + @mustCallSuper + void runOnDispose() { + final ref = this.ref; + if (ref == null || !ref._mounted) return; + + ref._mounted = false; + + _pendingRetryTimer?.cancel(); + _pendingRetryTimer = null; + + // Pause current subscriptions and queue them to be deleted upon the completion + // of the next rebuild. + if (subscriptions case final subs?) { + (_inactiveSubscriptions ??= []).addAll(subs); + _pauseSubscriptions(subs); + } + subscriptions = null; + + ref._onDisposeListeners?.forEach(runGuarded); + + for (final observer in container.observers) { + runUnaryGuarded(observer.didDisposeProvider, _currentObserverContext()); + } + + ref._keepAliveLinks = null; + ref._onDisposeListeners = null; + ref._onCancelListeners = null; + ref._onResumeListeners = null; + ref._onAddListeners = null; + ref._onRemoveListeners = null; + ref._onChangeSelfListeners = null; + ref._onErrorSelfListeners = null; + _didCancelOnce = false; + + assert( + ref._keepAliveLinks == null || ref._keepAliveLinks!.isEmpty, + 'Cannot call keepAlive() within onDispose listeners', + ); + } + + /// Clears the state of a [ProviderElement]. + /// + /// This is in-between [dispose] and [runOnDispose]. + /// It is used to clear the state of a provider, without unsubscribing + /// [weakDependents]. + @mustCallSuper + void clearState() { + runOnDispose(); + _didMount = false; + _stateResult = null; + ref = null; + + if (dependents case final subs?) { + _closeSubscriptions(subs); + dependents = null; + } + if (_inactiveSubscriptions case final subs?) { + _closeSubscriptions(subs); + _inactiveSubscriptions = null; + } + } + + /// Release the resources associated to this [ProviderElement]. + /// + /// This will be invoked when: + /// - the provider is using `autoDispose` and it is no-longer used. + /// - the associated [ProviderContainer] is disposed + /// + /// On the other hand, this life-cycle will not be executed when a provider + /// rebuilds. + /// + /// As opposed to [runOnDispose], this life-cycle is executed only once + /// for the lifetime of this element. + @protected + @mustCallSuper + void dispose() { + clearState(); + + _closeSubscriptions(weakDependents); + + visitListenables((notifier) { + notifier.dispose(); + }); + } + + @override + String toString() { + final buffer = StringBuffer('$runtimeType('); + + buffer.writeAll( + [ + switch (_stateResult) { + null => 'state: uninitialized', + ResultData(:final state) => 'state: $state', + ResultError(:final error, :final stackTrace) => + 'state: error $error\n$stackTrace', + }, + if (provider != origin) 'provider: $provider', + 'origin: $origin', + ], + ', ', + ); + + buffer.write(')'); + + return buffer.toString(); + } + + /// Visit the [$ProviderElement]s of providers that are listening to this element. + /// + /// A provider is considered as listening to this element if it either [Ref.watch] + /// or [Ref.listen] this element. + /// + /// This method does not guarantee that a dependency is visited only once. + /// If a provider both [Ref.watch] and [Ref.listen] an element, or if a provider + /// [Ref.listen] multiple times to an element, it may be visited multiple times. + void visitChildren( + void Function(ProviderElement element) elementVisitor, + ) { + void lookup(Iterable children) { + for (final child in children) { + switch (child.source) { + case final ProviderElement dependent: + elementVisitor(dependent); + case ProviderContainer(): + break; + } + } + } + + lookup(weakDependents); + if (dependents case final dependents?) lookup(dependents); + } + + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) {} + + /// Visit the [ProviderElement]s that this provider is listening to. + /// + /// A provider is considered as listening to this element if it either [Ref.watch] + /// or [Ref.listen] this element. + /// + /// This method does not guarantee that a provider is visited only once. + /// If this provider both [Ref.watch] and [Ref.listen] an element, or if it + /// [Ref.listen] multiple times to an element, that element may be visited multiple times. + void visitAncestors( + void Function(ProviderElement element) visitor, + ) { + final subscriptions = this.subscriptions; + if (subscriptions != null) { + for (var i = 0; i < subscriptions.length; i++) { + final sub = subscriptions[i]; + visitor(sub._listenedElement); + } + } + } +} diff --git a/packages/riverpod/lib/src/core/family.dart b/packages/riverpod/lib/src/core/family.dart new file mode 100644 index 000000000..63e48623a --- /dev/null +++ b/packages/riverpod/lib/src/core/family.dart @@ -0,0 +1,220 @@ +part of '../framework.dart'; + +/// A typedef representing the constructor of any classical provider. +@internal +typedef FunctionalProviderFactory< // + ProviderT, + CreatedT, + ArgT> + = ProviderT Function( + Create create, { + required String? name, + required List? dependencies, + required List? allTransitiveDependencies, + required bool isAutoDispose, + required Family from, + required ArgT argument, + required Retry? retry, +}); + +/// A typedef representing the constructor of a [NotifierProvider]. +@internal +typedef ClassProviderFactory< // + NotifierT, + ProviderT, + CreatedT, + ArgT> + = ProviderT Function( + NotifierT Function() create, { + required String? name, + required Iterable? dependencies, + required Iterable? allTransitiveDependencies, + required RunNotifierBuild? runNotifierBuildOverride, + required bool isAutoDispose, + required Family from, + required ArgT argument, + required Retry? retry, +}); + +/// A [Create] equivalent used by [Family]. +@internal +typedef FamilyCreate = CreatedT Function(Ref ref, ArgT arg); + +/// A base class for all families +abstract class Family extends ProviderOrFamily implements _FamilyOverride { + /// A base class for all families + const Family({ + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.isAutoDispose, + required super.retry, + }); + + @override + Family get from => this; +} + +/// Setup how a family is overridden +@internal +typedef SetupFamilyOverride = void Function( + Arg argument, + void Function({ + required ProviderBase origin, + required ProviderBase override, + }), +); + +/// A base implementation for [Family], used by the various providers to +/// help them define a [Family]. +/// +/// This API is not meant for public consumption. +@internal +class FunctionalFamily< // + StateT, + ArgT, + CreatedT, + ProviderT extends $FunctionalProvider> extends Family { + /// A base implementation for [Family], used by the various providers to + /// help them define a [Family]. + /// + /// This API is not meant for public consumption. + const FunctionalFamily( + this._createFn, { + required FunctionalProviderFactory + providerFactory, + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.isAutoDispose, + required super.retry, + }) : _providerFactory = providerFactory; + + final FunctionalProviderFactory _providerFactory; + + final CreatedT Function(Ref ref, ArgT arg) _createFn; + + /// {@template family.call} + /// Create a provider from an external value. + /// + /// That external value should be immutable and preferably override `==`/`hashCode`. + /// See the documentation of [Provider.family] for more information. + /// {@endtemplate} + ProviderT call(ArgT argument) { + return _providerFactory( + (ref) => _createFn(ref, argument), + name: name, + isAutoDispose: isAutoDispose, + from: this, + argument: argument, + dependencies: null, + allTransitiveDependencies: null, + retry: retry, + ); + } + + @override + String? debugGetCreateSourceHash() => null; + + /// {@macro riverpod.override_with} + Override overrideWith( + CreatedT Function(Ref ref, ArgT arg) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ProviderT; + + return provider + .$copyWithCreate((ref) => create(ref, provider.argument as ArgT)) + .$createElement(pointer); + }, + ); + } + + @override + String toString() => name ?? describeIdentity(this); +} + +/// A base implementation for [Family] specific to `Notifier`-based providers. +/// +/// It offers a unique "create" function which does not take any argument. +/// +/// This API is not meant for public consumption. +@internal +class ClassFamily< // + NotifierT extends NotifierBase< // + StateT, + CreatedT>, + StateT, + ArgT, + CreatedT, + ProviderT extends $ClassProvider> + extends Family { + /// A base implementation for [Family], used by the various providers to + /// help them define a [Family]. + /// + /// This API is not meant for public consumption. + const ClassFamily( + this._createFn, { + required this.providerFactory, + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.isAutoDispose, + required super.retry, + }); + + @internal + final ClassProviderFactory + providerFactory; + + final NotifierT Function() _createFn; + + /// {@macro family.call} + ProviderT call(ArgT argument) { + return providerFactory( + _createFn, + name: name, + isAutoDispose: isAutoDispose, + from: this, + retry: retry, + argument: argument, + dependencies: null, + allTransitiveDependencies: null, + runNotifierBuildOverride: null, + ); + } + + @override + String? debugGetCreateSourceHash() => null; + + /// {@macro riverpod.override_with} + Override overrideWith(NotifierT Function() create) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ProviderT; + + return provider.$copyWithCreate(create).$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with} + Override overrideWithBuild( + RunNotifierBuild build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ProviderT; + + return provider.$copyWithBuild(build).$createElement(pointer); + }, + ); + } + + @override + String toString() => name ?? describeIdentity(this); +} diff --git a/packages/riverpod/lib/src/framework/foundation.dart b/packages/riverpod/lib/src/core/foundation.dart similarity index 75% rename from packages/riverpod/lib/src/framework/foundation.dart rename to packages/riverpod/lib/src/core/foundation.dart index 9882cb229..89c7abd9c 100644 --- a/packages/riverpod/lib/src/framework/foundation.dart +++ b/packages/riverpod/lib/src/core/foundation.dart @@ -1,17 +1,21 @@ part of '../framework.dart'; +/// A shared interface between [ProviderListenable] and [Family]. +abstract class ProviderListenableOrFamily {} + /// A common interface shared by [ProviderBase] and [Family] -@sealed -abstract class ProviderOrFamily implements ProviderListenableOrFamily { +sealed class ProviderOrFamily implements ProviderListenableOrFamily { /// A common interface shared by [ProviderBase] and [Family] const ProviderOrFamily({ required this.name, required this.dependencies, required this.allTransitiveDependencies, + required this.isAutoDispose, + required this.retry, }); /// The family that this provider/family depends on. - Family? get from; + Family? get from; /// {@template riverpod.name} /// A custom label for providers. @@ -20,6 +24,17 @@ abstract class ProviderOrFamily implements ProviderListenableOrFamily { /// {@endtemplate} final String? name; + /// {@template riverpod.retry} + /// The default retry logic used by providers associated to this container. + /// + /// The default implementation: + /// - has unlimited retries + /// - starts with a delay of 200ms + /// - doubles the delay on each retry up to 6.4 seconds + /// - retries all failures + /// {@endtemplate} + final Retry? retry; + /// The list of providers that this provider potentially depends on. /// /// Specifying this list is strictly equivalent to saying "This provider may @@ -84,10 +99,36 @@ abstract class ProviderOrFamily implements ProviderListenableOrFamily { /// All the dependencies of a provider and their dependencies too. final Iterable? allTransitiveDependencies; + + /// Whether the state associated to this provider should be disposed + /// automatically when the provider stops being listened. + final bool isAutoDispose; + + /// A debug-only function for obtaining a hash of the source code of the + /// initialization function. + /// + /// If after a hot-reload this function returns a different result, the + /// provider will be re-executed. + /// + /// This method only returns a non-null value when using `riverpod_generator`. + // This is voluntarily not implemented by default, to force all non-generated + // providers to apply the LegacyProviderMixin. + @internal + String? debugGetCreateSourceHash(); } -/// A shared interface between [ProviderListenable] and [Family]. -abstract class ProviderListenableOrFamily {} +extension on ProviderListenableOrFamily { + ProviderBase? get debugListenedProvider { + final that = this; + return switch (that) { + ProviderBase() => that, + _ProviderSelector() => that.provider.debugListenedProvider, + _AsyncSelector() => that.provider.debugListenedProvider, + ProviderElementProxy() => that.provider, + _ => null, + }; + } +} /// Computes the list of all dependencies of a provider. @internal @@ -123,16 +164,41 @@ Set? computeAllTransitiveDependencies( /// distinguish instances of the same class (hash collisions are /// possible, but rare enough that its use in debug output is useful). /// * [Object.runtimeType], the [Type] of an object. +@internal String describeIdentity(Object? object) { return '${object.runtimeType}#${shortHash(object)}'; } // Copied from Flutter /// [Object.hashCode]'s 20 least-significant bits. +@internal String shortHash(Object? object) { return object.hashCode.toUnsigned(20).toRadixString(16).padLeft(5, '0'); } +@internal +mixin ProviderListenableWithOrigin on ProviderListenable { + @override + ProviderSubscriptionWithOrigin addListener( + Node source, + void Function(OutT? previous, OutT next) listener, { + required void Function(Object error, StackTrace stackTrace)? onError, + required void Function()? onDependencyMayHaveChanged, + required bool fireImmediately, + required bool weak, + }); + + @override + ProviderListenable select( + Selected Function(OutT value) selector, + ) { + return _ProviderSelector( + provider: this, + selector: selector, + ); + } +} + /// A base class for all providers, used to consume a provider. /// /// It is used by [ProviderContainer.listen] and `ref.watch` to listen to @@ -140,19 +206,17 @@ String shortHash(Object? object) { /// /// Should override ==/hashCode when possible @immutable -mixin ProviderListenable implements ProviderListenableOrFamily { +mixin ProviderListenable implements ProviderListenableOrFamily { /// Starts listening to this transformer - ProviderSubscription addListener( - Node node, - void Function(State? previous, State next) listener, { + ProviderSubscription addListener( + Node source, + void Function(StateT? previous, StateT next) listener, { required void Function(Object error, StackTrace stackTrace)? onError, required void Function()? onDependencyMayHaveChanged, required bool fireImmediately, + required bool weak, }); - /// Obtains the result of this provider expression without adding listener. - State read(Node node); - /// Partially listen to a provider. /// /// The [select] function allows filtering unwanted rebuilds of a Widget @@ -216,56 +280,9 @@ mixin ProviderListenable implements ProviderListenableOrFamily { /// } /// ``` /// - /// This will further optimise our widget by rebuilding it only when "isAdult" + /// This will further optimize our widget by rebuilding it only when "isAdult" /// changed instead of whenever the age changes. ProviderListenable select( - Selected Function(State value) selector, - ) { - return _ProviderSelector( - provider: this, - selector: selector, - ); - } -} - -/// Represents the subscription to a [ProviderListenable] -@optionalTypeArgs -abstract class ProviderSubscription { - /// Represents the subscription to a [ProviderListenable] - ProviderSubscription(this.source) { - final Object listener = source; - if (listener is ProviderElementBase) { - final subs = listener._subscriptions ??= []; - subs.add(this); - } - } - - /// The object that listens to the associated [ProviderListenable]. - /// - /// This is typically a [ProviderElementBase] or a [ProviderContainer], - /// but may be other values in the future. - final Node source; - - /// Whether the subscription is closed. - bool get closed => _closed; - var _closed = false; - - /// Obtain the latest value emitted by the provider. - /// - /// This method throws if [closed] is true. - State read(); - - /// Stops listening to the provider. - /// - /// It is safe to call this method multiple times. - @mustCallSuper - void close() { - if (_closed) return; - _closed = true; - - final Object listener = source; - if (listener is ProviderElementBase) { - listener._subscriptions?.remove(this); - } - } + Selected Function(StateT value) selector, + ); } diff --git a/packages/riverpod/lib/src/core/modifiers/future.dart b/packages/riverpod/lib/src/core/modifiers/future.dart new file mode 100644 index 000000000..341e6eb3e --- /dev/null +++ b/packages/riverpod/lib/src/core/modifiers/future.dart @@ -0,0 +1,555 @@ +part of '../../framework.dart'; + +/// Internal typedef for cancelling the subscription to an async operation +@internal +typedef AsyncSubscription = ({ + /// The provider was disposed, but may rebuild later + void Function() cancel, + void Function()? pause, + void Function()? resume, + + /// The provider was disposed + void Function()? abort, +}); + +/// Implementation detail of `riverpod_generator`. +/// Do not use. +mixin $AsyncClassModifier + on NotifierBase, CreatedT> { + /// 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 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 [AsyncNotifier.build] to be re-executed. + @visibleForTesting + @protected + @override + AsyncValue get state; + + @visibleForTesting + @protected + @override + set state(AsyncValue newState); + + /// {@template riverpod.async_notifier.future} + /// Obtains a [Future] that resolves with the first [state] value that is not + /// [AsyncLoading]. + /// + /// This future will not necessarily wait for [AsyncNotifier.build] to complete. + /// If [state] is modified before [AsyncNotifier.build] completes, then [future] + /// will resolve with that new [state] value. + /// + /// 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} + @visibleForTesting + @protected + Future get future { + final ref = _ref; + if (ref == null) { + throw StateError(uninitializedElementError); + } + + ref._throwIfInvalidUsage(); + + final element = ref._element; + element as FutureModifierElement; + + element.flush(); + return element.futureNotifier.value; + } + + /// 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. + @visibleForTesting + @protected + Future update( + FutureOr Function(StateT previousState) cb, { + FutureOr Function(Object err, StackTrace stackTrace)? onError, + }) async { + final newState = await future.then(cb, onError: onError); + state = AsyncData(newState); + return newState; + } + + /// A method invoked when the state exposed by this [AsyncNotifier] changes. + /// + /// 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. + /// + /// 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. + /// + /// See also: + /// - [ProviderBase.select] and [$FutureModifier.selectAsync], which are + /// alternative ways to filter out changes to [state]. + @override + @protected + bool updateShouldNotify( + AsyncValue previous, + AsyncValue next, + ) { + return FutureModifierElement.handleUpdateShouldNotify( + previous, + next, + ); + } +} + +/// Implementation detail for `riverpod_generator`. +/// Do not use. +base mixin $FutureModifier on ProviderBase> { + /// Obtains the [Future] representing this provider. + /// + /// The instance of [Future] obtained may change over time. This typically + /// happens when a new "data" or "error" is emitted, or when the provider + /// re-enters a "loading" state. + /// + /// This modifier enables using `async`/`await` to easily combine + /// providers together: + /// + /// ```dart + /// final configsProvider = FutureProvider((ref) async => Configs()); + /// + /// final productsProvider = FutureProvider((ref) async { + /// // Wait for the configurations to resolve + /// final configs = await ref.watch(configsProvider.future); + /// + /// // Do something with the result + /// return await http.get('${configs.host}/products'); + /// }); + /// ``` + Refreshable> get future => _future; + + _ProviderRefreshable, AsyncValue> get _future { + return ProviderElementProxy, AsyncValue>( + this, + (element) { + element as FutureModifierElement; + + return element.futureNotifier; + }, + ); + } + + /// {@template riverpod.async_select} + /// A variant of [select] for asynchronous values + /// + /// [selectAsync] is useful for filtering rebuilds of a provider + /// when it depends on asynchronous values, which we want to await. + /// + /// A common use-case would be to combine [selectAsync] with + /// [FutureProvider] to perform an async operation, where that + /// async operation depends on the result of another async operation. + /// + /// + /// ```dart + /// // A provider which asynchronously loads configurations, + /// // which may change over time. + /// final configsProvider = StreamProvider((ref) async { + /// // TO-DO fetch the configurations, such as by using Firebase + /// }); + /// + /// // A provider which fetches a list of products based on the configurations + /// final productsProvider = FutureProvider((ref) async { + /// // We obtain the host from the configs, while ignoring changes to + /// // other properties. As such, the productsProvider will rebuild only + /// // if the host changes + /// final host = await ref.watch(configsProvider.selectAsync((config) => config.host)); + /// + /// return http.get('$host/products'); + /// }); + /// ``` + /// {@endtemplate} + ProviderListenable> selectAsync( + Output Function(StateT data) selector, + ) { + return _AsyncSelector>( + selector: selector, + provider: this, + future: _future, + ); + } +} + +mixin FutureModifierClassElement< + NotifierT extends NotifierBase< // + AsyncValue, + CreatedT>, + StateT, + CreatedT> + on + FutureModifierElement, + ClassProviderElement, CreatedT> { + @override + void handleNotifier(Object? notifier, {required bool seamless}) { + asyncTransition(AsyncLoading(), seamless: seamless); + } + + @override + void handleError( + Object error, + StackTrace stackTrace, { + required bool seamless, + }) { + triggerRetry(error); + onError(AsyncError(error, stackTrace), seamless: seamless); + } +} + +/// Mixin to help implement logic for listening to [Future]s/[Stream]s and setup +/// `provider.future` + convert the object into an [AsyncValue]. +@internal +mixin FutureModifierElement on ProviderElement> { + /// A default implementation for [ProviderElement.updateShouldNotify]. + static bool handleUpdateShouldNotify( + AsyncValue previous, + AsyncValue next, + ) { + final wasLoading = previous.isLoading; + final isLoading = next.isLoading; + + if (wasLoading || isLoading) return wasLoading != isLoading; + + return true; + } + + /// An observable for [FutureProvider.future]. + @internal + final futureNotifier = $ElementLense>(); + Completer? _futureCompleter; + Future? _lastFuture; + AsyncSubscription? _cancelSubscription; + + /// Internal utility for transitioning an [AsyncValue] after a provider refresh. + /// + /// [seamless] controls how the previous state is preserved: + /// - seamless:true => import previous state and skip loading + /// - seamless:false => import previous state and prefer loading + void asyncTransition( + AsyncValue newState, { + required bool seamless, + }) { + final previous = stateResult?.requireState; + + if (previous == null) { + super.setStateResult(ResultData(newState)); + } else { + super.setStateResult( + ResultData( + newState + .cast() + .copyWithPrevious(previous, isRefresh: seamless), + ), + ); + } + } + + @override + @protected + void setStateResult($Result> newState) { + newState.requireState.map( + loading: onLoading, + error: onError, + data: onData, + ); + } + + @internal + void onLoading(AsyncLoading value, {bool seamless = false}) { + asyncTransition(value, seamless: seamless); + if (_futureCompleter == null) { + final completer = _futureCompleter = Completer(); + futureNotifier.result = ResultData(completer.future); + } + } + + /// Life-cycle for when an error from the provider's "build" method is received. + /// + /// Might be invoked after the element is disposed in the case where `provider.future` + /// has yet to complete. + @internal + void onError(AsyncError value, {bool seamless = false}) { + asyncTransition(value, seamless: seamless); + + for (final observer in container.observers) { + runTernaryGuarded( + observer.providerDidFail, + _currentObserverContext(), + value.error, + value.stackTrace, + ); + } + + final completer = _futureCompleter; + if (completer != null) { + completer + ..future.ignore() + ..completeError( + value.error, + value.stackTrace, + ); + _futureCompleter = null; + } else { + futureNotifier.result = $Result.data( + Future.error( + value.error, + value.stackTrace, + )..ignore(), + ); + } + } + + /// Life-cycle for when a data from the provider's "build" method is received. + /// + /// Might be invoked after the element is disposed in the case where `provider.future` + /// has yet to complete. + @internal + void onData(AsyncData value, {bool seamless = false}) { + asyncTransition(value, seamless: seamless); + + final completer = _futureCompleter; + if (completer != null) { + completer.complete(value.value); + _futureCompleter = null; + } else { + futureNotifier.result = $Result.data(Future.value(value.value)); + } + } + + /// Listens to a [Stream] and convert it into an [AsyncValue]. + @preferInline + @internal + WhenComplete handleStream( + Stream Function() create, { + required bool seamless, + }) { + return _handleAsync(seamless: seamless, ({ + required data, + required done, + required error, + required last, + }) { + final stream = create(); + + return stream.listenAndTrackLast( + last, + lastOrElseError: _missingLastValueError, + onData: data, + onError: error, + onDone: done, + ); + }); + } + + @override + void onCancel() { + super.onCancel(); + + _cancelSubscription?.pause?.call(); + } + + @override + void onResume() { + super.onResume(); + + _cancelSubscription?.resume?.call(); + } + + StateError _missingLastValueError() { + return StateError( + 'The provider $origin was disposed during loading state, ' + 'yet no value could be emitted.', + ); + } + + /// Listens to a [Future] and convert it into an [AsyncValue]. + @preferInline + @internal + WhenComplete handleFuture( + FutureOr Function() create, { + required bool seamless, + }) { + return _handleAsync(seamless: seamless, ({ + required data, + required done, + required error, + required last, + }) { + final futureOr = create(); + if (futureOr is! Future) { + data(futureOr); + done(); + return null; + } + // Received a Future + + var running = true; + void cancel() { + running = false; + } + + futureOr.then( + (value) { + if (!running) return; + data(value); + done(); + }, + // ignore: avoid_types_on_closure_parameters + onError: (Object err, StackTrace stackTrace) { + if (!running) return; + error(err, stackTrace); + done(); + }, + ); + + last(futureOr); + + return ( + cancel: cancel, + // We don't call `cancel` here to let `provider.future` resolve with + // the last value emitted by the future. + abort: null, + pause: null, + resume: null, + ); + }); + } + + /// Listens to a [Future] and transforms it into an [AsyncValue]. + WhenComplete _handleAsync( + AsyncSubscription? Function({ + required void Function(StateT) data, + required void Function(Object, StackTrace) error, + required void Function() done, + required void Function(Future) last, + }) listen, { + required bool seamless, + }) { + onLoading(AsyncLoading(), seamless: seamless); + + void callOnError(Object error, StackTrace stackTrace) { + triggerRetry(error); + onError(AsyncError(error, stackTrace), seamless: seamless); + } + + void Function()? onDone; + var isDone = false; + + try { + _cancelSubscription = listen( + data: (value) { + onData(AsyncData(value), seamless: seamless); + }, + error: callOnError, + last: (last) { + assert(_lastFuture == null, 'bad state'); + _lastFuture = last; + }, + done: () { + _lastFuture = null; + isDone = true; + onDone?.call(); + }, + ); + } catch (error, stackTrace) { + callOnError(error, stackTrace); + } + + return (onDoneCb) { + onDone = onDoneCb; + // Handle synchronous completion + if (isDone) onDoneCb(); + }; + } + + @override + @internal + void runOnDispose() { + // Stops listening to the previous async operation + _lastFuture = null; + _cancelSubscription?.cancel(); + _cancelSubscription = null; + super.runOnDispose(); + } + + @override + void dispose() { + final completer = _futureCompleter; + if (completer != null) { + // Whatever happens after this, the error is emitted post dispose of the provider. + // So the error doesn't matter anymore. + completer.future.ignore(); + + final lastFuture = _lastFuture; + if (lastFuture != null) { + _cancelSubscription?.abort?.call(); + + // Prevent super.dispose from cancelling the subscription on the "last" + // stream value, so that it can be sent to `provider.future`. + _lastFuture = null; + _cancelSubscription = null; + } else { + // The listened stream completed during a "loading" state. + completer.completeError( + _missingLastValueError(), + StackTrace.current, + ); + } + } + super.dispose(); + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + listenableVisitor(futureNotifier); + } +} + +extension on Stream { + AsyncSubscription listenAndTrackLast( + void Function(Future) last, { + required Object Function() lastOrElseError, + required void Function(T event) onData, + required void Function(Object error, StackTrace stackTrace) onError, + required void Function() onDone, + }) { + late StreamSubscription subscription; + subscription = listen(onData, onError: onError, onDone: onDone); + + final asyncSub = ( + cancel: subscription.cancel, + pause: subscription.pause, + resume: subscription.resume, + abort: subscription.cancel, + ); + + return asyncSub; + } +} diff --git a/packages/riverpod/lib/src/core/modifiers/select.dart b/packages/riverpod/lib/src/core/modifiers/select.dart new file mode 100644 index 000000000..daac3a3b5 --- /dev/null +++ b/packages/riverpod/lib/src/core/modifiers/select.dart @@ -0,0 +1,146 @@ +part of '../../framework.dart'; + +/// An abstraction of both [ProviderContainer] and [$ProviderElement] used by +/// [ProviderListenable]. +sealed class Node { + // /// Starts listening to this transformer + // ProviderSubscription listen( + // ProviderListenable listenable, + // void Function(StateT? previous, StateT next) listener, { + // required void Function(Object error, StackTrace stackTrace)? onError, + // required bool fireImmediately, + // required bool weak, + // }); + + /// Obtain the [ProviderElement] of a provider, creating it if necessary. + @internal + ProviderElement readProviderElement( + ProviderBase provider, + ); +} + +var _debugIsRunningSelector = false; + +/// An internal class for `ProviderBase.select`. +@sealed +class _ProviderSelector + with + ProviderListenable, + ProviderListenableWithOrigin { + /// An internal class for `ProviderBase.select`. + _ProviderSelector({ + required this.provider, + required this.selector, + }); + + /// The provider that was selected + final ProviderListenableWithOrigin provider; + + /// The selector applied + final OutputT Function(InputT) selector; + + $Result _select($Result value) { + if (kDebugMode) _debugIsRunningSelector = true; + + try { + return switch (value) { + ResultData(:final state) => $Result.data(selector(state)), + ResultError(:final error, :final stackTrace) => + $Result.error(error, stackTrace), + }; + } catch (err, stack) { + return $Result.error(err, stack); + } finally { + if (kDebugMode) _debugIsRunningSelector = false; + } + } + + void _selectOnChange({ + required InputT newState, + required $Result? lastSelectedValue, + required void Function(Object error, StackTrace stackTrace) onError, + required void Function(OutputT? prev, OutputT next) listener, + required void Function($Result newState) onChange, + }) { + final newSelectedValue = _select($Result.data(newState)); + if (lastSelectedValue == null || + !lastSelectedValue.hasState || + !newSelectedValue.hasState || + lastSelectedValue.requireState != newSelectedValue.requireState) { + onChange(newSelectedValue); + switch (newSelectedValue) { + case ResultData(:final state): + listener( + lastSelectedValue?.stateOrNull, + state, + ); + case ResultError(:final error, :final stackTrace): + onError(error, stackTrace); + } + } + } + + @override + ProviderSubscriptionWithOrigin addListener( + Node node, + void Function(OutputT? previous, OutputT next) listener, { + required void Function(Object error, StackTrace stackTrace)? onError, + required void Function()? onDependencyMayHaveChanged, + required bool fireImmediately, + required bool weak, + }) { + onError ??= Zone.current.handleUncaughtError; + + late final ProviderSubscriptionView providerSub; + $Result? lastSelectedValue; + final sub = provider.addListener( + node, + (prev, input) { + _selectOnChange( + newState: input, + lastSelectedValue: lastSelectedValue, + listener: providerSub._notifyData, + onError: providerSub._notifyError, + onChange: (newState) => lastSelectedValue = newState, + ); + }, + fireImmediately: false, + weak: weak, + onDependencyMayHaveChanged: onDependencyMayHaveChanged, + onError: onError, + ); + + if (!weak) lastSelectedValue = _select($Result.guard(sub.read)); + + providerSub = ProviderSubscriptionView( + innerSubscription: sub, + listener: listener, + onError: onError, + read: () { + // flushes the provider + sub.read(); + + // Using ! because since `sub.read` flushes the inner subscription, + // it is guaranteed that `lastSelectedValue` is not null. + return switch (lastSelectedValue!) { + ResultData(:final state) => state, + ResultError(:final error, :final stackTrace) => + throwErrorWithCombinedStackTrace( + error, + stackTrace, + ), + }; + }, + ); + + if (fireImmediately) { + _handleFireImmediately( + lastSelectedValue!, + listener: providerSub._notifyData, + onError: providerSub._notifyError, + ); + } + + return providerSub; + } +} diff --git a/packages/riverpod/lib/src/core/modifiers/select_async.dart b/packages/riverpod/lib/src/core/modifiers/select_async.dart new file mode 100644 index 000000000..56e7e5c9c --- /dev/null +++ b/packages/riverpod/lib/src/core/modifiers/select_async.dart @@ -0,0 +1,189 @@ +part of '../../framework.dart'; + +/// An internal class for `ProviderBase.selectAsync`. +@sealed +class _AsyncSelector + with + ProviderListenable>, + ProviderListenableWithOrigin, OriginT> { + /// An internal class for `ProviderBase.select`. + _AsyncSelector({ + required this.provider, + required this.future, + required this.selector, + }); + + /// The provider that was selected + final ProviderListenableWithOrigin, OriginT> provider; + + /// The future associated to the listened provider + final ProviderListenableWithOrigin, OriginT> future; + + /// The selector applied + final OutputT Function(InputT) selector; + + $Result _select(InputT value) { + if (kDebugMode) _debugIsRunningSelector = true; + + try { + return $Result.data(selector(value)); + } catch (err, stack) { + return $Result.error(err, stack); + } finally { + if (kDebugMode) _debugIsRunningSelector = false; + } + } + + @override + ProviderSubscriptionWithOrigin, OriginT> addListener( + Node node, + void Function(Future? previous, Future next) listener, { + required void Function(Object error, StackTrace stackTrace)? onError, + required void Function()? onDependencyMayHaveChanged, + required bool fireImmediately, + required bool weak, + }) { + late final ProviderSubscriptionView, OriginT> providerSub; + + $Result? lastSelectedValue; + Completer? selectedCompleter; + Future? selectedFuture; + + void emitData(OutputT data, {required bool callListeners}) { + final previousFuture = selectedFuture; + if (selectedCompleter != null) { + selectedCompleter!.complete(data); + selectedCompleter = null; + } else { + selectedFuture = Future.value(data); + if (callListeners) { + providerSub._notifyData(previousFuture, selectedFuture!); + } + } + } + + void emitError( + Object err, + StackTrace? stack, { + required bool callListeners, + }) { + final previousFuture = selectedFuture; + if (selectedCompleter != null) { + selectedCompleter!.completeError(err, stack); + selectedCompleter = null; + } else { + selectedFuture = Future.error(err, stack); + if (callListeners) { + providerSub._notifyData(previousFuture, selectedFuture!); + } + } + } + + void playValue( + AsyncValue value, { + bool callListeners = true, + }) { + void onLoading(AsyncValue loading) { + if (selectedFuture == null) { + // The first time a future is emitted + + selectedCompleter = Completer(); + selectedFuture = selectedCompleter!.future; + } + + // We don't notify listeners when the future changes since + // they want to filter rebuilds based on the result + } + + value.map( + loading: onLoading, + data: (value) { + if (value.isRefreshing) { + onLoading(value); + return; + } + + final newSelectedValue = _select(value.value); + switch (newSelectedValue) { + case ResultData(): + if (newSelectedValue != lastSelectedValue) { + emitData( + newSelectedValue.state, + callListeners: callListeners, + ); + } + case ResultError(): + emitError( + newSelectedValue.error, + newSelectedValue.stackTrace, + callListeners: callListeners, + ); + } + + lastSelectedValue = newSelectedValue; + }, + error: (value) { + if (value.isRefreshing) { + onLoading(value); + return; + } + + emitError( + value.error, + value.stackTrace, + callListeners: callListeners, + ); + + // Error in the provider, it should've already been propagated + // so no need to pollute the stack + selectedFuture!.ignore(); + }, + ); + } + + final sub = provider.addListener( + node, + (prev, input) => playValue(input), + weak: weak, + onDependencyMayHaveChanged: onDependencyMayHaveChanged, + onError: onError, + fireImmediately: false, + ); + + playValue(sub.read(), callListeners: false); + + if (fireImmediately) { + listener(null, selectedFuture!); + } + + return providerSub = ProviderSubscriptionView, OriginT>( + innerSubscription: sub, + listener: listener, + onError: onError, + read: () { + // Flush + sub.read(); + return selectedFuture!; + }, + onClose: () { + final completer = selectedCompleter; + if (completer != null && !completer.isCompleted) { + final sub = future.addListener( + node, + (prev, next) {}, + weak: weak, + onDependencyMayHaveChanged: () {}, + onError: onError, + fireImmediately: false, + ); + + sub + .read() + .then((v) => _select(v).requireState) + .then(completer.complete, onError: completer.completeError) + .whenComplete(sub.close); + } + }, + ); + } +} diff --git a/packages/riverpod/lib/src/core/override.dart b/packages/riverpod/lib/src/core/override.dart new file mode 100644 index 000000000..6b7e862bd --- /dev/null +++ b/packages/riverpod/lib/src/core/override.dart @@ -0,0 +1,152 @@ +part of '../framework.dart'; + +/// An object used by [ProviderContainer]/`ProviderScope` to override the behavior +/// of a provider/family for part of the application. +/// +/// Do not extend or implement. +sealed class Override {} + +sealed class _ProviderOverride implements Override {} + +extension on _ProviderOverride { + /// The provider that is overridden. + ProviderBase get origin { + final that = this; + return switch (that) { + ProviderBase() => that, + $ProviderOverride() => that.origin, + }; + } + + /// The new provider behavior. + ProviderBase get providerOverride { + final that = this; + return switch (that) { + ProviderBase() => that, + $ProviderOverride() => that.providerOverride, + }; + } +} + +sealed class _FamilyOverride implements Override {} + +extension on _FamilyOverride { + /// The provider that is overridden. + Family get from { + final that = this; + return switch (that) { + Family() => that, + $FamilyOverride() => that.from, + }; + } +} + +/// An object used by [ProviderContainer] to override the behavior of a provider +/// for a part of the application. +/// +/// Do not use. +/// +/// See also: +/// +/// - [ProviderContainer], which uses this object. +/// - `overrideWithValue`, which creates a [$ProviderOverride]. +class $ProviderOverride implements _ProviderOverride { + /// Override a provider + $ProviderOverride({ + required this.origin, + required this.providerOverride, + }); + + /// The provider that is overridden. + final ProviderBase origin; + + /// The new provider behavior. + final ProviderBase providerOverride; + + @mustBeOverridden + @override + String toString() { + switch (providerOverride) { + case $ValueProvider(:final _value): + return '$origin.overrideWithValue($_value)'; + default: + return '$origin.overrideWith(...)'; + } + } +} + +/// When a provider is automatically scoped due to specifying `dependencies`. +@internal +class TransitiveProviderOverride implements $ProviderOverride { + TransitiveProviderOverride(this.origin); + + @override + final ProviderBase origin; + + @override + ProviderBase get providerOverride => origin; + + @override + String toString() => '$origin'; +} + +/// Do not use: Internal object to used by [ProviderContainer]/`ProviderScope` +/// to override the behavior of a "family" for part of the application. +abstract class $FamilyOverride implements _FamilyOverride { + /// Do not use: Internal object to used by [ProviderContainer]/`ProviderScope` + /// to override the behavior of a "family" for part of the application. + factory $FamilyOverride({ + required ProviderElement Function($ProviderPointer pointer) createElement, + required Family from, + }) = _FamilyOverrideImpl; + + /// The family that was overridden. + Family get from; + + /// The overridden [ProviderBase.$createElement]. + ProviderElement createElement($ProviderPointer pointer); + + @mustBeOverridden + @override + String toString(); +} + +@internal +class TransitiveFamilyOverride implements $FamilyOverride { + TransitiveFamilyOverride(this.from); + + @override + final Family from; + + @override + ProviderElement createElement($ProviderPointer pointer) { + return pointer.origin.$createElement(pointer); + } + + @override + String toString() => '$from'; +} + +/// An [Override] for families +class _FamilyOverrideImpl implements $FamilyOverride { + /// An [Override] for families + // ignore: library_private_types_in_public_api + _FamilyOverrideImpl({ + required ProviderElement Function($ProviderPointer pointer) createElement, + required this.from, + }) : _createElement = createElement; + + @override + final Family from; + + final ProviderElement Function($ProviderPointer pointer) _createElement; + + @override + ProviderElement createElement($ProviderPointer pointer) { + return _createElement(pointer); + } + + @mustBeOverridden + @override + String toString() => '$from.overrideWith(...)'; +} diff --git a/packages/riverpod/lib/src/core/override_with_value.dart b/packages/riverpod/lib/src/core/override_with_value.dart new file mode 100644 index 000000000..b3c97272d --- /dev/null +++ b/packages/riverpod/lib/src/core/override_with_value.dart @@ -0,0 +1,117 @@ +part of '../framework.dart'; + +/// A provider that is driven by a value instead of a function. +/// +/// This is an implementation detail of `overrideWithValue`. +final class $ValueProvider extends ProviderBase + with LegacyProviderMixin { + /// Creates a [$ValueProvider]. + const $ValueProvider(this._value) + : super( + name: null, + from: null, + argument: null, + allTransitiveDependencies: null, + dependencies: null, + isAutoDispose: false, + retry: null, + ); + + final StateT _value; + + @override + Iterable? get dependencies => null; + + @override + Set? get allTransitiveDependencies => null; + + @internal + @override + // ignore: library_private_types_in_public_api, not public API + _ValueProviderElement $createElement($ProviderPointer pointer) { + return _ValueProviderElement(this, pointer); + } +} + +/// The [ProviderElement] of a [$ValueProvider] +class _ValueProviderElement extends ProviderElement { + /// The [ProviderElement] of a [$ValueProvider] + _ValueProviderElement(this.provider, super.pointer); + + /// A custom listener called when `overrideWithValue` changes + /// with a different value. + void Function(StateT value)? onChange; + + @override + $ValueProvider provider; + + @override + void update(covariant $ValueProvider newProvider) { + super.update(newProvider); + provider = newProvider; + final newValue = provider._value; + + // `getState` will never be in error/loading state since there is no "create" + final previousState = stateResult! as ResultData; + + if (newValue != previousState.state) { + // Asserts would otherwise prevent a provider rebuild from updating + // other providers + if (kDebugMode) _debugSkipNotifyListenersAsserts = true; + + _setValue(newValue); + + // Asserts would otherwise prevent a provider rebuild from updating + // other providers + if (kDebugMode) _debugSkipNotifyListenersAsserts = false; + + onChange?.call(newValue); + } + } + + void _setValue(StateT value) => setStateResult(ResultData(value)); + + @override + WhenComplete create(Ref ref, {required bool didChangeDependency}) { + _setValue(provider._value); + + return null; + } + + @override + bool updateShouldNotify(StateT previous, StateT next) { + return true; + } +} + +@internal +final class $AsyncValueProvider + extends $ValueProvider> { + const $AsyncValueProvider(super._value); + + @override + // ignore: library_private_types_in_public_api, not public API + _AsyncValueProviderElement $createElement( + $ProviderPointer pointer, + ) { + return _AsyncValueProviderElement(this, pointer); + } +} + +class _AsyncValueProviderElement + extends _ValueProviderElement> + with FutureModifierElement { + _AsyncValueProviderElement(super.provider, super.pointer); + + @override + void _setValue(AsyncValue value) { + switch (value) { + case AsyncData(): + onData(value, seamless: true); + case AsyncError(): + onError(value, seamless: true); + case AsyncLoading(): + onLoading(value, seamless: true); + } + } +} diff --git a/packages/riverpod/lib/src/core/provider/functional_provider.dart b/packages/riverpod/lib/src/core/provider/functional_provider.dart new file mode 100644 index 000000000..e34891e96 --- /dev/null +++ b/packages/riverpod/lib/src/core/provider/functional_provider.dart @@ -0,0 +1,69 @@ +part of '../../framework.dart'; + +/// Implementation detail of `riverpod_generator`. +/// Do not use, as this can be removed at any time. +abstract base class $FunctionalProvider< // + StateT, + CreatedT> // + extends ProviderBase { + /// Implementation detail of `riverpod_generator`. + /// Do not use, as this can be removed at any time. + const $FunctionalProvider({ + required super.name, + required super.from, + required super.argument, + required super.dependencies, + required super.allTransitiveDependencies, + required super.isAutoDispose, + required super.retry, + }); + + /// Clone a provider with a different initialization method. + /// + /// This is an implementation detail of Riverpod and should not be used. + @visibleForOverriding + $FunctionalProvider $copyWithCreate( + Create create, + ); + + /// {@template riverpod.override_with} + /// Override the provider with a new initialization function. + /// + /// This will also disable the auto-scoping mechanism, meaning that if the + /// overridden provider specified `dependencies`, it will have no effect. + /// + /// The override must not specify a `dependencies`. + /// + /// Some common use-cases are: + /// - testing, by replacing a service with a fake implementation, or to reach + /// a very specific state easily. + /// - multiple environments, by changing the implementation of a class + /// based on the platform or other parameters. + /// + /// This function should be used in combination with `ProviderScope.overrides` + /// or `ProviderContainer.overrides`: + /// + /// ```dart + /// final myService = Provider((ref) => MyService()); + /// + /// runApp( + /// ProviderScope( + /// overrides: [ + /// // Replace the implementation of the provider with a different one + /// myService.overrideWith((ref) { + /// ref.watch('other'); + /// return MyFakeService(), + /// })), + /// ], + /// child: MyApp(), + /// ), + /// ); + /// ``` + /// {@endtemplate} + Override overrideWith(Create create) { + return $ProviderOverride( + origin: this, + providerOverride: $copyWithCreate(create), + ); + } +} diff --git a/packages/riverpod/lib/src/core/provider/notifier_provider.dart b/packages/riverpod/lib/src/core/provider/notifier_provider.dart new file mode 100644 index 000000000..1be826c16 --- /dev/null +++ b/packages/riverpod/lib/src/core/provider/notifier_provider.dart @@ -0,0 +1,259 @@ +part of '../../framework.dart'; + +/// An error thrown if a Notifier is associated multiple times with a provider. +@internal +const alreadyInitializedError = ''' +A NotifierProvider returned a Notifier instance that is already associated +with another provider. + +To fix, do not reuse the same Notifier instance multiple times. +NotifierProviders are expected to always create a new Notifier instance. +'''; + +/// The error message for when a notifier is used when uninitialized. +@internal +const uninitializedElementError = ''' +Tried to use a notifier in an uninitialized state. +This means that you tried to either: +- Use ref/state inside the constructor of a notifier. + In this case you should move your logic inside the "build" method instead. +- Use ref/state after the notifier was disposed. + In this case, consider using `ref.onDispose` earlier in your notifier's lifecycle + to abort any pending logic that could try to use `ref/state`. +'''; + +/// The prototype of `Notifier.build` overrides. +@internal +typedef RunNotifierBuild = CreatedT Function( + Ref ref, + NotifierT notifier, +); + +/// A base class for all "notifiers". +/// +/// - [StateT] is the type of [state]. +/// - [CreatedT] is the type of the value returned by the notifier's `build` method. +/// +/// This is a good interface to target for writing mixins for Notifiers. +/// +/// To perform logic before/after the `build` method of a notifier, you can override +/// [runBuild]: +/// +/// ```dart +/// mixin MyMixin extends NotifierBase> { +/// @override +/// FutureOr runBuild() { +/// // It is safe to use "ref" here. +/// ref.listenSelf((prev, next) => print("New state $next")); +/// +/// // Before +/// final result = super.runBuild(); +/// // After +/// return result; +/// } +/// } +/// ``` +abstract class NotifierBase { + _Ref? _ref; + @protected + Ref get ref => $ref; + + /// Listens to changes on the value exposed by this provider. + /// + /// The listener will be called immediately after the provider completes building. + /// + /// As opposed to [Ref.listen], the listener will be called even if + /// [updateShouldNotify] returns false, meaning that the previous + /// and new value can potentially be identical. + /// + /// Returns a function which can be called to remove the listener. + @protected + RemoveListener listenSelf( + void Function(StateT? previous, StateT next) listener, { + void Function(Object error, StackTrace stackTrace)? onError, + }) { + return $ref.listenSelf(listener, onError: onError); + } + + @visibleForTesting + @protected + StateT get state => $ref.state; + + @visibleForTesting + @protected + set state(StateT newState) => $ref.state = newState; + + CreatedT runBuild(); + + @visibleForOverriding + bool updateShouldNotify(StateT previous, StateT next); +} + +@internal +extension ClassBaseX on NotifierBase { + ProviderElement? get element => _ref?._element; + + @internal + // ignore: library_private_types_in_public_api, not public + _Ref get $ref { + final ref = _ref; + if (ref == null) throw StateError(uninitializedElementError); + + return ref; + } +} + +/// Implementation detail of `riverpod_generator`. +/// Do not use. +abstract base class $ClassProvider< // + NotifierT extends NotifierBase< // + StateT, + CreatedT>, + StateT, + CreatedT> extends ProviderBase { + const $ClassProvider({ + required super.name, + required super.from, + required super.argument, + required super.dependencies, + required super.allTransitiveDependencies, + required super.isAutoDispose, + required super.retry, + required this.runNotifierBuildOverride, + }); + + Refreshable get notifier { + return ProviderElementProxy( + this, + (element) => + (element as ClassProviderElement) + .classListenable, + ); + } + + @internal + final RunNotifierBuild? runNotifierBuildOverride; + + @internal + NotifierT create(); + + @visibleForOverriding + $ClassProvider $copyWithCreate( + NotifierT Function() create, + ); + + @visibleForOverriding + $ClassProvider $copyWithBuild( + RunNotifierBuild build, + ); + + /// {@macro riverpod.override_with} + Override overrideWith(NotifierT Function() create) { + return $ProviderOverride( + origin: this, + providerOverride: $copyWithCreate(create), + ); + } + + /// {@template riverpod.override_with_build} + /// Hello world + /// {@endtemplate} + Override overrideWithBuild( + RunNotifierBuild build, + ) { + return $ProviderOverride( + origin: this, + providerOverride: $copyWithBuild(build), + ); + } + + @override + ClassProviderElement< // + NotifierT, + StateT, + CreatedT> $createElement($ProviderPointer pointer); +} + +@internal +abstract class ClassProviderElement< // + NotifierT extends NotifierBase< // + StateT, + CreatedT>, + StateT, + CreatedT> // + extends ProviderElement { + ClassProviderElement(super.pointer); + + @override + $ClassProvider get provider; + + final classListenable = $ElementLense(); + + @mustCallSuper + @override + WhenComplete create( + // ignore: library_private_types_in_public_api, not public + _Ref ref, { + required bool didChangeDependency, + }) { + final seamless = !didChangeDependency; + + final result = classListenable.result = $Result.guard(() { + final notifier = provider.create(); + if (notifier._ref != null) { + throw StateError(alreadyInitializedError); + } + + notifier._ref = ref; + return notifier; + }); + + switch (result) { + case ResultData(): + try { + handleNotifier(result.state, seamless: seamless); + + final created = + provider.runNotifierBuildOverride?.call(ref, result.state) ?? + result.state.runBuild(); + handleValue(created, seamless: seamless); + } catch (err, stack) { + handleError(err, stack, seamless: seamless); + } + case ResultError(): + handleError( + result.error, + result.stackTrace, + seamless: seamless, + ); + } + + return null; + } + + void handleNotifier(NotifierT notifier, {required bool seamless}) { + // Overridden by FutureModifier mixin + } + + void handleValue(CreatedT created, {required bool seamless}); + void handleError( + Object error, + StackTrace stackTrace, { + required bool seamless, + }); + + @override + bool updateShouldNotify(StateT previous, StateT next) { + return classListenable.result?.stateOrNull + ?.updateShouldNotify(previous, next) ?? + true; + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + listenableVisitor(classListenable); + } +} diff --git a/packages/riverpod/lib/src/core/provider/provider.dart b/packages/riverpod/lib/src/core/provider/provider.dart new file mode 100644 index 000000000..2540fd376 --- /dev/null +++ b/packages/riverpod/lib/src/core/provider/provider.dart @@ -0,0 +1,136 @@ +part of '../../framework.dart'; + +/// A callback used by providers to create the value exposed. +/// +/// If an exception is thrown within that callback, all attempts at reading +/// the provider associated with the given callback will throw. +/// +/// The parameter [ref] can be used to interact with other providers +/// and the life-cycles of this provider. +/// +/// See also: +/// +/// - [Ref], which exposes the methods to read other providers. +/// - [Provider], a provider that uses [Create] to expose an immutable value. +@internal +typedef Create = CreatedT Function(Ref ref); + +/// A callback used to catches errors +@internal +typedef OnError = void Function(Object error, StackTrace stackTrace); + +/// A base class for _all_ providers. +@immutable +// Marked as "base" because linters/generators rely on fields on const provider instances. +abstract base class ProviderBase extends ProviderOrFamily + with + ProviderListenable, + ProviderListenableWithOrigin + implements Refreshable, _ProviderOverride { + /// A base class for _all_ providers. + const ProviderBase({ + required super.name, + required this.from, + required this.argument, + required super.dependencies, + required super.allTransitiveDependencies, + required super.isAutoDispose, + required super.retry, + }) : assert( + from == null || allTransitiveDependencies == null, + 'When from a family, providers cannot specify dependencies.', + ); + + /// If this provider was created with the `.family` modifier, [from] is the `.family` instance. + @override + final Family? from; + + /// If this provider was created with the `.family` modifier, [argument] is + /// the variable that was used. + /// + /// On generated providers, this will be a record of all arguments. + final Object? argument; + + @override + ProviderSubscriptionWithOrigin addListener( + Node source, + void Function(StateT? previous, StateT next) listener, { + required void Function(Object error, StackTrace stackTrace)? onError, + required void Function()? onDependencyMayHaveChanged, + required bool fireImmediately, + required bool weak, + }) { + assert( + !fireImmediately || !weak, + 'Cannot use fireImmediately with weak listeners', + ); + + onError ??= Zone.current.handleUncaughtError; + + final element = source.readProviderElement(this); + + if (!weak) element.flush(); + + if (fireImmediately) { + _handleFireImmediately( + element.stateResult!, + listener: listener, + onError: onError, + ); + } + + return ProviderStateSubscription( + source: source, + listenedElement: element, + weak: weak, + listener: listener, + onError: onError, + ); + } + + /// An internal method that defines how a provider behaves. + @visibleForOverriding + ProviderElement $createElement($ProviderPointer pointer); + + @override + String toString() { + var leading = ''; + if (from != null) { + leading = '($argument)'; + } + + String label; + if (name case final name?) { + label = name; + } else { + label = describeIdentity(this); + } + + return '$label$leading'; + } +} + +/// A mixin that implements some methods for non-generic providers. +@internal +base mixin LegacyProviderMixin on ProviderBase { + @override + int get hashCode { + if (from == null) return super.hashCode; + + return from.hashCode ^ argument.hashCode; + } + + @override + bool operator ==(Object other) { + if (from == null) return identical(other, this); + + return other.runtimeType == runtimeType && + other is ProviderBase && + other.from == from && + other.argument == argument; + } + + @internal + @override + String? debugGetCreateSourceHash() => null; +} diff --git a/packages/riverpod/lib/src/core/provider_container.dart b/packages/riverpod/lib/src/core/provider_container.dart new file mode 100644 index 000000000..976b94f9b --- /dev/null +++ b/packages/riverpod/lib/src/core/provider_container.dart @@ -0,0 +1,1213 @@ +part of '../framework.dart'; + +extension on String { + String indentAfterFirstLine(int level) { + final indent = ' ' * level; + return split('\n').join('\n$indent'); + } +} + +abstract class _PointerBase { + bool get isTransitiveOverride; + + /// The container in which the element of this provider will be mounted. + ProviderContainer get targetContainer; +} + +@internal +class $ProviderPointer implements _PointerBase { + $ProviderPointer({ + required this.providerOverride, + required this.targetContainer, + required this.origin, + }); + + @override + bool get isTransitiveOverride => + providerOverride is TransitiveProviderOverride; + + final ProviderBase origin; + + /// The override associated with this provider, if any. + /// + /// If non-null, this pointer should **never** be removed. + /// + /// This override may be implicitly created by [ProviderOrFamily.allTransitiveDependencies]. + // ignore: library_private_types_in_public_api, not public API + _ProviderOverride? providerOverride; + ProviderElement? element; + @override + final ProviderContainer targetContainer; + + @override + String toString() { + final buffer = StringBuffer(); + buffer.writeln('ProviderPointer$hashCode('); + + buffer.writeln(' targetContainer: $targetContainer'); + buffer.writeln(' override: $providerOverride'); + buffer.writeln(' element: $element'); + + buffer.write(')'); + + return buffer.toString(); + } +} + +extension + on Map { + /// - [currentContainer]: The container trying to read this pointer. + PointerT _upsert( + ProviderT provider, { + required ProviderContainer currentContainer, + required ProviderContainer? targetContainer, + required PointerT Function(ProviderContainer) inherit, + required PointerT Function({ProviderT? override}) scope, + }) { + final pointer = this[provider]; + if (pointer != null) return pointer; + + final deepestTransitiveDependencyContainer = currentContainer + ._pointerManager + .findDeepestTransitiveDependencyProviderContainer(provider); + + final target = deepestTransitiveDependencyContainer ?? + pointer?.targetContainer ?? + targetContainer ?? + currentContainer._root ?? + currentContainer; + + if (target == currentContainer) { + return this[provider] = scope( + override: + deepestTransitiveDependencyContainer == null ? null : provider, + ); + } + + return this[provider] = inherit(target); + } +} + +extension on ProviderOrFamily { + bool get canBeTransitivelyOverridden { + final allTransitiveDependencies = this.allTransitiveDependencies; + return allTransitiveDependencies != null && + allTransitiveDependencies.isNotEmpty; + } +} + +@internal +class ProviderDirectory implements _PointerBase { + ProviderDirectory.empty( + ProviderContainer container, { + required this.familyOverride, + }) : pointers = HashMap(), + targetContainer = container; + + ProviderDirectory.from( + ProviderDirectory pointer, { + ProviderContainer? targetContainer, + _FamilyOverride? familyOverride, + }) : assert( + (familyOverride == null) == (targetContainer == null), + 'Either both or neither of familyOverride and targetContainer should be null', + ), + familyOverride = familyOverride ?? pointer.familyOverride, + targetContainer = targetContainer ?? pointer.targetContainer, + pointers = HashMap.fromEntries( + pointer.pointers.entries.where((e) => !e.value.isTransitiveOverride), + ); + + @override + bool get isTransitiveOverride => familyOverride is TransitiveFamilyOverride; + + /// The override associated with this provider, if any. + /// + /// If non-null, this pointer should **never** be removed. + /// + /// This override may be implicitly created by [ProviderOrFamily.allTransitiveDependencies]. + // ignore: library_private_types_in_public_api, not public API + _FamilyOverride? familyOverride; + final HashMap, $ProviderPointer> pointers; + @override + ProviderContainer targetContainer; + + void addProviderOverride( + // ignore: library_private_types_in_public_api, not public API + _ProviderOverride override, { + required ProviderContainer targetContainer, + }) { + final origin = override.origin; + + pointers[origin] = $ProviderPointer( + targetContainer: targetContainer, + providerOverride: override, + origin: origin, + ); + } + + $ProviderPointer upsertPointer( + ProviderBase provider, { + required ProviderContainer currentContainer, + }) { + return pointers._upsert( + provider, + currentContainer: currentContainer, + targetContainer: targetContainer, + inherit: (target) => target._pointerManager.upsertPointer(provider), + scope: ({override}) => $ProviderPointer( + targetContainer: currentContainer, + providerOverride: override == null || provider.from != null // + ? null + : TransitiveProviderOverride(override), + origin: provider, + ), + ); + } + + /// Initializes a provider and returns its pointer. + /// + /// Overridden providers, be it directly or transitively, + /// are mounted in the current container. + /// + /// Non-overridden providers are mounted in the root container. + $ProviderPointer mount( + ProviderBase origin, { + required ProviderContainer currentContainer, + }) { + final pointer = upsertPointer( + origin, + currentContainer: currentContainer, + ); + + if (pointer.element == null) { + ProviderElement? element; + + switch ((pointer.providerOverride, familyOverride)) { + // The provider is overridden. This takes over any family override + case (final override?, _): + element = override.providerOverride.$createElement(pointer); + + // The family was overridden using overrideWith & co. + case (null, final $FamilyOverride override): + element = override.createElement(pointer); + + // Either the provider wasn't overridden or it was scoped. + case (null, _FamilyOverride() || null): + element = origin.$createElement(pointer); + } + + /// Assigning the element before calling "mount" to guarantee + /// that even if something goes very wrong, such as a recursive + /// initialization or "mount" throwing, next read will not try to + /// initialize the provider again. + /// This has otherwise no impact unless there is a bug. + pointer.element = element; + } + + return pointer; + } + + @override + String toString() { + final buffer = StringBuffer(); + buffer.writeln('ProviderDirectory$hashCode('); + + buffer.writeln(' targetContainer: $targetContainer'); + buffer.writeln(' override: $familyOverride'); + + buffer.write(' pointers: {'); + for (final entry in pointers.entries) { + buffer.write( + '\n ${entry.key}: ${entry.value.toString().indentAfterFirstLine(2)},', + ); + } + if (pointers.isNotEmpty) { + buffer.writeln('\n }'); + } else { + buffer.writeln('}'); + } + + buffer.write(')'); + + return buffer.toString(); + } +} + +@internal +typedef Retry = Duration? Function(int retryCount, Object error); + +/// An object responsible for storing the a O(1) access to providers, +/// while also enabling the "scoping" of providers and ensuring all [ProviderContainer]s +/// are in sync. +/// +/// Instead of storing a [Map], we voluntarily +/// introduce a level of indirection by storing a [Map]. +/// +/// Then, when overriding a provider, it is guaranteed that the [ProviderContainer] +/// and all of its children have the same [$ProviderPointer] for a overridden provider. +/// +/// This way, we can read an overridden provider from any of the [ProviderContainer]s. +/// And no-matter where the first read is made, all [ProviderContainer]s will +/// share the same state. +@internal +class ProviderPointerManager { + ProviderPointerManager( + List overrides, { + required this.container, + required this.orphanPointers, + HashMap? familyPointers, + }) : familyPointers = familyPointers ?? HashMap() { + _initializeOverrides(overrides); + } + + factory ProviderPointerManager.from( + ProviderContainer parent, + List overrides, { + required ProviderContainer container, + }) { + if (overrides.isEmpty) return parent._pointerManager; + + return ProviderPointerManager( + overrides, + container: container, + // Always folks orphan pointers, because of possible transitive overrides. + orphanPointers: ProviderDirectory.from( + parent._pointerManager.orphanPointers, + ), + + familyPointers: HashMap.fromEntries( + parent._pointerManager.familyPointers.entries + .where( + (e) => + !e.value.isTransitiveOverride && + // Exclude families that may be automatically scoped unless they are overridden. + (!e.key.canBeTransitivelyOverridden || + e.value.familyOverride != null), + ) + .map( + (e) { + if (e.key.allTransitiveDependencies == null) return e; + + return MapEntry(e.key, ProviderDirectory.from(e.value)); + }, + ), + ), + ); + } + + final ProviderContainer container; + final ProviderDirectory orphanPointers; + final HashMap familyPointers; + + void _initializeProviderOverride( + _ProviderOverride override, + ) { + final from = override.origin.from; + + if (from == null) { + orphanPointers.addProviderOverride( + override, + targetContainer: container, + ); + return; + } + + final familyPointer = familyPointers[from] ??= ProviderDirectory.empty( + container._root ?? container, + familyOverride: null, + ); + + familyPointer.addProviderOverride( + override, + targetContainer: container, + ); + } + + void _initializeOverrides(List overrides) { + for (final override in overrides) { + switch (override) { + case _ProviderOverride(): + _initializeProviderOverride(override); + case _FamilyOverride(): + final overriddenFamily = override.from; + + final previousPointer = familyPointers[overriddenFamily]; + if (previousPointer != null) { + /// A provider from that family was overridden first. + /// We override the family but preserve the provider overrides too. + + previousPointer + ..familyOverride = override + ..targetContainer = container + // Remove inherited family values and keep only local ones + ..pointers.removeWhere( + (key, value) => value.targetContainer != container, + ); + continue; + } + + familyPointers[overriddenFamily] = ProviderDirectory.empty( + container, + familyOverride: override, + ); + } + } + } + + /// Obtains the [ProviderContainer] in which provider/family should be mounted, + /// if the provider is locally scoped. + /// + /// Returns `null` if it should be mounted at the root. + ProviderContainer? findDeepestTransitiveDependencyProviderContainer( + ProviderOrFamily provider, + ) { + if (container._parent == null) return null; + if (!provider.canBeTransitivelyOverridden) { + return null; + } + + final overrides = provider.allTransitiveDependencies! + .expand((dependency) { + switch (dependency) { + case Family(): + final familyPointer = familyPointers[dependency]; + if (familyPointer == null) return const []; + + return [familyPointer.targetContainer].followedBy( + familyPointer.pointers.values.map((e) => e.targetContainer), + ); + case ProviderBase(): + return [ + if (readPointer(dependency)?.targetContainer case final container?) + container, + ]; + } + }); + + return overrides.fold(null, (deepestContainer, target) { + if (deepestContainer == null || deepestContainer.depth < target.depth) { + return target; + } + + return deepestContainer; + }); + } + + /// Initializes a family and returns its pointer. + /// + /// Overridden families, be it directly or transitively, + /// are mounted in the current container. + /// + /// Non-overridden families are mounted in the root container. + ProviderDirectory _mountFamily(Family family) { + return familyPointers._upsert( + family, + currentContainer: container, + targetContainer: null, + inherit: (target) { + final parentPointer = target._pointerManager._mountFamily(family); + + return ProviderDirectory.from(parentPointer); + }, + scope: ({override}) { + final familyOverride = override == null // + ? null + : TransitiveFamilyOverride(override); + + final parent = container.parent?._pointerManager.familyPointers[family]; + + if (parent != null) { + return ProviderDirectory.from( + parent, + targetContainer: container, + familyOverride: familyOverride, + ); + } + + return ProviderDirectory.empty( + container, + familyOverride: familyOverride, + ); + }, + ); + } + + ProviderDirectory? readDirectory(ProviderBase provider) { + final from = provider.from; + + if (from == null) { + return orphanPointers; + } else { + return familyPointers[from]; + } + } + + $ProviderPointer? readPointer(ProviderBase provider) { + return readDirectory(provider)?.pointers[provider]; + } + + ProviderElement? readElement(ProviderBase provider) { + return readPointer(provider)?.element; + } + + ProviderDirectory upsertDirectory(ProviderBase provider) { + final from = provider.from; + + if (from == null) { + return orphanPointers; + } else { + return _mountFamily(from); + } + } + + $ProviderPointer upsertPointer(ProviderBase provider) { + return upsertDirectory(provider).mount( + provider, + currentContainer: container, + ); + } + + ProviderElement upsertElement(ProviderBase provider) { + return upsertPointer(provider).element!; + } + + /// Traverse the [ProviderElement]s associated with this [ProviderContainer]. + Iterable<$ProviderPointer> listProviderPointers() { + return orphanPointers.pointers.values + .where((pointer) => pointer.targetContainer == container) + .followedBy( + familyPointers.values + .where((pointer) => pointer.targetContainer == container) + .expand((e) => e.pointers.values), + ); + } + + /// Read the [ProviderElement] for a provider, without creating it if it doesn't exist. + Iterable listFamily(Family family) { + final _familyPointers = familyPointers[family]; + if (_familyPointers == null) return const []; + + return _familyPointers.pointers.values.map((e) => e.element).nonNulls; + } + + /// Remove a provider from this container. + /// + /// Noop if the provider is from an override or doesn't exist. + /// + /// Returns the associated pointer, even if it was not removed. + $ProviderPointer? remove(ProviderBase provider) { + final directory = readDirectory(provider); + if (directory == null) return null; + + final pointer = directory.pointers[provider]; + // If null, nothing to remove. + if (pointer == null) return null; + // If from an override, must not be removed unless it is a transitive override + if (pointer.providerOverride != null && + pointer.providerOverride is! TransitiveProviderOverride) { + return pointer; + } + + directory.pointers.remove(provider); + + final from = provider.from; + // Cleanup family if empty. + // We do so only if it isn't from an override, as overrides are + // must never be removed. + if (from != null && directory.pointers.isEmpty) { + if (directory.familyOverride == null || + directory.familyOverride is TransitiveFamilyOverride) { + familyPointers.remove(from); + } + } + + return pointer; + } + + @override + String toString() { + final buffer = StringBuffer(); + buffer.writeln('ProviderPointerManager#${shortHash(this)}('); + + buffer.writeln(' container: $container'); + buffer.writeln( + ' orphanPointers: ${orphanPointers.toString().indentAfterFirstLine(2)}', + ); + + buffer.write(' familyPointers: {'); + + for (final entry in familyPointers.entries) { + buffer.write( + '\n ${entry.key}: ${entry.value.toString().indentAfterFirstLine(2)},', + ); + } + if (familyPointers.isNotEmpty) { + buffer.writeln('\n }'); + } else { + buffer.writeln('}'); + } + + buffer.write(')'); + + return buffer.toString(); + } +} + +/// {@template riverpod.provider_container} +/// An object that stores the state of the providers and allows overriding the +/// behavior of a specific provider. +/// +/// If you are using Flutter, you do not need to care about this object +/// (outside of testing), as it is implicitly created for you by `ProviderScope`. +/// +/// Inside tests, consider using [ProviderContainer.test]. +/// This will automatically dispose the container at the end of the test. +/// {@endtemplate} +@sealed +class ProviderContainer implements Node { + /// {@macro riverpod.provider_container} + ProviderContainer({ + ProviderContainer? parent, + List overrides = const [], + List? observers, + Retry? retry, + }) : _debugOverridesLength = overrides.length, + depth = parent == null ? 0 : parent.depth + 1, + _parent = parent, + retry = retry ?? parent?.retry, + observers = [ + ...?observers, + if (parent != null) ...parent.observers, + ], + _root = parent?._root ?? parent { + if (parent != null) { + if (parent.disposed) { + throw StateError( + 'Cannot create a ProviderContainer that has a disposed parent', + ); + } + } + + if (kDebugMode) { + final overrideOrigins = {}; + for (final override in overrides) { + switch (override) { + case _ProviderOverride(): + if (parent != null && + override.origin.allTransitiveDependencies == null && + override.origin.from?.allTransitiveDependencies == null) { + throw AssertionError( + 'Tried to scope a provider that did not specify "dependencies": ${override.origin}', + ); + } + + if (!overrideOrigins.add(override.origin)) { + throw AssertionError( + 'Tried to override a provider twice within the same container: ${override.origin}', + ); + } + case _FamilyOverride(): + if (parent != null && + override.from.allTransitiveDependencies == null) { + throw AssertionError( + 'Tried to scope a family that did not specify "dependencies": ${override.from}', + ); + } + + if (!overrideOrigins.add(override.from)) { + throw AssertionError( + 'Tried to override a family twice within the same container: ${override.from}', + ); + } + } + } + } + + _pointerManager = parent != null + ? ProviderPointerManager.from(parent, overrides, container: this) + : ProviderPointerManager( + overrides, + container: this, + orphanPointers: ProviderDirectory.empty(this, familyOverride: null), + ); + + // Mutate the parent & global state only at the very end. + // This ensures that if an error is thrown, the parent & global state + // are not affected. + parent?._children.add(this); + } + + /// An automatically disposed [ProviderContainer]. + /// + /// This also adds an internal check at the end of tests that verifies + /// that all containers were disposed. + /// + /// This constructor works only inside tests, by relying on `package:test`'s + /// `addTearDown`. + @visibleForTesting + factory ProviderContainer.test({ + ProviderContainer? parent, + List overrides = const [], + List? observers, + Retry? retry, + }) { + final container = ProviderContainer( + parent: parent, + overrides: overrides, + observers: observers, + retry: retry, + ); + test.addTearDown(container.dispose); + + return container; + } + + final int _debugOverridesLength; + + /// The object that handles when providers are refreshed and disposed. + @internal + late final ProviderScheduler scheduler = ProviderScheduler(); + + /// {@macro riverpod.retry} + final Retry? retry; + + /// How deep this [ProviderContainer] is in the graph of containers. + /// + /// Starts at 0. + @internal + final int depth; + final ProviderContainer? _root; + final ProviderContainer? _parent; + + final _children = []; + + /// All the containers that have this container as `parent`. + /// + /// Do not use in production + List get debugChildren => UnmodifiableListView(_children); + + late final ProviderPointerManager _pointerManager; + + /// The list of observers attached to this container. + /// + /// Observers can be useful for logging purpose. + /// + /// This list includes the observers of this container and that of its "parent" + /// too. + final List observers; + + /// Whether [dispose] was called or not. + /// + /// This disables the different methods of [ProviderContainer], resulting in + /// a [StateError] when attempting to use them. + bool _disposed = false; + + /// Awaits for providers to rebuild/be disposed and for listeners to be notified. + /// + /// + /// This call is recursive and will wait for ancestor [ProviderContainer]s to + /// rebuild their providers too. + Future pump() async { + final a = scheduler.pendingFuture; + + await Future.wait([ + if (a != null) a, + if (parent case final parent?) parent.pump(), + ]); + } + + /// Reads a provider without listening to it and returns the currently + /// exposed value. + /// + /// ```dart + /// final greetingProvider = Provider((_) => 'Hello world'); + /// + /// void main() { + /// final container = ProviderContainer(); + /// + /// print(container.read(greetingProvider)); // Hello World + /// } + /// ``` + Result read( + ProviderListenable provider, + ) { + final sub = listen(provider, (_, __) {}); + + try { + return sub.read(); + } finally { + sub.close(); + } + } + + /// {@macro riverpod.exists} + bool exists(ProviderBase provider) { + return _pointerManager + .readDirectory(provider) + ?.pointers[provider] + ?.element != + null; + } + + /// Executes [ProviderElement.debugReassemble] on all the providers. + void debugReassemble() { + if (kDebugMode) { + for (final element in getAllProviderElements()) { + element.debugReassemble(); + } + } + } + + /// {@macro riverpod.listen} + ProviderSubscription listen( + ProviderListenable provider, + void Function(State? previous, State next) listener, { + bool fireImmediately = false, + bool weak = false, + void Function(Object error, StackTrace stackTrace)? onError, + }) { + final sub = provider.addListener( + this, + listener, + fireImmediately: fireImmediately, + weak: weak, + onError: onError, + onDependencyMayHaveChanged: null, + ); + + switch (sub) { + case final ProviderSubscriptionImpl sub: + sub._listenedElement.addDependentSubscription(sub); + } + + return sub; + } + + /// {@macro riverpod.invalidate} + void invalidate( + ProviderOrFamily provider, { + bool asReload = false, + }) { + switch (provider) { + case ProviderBase(): + _pointerManager + .readElement(provider) + ?.invalidateSelf(asReload: asReload); + case Family(): + for (final element in _pointerManager.listFamily(provider)) { + element.invalidateSelf(asReload: asReload); + } + } + } + + /// {@macro riverpod.refresh} + StateT refresh(Refreshable refreshable) { + final providerToRefresh = switch (refreshable) { + ProviderBase() => refreshable, + _ProviderRefreshable(:final provider) => provider + }; + invalidate(providerToRefresh); + + return read(refreshable); + } + + void _recursivePointerRemoval( + ProviderBase provider, + $ProviderPointer pointer, + ) { + for (final child in _children) { + final childPointer = child._pointerManager.readPointer(provider); + + if (childPointer != null && childPointer != pointer) { + continue; + } + + child._recursivePointerRemoval(provider, pointer); + } + + _pointerManager.remove(provider); + } + + void _disposeProvider(ProviderBase provider) { + final pointer = _pointerManager.remove(provider); + // The provider is already disposed, so we don't need to do anything + if (pointer == null) return; + + _recursivePointerRemoval(provider, pointer); + + pointer.element?.dispose(); + pointer.element = null; + } + + /// Updates the list of provider overrides. + /// + /// If you are using flutter, this is done implicitly for you by `ProviderScope`. + /// + /// Updating a `overrideWithValue` with a different value + /// will cause the listeners to rebuild. + /// + /// It is not possible, to remove or add new overrides, only update existing ones. + void updateOverrides(List overrides) { + if (_disposed) { + throw StateError( + 'Called updateOverrides on a ProviderContainer that was already disposed', + ); + } + + assert( + _debugOverridesLength == overrides.length, + 'Tried to change the number of overrides. This is not allowed – ' + 'overrides cannot be removed/added, they can only be updated.', + ); + + for (final override in overrides) { + void debugValidateOverride( + Override? previousOverride, + Type newOverrideType, + ) { + if (previousOverride == null) { + throw AssertionError( + 'Tried to update the override of a provider that was not overridden before', + ); + } + + assert( + previousOverride.runtimeType == newOverrideType, + 'Replaced the override of type ${previousOverride.runtimeType} ' + 'with an override of type $newOverrideType, which is different.\n' + 'Changing the kind of override or reordering overrides is not supported.', + ); + } + + switch (override) { + case _ProviderOverride(): + final pointer = _pointerManager.readPointer(override.origin); + + if (kDebugMode) { + debugValidateOverride( + pointer?.providerOverride, + override.runtimeType, + ); + } + + pointer!.providerOverride = override; + + final element = pointer.element; + if (element == null) continue; + + runUnaryGuarded(element.update, override.providerOverride); + + case _FamilyOverride(): + final pointer = _pointerManager.familyPointers[override.from]; + + if (kDebugMode) { + debugValidateOverride( + pointer?.familyOverride, + override.runtimeType, + ); + } + + pointer!.familyOverride = override; + } + } + } + + @internal + @override + ProviderElement readProviderElement( + ProviderBase provider, + ) { + if (_disposed) { + throw StateError( + 'Tried to read a provider from a ProviderContainer that was already disposed', + ); + } + + final element = _pointerManager.upsertElement(provider); + + return element as ProviderElement; + } + + void _dispose({ + // A flag to optimize recursive dispose calls. + // When disposing a graph of containers, there is no need to call `children.remove` + // individually, as all children will be disposed at once. + required bool updateChildren, + }) { + if (_disposed) return; + _disposed = true; + + // We dispose children before disposing "this" + // This is important to dispose providers from leaves to roots. + // We can safely iterate over "children" without using "toList" thanks to + // the "updateChildren" flag. + for (final child in _children) { + child._dispose(updateChildren: false); + } + + if (updateChildren) _parent?._children.remove(this); + + if (_root == null) scheduler.dispose(); + + for (final element in getAllProviderElementsInOrder().toList().reversed) { + element.dispose(); + } + } + + /// Release all the resources associated with this [ProviderContainer]. + /// + /// This will destroy the state of all providers associated with this + /// [ProviderContainer] and call [Ref.onDispose] listeners. + /// + /// It is safe to call this method multiple times. Subsequent calls will be no-op. + /// + /// If this container has non-disposed child [ProviderContainer]s (cf `parent`), + /// then this method will dispose those children first. + /// Therefore, disposing the root [ProviderContainer] the entire graph. + void dispose() => _dispose(updateChildren: true); + + /// Traverse the [ProviderElement]s associated with this [ProviderContainer]. + @internal + Iterable getAllProviderElements() { + return _pointerManager + .listProviderPointers() + .map((e) => e.element) + .nonNulls + .where((e) => e.container == this); + } + + /// Visit all nodes of the graph at most once, from roots to leaves. + /// + /// This is fairly expensive and should be avoided as much as possible. + /// If you do not need for providers to be sorted, consider using [getAllProviderElements] + /// instead, which returns an unsorted list and is significantly faster. + @internal + Iterable getAllProviderElementsInOrder() sync* { + final visitedNodes = HashSet(); + final queue = DoubleLinkedQueue(); + + // get providers that don't depend on other providers from this container + for (final pointer in _pointerManager.listProviderPointers()) { + if (pointer.targetContainer != this) continue; + final element = pointer.element; + if (element == null) continue; + + var hasAncestorsInContainer = false; + element.visitAncestors((element) { + // We ignore dependencies that are defined in another container, as + // they are in a separate graph + if (element.container == this) { + hasAncestorsInContainer = true; + } + }); + + if (!hasAncestorsInContainer) { + queue.add(element); + } + } + + while (queue.isNotEmpty) { + final element = queue.removeFirst(); + + if (!visitedNodes.add(element)) { + // Already visited + continue; + } + + yield element; + + // Queue the children of this element, but only if all of its ancestors + // were already visited before. + // If a child does not have all of its ancestors visited, when those + // ancestors will be visited, they will retry visiting this child. + element.visitChildren((dependent) { + if (dependent.container == this) { + // All the parents of a node must have been visited before a node is visited + var areAllAncestorsAlreadyVisited = true; + dependent.visitAncestors((e) { + if (e.container == this && !visitedNodes.contains(e)) { + areAllAncestorsAlreadyVisited = false; + } + }); + + if (areAllAncestorsAlreadyVisited) queue.add(dependent); + } + }); + } + } + + @override + String toString() => 'ProviderContainer#${shortHash(this)}()'; +} + +@internal +extension ProviderContainerTest on ProviderContainer { + bool get disposed => _disposed; + + ProviderContainer? get root => _root; + ProviderContainer? get parent => _parent; + + List get children => _children; + + ProviderPointerManager get pointerManager => _pointerManager; +} + +/// Information about the pending mutation, when [ProviderObserver] emits +/// an event while a mutation is in progress. +class MutationContext { + @internal + MutationContext(this.invocation, this.notifier); + + /// Information about the method invoked by the mutation, and its arguments. + final Invocation invocation; + + /// The notifier that triggered the mutation. + final NotifierBase notifier; +} + +/// Information about the [ProviderObserver] event. +class ProviderObserverContext { + @internal + ProviderObserverContext( + this.provider, + this.container, { + this.mutation, + }); + + /// The provider that triggered the event. + final ProviderBase provider; + + /// The container that owns [provider]'s state. + final ProviderContainer container; + + /// The pending mutation while the observer was called. + /// + /// Pretty much all observer events may be triggered by a mutation under some + /// conditions. + /// For example, if a mutation refreshes another provider, then + /// [ProviderObserver.didDisposeProvider] will contain the mutation that + /// disposed the provider. + final MutationContext? mutation; +} + +/// An object that listens to the changes of a [ProviderContainer]. +/// +/// This can be used for logging or making devtools. +abstract class ProviderObserver { + /// An object that listens to the changes of a [ProviderContainer]. + /// + /// This can be used for logging or making devtools. + const ProviderObserver(); + + /// A provider was initialized, and the value exposed is [value]. + /// + /// [value] will be `null` if the provider threw during initialization. + void didAddProvider( + ProviderObserverContext context, + Object? value, + ) {} + + /// A provider emitted an error, be it by throwing during initialization + /// or by having a [Future]/[Stream] emit an error + void providerDidFail( + ProviderObserverContext context, + Object error, + StackTrace stackTrace, + ) {} + + /// Called by providers when they emit a notification. + /// + /// - [newValue] will be `null` if the provider threw during initialization. + /// - [previousValue] will be `null` if the previous build threw during initialization. + /// + /// If the change is caused by a "mutation", [mutation] will be the invocation + /// that caused the state change. + /// This includes when a mutation manually calls `state=`: + /// + /// ```dart + /// @riverpod + /// class Example extends _$Example { + /// @override + /// int count() => 0; + /// + /// @mutation + /// int increment() { + /// state++; // This will trigger `didUpdateProvider` and "mutation" will be `#increment` + /// + /// // ... + /// } + /// } + /// ``` + void didUpdateProvider( + ProviderObserverContext context, + Object? previousValue, + Object? newValue, + ) {} + + /// A provider was disposed + void didDisposeProvider(ProviderObserverContext context) {} + + /// A mutation was reset. + /// + /// This includes both manual calls to [MutationBase.reset] and automatic + /// resets. + /// + /// {@macro auto_reset} + void mutationReset(ProviderObserverContext context) {} + + /// A mutation was started. + /// + /// {@template obs_mutation_arg} + /// [mutation] is strictly the same as [ProviderObserverContext.mutation]. + /// It is provided as a convenience, as this life-cycle is guaranteed + /// to have a non-null [ProviderObserverContext.mutation]. + /// {@endtemplate} + void mutationStart( + ProviderObserverContext context, + MutationContext mutation, + ) {} + + /// A mutation failed. + /// + /// [error] is the error thrown by the mutation. + /// [stackTrace] is the stack trace of the error. + /// + /// {@macro obs_mutation_arg} + void mutationError( + ProviderObserverContext context, + MutationContext mutation, + Object error, + StackTrace stackTrace, + ) {} + + /// A mutation succeeded. + /// + /// [result] is the value returned by the mutation. + /// + /// {@macro obs_mutation_arg} + void mutationSuccess( + ProviderObserverContext context, + MutationContext mutation, + Object? result, + ) {} +} + +/// An implementation detail for the override mechanism of providers +@internal +typedef SetupOverride = void Function({ + required ProviderBase origin, + required ProviderBase override, +}); + +/// An error thrown when a call to [Ref.read]/[Ref.watch] +/// leads to a provider depending on itself. +/// +/// Circular dependencies are both not supported for performance reasons +/// and maintainability reasons. +/// Consider reading about unidirectional data flow to learn about the +/// benefits of avoiding circular dependencies. +@internal +class CircularDependencyError extends Error { + CircularDependencyError._(); +} diff --git a/packages/riverpod/lib/src/core/provider_subscription.dart b/packages/riverpod/lib/src/core/provider_subscription.dart new file mode 100644 index 000000000..cf0c6386c --- /dev/null +++ b/packages/riverpod/lib/src/core/provider_subscription.dart @@ -0,0 +1,382 @@ +part of '../framework.dart'; + +/// Represents the subscription to a [ProviderListenable]. +/// +// This always is implemented with ProviderSubscriptionWithOrigin. +// This interface exists to remove the redundant type parameters. +@optionalTypeArgs +sealed class ProviderSubscription { + /// Whether the subscription is closed. + bool get closed; + bool get weak; + bool get isPaused; + + /// The object that listens to the associated [ProviderListenable]. + /// + /// This is typically a [ProviderElement] or a [ProviderContainer], + /// but may be other values in the future. + Node get source; + + void pause(); + void resume(); + + /// Obtain the latest value emitted by the provider. + /// + /// This method throws if [closed] is true. + OutT read(); + + /// Stops listening to the provider. + /// + /// It is safe to call this method multiple times. + void close(); +} + +@internal +@optionalTypeArgs +sealed class ProviderSubscriptionWithOrigin + extends ProviderSubscription implements Pausable { + ProviderBase get origin; + ProviderElement get _listenedElement; + + void _onOriginData(StateT? prev, StateT next); + void _onOriginError(Object error, StackTrace stackTrace); + + OutT _callRead(); + + @override + OutT read() { + if (closed) { + throw StateError( + 'called ProviderSubscription.read on a subscription that was closed', + ); + } + _listenedElement.mayNeedDispose(); + _listenedElement.flush(); + + return _callRead(); + } +} + +@internal +@optionalTypeArgs +abstract base class ProviderSubscriptionImpl + extends ProviderSubscriptionWithOrigin with _OnPauseMixin { + @override + bool get isPaused => _isPaused; + + /// Whether the subscription is closed. + @override + bool get closed => _closed; + var _closed = false; + + /// Whether an event was sent while this subscription was paused. + /// + /// This enables re-rending the last missing event when the subscription is resumed. + ({ + (OutT?, OutT)? data, + (Object, StackTrace)? error, + })? _missedCalled; + void Function(OutT? prev, OutT next) get _listener; + OnError get _errorListener; + + @mustCallSuper + @override + void onCancel() { + _listenedElement.onSubscriptionPause(this); + } + + @mustCallSuper + @override + void onResume() { + _listenedElement.onSubscriptionResume(this); + if (closed) return; + if (_missedCalled?.data case final event?) { + final prev = event.$1; + final next = event.$2; + + _missedCalled = null; + _notifyData(prev, next); + } else if (_missedCalled?.error case final event?) { + final error = event.$1; + final stackTrace = event.$2; + + _missedCalled = null; + _notifyError(error, stackTrace); + } + } + + void _notifyData(OutT? prev, OutT next) { + assert(!closed, 'cannot notify after close'); + if (isPaused) { + _missedCalled = (data: (prev, next), error: null); + return; + } + + _listener(prev, next); + } + + void _notifyError(Object error, StackTrace stackTrace) { + assert(!closed, 'cannot notify after close'); + if (isPaused) { + _missedCalled = (data: null, error: (error, stackTrace)); + return; + } + + _errorListener(error, stackTrace); + } + + /// Stops listening to the provider. + /// + /// It is safe to call this method multiple times. + @override + @mustCallSuper + void close() { + if (_closed) return; + _closed = true; + + _listenedElement.removeDependentSubscription(this); + } +} + +abstract class Pausable { + bool get isPaused; + + void pause(); + void resume(); + + void onCancel(); + void onResume(); +} + +mixin _OnPauseMixin on Pausable { + bool get _isPaused => _pauseCount > 0; + var _pauseCount = 0; + + @override + @mustCallSuper + void pause() { + final shouldCallCancel = _pauseCount == 0; + _pauseCount++; + + if (shouldCallCancel) onCancel(); + } + + @override + @mustCallSuper + void resume() { + final shouldCallResume = _pauseCount == 1; + _pauseCount = math.max(_pauseCount - 1, 0); + + if (shouldCallResume) onResume(); + } + + @override + void onResume(); + + @override + void onCancel(); +} + +@internal +base class ProviderSubscriptionView + extends ProviderSubscriptionImpl { + ProviderSubscriptionView({ + required this.innerSubscription, + required OutT Function() read, + void Function()? onClose, + required void Function(OutT? prev, OutT next) listener, + required OnError? onError, + }) : _read = read, + _onClose = onClose, + _listener = listener, + _errorListener = onError ?? Zone.current.handleUncaughtError; + + final ProviderSubscriptionWithOrigin innerSubscription; + final OutT Function() _read; + final void Function()? _onClose; + + @override + final OnError _errorListener; + + @override + final void Function(OutT? prev, OutT next) _listener; + + @override + ProviderBase get origin => innerSubscription.origin; + + @override + ProviderElement get _listenedElement => + innerSubscription._listenedElement; + + @override + bool get weak => innerSubscription.weak; + + @override + Node get source => innerSubscription.source; + + @override + void _onOriginData(OriginT? prev, OriginT next) { + innerSubscription._onOriginData(prev, next); + } + + @override + void _onOriginError(Object error, StackTrace stackTrace) { + innerSubscription._onOriginError(error, stackTrace); + } + + @override + void onCancel() { + super.onCancel(); + switch (innerSubscription) { + case final ProviderSubscriptionImpl sub: + sub.onCancel(); + } + } + + @override + void onResume() { + super.onResume(); + switch (innerSubscription) { + case final ProviderSubscriptionImpl sub: + sub.onResume(); + } + } + + @override + void pause() { + innerSubscription.pause(); + super.pause(); + } + + @override + void resume() { + innerSubscription.resume(); + super.resume(); + } + + @override + void close() { + if (_closed) return; + + _onClose?.call(); + innerSubscription.close(); + super.close(); + } + + @override + OutT _callRead() => _read(); +} + +final class DelegatingProviderSubscription + extends ProviderSubscriptionImpl { + DelegatingProviderSubscription({ + required this.origin, + required this.source, + required this.weak, + required OnError? errorListener, + required ProviderElement listenedElement, + required void Function(OutT? prev, OutT next) listener, + void Function(OriginT? prev, OriginT next)? onOriginData, + void Function(Object error, StackTrace stackTrace)? onOriginError, + required OutT Function() read, + required void Function()? onClose, + }) : _errorListener = errorListener ?? Zone.current.handleUncaughtError, + _listenedElement = listenedElement, + _listener = listener, + _onOriginDataCb = onOriginData, + _onOriginErrorCb = onOriginError, + _readCb = read, + _onCloseCb = onClose; + + @override + final ProviderBase origin; + @override + final Node source; + @override + final bool weak; + @override + final OnError _errorListener; + @override + final ProviderElement _listenedElement; + @override + final void Function(OutT? prev, OutT next) _listener; + final void Function(OriginT? prev, OriginT next)? _onOriginDataCb; + final void Function(Object error, StackTrace stackTrace)? _onOriginErrorCb; + final OutT Function() _readCb; + final void Function()? _onCloseCb; + + @override + void _onOriginData(OriginT? prev, OriginT next) => + _onOriginDataCb?.call(prev, next); + + @override + void _onOriginError(Object error, StackTrace stackTrace) => + _onOriginErrorCb?.call(error, stackTrace); + + @override + OutT _callRead() => _readCb(); + + @override + void close() { + if (_closed) return; + + _onCloseCb?.call(); + super.close(); + } +} + +/// When a provider listens to another provider using `listen` +@internal +final class ProviderStateSubscription + extends ProviderSubscriptionImpl { + ProviderStateSubscription({ + required this.source, + required this.weak, + required ProviderElement listenedElement, + required void Function(StateT? prev, StateT next) listener, + required OnError onError, + }) : _listenedElement = listenedElement, + _listener = listener, + _errorListener = onError; + + @override + ProviderBase get origin => _listenedElement.origin; + + @override + final Node source; + @override + final ProviderElement _listenedElement; + @override + final bool weak; + + // Why can't this be typed correctly? + @override + final void Function(StateT? prev, StateT next) _listener; + @override + final OnError _errorListener; + + @override + StateT _callRead() => _listenedElement.readSelf(); + + @override + void _onOriginData(StateT? prev, StateT next) => _notifyData(prev, next); + + @override + void _onOriginError(Object error, StackTrace stackTrace) => + _notifyError(error, stackTrace); +} + +/// Deals with the internals of synchronously calling the listeners +/// when using `fireImmediately: true` +void _handleFireImmediately( + $Result currentState, { + required void Function(StateT? previous, StateT current) listener, + required void Function(Object error, StackTrace stackTrace) onError, +}) { + switch (currentState) { + case ResultData(): + runBinaryGuarded(listener, null, currentState.state); + case ResultError(): + runBinaryGuarded(onError, currentState.error, currentState.stackTrace); + } +} diff --git a/packages/riverpod/lib/src/core/proxy_provider_listenable.dart b/packages/riverpod/lib/src/core/proxy_provider_listenable.dart new file mode 100644 index 000000000..a7bdd1c67 --- /dev/null +++ b/packages/riverpod/lib/src/core/proxy_provider_listenable.dart @@ -0,0 +1,176 @@ +part of '../framework.dart'; + +class $LazyProxyListenable + with ProviderListenable, ProviderListenableWithOrigin { + $LazyProxyListenable(this.provider, this._lense); + + final ProviderBase provider; + final $ElementLense Function( + ProviderElement element, + ) _lense; + + @override + ProviderSubscriptionWithOrigin addListener( + Node source, + void Function(OutT? previous, OutT next) listener, { + required void Function(Object error, StackTrace stackTrace)? onError, + required void Function()? onDependencyMayHaveChanged, + required bool fireImmediately, + required bool weak, + }) { + final element = source.readProviderElement(provider); + + final listenable = _lense(element); + if (fireImmediately) { + switch (listenable.result) { + case null: + break; + case final ResultData data: + runBinaryGuarded(listener, null, data.state); + case final ResultError error: + if (onError != null) { + runBinaryGuarded(onError, error.error, error.stackTrace); + } + } + } + + late final ProviderSubscriptionImpl sub; + final removeListener = listenable.addListener( + (a, b) => sub._notifyData(a, b), + onError: onError, + onDependencyMayHaveChanged: onDependencyMayHaveChanged, + ); + + return sub = DelegatingProviderSubscription( + listenedElement: element, + source: source, + weak: weak, + origin: provider, + onClose: removeListener, + errorListener: onError, + listener: listener, + read: () => listenable.value, + ); + } +} + +/// An internal utility for reading alternate values of a provider. +/// +/// For example, this is used by [FutureProvider] to differentiate: +/// +/// ```dart +/// ref.watch(futureProvider); +/// ``` +/// +/// from: +/// +/// ```dart +/// ref.watch(futureProvider.future); +/// ``` +/// +/// This API is not meant for public consumption. +@internal +class ProviderElementProxy + with + ProviderListenable, + ProviderListenableWithOrigin, + _ProviderRefreshable { + /// An internal utility for reading alternate values of a provider. + /// + /// For example, this is used by [FutureProvider] to differentiate: + /// + /// ```dart + /// ref.watch(futureProvider); + /// ``` + /// + /// from: + /// + /// ```dart + /// ref.watch(futureProvider.future); + /// ``` + /// + /// This API is not meant for public consumption. + const ProviderElementProxy( + this.provider, + this._lense, { + this.flushElement = false, + }); + + final bool flushElement; + + @override + final ProviderBase provider; + final $ElementLense Function( + ProviderElement element, + ) _lense; + + @override + ProviderSubscriptionWithOrigin addListener( + Node source, + void Function(OutT? previous, OutT next) listener, { + required void Function(Object error, StackTrace stackTrace)? onError, + required void Function()? onDependencyMayHaveChanged, + required bool fireImmediately, + required bool weak, + }) { + final element = source.readProviderElement(provider); + + // While we don't care about changes to the element, calling addListener + // is necessary to tell the listened element that it is being listened. + // We do it at the top of the file to trigger a "flush" before adding + // a listener to the notifier. + // This avoids the listener from being immediately notified of a new + // future when adding the listener refreshes the future. + final innerSub = provider.addListener( + source, + (prev, next) {}, + fireImmediately: false, + weak: weak, + onDependencyMayHaveChanged: onDependencyMayHaveChanged, + onError: null, + ); + + final notifier = _lense(element); + if (fireImmediately) { + switch (notifier.result) { + case null: + break; + case final ResultData data: + runBinaryGuarded(listener, null, data.state); + case final ResultError error: + if (onError != null) { + runBinaryGuarded(onError, error.error, error.stackTrace); + } + } + } + + late ProviderSubscriptionView sub; + final removeListener = notifier.addListener( + (prev, next) => sub._notifyData(prev, next), + onError: onError, + onDependencyMayHaveChanged: onDependencyMayHaveChanged, + ); + + return sub = ProviderSubscriptionView( + innerSubscription: innerSub, + onClose: removeListener, + listener: listener, + onError: onError, + read: () { + final element = source.readProviderElement(provider); + element.flush(); + element.mayNeedDispose(); + + return _lense(element).value; + }, + ); + } + + @override + bool operator ==(Object other) => + other is ProviderElementProxy && + other.provider == provider; + + @override + int get hashCode => provider.hashCode; +} diff --git a/packages/riverpod/lib/src/framework/ref.dart b/packages/riverpod/lib/src/core/ref.dart similarity index 56% rename from packages/riverpod/lib/src/framework/ref.dart rename to packages/riverpod/lib/src/core/ref.dart index e4122d5ae..cc9e12df1 100644 --- a/packages/riverpod/lib/src/framework/ref.dart +++ b/packages/riverpod/lib/src/core/ref.dart @@ -1,5 +1,33 @@ part of '../framework.dart'; +@internal +extension $RefArg on Ref { + // Implementation detail, do not use + Object? get $arg => _element.origin.argument; + + // Implementation detail, do not use + ProviderElement get $element => _element; +} + +@internal +class UnmountedRefException implements Exception { + UnmountedRefException(this.origin); + + final ProviderBase origin; + + @override + String toString() { + return ''' +Cannot use the Ref of $origin after it has been disposed. This typically happens if: +- A provider rebuilt, but the previous "build" was still pending and is still performing operations. + You should therefore either use `ref.onDispose` to cancel pending work, or + check `ref.mounted` after async gaps or anything that could invalidate the provider. +- You tried to use Ref inside `onDispose` or other life-cycles. + This is not supported, as the provider is already being disposed. +'''; + } +} + /// {@template riverpod.provider_ref_base} /// An object used by providers to interact with other providers and the life-cycles /// of the application. @@ -10,10 +38,112 @@ part of '../framework.dart'; /// - [onDispose], a method that allows performing a task when the provider is destroyed. /// {@endtemplate} @optionalTypeArgs -abstract class Ref< - @Deprecated('Will be removed in 3.0') State extends Object?> { +sealed class Ref { + ProviderElement get _element; + List? _keepAliveLinks; + List? _onDisposeListeners; + List? _onResumeListeners; + List? _onCancelListeners; + List? _onAddListeners; + List? _onRemoveListeners; + + bool get mounted => _mounted; + var _mounted = true; + /// The [ProviderContainer] that this provider is associated with. - ProviderContainer get container; + ProviderContainer get container => _element.container; + + void _debugAssertCanDependOn(ProviderListenableOrFamily listenable) { + final dependency = switch (listenable) { + ProviderOrFamily() => listenable, + _ => listenable.debugListenedProvider, + }; + + if (dependency == null) return; + + final origin = _element.origin; + final provider = _element.provider; + + assert( + dependency != origin, + 'A provider cannot depend on itself', + ); + + final dependencies = origin.from?.dependencies ?? origin.dependencies ?? []; + final targetDependencies = + dependency.from?.dependencies ?? dependency.dependencies; + + if ( + // If the target has a null "dependencies", it should never be scoped. + !(targetDependencies == null || + // Ignore dependency check if from an override + provider != origin || + // Families are allowed to depend on themselves with different parameters. + (origin.from != null && dependency.from == origin.from) || + dependencies.contains(dependency.from) || + dependencies.contains(dependency))) { + throw StateError(''' +The provider `$origin` depends on `$dependency`, which may be scoped. +Yet `$dependency` is not part of `$origin`'s `dependencies` list. + +To fix, add $dependency to $origin's 'dependencies' parameter. +This can be done with either: + +@Riverpod(dependencies: []) + + +or: + +final = Provider(dependencies: []); +'''); + } + + final queue = Queue(); + _element.visitChildren(queue.add); + + while (queue.isNotEmpty) { + final current = queue.removeFirst(); + current.visitChildren(queue.add); + + if (current.origin == dependency) { + throw CircularDependencyError._(); + } + } + } + + void _throwIfInvalidUsage() { + assert( + !_debugIsRunningSelector, + 'Cannot call ref. inside a selector', + ); + if (!mounted) { + throw UnmountedRefException(_element.origin); + } + } + + /// Requests for the state of a provider to not be disposed when all the + /// listeners of the provider are removed. + /// + /// Returns an object which allows cancelling this operation, therefore + /// allowing the provider to dispose itself when all listeners are removed. + /// + /// If [keepAlive] is invoked multiple times, all [KeepAliveLink] will have + /// to be closed for the provider to dispose itself when all listeners are removed. + KeepAliveLink keepAlive() { + _throwIfInvalidUsage(); + + final links = _keepAliveLinks ??= []; + + late KeepAliveLink link; + link = KeepAliveLink._(() { + if (links.remove(link)) { + if (links.isEmpty) _element.mayNeedDispose(); + } + }); + links.add(link); + + return link; + } /// {@template riverpod.refresh} /// Forces a provider to re-evaluate its state immediately, and return the created value. @@ -42,7 +172,13 @@ abstract class Ref< /// to restart a specific provider. /// {@endtemplate} @useResult - T refresh(Refreshable provider); + T refresh(Refreshable refreshable) { + _throwIfInvalidUsage(); + + if (kDebugMode) _debugAssertCanDependOn(refreshable); + + return container.refresh(refreshable); + } /// {@template riverpod.invalidate} /// Invalidates the state of the provider, destroying the state immediately @@ -54,12 +190,34 @@ abstract class Ref< /// But if a provider is not listened to, the rebuild may be delayed until /// the provider is listened to again. /// - /// Calling [invalidate] multiple times will cause a single recomputation of the state. + /// Calling [invalidate] multiple times will refresh the provider only + /// once. + /// Calling [invalidate] will cause the provider to be disposed immediately. + /// + /// - [asReload] (false by default) can be optionally passed to tell + /// Riverpod to clear the state before refreshing it. + /// This is only useful for asynchronous providers, as by default, + /// [AsyncValue] keeps a reference on state during loading states. + /// Using [asReload] will disable this behavior and count as a + /// "hard refresh". /// - /// If used on a provider which is not initialized or disposed, - /// this method will have no effect. + /// If used on a provider which is not initialized or disposed, this method will have no effect. /// {@endtemplate} - void invalidate(ProviderOrFamily provider); + void invalidate(ProviderOrFamily providerOrFamily, {bool asReload = false}) { + _throwIfInvalidUsage(); + if (kDebugMode) _debugAssertCanDependOn(providerOrFamily); + + container.invalidate(providerOrFamily, asReload: asReload); + } + + /// Invokes [invalidate] on itself. + /// + /// {@macro riverpod.invalidate} + void invalidateSelf({bool asReload = false}) { + _throwIfInvalidUsage(); + + _element.invalidateSelf(asReload: asReload); + } /// Notify dependents that this provider has changed. /// @@ -76,55 +234,52 @@ abstract class Ref< /// } /// } /// ``` - void notifyListeners(); + void notifyListeners() { + _throwIfInvalidUsage(); - /// Listens to changes on the value exposed by this provider. - /// - /// The listener will be called immediately after the provider completes building. - /// - /// As opposed to [listen], the listener will be called even if - /// [ProviderElementBase.updateShouldNotify] returns false, meaning that the previous - /// and new value can potentially be identical. - @Deprecated('Will be removed in 3.0. Use Notifier.listenSelf instead') - void listenSelf( - void Function(State? previous, State next) listener, { - void Function(Object error, StackTrace stackTrace)? onError, - }); + final currentResult = _element.stateResult; + // If `notifyListeners` is used during `build`, the result will be null. + // Throwing would be unnecessarily inconvenient, so we simply skip it. + if (currentResult == null) return; - /// Invalidates the state of the provider, causing it to refresh. - /// - /// The refresh is not immediate and is instead delayed to the next read - /// or next frame. - /// - /// Calling [invalidateSelf] multiple times will refresh the provider only - /// once. - /// - /// Calling [invalidateSelf] will cause the provider to be disposed immediately. - void invalidateSelf(); + if (_element._didBuild) { + _element._notifyListeners( + currentResult, + currentResult, + checkUpdateShouldNotify: false, + ); + } + } /// A life-cycle for whenever a new listener is added to the provider. /// + /// Returns a function which can be called to remove the listener. + /// /// See also: /// - [onRemoveListener], for when a listener is removed - void onAddListener(void Function() cb); + RemoveListener onAddListener(void Function() cb) { + _throwIfInvalidUsage(); + + final list = _onAddListeners ??= []; + list.add(cb); + + return () => list.remove(cb); + } /// A life-cycle for whenever a listener is removed from the provider. /// + /// Returns a function which can be called to remove the listener. + /// /// See also: /// - [onAddListener], for when a listener is added - void onRemoveListener(void Function() cb); + RemoveListener onRemoveListener(void Function() cb) { + _throwIfInvalidUsage(); - /// A life-cycle for when a provider is listened again after it was paused - /// (and [onCancel] was triggered). - /// - /// See also: - /// - [keepAlive], which can be combined with [onCancel] for - /// advanced manipulation on when the provider should get disposed. - /// - [Provider.autoDispose], a modifier which tell a provider that it should - /// destroy its state when no longer listened to. - /// - [onDispose], a life-cycle for when a provider is disposed. - /// - [onCancel], a life-cycle for when all listeners of a provider are removed. - void onResume(void Function() cb); + final list = _onRemoveListeners ??= []; + list.add(cb); + + return () => list.remove(cb); + } /// Add a listener to perform an operation when the last listener of the provider /// is removed. @@ -136,6 +291,8 @@ abstract class Ref< /// _will_ get paused/dispose. It is possible that after the last listener /// is removed, a new listener is immediately added. /// + /// Returns a function which can be called to remove the listener. + /// /// See also: /// - [keepAlive], which can be combined with [onCancel] for /// advanced manipulation on when the provider should get disposed. @@ -143,7 +300,35 @@ abstract class Ref< /// destroy its state when no longer listened to. /// - [onDispose], a life-cycle for when a provider is disposed. /// - [onResume], a life-cycle for when the provider is listened to again. - void onCancel(void Function() cb); + RemoveListener onCancel(void Function() cb) { + _throwIfInvalidUsage(); + + final list = _onCancelListeners ??= []; + list.add(cb); + + return () => list.remove(cb); + } + + /// A life-cycle for when a provider is listened again after it was paused + /// (and [onCancel] was triggered). + /// + /// Returns a function which can be called to remove the listener. + /// + /// See also: + /// - [keepAlive], which can be combined with [onCancel] for + /// advanced manipulation on when the provider should get disposed. + /// - [Provider.autoDispose], a modifier which tell a provider that it should + /// destroy its state when no longer listened to. + /// - [onDispose], a life-cycle for when a provider is disposed. + /// - [onCancel], a life-cycle for when all listeners of a provider are removed. + RemoveListener onResume(void Function() cb) { + _throwIfInvalidUsage(); + + final list = _onResumeListeners ??= []; + list.add(cb); + + return () => list.remove(cb); + } /// Adds a listener to perform an operation right before the provider is destroyed. /// @@ -184,6 +369,8 @@ abstract class Ref< /// if an exception happens before [onDispose] is called, then /// some of your objects may not be disposed. /// + /// Returns a function which can be called to remove the listener. + /// /// See also: /// /// - [Provider.autoDispose], a modifier which tell a provider that it should @@ -191,7 +378,14 @@ abstract class Ref< /// - [ProviderContainer.dispose], to destroy all providers associated with /// a [ProviderContainer] at once. /// - [onCancel], a life-cycle for when all listeners of a provider are removed. - void onDispose(void Function() cb); + RemoveListener onDispose(void Function() listener) { + _throwIfInvalidUsage(); + + final list = _onDisposeListeners ??= []; + list.add(listener); + + return () => list.remove(listener); + } /// Read the state associated with a provider, without listening to that provider. /// @@ -227,7 +421,15 @@ abstract class Ref< /// /// If possible, avoid using [read] and prefer [watch], which is generally /// safer to use. - T read(ProviderListenable provider); + T read(ProviderListenable listenable) { + _throwIfInvalidUsage(); + + final result = container.read(listenable); + + if (kDebugMode) _debugAssertCanDependOn(listenable); + + return result; + } /// {@template riverpod.exists} /// Determines whether a provider is initialized or not. @@ -262,7 +464,15 @@ abstract class Ref< /// }); /// ``` /// {@endtemplate} - bool exists(ProviderBase provider); + bool exists(ProviderBase provider) { + _throwIfInvalidUsage(); + + final result = container.exists(provider); + + if (kDebugMode) _debugAssertCanDependOn(provider); + + return result; + } /// Obtains the state of a provider and causes the state to be re-evaluated /// when that provider emits a new value. @@ -314,26 +524,30 @@ abstract class Ref< /// - if multiple widgets depends on `sortedTodosProvider` the list will be /// sorted only once. /// - if nothing is listening to `sortedTodosProvider`, then no sort is performed. - T watch(ProviderListenable provider); - - /// Requests for the state of a provider to not be disposed when all the - /// listeners of the provider are removed. /// - /// Returns an object which allows cancelling this operation, therefore - /// allowing the provider to dispose itself when all listeners are removed. - /// - /// If [keepAlive] is invoked multiple times, all [KeepAliveLink] will have - /// to be closed for the provider to dispose itself when all listeners are removed. /// /// **Note**: - /// This is only useful if your provider is using "auto dispose". - /// If your provider is not using "auto dispose", then this method has no effect. - /// - /// **Note**: - /// A provider that is kept alive may still be paused. - /// If a provider is not listened, regardless of whether it is kept alive or not, - /// the provider won't rebuild when using [watch] until it is listened to again. - KeepAliveLink keepAlive(); + /// This can be considered as the combination of [listen] and [invalidateSelf] : + /// ```dart + /// T watch(ProviderListenable provider) { + /// final sub = listen(provider, (previous, next) { + /// invalidateSelf(asReload: true); + /// }); + /// return sub.read(); + /// } + /// ``` + T watch(ProviderListenable listenable) { + _throwIfInvalidUsage(); + late ProviderSubscription sub; + sub = _element.listen( + listenable, + (prev, value) => invalidateSelf(asReload: true), + onError: (err, stack) => invalidateSelf(asReload: true), + onDependencyMayHaveChanged: _element._markDependencyMayHaveChanged, + ); + + return sub.read(); + } /// {@template riverpod.listen} /// Listen to a provider and call [listener] whenever its value changes. @@ -353,32 +567,94 @@ abstract class Ref< /// and emit a valid value out of it. As such, if a /// [FutureProvider]/[StreamProvider] fail, [onError] will not be called. /// Instead the listener will receive an [AsyncError]. + /// + /// - [weak] (false by default) can be optionally passed to have the listener + /// not cause the provider to be initialized and kept alive. + /// This enables listening to changes on a provider, without causing it to + /// perform any work if it currently isn't used. /// {@endtemplate} ProviderSubscription listen( ProviderListenable provider, void Function(T? previous, T next) listener, { void Function(Object error, StackTrace stackTrace)? onError, - bool fireImmediately, - }); + bool weak = false, + bool fireImmediately = false, + }) { + return _element.listen( + provider, + listener, + weak: weak, + onError: onError, + fireImmediately: fireImmediately, + ); + } } -/// A [Ref] for providers that are automatically destroyed when -/// no longer used. -/// -/// The difference with [Ref] is that it has an extra -/// [keepAlive] function to help determine if the state can be destroyed -/// or not. -@Deprecated('Will be removed in 3.0. Use Ref instead') -abstract class AutoDisposeRef extends Ref { - /// Whether to destroy the state of the provider when all listeners are removed or not. - /// - /// Can be changed at any time, in which case when setting it to `false`, - /// may destroy the provider state if it currently has no listeners. - /// - /// Defaults to `false`. - @Deprecated('use keepAlive() instead') - bool get maintainState; - - @Deprecated('use keepAlive() instead') - set maintainState(bool value); +class _Ref extends Ref { + /// {@macro riverpod.provider_ref_base} + _Ref(this._element); + + @override + final ProviderElement _element; + + List? _onChangeSelfListeners; + List? _onErrorSelfListeners; + + /// Obtains the state currently exposed by this provider. + /// + /// Mutating this property will notify the provider listeners. + /// + /// If called before a value was set, there are two possible scenarios: + /// - on synchronous providers, this will throw a [StateError]. + /// - on asynchronous providers, this will return an [AsyncLoading]. + /// + /// Will throw if the provider threw during creation. + StateT get state { + _throwIfInvalidUsage(); + + return _element.readSelf(); + } + + set state(StateT newState) { + _throwIfInvalidUsage(); + + _element.setStateResult(ResultData(newState)); + } + + /// Listens to changes on the value exposed by this provider. + /// + /// The listener will be called immediately after the provider completes building. + /// + /// As opposed to [listen], the listener will be called even if + /// [ProviderElement.updateShouldNotify] returns false, meaning that the previous + /// and new value can potentially be identical. + RemoveListener listenSelf( + void Function(StateT? previous, StateT next) listener, { + void Function(Object error, StackTrace stackTrace)? onError, + }) { + _onChangeSelfListeners ??= []; + _onChangeSelfListeners!.add(listener); + + if (onError != null) { + _onErrorSelfListeners ??= []; + _onErrorSelfListeners!.add(onError); + } + + return () { + _onChangeSelfListeners?.remove(listener); + _onErrorSelfListeners?.remove(onError); + }; + } +} + +/// A object that maintains a provider alive. +class KeepAliveLink { + KeepAliveLink._(this._close); + + final void Function() _close; + + /// Release this [KeepAliveLink], allowing the associated provider to + /// be disposed if the provider is no-longer listener nor has any + /// remaining [KeepAliveLink]. + void close() => _close(); } diff --git a/packages/riverpod/lib/src/framework/scheduler.dart b/packages/riverpod/lib/src/core/scheduler.dart similarity index 77% rename from packages/riverpod/lib/src/framework/scheduler.dart rename to packages/riverpod/lib/src/core/scheduler.dart index c20529c83..37d70ba30 100644 --- a/packages/riverpod/lib/src/framework/scheduler.dart +++ b/packages/riverpod/lib/src/core/scheduler.dart @@ -5,8 +5,10 @@ part of '../framework.dart'; @internal typedef Vsync = void Function(void Function()); -void _defaultVsync(void Function() task) { - Future(task); +void Function()? _defaultVsync(void Function() task) { + final timer = Timer(Duration.zero, task); + + return timer.cancel; } /// The object that handles when providers are refreshed and disposed. @@ -28,7 +30,7 @@ class ProviderScheduler { /// /// Defaults to refreshing providers at the end of the next event-loop. @internal - void Function(void Function()) get vsync { + void Function()? 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 @@ -44,25 +46,29 @@ class ProviderScheduler { for (final flutterVsync in flutterVsyncs) { flutterVsync(invoke); } + + return; }; } return _defaultVsync; } - final _stateToDispose = >[]; - final _stateToRefresh = []; + final _stateToDispose = []; + final _stateToRefresh = []; Completer? _pendingTaskCompleter; /// A future that completes when the next task is done. Future? get pendingFuture => _pendingTaskCompleter?.future; + void Function()? _cancel; + /// Schedules a provider to be refreshed. /// /// The refresh will happen at the end of the next event-loop, /// and only if the provider is active. - void scheduleProviderRefresh(ProviderElementBase element) { + void scheduleProviderRefresh(ProviderElement element) { _stateToRefresh.add(element); _scheduleTask(); @@ -77,10 +83,11 @@ class ProviderScheduler { // disposed. if (_pendingTaskCompleter != null || _disposed) return; _pendingTaskCompleter = Completer(); - vsync(_task); + _cancel = vsync(_task); } void _task() { + _cancel = null; final pendingTaskCompleter = _pendingTaskCompleter; if (pendingTaskCompleter == null) return; pendingTaskCompleter.complete(); @@ -97,19 +104,17 @@ class ProviderScheduler { /// child will automatically refresh its parent when it will try to read it for (var i = 0; i < _stateToRefresh.length; i++) { final element = _stateToRefresh[i]; - if (element.hasListeners) element.flush(); + if (element.isActive) element.flush(); } } /// Schedules a provider to be disposed. /// /// The provider will be disposed at the end of the next event-loop, - void scheduleProviderDispose( - AutoDisposeProviderElementMixin element, - ) { + void scheduleProviderDispose(ProviderElement element) { assert( - !element.hasListeners, - 'Tried to dispose ${element._provider} , but still has listeners', + !element.isActive, + 'Tried to dispose ${element.origin} , but still has listeners', ); _stateToDispose.add(element); @@ -124,17 +129,20 @@ class ProviderScheduler { /// and the second time it is traversed, it won't anymore. for (var i = 0; i < _stateToDispose.length; i++) { final element = _stateToDispose[i]; + final links = element.ref?._keepAliveLinks; - final links = element._keepAliveLinks; - - // ignore: deprecated_member_use_from_same_package - if (element.maintainState || - (links != null && links.isNotEmpty) || - element.hasListeners || - element._container._disposed) { + if ((links != null && links.isNotEmpty) || + element.container._disposed || + element.hasNonWeakListeners) { continue; } - element._container._disposeProvider(element._origin); + + if (element.weakDependents.isEmpty) { + element.container._disposeProvider(element.origin); + } else { + // Don't delete the pointer if there are some "weak" listeners active. + element.clearState(); + } } } @@ -143,5 +151,6 @@ class ProviderScheduler { _disposed = true; _pendingTaskCompleter?.complete(); _pendingTaskCompleter = null; + _cancel?.call(); } } diff --git a/packages/riverpod/lib/src/framework.dart b/packages/riverpod/lib/src/framework.dart index 478be6b4f..c722c8cd9 100644 --- a/packages/riverpod/lib/src/framework.dart +++ b/packages/riverpod/lib/src/framework.dart @@ -2,24 +2,30 @@ library framework; import 'dart:async'; import 'dart:collection'; +import 'dart:math' as math; import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:state_notifier/state_notifier.dart'; +import 'package:test/test.dart' as test; import 'common/env.dart'; +import 'common/pragma.dart'; import 'internals.dart'; +import 'mutation.dart'; -part 'framework/always_alive.dart'; -part 'framework/auto_dispose.dart'; -part 'framework/async_selector.dart'; -part 'framework/provider_base.dart'; -part 'framework/element.dart'; -part 'framework/container.dart'; -part 'framework/family.dart'; -part 'framework/listen.dart'; -part 'framework/foundation.dart'; -part 'framework/proxy_provider_listenable.dart'; -part 'framework/ref.dart'; -part 'framework/selector.dart'; -part 'framework/scheduler.dart'; -part 'framework/value_provider.dart'; +part 'core/modifiers/select_async.dart'; +part 'core/provider/provider.dart'; +part 'core/provider/functional_provider.dart'; +part 'core/provider/notifier_provider.dart'; +part 'core/element.dart'; +part 'core/provider_container.dart'; +part 'core/family.dart'; +part 'core/provider_subscription.dart'; +part 'core/foundation.dart'; +part 'core/proxy_provider_listenable.dart'; +part 'core/ref.dart'; +part 'core/modifiers/select.dart'; +part 'core/scheduler.dart'; +part 'core/override_with_value.dart'; +part 'core/override.dart'; +part 'core/modifiers/future.dart'; diff --git a/packages/riverpod/lib/src/framework/always_alive.dart b/packages/riverpod/lib/src/framework/always_alive.dart deleted file mode 100644 index 23a13e239..000000000 --- a/packages/riverpod/lib/src/framework/always_alive.dart +++ /dev/null @@ -1,40 +0,0 @@ -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 -/// both a provider and `provider.select`. -/// -/// Do not implement or extend. -@Deprecated('Will be removed in 3.0.0. Use ProviderListenable instead') -mixin AlwaysAliveProviderListenable on ProviderListenable { - @override - AlwaysAliveProviderListenable select( - Selected Function(State value) selector, - ) { - return _AlwaysAliveProviderSelector( - provider: this, - selector: selector, - ); - } -} - -/// A base class for providers that never dispose themselves. -/// -/// This is the default base class for providers, unless a provider was marked -/// with the `.autoDispose` modifier, like: `Provider.autoDispose(...)` -@Deprecated('Will be removed in 3.0.0. Use ProviderBase instead') -mixin AlwaysAliveProviderBase on ProviderBase - implements - AlwaysAliveProviderListenable, - AlwaysAliveRefreshable { - @override - AlwaysAliveProviderListenable select( - Selected Function(State value) selector, - ) { - return _AlwaysAliveProviderSelector( - provider: this, - selector: selector, - ); - } -} diff --git a/packages/riverpod/lib/src/framework/async_selector.dart b/packages/riverpod/lib/src/framework/async_selector.dart deleted file mode 100644 index 54715fd13..000000000 --- a/packages/riverpod/lib/src/framework/async_selector.dart +++ /dev/null @@ -1,260 +0,0 @@ -part of '../framework.dart'; - -/// Adds [selectAsync] to [ProviderListenable] -@internal -mixin AsyncSelector on ProviderListenable> { - /// The future that [selectAsync] will query - Refreshable> get future; - - /// {@template riverpod.async_select} - /// A variant of [select] for asynchronous values - /// - /// [selectAsync] is useful for filtering rebuilds of a provider - /// when it depends on asynchronous values, which we want to await. - /// - /// A common use-case would be to combine [selectAsync] with - /// [FutureProvider] to perform an async operation, where that - /// async operation depends on the result of another async operation. - /// - /// - /// ```dart - /// // A provider which asynchronously loads configurations, - /// // which may change over time. - /// final configsProvider = StreamProvider((ref) async { - /// // TO-DO fetch the configurations, such as by using Firebase - /// }); - /// - /// // A provider which fetches a list of products based on the configurations - /// final productsProvider = FutureProvider((ref) async { - /// // We obtain the host from the configs, while ignoring changes to - /// // other properties. As such, the productsProvider will rebuild only - /// // if the host changes - /// final host = await ref.watch(configsProvider.selectAsync((config) => config.host)); - /// - /// return http.get('$host/products'); - /// }); - /// ``` - /// {@endtemplate} - ProviderListenable> selectAsync( - Output Function(Input data) selector, - ) { - return _AlwaysAliveAsyncSelector( - selector: selector, - provider: this, - future: future, - ); - } -} - -// ignore: deprecated_member_use_from_same_package -/// Adds [selectAsync] to [AlwaysAliveProviderListenable] -@internal -mixin AlwaysAliveAsyncSelector - // ignore: deprecated_member_use_from_same_package - on AlwaysAliveProviderListenable> { - /// The future that [selectAsync] will query - // ignore: deprecated_member_use_from_same_package - AlwaysAliveRefreshable> get future; - - /// {@macro riverpod.async_select} - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderListenable> selectAsync( - Output Function(Input data) selector, - ) { - return _AlwaysAliveAsyncSelector( - selector: selector, - provider: this, - future: future, - ); - } -} - -class _AlwaysAliveAsyncSelector - extends _AsyncSelector - with - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderListenable> { - _AlwaysAliveAsyncSelector({ - required super.provider, - required super.future, - required super.selector, - }); -} - -/// An internal class for `ProviderBase.selectAsync`. -@sealed -class _AsyncSelector with ProviderListenable> { - /// An internal class for `ProviderBase.select`. - _AsyncSelector({ - required this.provider, - required this.future, - required this.selector, - }); - - /// The provider that was selected - final ProviderListenable> provider; - - /// The future associated to the listened provider - final ProviderListenable> future; - - /// The selector applied - final Output Function(Input) selector; - - Result _select(Input value) { - assert( - () { - _debugIsRunningSelector = true; - return true; - }(), - '', - ); - - try { - return Result.data(selector(value)); - } catch (err, stack) { - return Result.error(err, stack); - } finally { - assert( - () { - _debugIsRunningSelector = false; - return true; - }(), - '', - ); - } - } - - @override - _SelectorSubscription, Future> addListener( - Node node, - void Function(Future? previous, Future next) listener, { - required void Function(Object error, StackTrace stackTrace)? onError, - required void Function()? onDependencyMayHaveChanged, - required bool fireImmediately, - }) { - Result? lastSelectedValue; - Completer? selectedCompleter; - Future? selectedFuture; - - void emitData(Output data, {required bool callListeners}) { - final previousFuture = selectedFuture; - if (selectedCompleter != null) { - selectedCompleter!.complete(data); - selectedCompleter = null; - } else { - selectedFuture = Future.value(data); - if (callListeners) listener(previousFuture, selectedFuture!); - } - } - - void emitError( - Object err, - StackTrace? stack, { - required bool callListeners, - }) { - final previousFuture = selectedFuture; - if (selectedCompleter != null) { - selectedCompleter!.completeError(err, stack); - selectedCompleter = null; - } else { - selectedFuture = Future.error(err, stack); - if (callListeners) listener(previousFuture, selectedFuture!); - } - } - - void playValue( - AsyncValue value, { - bool callListeners = true, - }) { - void onLoading(AsyncValue loading) { - if (selectedFuture == null) { - // The first time a future is emitted - - selectedCompleter = Completer(); - selectedFuture = selectedCompleter!.future; - } - - // We don't notify listeners when the future changes since - // they want to filter rebuilds based on the result - } - - value.map( - loading: onLoading, - data: (value) { - if (value.isRefreshing) { - onLoading(value); - return; - } - - final newSelectedValue = _select(value.value); - - newSelectedValue.map( - data: (newSelectedValue) { - if (newSelectedValue != lastSelectedValue) { - emitData( - newSelectedValue.state, - callListeners: callListeners, - ); - } - }, - error: (newSelectedValue) { - emitError( - newSelectedValue.error, - newSelectedValue.stackTrace, - callListeners: callListeners, - ); - }, - ); - - lastSelectedValue = newSelectedValue; - }, - error: (value) { - if (value.isRefreshing) { - onLoading(value); - return; - } - - emitError( - value.error, - value.stackTrace, - callListeners: callListeners, - ); - - // Error in the provider, it should've already been propagated - // so no need to pollute the stack - selectedFuture!.ignore(); - }, - ); - } - - final sub = node.listen>( - provider, - (prev, input) => playValue(input), - onError: onError, - ); - - playValue(sub.read(), callListeners: false); - - if (fireImmediately) { - listener(null, selectedFuture!); - } - - return _SelectorSubscription( - node, - sub, - () => selectedFuture!, - onClose: () { - final completer = selectedCompleter; - if (completer != null && !completer.isCompleted) { - read(node).then( - completer.complete, - onError: completer.completeError, - ); - } - }, - ); - } - - @override - Future read(Node node) => future.read(node).then(selector); -} diff --git a/packages/riverpod/lib/src/framework/auto_dispose.dart b/packages/riverpod/lib/src/framework/auto_dispose.dart deleted file mode 100644 index 8ca75f381..000000000 --- a/packages/riverpod/lib/src/framework/auto_dispose.dart +++ /dev/null @@ -1,50 +0,0 @@ -part of '../framework.dart'; - -/// A mixin that adds auto dispose support to a [ProviderElementBase]. -@internal -mixin AutoDisposeProviderElementMixin on ProviderElementBase - implements - // ignore: deprecated_member_use_from_same_package - AutoDisposeRef { - bool _maintainState = false; - @Deprecated('Use `keepAlive()` instead') - @override - bool get maintainState => _maintainState; - @override - set maintainState(bool value) { - _maintainState = value; - if (!value) mayNeedDispose(); - } - - @override - void mayNeedDispose() { - final links = _keepAliveLinks; - - // ignore: deprecated_member_use_from_same_package - if (!maintainState && !hasListeners && (links == null || links.isEmpty)) { - _container.scheduler.scheduleProviderDispose(this); - } - } - - @override - void runOnDispose() { - _keepAliveLinks?.clear(); - super.runOnDispose(); - assert( - _keepAliveLinks == null || _keepAliveLinks!.isEmpty, - 'Cannot call keepAlive() within onDispose listeners', - ); - } -} - -/// A object which maintains a provider alive. -class KeepAliveLink { - KeepAliveLink._(this._close); - - final void Function() _close; - - /// Release this [KeepAliveLink], allowing the associated provider to - /// be disposed if the provider is no-longer listener nor has any - /// remaining [KeepAliveLink]. - void close() => _close(); -} diff --git a/packages/riverpod/lib/src/framework/container.dart b/packages/riverpod/lib/src/framework/container.dart deleted file mode 100644 index 99beff446..000000000 --- a/packages/riverpod/lib/src/framework/container.dart +++ /dev/null @@ -1,803 +0,0 @@ -part of '../framework.dart'; - -ProviderBase? _circularDependencyLock; - -class _FamilyOverrideRef { - _FamilyOverrideRef(this.override, this.container); - - FamilyOverride override; - final ProviderContainer container; -} - -/// An object that contains a [ProviderElementBase]. -/// -/// This object is used to implement the scoping mechanism of providers, -/// by allowing a [ProviderContainer] to "inherit" the [_StateReader]s from -/// its ancestor, while preserving the "mount providers on demand" logic. -class _StateReader { - _StateReader({ - required this.origin, - required this.override, - required this.container, - required this.isDynamicallyCreated, - }); - - final ProviderBase origin; - ProviderBase override; - final ProviderContainer container; - - /// Whether the [_StateReader] was created on first provider read instead of - /// at the creation of the [ProviderContainer] - final bool isDynamicallyCreated; - - ProviderElementBase? _element; - - ProviderElementBase getElement() => _element ??= _create(); - - ProviderElementBase _create() { - if (origin == _circularDependencyLock) { - throw CircularDependencyError._(); - } - _circularDependencyLock ??= origin; - try { - final element = override.createElement() - .._provider = override - .._origin = origin - .._container = container - ..mount(); - - element.getState()!.map( - // ignore: avoid_types_on_closure_parameters - data: (ResultData data) { - for (final observer in container.observers) { - runTernaryGuarded( - observer.didAddProvider, - origin, - data.state, - container, - ); - } - }, - error: (error) { - for (final observer in container.observers) { - runTernaryGuarded( - observer.didAddProvider, - origin, - null, - container, - ); - } - for (final observer in container.observers) { - runQuaternaryGuarded( - observer.providerDidFail, - origin, - error.error, - error.stackTrace, - container, - ); - } - }, - ); - return element; - } finally { - if (_circularDependencyLock == origin) { - _circularDependencyLock = null; - } - } - } -} - -var _debugVerifyDependenciesAreRespectedEnabled = true; - -/// {@template riverpod.provider_container} -/// An object that stores the state of the providers and allows overriding the -/// behavior of a specific provider. -/// -/// If you are using Flutter, you do not need to care about this object -/// (outside of testing), as it is implicitly created for you by `ProviderScope`. -/// {@endtemplate} -@sealed -class ProviderContainer implements Node { - /// {@macro riverpod.provider_container} - ProviderContainer({ - ProviderContainer? parent, - List overrides = const [], - List? observers, - }) : _debugOverridesLength = overrides.length, - depth = parent == null ? 0 : parent.depth + 1, - _parent = parent, - observers = [ - ...?observers, - if (parent != null) ...parent.observers, - ], - _stateReaders = { - if (parent != null) - for (final entry in parent._stateReaders.entries) - if (!entry.value.isDynamicallyCreated) entry.key: entry.value, - }, - _root = parent?._root ?? parent { - if (parent != null) { - parent._children.add(this); - _overrideForFamily.addAll(parent._overrideForFamily); - } - - for (final override in overrides) { - if (override is ProviderOverride) { - _overrideForProvider[override._origin] = override._override; - _stateReaders[override._origin] = _StateReader( - origin: override._origin, - override: override._override, - container: this, - isDynamicallyCreated: false, - ); - } else if (override is FamilyOverride) { - _overrideForFamily[override.overriddenFamily] = _FamilyOverrideRef( - override, - this, - ); - } - } - } - - final int _debugOverridesLength; - - /// A function that controls the refresh rate of providers. - /// - /// Defaults to refreshing providers at the end of the next event-loop. - @Deprecated('Will be removed in 3.0.0') - @internal - void Function(void Function() task) get vsync { - return vsyncOverride ?? _defaultVsync; - } - - /// A way to override [vsync], used by Flutter to synchronize a container - /// with the widget tree. - @Deprecated('Will be removed in 3.0.0') - @internal - void Function(void Function() task)? vsyncOverride; - - /// The object that handles when providers are refreshed and disposed. - @internal - late final ProviderScheduler scheduler = ProviderScheduler(); - - /// How deep this [ProviderContainer] is in the graph of containers. - /// - /// Starts at 0. - final int depth; - final ProviderContainer? _root; - final ProviderContainer? _parent; - - final _children = []; - - /// All the containers that have this container as `parent`. - /// - /// Do not use in production - List get debugChildren => UnmodifiableListView(_children); - - final _overrideForProvider = - HashMap, ProviderBase>(); - final _overrideForFamily = HashMap, _FamilyOverrideRef>(); - final Map, _StateReader> _stateReaders; - - /// The list of observers attached to this container. - /// - /// Observers can be useful for logging purpose. - /// - /// This list includes the observers of this container and that of its "parent" - /// too. - final List observers; - - /// A debug utility used by `flutter_riverpod`/`hooks_riverpod` to check - /// if it is safe to modify a provider. - /// - /// This corresponds to all the widgets that a [Provider] is associated with. - @Deprecated('Will be removed in 3.0.0') - @internal - void Function()? debugCanModifyProviders; - - /// Whether [dispose] was called or not. - /// - /// This disables the different methods of [ProviderContainer], resulting in - /// a [StateError] when attempting to use them. - bool _disposed = false; - - /// An internal utility for checking if a [ProviderContainer] has a fast - /// path for reading a provider. - /// - /// This should not be used and is an implementation detail of [ProviderContainer]. - /// It could be removed at any time without a major version bump. - @internal - @visibleForTesting - bool hasStateReaderFor(ProviderListenable provider) { - return _stateReaders.containsKey(provider); - } - - /// Awaits for providers to rebuild/be disposed and for listeners to be notified. - Future pump() async { - final a = scheduler.pendingFuture; - final b = _parent?.scheduler.pendingFuture; - - await Future.wait([ - if (a != null) a, - if (b != null) b, - ]); - } - - /// Reads a provider without listening to it and returns the currently - /// exposed value. - /// - /// ```dart - /// final greetingProvider = Provider((_) => 'Hello world'); - /// - /// void main() { - /// final container = ProviderContainer(); - /// - /// print(container.read(greetingProvider)); // Hello World - /// } - /// ``` - Result read( - ProviderListenable provider, - ) { - return provider.read(this); - } - - /// {@macro riverpod.exists} - bool exists(ProviderBase provider) { - final element = _getOrNull(provider)?._element; - - return element != null; - } - - /// Executes [ProviderElementBase.debugReassemble] on all the providers. - void debugReassemble() { -// TODO hot-reload handle provider type change -// TODO hot-reload handle provider response type change -// TODO hot-reload handle provider -> family -// TODO hot-reload handle family adding parameters -// TODO found "Future already completed error" after adding family parameter - - assert( - () { - for (final element in getAllProviderElements()) { - element.debugReassemble(); - } - - return true; - }(), - '', - ); - } - - /// {@macro riverpod.listen} - @override - ProviderSubscription listen( - ProviderListenable provider, - void Function(State? previous, State next) listener, { - bool fireImmediately = false, - void Function(Object error, StackTrace stackTrace)? onError, - }) { - // TODO test always flushed provider - return provider.addListener( - this, - listener, - fireImmediately: fireImmediately, - onError: onError, - onDependencyMayHaveChanged: null, - ); - } - - /// {@macro riverpod.invalidate} - void invalidate(ProviderOrFamily provider) { - if (provider is ProviderBase) { - final reader = _getOrNull(provider); - - reader?._element?.invalidateSelf(); - } else { - provider as Family; - - final familyContainer = - _overrideForFamily[provider]?.container ?? _root ?? this; - - for (final stateReader in familyContainer._stateReaders.values) { - if (stateReader.origin.from != provider) continue; - stateReader._element?.invalidateSelf(); - } - } - } - - /// {@macro riverpod.refresh} - State refresh(Refreshable provider) { - invalidate(provider._origin); - return read(provider); - } - - void _disposeProvider(ProviderBase provider) { - final reader = _getOrNull(provider); - // The provider is already disposed, so we don't need to do anything - if (reader == null) return; - - reader._element?.dispose(); - - if (reader.isDynamicallyCreated) { - // Since the StateReader is implicitly created, we don't keep it - // on provider dispose, to avoid memory leak - - void removeStateReaderFrom(ProviderContainer container) { - /// Checking if the reader is the same instance is important, - /// as it is possible that the provider was overridden. - if (container._stateReaders[provider] == reader) { - container._stateReaders.remove(provider); - } - container._children.forEach(removeStateReaderFrom); - } - - removeStateReaderFrom(this); - } else { - reader._element = null; - } - } - - /// Updates the list of provider overrides. - /// - /// If you are using flutter, this is done implicitly for you by `ProviderScope`. - /// - /// Updating a `overrideWithValue` with a different value - /// will cause the listeners to rebuild. - /// - /// It is not possible, to remove or add new overrides, only update existing ones. - void updateOverrides(List overrides) { - if (_disposed) { - throw StateError( - 'Called updateOverrides on a ProviderContainer that was already disposed', - ); - } - - assert( - _debugOverridesLength == overrides.length, - 'Tried to change the number of overrides. This is not allowed – ' - 'overrides cannot be removed/added, they can only be updated.', - ); - - List? unusedOverrides; - assert( - () { - unusedOverrides = [...overrides]; - return true; - }(), - '', - ); - - for (final override in overrides) { - if (override is ProviderOverride) { - assert( - () { - unusedOverrides!.remove(override); - return true; - }(), - '', - ); - - assert( - _overrideForProvider[override._origin].runtimeType == - override._override.runtimeType, - 'Replaced the override of type ${_overrideForProvider[override._origin].runtimeType} ' - 'with an override of type ${override._override.runtimeType}, which is different.\n' - 'Changing the kind of override or reordering overrides is not supported.', - ); - - // _stateReaders[origin] cannot be null for overridden providers. - final reader = _stateReaders[override._origin]!; - - reader.override = - _overrideForProvider[override._origin] = override._override; - - final element = reader._element; - if (element == null) continue; - - runUnaryGuarded(element.update, override._override); - } else if (override is FamilyOverride) { - assert( - () { - unusedOverrides!.remove(override); - return true; - }(), - '', - ); - // TODO assert family override did not change - - _overrideForFamily[override.overriddenFamily]!.override = override; - } - } - - assert( - unusedOverrides!.isEmpty, - 'Updated the list of overrides with providers that were not overridden before', - ); - } - - @override - ProviderElementBase readProviderElement( - ProviderBase provider, - ) { - if (_disposed) { - throw StateError( - 'Tried to read a provider from a ProviderContainer that was already disposed', - ); - } - - final reader = _putIfAbsent(provider); - - assert( - () { - // Avoid having the assert trigger itself exponentially - if (!_debugVerifyDependenciesAreRespectedEnabled) return true; - - try { - _debugVerifyDependenciesAreRespectedEnabled = false; - - // Check that this containers doesn't have access to an overridden - // dependency of the targeted provider - final targetElement = reader.getElement(); - final visitedDependencies = >{}; - final queue = Queue>(); - targetElement.visitAncestors((e) => queue.add(e.origin)); - - while (queue.isNotEmpty) { - final dependency = queue.removeFirst(); - if (visitedDependencies.add(dependency)) { - final dependencyElement = - readProviderElement(dependency); - - assert( - targetElement.provider != targetElement.origin || - dependencyElement == - targetElement.container - .readProviderElement(dependency), - ''' -Tried to read $provider from a place where one of its dependencies were overridden but the provider is not. - -To fix this error, you can add $dependency (a) to the "dependencies" of $provider (b) such that we have: - -``` -final a = Provider(...); -final b = Provider((ref) => ref.watch(a), dependencies: [a]); -``` -''', - ); - - dependencyElement.visitAncestors((e) => queue.add(e.origin)); - } - } - } finally { - _debugVerifyDependenciesAreRespectedEnabled = true; - } - return true; - }(), - '', - ); - - return reader.getElement() as ProviderElementBase; - } - - /// Obtains a [_StateReader] for a provider, but do not create it if it does - /// not exist. - _StateReader? _getOrNull(ProviderBase provider) { - return _stateReaders[provider] ?? - - /// No need to check "parent". We can directly check "root", because - /// if the provider is not in the root, it must have been overridden. - /// In which case, it is guaranteed to be in the current container already. - _root?._getOrNull(provider); - } - - /// Create a [_StateReader] for a provider if it does not exist. - /// If one already exists, returns it. - _StateReader _putIfAbsent(ProviderBase provider) { - final currentReader = _stateReaders[provider]; - if (currentReader != null) return currentReader; - - _StateReader getReader() { - if (provider.from != null) { - // reading a family - - final familyOverrideRef = _overrideForFamily[provider.from]; - if (familyOverrideRef != null) { - // A family was overridden, so we implicitly mount the readers - - if (familyOverrideRef.container._stateReaders.containsKey(provider)) { - return familyOverrideRef.container._stateReaders[provider]!; - } - - void setupOverride({ - required ProviderBase origin, - required ProviderBase override, - }) { - assert( - origin == override || override.dependencies == null, - 'A provider override cannot specify `dependencies`', - ); - - // setupOverride may be called multiple times on different providers - // of the same family (provider vs provider.modifier), so we use ??= - // to initialize the providers only once - familyOverrideRef.container._stateReaders[origin] ??= _StateReader( - origin: origin, - override: override, - container: familyOverrideRef.container, - isDynamicallyCreated: true, - ); - } - - final providerOverride = - familyOverrideRef.override.getProviderOverride(provider); - - setupOverride(origin: provider, override: providerOverride); - - // if setupOverride overrode the provider, it was already initialized - // in the code above. Otherwise we initialize it as if it was not overridden - return familyOverrideRef.container._stateReaders[provider] ?? - _StateReader( - origin: provider, - override: provider, - container: familyOverrideRef.container, - isDynamicallyCreated: true, - ); - } - } - - final root = _root; - if (root != null) { - // On scoped containers, check for implicit override. - - final dependencies = provider.from?.allTransitiveDependencies ?? - provider.allTransitiveDependencies; - - final containerForDependencyOverride = dependencies - ?.map((dep) { - final reader = _stateReaders[dep]; - if (reader != null) { - return reader.container; - } - final familyOverride = _overrideForFamily[dep]; - return familyOverride?.container; - }) - .where((container) => container != null) - .toList(); - - if (containerForDependencyOverride != null && - containerForDependencyOverride.isNotEmpty) { - // a dependency of the provider was overridden, so the provider is overridden too - - final deepestOverrideContainer = containerForDependencyOverride - .fold(root, (previous, container) { - if (container!.depth > previous.depth) { - return container; - } - return previous; - }); - - /// Insert the StateReader in the container that it belongs to, - /// and import it locally - return deepestOverrideContainer._stateReaders.putIfAbsent(provider, - () { - return _StateReader( - origin: provider, - override: provider, - container: deepestOverrideContainer, - isDynamicallyCreated: true, - ); - }); - } - } - - if (_root?._stateReaders.containsKey(provider) ?? false) { - // For un-overridden providers, it is possible that the provider was - // read in the root ProviderContainer before this container. In which case - // we reuse the existing state instead of creating a new one. - return _root!._stateReaders[provider]!; - } - - // The provider had no existing state and no override, so we're - // mounting it on the root container. - final reader = _StateReader( - origin: provider, - // If a provider did not have an associated StateReader then it is - // guaranteed to not be overridden - override: provider, - container: _root ?? this, - isDynamicallyCreated: true, - ); - - if (_root != null) { - _root!._stateReaders[provider] = reader; - } - - return reader; - } - - return _stateReaders[provider] = getReader(); - } - - /// Release all the resources associated with this [ProviderContainer]. - /// - /// This will destroy the state of all providers associated with this - /// [ProviderContainer] and call [Ref.onDispose] listeners. - void dispose() { - if (_disposed) return; - - _disposed = true; - _parent?._children.remove(this); - - if (_root == null) scheduler.dispose(); - - for (final element in getAllProviderElementsInOrder().toList().reversed) { - element.dispose(); - } - } - - /// Traverse the [ProviderElementBase]s associated with this [ProviderContainer]. - Iterable getAllProviderElements() sync* { - for (final reader in _stateReaders.values) { - if (reader._element != null && reader.container == this) { - yield reader._element!; - } - } - } - - /// Visit all nodes of the graph at most once, from roots to leaves. - /// - /// This is fairly expensive and should be avoided as much as possible. - /// If you do not need for providers to be sorted, consider using [getAllProviderElements] - /// instead, which returns an unsorted list and is significantly faster. - Iterable getAllProviderElementsInOrder() sync* { - final visitedNodes = HashSet(); - final queue = DoubleLinkedQueue(); - - // get providers that don't depend on other providers from this container - for (final reader in _stateReaders.values) { - if (reader.container != this) continue; - final element = reader._element; - if (element == null) continue; - - var hasAncestorsInContainer = false; - element.visitAncestors((element) { - // We ignore dependencies that are defined in another container, as - // they are in a separate graph - if (element._container == this) { - hasAncestorsInContainer = true; - } - }); - - if (!hasAncestorsInContainer) { - queue.add(element); - } - } - - while (queue.isNotEmpty) { - final element = queue.removeFirst(); - - if (!visitedNodes.add(element)) { - // Already visited - continue; - } - - yield element; - - // Queue the children of this element, but only if all of its ancestors - // were already visited before. - // If a child does not have all of its ancestors visited, when those - // ancestors will be visited, they will retry visiting this child. - element.visitChildren( - elementVisitor: (dependent) { - if (dependent.container == this) { - // All the parents of a node must have been visited before a node is visited - var areAllAncestorsAlreadyVisited = true; - dependent.visitAncestors((e) { - if (e._container == this && !visitedNodes.contains(e)) { - areAllAncestorsAlreadyVisited = false; - } - }); - - if (areAllAncestorsAlreadyVisited) queue.add(dependent); - } - }, - // We only care about Elements here, so let's ignore notifiers - notifierVisitor: (_) {}, - ); - } - } -} - -/// An object that listens to the changes of a [ProviderContainer]. -/// -/// This can be used for logging or making devtools. -abstract class ProviderObserver { - /// An object that listens to the changes of a [ProviderContainer]. - /// - /// This can be used for logging or making devtools. - const ProviderObserver(); - - /// A provider was initialized, and the value exposed is [value]. - /// - /// [value] will be `null` if the provider threw during initialization. - void didAddProvider( - ProviderBase provider, - Object? value, - ProviderContainer container, - ) {} - - /// A provider emitted an error, be it by throwing during initialization - /// or by having a [Future]/[Stream] emit an error - void providerDidFail( - ProviderBase provider, - Object error, - StackTrace stackTrace, - ProviderContainer container, - ) {} - - /// Called by providers when they emit a notification. - /// - /// - [newValue] will be `null` if the provider threw during initialization. - /// - [previousValue] will be `null` if the previous build threw during initialization. - void didUpdateProvider( - ProviderBase provider, - Object? previousValue, - Object? newValue, - ProviderContainer container, - ) {} - - /// A provider was disposed - void didDisposeProvider( - ProviderBase provider, - ProviderContainer container, - ) {} -} - -/// An implementation detail for the override mechanism of providers -@internal -typedef SetupOverride = void Function({ - required ProviderBase origin, - required ProviderBase override, -}); - -/// An object used by [ProviderContainer] to override the behavior of a provider -/// for a part of the application. -/// -/// Do not implement/extend this class. -/// -/// See also: -/// -/// - [ProviderContainer], which uses this object. -/// - `overrideWithValue`, which creates a [ProviderOverride]. -@internal -class ProviderOverride implements Override { - /// Override a provider - ProviderOverride({ - required ProviderBase origin, - required ProviderBase override, - }) : _origin = origin, - _override = override; - - /// The provider that is overridden. - final ProviderBase _origin; - - /// The new provider behavior. - final ProviderBase _override; -} - -/// An object used by [ProviderContainer]/`ProviderScope` to override the behavior -/// of a provider/family for part of the application. -/// -/// Do not extend or implement. -abstract class Override {} - -/// An error thrown when a call to [Ref.read]/[Ref.watch] -/// leads to a provider depending on itself. -/// -/// Circular dependencies are both not supported for performance reasons -/// and maintainability reasons. -/// Consider reading about unidirectional data flow to learn about the -/// benefits of avoiding circular dependencies. -class CircularDependencyError extends Error { - CircularDependencyError._(); -} diff --git a/packages/riverpod/lib/src/framework/element.dart b/packages/riverpod/lib/src/framework/element.dart deleted file mode 100644 index e87a9188c..000000000 --- a/packages/riverpod/lib/src/framework/element.dart +++ /dev/null @@ -1,987 +0,0 @@ -part of '../framework.dart'; - -/// {@template riverpod.refreshable} -/// An interface for provider expressions that can be passed to `ref.refresh` -/// -/// This differentiates: -/// -/// ```dart -/// ref.watch(provider); -/// ref.watch(provider.future); -/// ``` -/// -/// from: -/// -/// ```dart -/// ref.watch(provider.select((value) => value)); -/// ``` -/// {@endtemplate} -abstract class Refreshable implements ProviderListenable { - /// The provider that is being refreshed. - ProviderBase get _origin; -} - -/// {@macro riverpod.refreshable} -@Deprecated('Will be removed in 3.0.0. Use Refreshable instead') -abstract class AlwaysAliveRefreshable - implements Refreshable, AlwaysAliveProviderListenable {} - -/// A debug utility used by `flutter_riverpod`/`hooks_riverpod` to check -/// if it is safe to modify a provider. -/// -/// This corresponds to all the widgets that a [Provider] is associated with. -@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 one of its dependencies changes. -/// -/// This class is not meant to be used directly and is an implementation detail -/// of providers. -/// Do not use. -/// {@endtemplate} -@optionalTypeArgs -abstract class ProviderElementBase implements Ref, Node { - /// {@macro riverpod.provider_element_base} - ProviderElementBase(this._provider); - - static ProviderElementBase? _debugCurrentlyBuildingElement; - - /// The last result of [ProviderBase.debugGetCreateSourceHash]. - /// - /// Available only in debug mode. - String? _debugCurrentCreateHash; - var _debugSkipNotifyListenersAsserts = false; - - /// The provider associated with this [ProviderElementBase], before applying overrides. - // Not typed as because of https://github.com/rrousselGit/riverpod/issues/1100 - ProviderBase get origin => _origin; - late ProviderBase _origin; - - /// The provider associated with this [ProviderElementBase], after applying overrides. - ProviderBase get provider => _provider; - ProviderBase _provider; - - /// The [ProviderContainer] that owns this [ProviderElementBase]. - @override - ProviderContainer get container => _container; - late final ProviderContainer _container; - - /// Whether this [ProviderElementBase] is currently listened to or not. - /// - /// This maps to listeners added with [listen]. - /// See also [mayNeedDispose], called when [hasListeners] may have changed. - bool get hasListeners => - (_dependents?.isNotEmpty ?? false) || _providerDependents.isNotEmpty; - - List? _keepAliveLinks; - var _dependencies = HashMap, Object>(); - HashMap, Object>? _previousDependencies; - List? _subscriptions; - List? _dependents; - - /// The element of the providers that depends on this provider. - final _providerDependents = >[]; - - List? _onDisposeListeners; - List? _onResumeListeners; - List? _onCancelListeners; - List? _onAddListeners; - List? _onRemoveListeners; - List? _onChangeSelfListeners; - List? _onErrorSelfListeners; - - bool _mustRecomputeState = false; - bool _dependencyMayHaveChanged = false; - bool _didChangeDependency = false; - - var _didCancelOnce = false; - - /// Whether the element was disposed or not - @internal - bool get mounted => _mounted; - bool _mounted = false; - - /// Whether the assert that prevents [requireState] from returning - /// if the state was not set before is enabled. - @visibleForOverriding - bool get debugAssertDidSetStateEnabled => true; - - bool _debugDidSetState = false; - bool _didBuild = false; - - /* STATE */ - Result? _state; - - /// Update the exposed value of a provider and notify its listeners. - /// - /// Listeners will only be notified if [updateShouldNotify] - /// returns true. - /// - /// This API is not meant for public consumption. Instead if a [Ref] needs - /// to expose a way to update the state, the practice is to expose a getter/setter. - @internal - void setState(StateT newState) { - assert( - () { - _debugDidSetState = true; - return true; - }(), - '', - ); - final previousResult = getState(); - final result = _state = ResultData(newState); - - if (_didBuild) { - _notifyListeners(result, previousResult); - } - } - - /// Obtains the current state, or null if the provider has yet to initialize. - /// - /// The returned object will contain error information, if any. - /// This function does not cause the provider to rebuild if it somehow was - /// outdated. - /// - /// This is not meant for public consumption. Instead, public API should use - /// [readSelf]. - @internal - Result? getState() => _state; - - /// Read the current value of a provider and: - /// - /// - if in error state, rethrow the error - /// - if the provider is not initialized, gracefully handle the error. - /// - /// This is not meant for public consumption. Instead, public API should use - /// [readSelf]. - @internal - StateT get requireState { - assert( - () { - if (debugAssertDidSetStateEnabled && !_debugDidSetState) { - throw StateError( - 'Tried to read the state of an uninitialized provider', - ); - } - return true; - }(), - '', - ); - - final state = getState(); - if (state == null) { - throw StateError('Tried to read the state of an uninitialized provider'); - } - - return state.when( - error: throwErrorWithCombinedStackTrace, - data: (data) => data, - ); - } - - /// Called when a provider is rebuilt. Used for providers to not notify their - /// listeners if the exposed value did not change. - @internal - bool updateShouldNotify(StateT previous, StateT next); - - /* /STATE */ - - /// A life-cycle executed when a hot-reload is performed. - /// - /// This is equivalent to Flutter's `State.reassemble`. - /// - /// This life-cycle is used to check for change in [ProviderBase.debugGetCreateSourceHash], - /// and invalidate the provider state on change. - void debugReassemble() { - assert( - () { - final previousHash = _debugCurrentCreateHash; - _debugCurrentCreateHash = provider.debugGetCreateSourceHash?.call(); - - if (previousHash != _debugCurrentCreateHash) { - invalidateSelf(); - } - - return true; - }(), - '', - ); - } - - /// Called the first time a provider is obtained. - @internal - void mount() { - _mounted = true; - assert( - () { - _debugCurrentCreateHash = provider.debugGetCreateSourceHash?.call(); - - return true; - }(), - '', - ); - buildState(); - - _state!.map( - data: (newState) { - final onChangeSelfListeners = _onChangeSelfListeners; - if (onChangeSelfListeners != null) { - for (var i = 0; i < onChangeSelfListeners.length; i++) { - Zone.current.runBinaryGuarded( - onChangeSelfListeners[i], - null, - newState.state, - ); - } - } - }, - error: (newState) { - final onErrorSelfListeners = _onErrorSelfListeners; - if (onErrorSelfListeners != null) { - for (var i = 0; i < onErrorSelfListeners.length; i++) { - Zone.current.runBinaryGuarded( - onErrorSelfListeners[i], - newState.error, - newState.stackTrace, - ); - } - } - }, - ); - } - - // ignore: use_setters_to_change_properties - /// Called when the override of a provider changes. - /// - /// See also: - /// - `overrideWithValue`, which relies on [update] to handle - /// the scenario where the value changed. - @internal - // ignore: use_setters_to_change_properties - void update(ProviderBase newProvider) { - _provider = newProvider; - } - - @override - KeepAliveLink keepAlive() { - final links = _keepAliveLinks ??= []; - - late KeepAliveLink link; - link = KeepAliveLink._(() { - if (links.remove(link)) { - if (links.isEmpty) mayNeedDispose(); - } - }); - links.add(link); - - return link; - } - - @override - void invalidate(ProviderOrFamily provider) { - assert(_debugAssertCanDependOn(provider), ''); - _container.invalidate(provider); - } - - @override - void invalidateSelf() { - if (_mustRecomputeState) return; - - _mustRecomputeState = true; - runOnDispose(); - mayNeedDispose(); - _container.scheduler.scheduleProviderRefresh(this); - - // We don't call this._markDependencyMayHaveChanged here because we voluntarily - // do not want to set the _dependencyMayHaveChanged flag to true. - // Since the dependency is known to have changed, there is no reason to try - // and "flush" it, as it will already get rebuilt. - visitChildren( - elementVisitor: (element) => element._markDependencyMayHaveChanged(), - notifierVisitor: (notifier) => notifier.notifyDependencyMayHaveChanged(), - ); - } - - /// A utility for re-initializing a provider when needed. - /// - /// Calling [flush] will only re-initialize the provider if it needs to rerun. - /// This can involve: - /// - a previous call to [invalidateSelf] - /// - a dependency of the provider has changed (such as when using [watch]). - /// - /// This is not meant for public consumption. Public API should hide - /// [flush] from users, such that they don't need to care about invoking this function. - @internal - void flush() { - _maybeRebuildDependencies(); - if (_mustRecomputeState) { - _mustRecomputeState = false; - _performBuild(); - } - } - - /// Iterates over the dependencies of this provider, calling [flush] on them too. - /// - /// This work is only performed if a dependency has notified that it might - /// need to be re-executed. - void _maybeRebuildDependencies() { - if (!_dependencyMayHaveChanged) return; - - _dependencyMayHaveChanged = false; - - visitAncestors( - (element) => element.flush(), - ); - } - - /// Initialize a provider and track dependencies used during the initialization. - /// - /// After a provider is initialized, this function takes care of unsubscribing - /// to dependencies that are no-longer used. - void _performBuild() { - assert( - _previousDependencies == null, - 'Bad state: _performBuild was called twice', - ); - final previousDependencies = _previousDependencies = _dependencies; - _dependencies = HashMap(); - - final previousStateResult = _state; - - assert( - () { - _debugDidSetState = false; - return true; - }(), - '', - ); - buildState(); - - if (!identical(_state, previousStateResult)) { - assert( - () { - // Asserts would otherwise prevent a provider rebuild from updating - // other providers - _debugSkipNotifyListenersAsserts = true; - return true; - }(), - '', - ); - _notifyListeners(_state!, previousStateResult); - assert( - () { - _debugSkipNotifyListenersAsserts = false; - return true; - }(), - '', - ); - } - - // Unsubscribe to everything that a provider no longer depends on. - for (final sub in previousDependencies.entries) { - sub.key - .._providerDependents.remove(this) - .._onRemoveListener(); - } - _previousDependencies = null; - - // TODO clear subscriptions only after the provider has been rebuilt - } - - /// Initialize a provider. - /// - /// This function **must** call [setState] or throw (or both). - /// - /// Exceptions within this function will be caught and set the provider in error - /// state. Then, reading this provider will rethrow the thrown exception. - /// - /// - [didChangeDependency] can be used to differentiate a rebuild caused - /// by [watch] from one caused by [refresh]/[invalidate]. - @visibleForOverriding - void create({required bool didChangeDependency}); - - /// Invokes [create] and handles errors. - @internal - void buildState() { - ProviderElementBase? debugPreviouslyBuildingElement; - final previousDidChangeDependency = _didChangeDependency; - _didChangeDependency = false; - assert( - () { - debugPreviouslyBuildingElement = _debugCurrentlyBuildingElement; - _debugCurrentlyBuildingElement = this; - return true; - }(), - '', - ); - _didBuild = false; - try { - // TODO move outside this function? - _mounted = true; - create(didChangeDependency: previousDidChangeDependency); - } catch (err, stack) { - assert( - () { - _debugDidSetState = true; - return true; - }(), - '', - ); - _state = Result.error(err, stack); - } finally { - _didBuild = true; - assert( - () { - _debugCurrentlyBuildingElement = debugPreviouslyBuildingElement; - return true; - }(), - '', - ); - - assert( - getState() != null, - 'Bad state, the provider did not initialize. Did "create" forget to set the state?', - ); - } - } - - @override - void notifyListeners() { - final currentResult = getState(); - // If `notifyListeners` is used during `build`, the result will be null. - // Throwing would be unnecessarily inconvenient, so we simply skip it. - if (currentResult == null) return; - - if (_didBuild) { - _notifyListeners( - currentResult, - currentResult, - checkUpdateShouldNotify: false, - ); - } - } - - void _notifyListeners( - Result newState, - Result? previousStateResult, { - bool checkUpdateShouldNotify = true, - }) { - assert( - () { - if (_debugSkipNotifyListenersAsserts) return true; - - assert( - _debugCurrentlyBuildingElement == null || - _debugCurrentlyBuildingElement == this, - ''' -Providers are not allowed to modify other providers during their initialization. - -The provider ${_debugCurrentlyBuildingElement!.origin} modified $origin while building. -''', - ); - - debugCanModifyProviders?.call(); - return true; - }(), - '', - ); - - final previousState = previousStateResult?.stateOrNull; - - // listenSelf listeners do not respect updateShouldNotify - newState.map( - data: (newState) { - final onChangeSelfListeners = _onChangeSelfListeners; - if (onChangeSelfListeners != null) { - for (var i = 0; i < onChangeSelfListeners.length; i++) { - Zone.current.runBinaryGuarded( - onChangeSelfListeners[i], - previousState, - newState.state, - ); - } - } - }, - error: (newState) { - final onErrorSelfListeners = _onErrorSelfListeners; - if (onErrorSelfListeners != null) { - for (var i = 0; i < onErrorSelfListeners.length; i++) { - Zone.current.runBinaryGuarded( - onErrorSelfListeners[i], - newState.error, - newState.stackTrace, - ); - } - } - }, - ); - - if (checkUpdateShouldNotify && - previousStateResult != null && - previousStateResult.hasState && - newState.hasState && - !updateShouldNotify( - previousState as StateT, - newState.requireState, - )) { - return; - } - - final listeners = _dependents?.toList(growable: false); - newState.map( - data: (newState) { - if (listeners != null) { - for (var i = 0; i < listeners.length; i++) { - final listener = listeners[i]; - if (listener is _ProviderStateSubscription) { - Zone.current.runBinaryGuarded( - listener.listener, - previousState, - newState.state, - ); - } - } - } - }, - error: (newState) { - if (listeners != null) { - for (var i = 0; i < listeners.length; i++) { - final listener = listeners[i]; - if (listener is _ProviderStateSubscription) { - Zone.current.runBinaryGuarded( - listener.onError, - newState.error, - newState.stackTrace, - ); - } - } - } - }, - ); - - for (var i = 0; i < _providerDependents.length; i++) { - _providerDependents[i]._markDependencyChanged(); - } - - for (final observer in _container.observers) { - runQuaternaryGuarded( - observer.didUpdateProvider, - origin, - previousState, - newState.stateOrNull, - _container, - ); - } - - for (final observer in _container.observers) { - newState.map( - data: (_) {}, - error: (newState) { - runQuaternaryGuarded( - observer.providerDidFail, - origin, - newState.error, - newState.stackTrace, - _container, - ); - }, - ); - } - } - - void _markDependencyChanged() { - _didChangeDependency = true; - if (_mustRecomputeState) return; - - // will notify children that their dependency may have changed - invalidateSelf(); - } - - void _markDependencyMayHaveChanged() { - if (_dependencyMayHaveChanged) return; - - _dependencyMayHaveChanged = true; - - visitChildren( - elementVisitor: (element) => element._markDependencyMayHaveChanged(), - notifierVisitor: (notifier) => notifier.notifyDependencyMayHaveChanged(), - ); - } - - bool _debugAssertCanDependOn(ProviderListenableOrFamily listenable) { - assert( - () { - if (listenable is! ProviderBase) return true; - - ProviderElementBase? listenableElement; - try { - // Initializing the provider, to make sure its dependencies are setup. - listenableElement = _container.readProviderElement(listenable); - } catch (err) { - // We don't care whether the provider is in error or not. We're just - // checking whether we're not in a circular dependency. - } - - assert( - listenable._origin != origin, - 'A provider cannot depend on itself', - ); - - assert( - // If the target has a null "dependencies", it should never be scoped. - // As such, the current provider's "dependencies" does not need to - // include the target in its dependencies. - listenable.dependencies == null || - provider != origin || - // Families are allowed to depend on themselves with different parameters. - (origin.from != null && listenable.from == origin.from) || - origin.dependencies == null || - origin.dependencies!.contains(listenable.from) || - origin.dependencies!.contains(listenable), - 'The provider $origin tried to read $listenable, but it specified a ' - "'dependencies' list yet that list does not contain $listenable.\n\n" - "To fix, add $listenable to $origin's 'dependencies' parameter", - ); - - final queue = Queue(); - visitAncestors(queue.add); - if (listenableElement != null) { - queue.add(listenableElement); - } - - while (queue.isNotEmpty) { - final current = queue.removeFirst(); - current.visitAncestors(queue.add); - - if (current.origin == _origin) { - throw CircularDependencyError._(); - } - } - - return true; - }(), - '', - ); - return true; - } - - void _assertNotOutdated() { - assert( - !_didChangeDependency, - 'Cannot use ref functions after the dependency of a provider changed but before the provider rebuilt', - ); - } - - @override - T refresh(Refreshable provider) { - _assertNotOutdated(); - assert(_debugAssertCanDependOn(provider), ''); - return _container.refresh(provider); - } - - @override - T read(ProviderListenable provider) { - _assertNotOutdated(); - assert(!_debugIsRunningSelector, 'Cannot call ref.read inside a selector'); - assert(_debugAssertCanDependOn(provider), ''); - return _container.read(provider); - } - - @override - bool exists(ProviderBase provider) => _container.exists(provider); - - @override - T watch(ProviderListenable listenable) { - _assertNotOutdated(); - assert(!_debugIsRunningSelector, 'Cannot call ref.watch inside a selector'); - - if (listenable is! ProviderBase) { - final sub = listen( - listenable, - (prev, value) => _markDependencyChanged(), - onError: (err, stack) => _markDependencyChanged(), - onDependencyMayHaveChanged: _markDependencyMayHaveChanged, - ); - - return sub.read(); - } - - assert(_debugAssertCanDependOn(listenable), ''); - - final element = _container.readProviderElement(listenable); - _dependencies.putIfAbsent(element, () { - final previousSub = _previousDependencies?.remove(element); - if (previousSub != null) { - return previousSub; - } - - assert( - () { - // Flushing the provider before adding a new dependency - // as otherwise this could cause false positives with certain asserts. - // It's done only in debug mode since `readSelf` will flush the value - // again anyway, and the only value of this flush is to not break asserts. - element.flush(); - return true; - }(), - '', - ); - - element - .._onListen() - .._providerDependents.add(this); - - return Object(); - }); - - return element.readSelf(); - } - - @override - ProviderElementBase readProviderElement(ProviderBase provider) { - return _container.readProviderElement(provider); - } - - @override - ProviderSubscription listen( - ProviderListenable listenable, - void Function(T? previous, T value) listener, { - void Function(Object error, StackTrace stackTrace)? onError, - bool fireImmediately = false, - // Not part of the public "Ref" API - void Function()? onDependencyMayHaveChanged, - }) { - _assertNotOutdated(); - assert(!_debugIsRunningSelector, 'Cannot call ref.read inside a selector'); - assert(_debugAssertCanDependOn(listenable), ''); - - return listenable.addListener( - this, - listener, - fireImmediately: fireImmediately, - onError: onError, - onDependencyMayHaveChanged: onDependencyMayHaveChanged, - ); - } - - @override - void listenSelf( - void Function(StateT? previous, StateT next) listener, { - void Function(Object error, StackTrace stackTrace)? onError, - }) { - // TODO do we want to expose a way to close the subscription? - // TODO do we want a fireImmediately? - - _onChangeSelfListeners ??= []; - _onChangeSelfListeners!.add(listener); - - if (onError != null) { - _onErrorSelfListeners ??= []; - _onErrorSelfListeners!.add(onError); - } - } - - /// Returns the currently exposed by a provider - /// - /// May throw if the provider threw when creating the exposed value. - StateT readSelf() { - flush(); - - return requireState; - } - - /// Visit the [ProviderElement]s of providers that are listening to this element. - /// - /// A provider is considered as listening to this element if it either [watch] - /// or [listen] this element. - /// - /// This method does not guarantee that a dependency is visited only once. - /// If a provider both [watch] and [listen] an element, or if a provider - /// [listen] multiple times to an element, it may be visited multiple times. - void visitChildren({ - required void Function(ProviderElementBase element) elementVisitor, - required void Function(ProxyElementValueNotifier element) notifierVisitor, - }) { - for (var i = 0; i < _providerDependents.length; i++) { - elementVisitor(_providerDependents[i]); - } - - final dependents = _dependents; - if (dependents != null) { - for (var i = 0; i < dependents.length; i++) { - final dependent = dependents[i].source; - if (dependent is ProviderElementBase) elementVisitor(dependent); - } - } - } - - /// Visit the [ProviderElementBase]s that this provider is listening to. - /// - /// A provider is considered as listening to this element if it either [watch] - /// or [listen] this element. - /// - /// This method does not guarantee that a provider is visited only once. - /// If this provider both [watch] and [listen] an element, or if it - /// [listen] multiple times to an element, that element may be visited multiple times. - void visitAncestors( - void Function(ProviderElementBase element) visitor, - ) { - _dependencies.keys.forEach(visitor); - - final subscriptions = _subscriptions; - if (subscriptions != null) { - for (var i = 0; i < subscriptions.length; i++) { - final sub = subscriptions[i]; - if (sub is _ProviderStateSubscription) { - visitor(sub.listenedElement); - } - } - } - } - - /// Release the resources associated to this [ProviderElementBase]. - /// - /// This will be invoked when: - /// - the provider is using `autoDispose` and it is no-longer used. - /// - the associated [ProviderContainer] is disposed - /// - /// On the other hand, this life-cycle will not be executed when a provider - /// rebuilds. - /// - /// As opposed to [runOnDispose], this life-cycle is executed only - /// for the lifetime of this element. - @protected - @mustCallSuper - void dispose() { - runOnDispose(); - - for (final sub in _dependencies.entries) { - sub.key._providerDependents.remove(this); - sub.key._onRemoveListener(); - } - _dependencies.clear(); - - // TODO test invalidateSelf() then dispose() properly unlinks dependencies - // TODO test [listen] calls are cleared - } - - void _onListen() { - _onAddListeners?.forEach(runGuarded); - if (_didCancelOnce && !hasListeners) { - _onResumeListeners?.forEach(runGuarded); - } - } - - void _onRemoveListener() { - _onRemoveListeners?.forEach(runGuarded); - if (!hasListeners) { - _didCancelOnce = true; - _onCancelListeners?.forEach(runGuarded); - } - mayNeedDispose(); - } - - /// Life-cycle for when a listener is removed. - /// - /// See also: - /// - /// - [AutoDisposeProviderElementMixin], which overrides this method to destroy the - /// state of a provider when no longer used. - @protected - @visibleForOverriding - void mayNeedDispose() {} - - @override - @mustCallSuper - void onDispose(void Function() listener) { - _assertNotOutdated(); - if (!_mounted) { - throw StateError('Cannot call onDispose after a provider was dispose'); - } - _onDisposeListeners ??= []; - _onDisposeListeners!.add(listener); - } - - /// Executes the [Ref.onDispose] listeners previously registered, then clear - /// the list of listeners. - @protected - @visibleForOverriding - @mustCallSuper - void runOnDispose() { - if (!_mounted) return; - _mounted = false; - - final subscriptions = _subscriptions; - if (subscriptions != null) { - while (subscriptions.isNotEmpty) { - late int debugPreviousLength; - if (kDebugMode) { - debugPreviousLength = subscriptions.length; - } - - final sub = subscriptions.first; - sub.close(); - - if (kDebugMode) { - assert( - subscriptions.length < debugPreviousLength, - 'ProviderSubscription.close did not remove the subscription', - ); - } - } - } - - _onDisposeListeners?.forEach(runGuarded); - - for (final observer in _container.observers) { - runBinaryGuarded( - observer.didDisposeProvider, - _origin, - _container, - ); - } - - _onDisposeListeners = null; - _onCancelListeners = null; - _onResumeListeners = null; - _onAddListeners = null; - _onRemoveListeners = null; - _onChangeSelfListeners = null; - _onErrorSelfListeners = null; - _didCancelOnce = false; - } - - @override - void onAddListener(void Function() cb) { - _onAddListeners ??= []; - _onAddListeners!.add(cb); - } - - @override - void onRemoveListener(void Function() cb) { - _onRemoveListeners ??= []; - _onRemoveListeners!.add(cb); - } - - @override - void onCancel(void Function() cb) { - _onCancelListeners ??= []; - _onCancelListeners!.add(cb); - } - - @override - void onResume(void Function() cb) { - _onResumeListeners ??= []; - _onResumeListeners!.add(cb); - } - - @override - String toString() { - return '$runtimeType(provider: $provider, origin: $origin)'; - } -} diff --git a/packages/riverpod/lib/src/framework/family.dart b/packages/riverpod/lib/src/framework/family.dart deleted file mode 100644 index 19e2c0b24..000000000 --- a/packages/riverpod/lib/src/framework/family.dart +++ /dev/null @@ -1,316 +0,0 @@ -part of '../framework.dart'; - -/// A typedef representing the constructor of any classical provider. -@internal -typedef ProviderCreate = ProviderT - Function( - Create create, { - required String? name, - required Iterable? dependencies, - required Set? allTransitiveDependencies, - required DebugGetCreateSourceHash? debugGetCreateSourceHash, - Family from, - Object? argument, -}); - -/// A typedef representing the constructor of a [NotifierProvider]. -@internal -typedef ProviderNotifierCreate = ProviderT - Function( - Created Function() create, { - required String? name, - required Iterable? dependencies, - required Set? allTransitiveDependencies, - required DebugGetCreateSourceHash? debugGetCreateSourceHash, - Family from, - Object? argument, -}); - -/// A [Create] equivalent used by [Family]. -@internal -typedef FamilyCreate = T Function(R ref, Arg arg); - -/// A base class for all families -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(); - - @override - Family? get from => null; - - @override - // ignore: deprecated_member_use_from_same_package - Family get overriddenFamily => this; -} - -mixin _FamilyMixin> - on Family { - /// Create a provider from an external value. - /// - /// That external value should be immutable and preferably override `==`/`hashCode`. - /// See the documentation of [Provider.family] for more information. - FamilyProvider call(Arg argument); - - /// Overrides the behavior of a family for a part of the application. - /// - /// {@macro riverpod.overrideWith} - Override overrideWithProvider( - FamilyProvider Function(Arg argument) override, - ) { - return FamilyOverrideImpl(this, override); - } - - @visibleForOverriding - @override - ProviderBase getProviderOverride(ProviderBase provider) { - return call(provider.argument as Arg); - } -} - -/// Setup how a family is overridden -@internal -typedef SetupFamilyOverride = void Function( - Arg argument, - void Function({ - required ProviderBase origin, - required ProviderBase override, - }), -); - -/// Do not use: Internal object to used by [ProviderContainer]/`ProviderScope` -/// to override the behavior of a "family" for part of the application. -@internal -abstract class FamilyOverride<@Deprecated('Will be removed in 3.0.0') State> - implements Override { - /// The family that was overridden. - // ignore: deprecated_member_use_from_same_package - Family get overriddenFamily; - - /// Obtains the new behavior for a provider associated to the overridden family. - @visibleForOverriding - // ignore: deprecated_member_use_from_same_package - ProviderBase getProviderOverride(ProviderBase provider); -} - -/// An [Override] for families -@internal -class FamilyOverrideImpl> - implements FamilyOverride { - /// An [Override] for families - // ignore: library_private_types_in_public_api - FamilyOverrideImpl(this.overriddenFamily, this._newCreate); - - final FamilyProvider Function(Arg arg) _newCreate; - - @override - // ignore: library_private_types_in_public_api - final _FamilyMixin overriddenFamily; - - @visibleForOverriding - @override - ProviderBase getProviderOverride(ProviderBase provider) { - final arg = provider.argument as Arg; - return _newCreate(arg); - } -} - -/// A base implementation for [Family], used by the various providers to -/// help them define a [Family]. -/// -/// This API is not meant for public consumption. -@internal -class FamilyBase, R, Arg, Created, - ProviderT extends ProviderBase> extends Family - with _FamilyMixin { - /// A base implementation for [Family], used by the various providers to - /// help them define a [Family]. - /// - /// This API is not meant for public consumption. - const FamilyBase( - this._createFn, { - required ProviderCreate providerFactory, - required this.name, - required this.dependencies, - required this.allTransitiveDependencies, - required this.debugGetCreateSourceHash, - }) : _providerFactory = providerFactory; - - final ProviderCreate _providerFactory; - - final Created Function(RefT ref, Arg arg) _createFn; - - @override - ProviderT call(Arg argument) => _providerFactory( - (ref) => _createFn(ref, argument), - name: name, - from: this, - argument: argument, - dependencies: dependencies, - allTransitiveDependencies: allTransitiveDependencies, - debugGetCreateSourceHash: debugGetCreateSourceHash, - ); - - @override - final String? name; - @override - final Iterable? dependencies; - @override - final Set? allTransitiveDependencies; - - /// {@macro riverpod.create_source_hash} - @internal - final DebugGetCreateSourceHash? debugGetCreateSourceHash; -} - -/// A base implementation for [Family], used by the various providers to -/// help them define a [Family]. -/// -/// This API is not meant for public consumption. - -@internal -class AutoDisposeFamilyBase, R, Arg, Created, - ProviderT extends ProviderBase> extends Family - with _FamilyMixin { - /// A base implementation for [Family], used by the various providers to - /// help them define a [Family]. - /// - /// This API is not meant for public consumption. - const AutoDisposeFamilyBase( - this._createFn, { - required ProviderCreate providerFactory, - required this.name, - required this.dependencies, - required this.allTransitiveDependencies, - required this.debugGetCreateSourceHash, - }) : _providerFactory = providerFactory; - - final ProviderCreate _providerFactory; - - final Created Function(RefT ref, Arg arg) _createFn; - - @override - ProviderT call(Arg argument) => _providerFactory( - (ref) => _createFn(ref, argument), - name: name, - from: this, - argument: argument, - dependencies: dependencies, - allTransitiveDependencies: allTransitiveDependencies, - debugGetCreateSourceHash: debugGetCreateSourceHash, - ); - - @override - final String? name; - @override - final Iterable? dependencies; - @override - final Set? allTransitiveDependencies; - - /// {@macro riverpod.create_source_hash} - @internal - final DebugGetCreateSourceHash? debugGetCreateSourceHash; -} - -/// A base implementation for [Family] specific to autoDispose `Notifier`-based providers. -/// -/// It offers a unique "create" function which does not take any argument. -/// -/// This API is not meant for public consumption. -@internal -class AutoDisposeNotifierFamilyBase, R, Arg, NotifierT, - ProviderT extends ProviderBase> extends Family - with _FamilyMixin { - /// A base implementation for [Family], used by the various providers to - /// help them define a [Family]. - /// - /// This API is not meant for public consumption. - const AutoDisposeNotifierFamilyBase( - this._createFn, { - required ProviderNotifierCreate providerFactory, - required this.name, - required this.dependencies, - required this.allTransitiveDependencies, - required this.debugGetCreateSourceHash, - }) : _providerFactory = providerFactory; - - final ProviderNotifierCreate _providerFactory; - - final NotifierT Function() _createFn; - - @override - ProviderT call(Arg argument) => _providerFactory( - _createFn, - name: name, - from: this, - argument: argument, - dependencies: dependencies, - allTransitiveDependencies: allTransitiveDependencies, - debugGetCreateSourceHash: debugGetCreateSourceHash, - ); - - @override - final String? name; - @override - final Iterable? dependencies; - @override - final Set? allTransitiveDependencies; - - /// {@macro riverpod.create_source_hash} - @internal - final DebugGetCreateSourceHash? debugGetCreateSourceHash; -} - -/// A base implementation for [Family] specific to `Notifier`-based providers. -/// -/// It offers a unique "create" function which does not take any argument. -/// -/// This API is not meant for public consumption. -@internal -class NotifierFamilyBase, R, Arg, NotifierT, - ProviderT extends ProviderBase> extends Family - with _FamilyMixin { - /// A base implementation for [Family], used by the various providers to - /// help them define a [Family]. - /// - /// This API is not meant for public consumption. - const NotifierFamilyBase( - this._createFn, { - required ProviderNotifierCreate providerFactory, - required this.name, - required this.dependencies, - required this.allTransitiveDependencies, - required this.debugGetCreateSourceHash, - }) : _providerFactory = providerFactory; - - final ProviderNotifierCreate _providerFactory; - - final NotifierT Function() _createFn; - - @override - ProviderT call(Arg argument) => _providerFactory( - _createFn, - name: name, - from: this, - argument: argument, - dependencies: dependencies, - allTransitiveDependencies: allTransitiveDependencies, - debugGetCreateSourceHash: debugGetCreateSourceHash, - ); - - @override - final String? name; - @override - final Iterable? dependencies; - @override - final Set? allTransitiveDependencies; - - /// {@macro riverpod.create_source_hash} - @internal - final DebugGetCreateSourceHash? debugGetCreateSourceHash; -} diff --git a/packages/riverpod/lib/src/framework/listen.dart b/packages/riverpod/lib/src/framework/listen.dart deleted file mode 100644 index 43b7b53d5..000000000 --- a/packages/riverpod/lib/src/framework/listen.dart +++ /dev/null @@ -1,15 +0,0 @@ -part of '../framework.dart'; - -/// Deals with the internals of synchronously calling the listeners -/// when using `fireImmediately: true` -@internal -void handleFireImmediately( - Result currentState, { - required void Function(State? previous, State current) listener, - required void Function(Object error, StackTrace stackTrace) onError, -}) { - currentState.map( - data: (data) => runBinaryGuarded(listener, null, data.state), - error: (error) => runBinaryGuarded(onError, error.error, error.stackTrace), - ); -} diff --git a/packages/riverpod/lib/src/framework/provider_base.dart b/packages/riverpod/lib/src/framework/provider_base.dart deleted file mode 100644 index 67127597d..000000000 --- a/packages/riverpod/lib/src/framework/provider_base.dart +++ /dev/null @@ -1,285 +0,0 @@ -part of '../framework.dart'; - -/// A callback used by providers to create the value exposed. -/// -/// If an exception is thrown within that callback, all attempts at reading -/// the provider associated with the given callback will throw. -/// -/// The parameter [ref] can be used to interact with other providers -/// and the life-cycles of this provider. -/// -/// See also: -/// -/// - [Ref], which exposes the methods to read other providers. -/// - [Provider], a provider that uses [Create] to expose an immutable value. -@internal -typedef Create = T Function(R ref); - -/// A callback used to catches errors -@internal -typedef OnError = void Function(Object, StackTrace); - -/// A typedef for `debugGetCreateSourceHash` parameters. -@internal -typedef DebugGetCreateSourceHash = String Function(); - -/// A base class for _all_ providers. -@immutable -abstract class ProviderBase extends ProviderOrFamily - with ProviderListenable - implements ProviderOverride, Refreshable { - /// A base class for _all_ providers. - const ProviderBase({ - required super.name, - required this.from, - required this.argument, - required this.debugGetCreateSourceHash, - required super.dependencies, - required super.allTransitiveDependencies, - }); - - @override - ProviderBase get _origin => this; - - @override - ProviderBase get _override => this; - - /// {@template riverpod.create_source_hash} - /// A debug-only fucntion for obtaining a hash of the source code of the - /// initialization function. - /// - /// If after a hot-reload this function returns a different result, the - /// provider will be re-executed. - /// - /// This variable is only set by `riverpod_generator`. - /// {@endtemplate} - @internal - final DebugGetCreateSourceHash? debugGetCreateSourceHash; - - /// If this provider was created with the `.family` modifier, [from] is the `.family` instance. - @override - final Family? from; - - /// If this provider was created with the `.family` modifier, [argument] is - /// the variable that was used. - /// - /// On generated providers, this will be a record of all arguments. - final Object? argument; - - @override - ProviderSubscription addListener( - Node node, - void Function(StateT? previous, StateT next) listener, { - required void Function(Object error, StackTrace stackTrace)? onError, - required void Function()? onDependencyMayHaveChanged, - required bool fireImmediately, - }) { - onError ??= Zone.current.handleUncaughtError; - - final element = node.readProviderElement(this); - - element.flush(); - if (fireImmediately) { - handleFireImmediately( - element.getState()!, - listener: listener, - onError: onError, - ); - } - - // Calling before initializing the subscription, - // to ensure that "hasListeners" represents the state _before_ - // the listener is added - element._onListen(); - - return _ProviderStateSubscription( - node, - listenedElement: element, - listener: (prev, next) => listener(prev as StateT?, next as StateT), - onError: onError, - ); - } - - @override - StateT read(Node node) { - final element = node.readProviderElement(this); - - element.flush(); - - // In case `read` was called on a provider that has no listener - element.mayNeedDispose(); - - return element.requireState; - } - - /// An internal method that defines how a provider behaves. - @visibleForOverriding - ProviderElementBase createElement(); - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode { - if (from == null) return super.hashCode; - - return from.hashCode ^ argument.hashCode; - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(Object other) { - if (from == null) return identical(other, this); - - return other.runtimeType == runtimeType && - other is ProviderBase && - other.from == from && - other.argument == argument; - } - - @override - String toString() { - var leading = ''; - if (from != null) { - leading = '($argument)'; - } - - var trailing = ''; - if (name != null) { - trailing = '$name:'; - } - - return '$trailing${describeIdentity(this)}$leading'; - } -} - -var _debugIsRunningSelector = false; - -/// When a provider listens to another provider using `listen` -@optionalTypeArgs -class _ProviderStateSubscription extends ProviderSubscription { - _ProviderStateSubscription( - super.source, { - required this.listenedElement, - required this.listener, - required this.onError, - }) { - final dependents = listenedElement._dependents ??= []; - dependents.add(this); - } - - // Why can't this be typed correctly? - final void Function(Object? prev, Object? state) listener; - final ProviderElementBase listenedElement; - final OnError onError; - - @override - StateT read() { - if (_closed) { - throw StateError( - 'called ProviderSubscription.read on a subscription that was closed', - ); - } - return listenedElement.readSelf(); - } - - @override - void close() { - if (!closed) { - listenedElement._dependents?.remove(this); - listenedElement._onRemoveListener(); - } - - super.close(); - } -} - -/// A mixin to add [overrideWithValue] capability to a provider. -// TODO merge with Provider directy -mixin OverrideWithValueMixin on ProviderBase { - /// {@template riverpod.overrridewithvalue} - /// Overrides a provider with a value, ejecting the default behaviour. - /// - /// This will also disable the auto-scoping mechanism, meaning that if the - /// overridden provider specified [dependencies], it will have no effect. - /// - /// Some common use-cases are: - /// - testing, by replacing a service with a fake implementation, or to reach - /// a very specific state easily. - /// - multiple environments, by changing the implementation of a class - /// based on the platform or other parameters. - /// - /// This function should be used in combination with `ProviderScope.overrides` - /// or `ProviderContainer.overrides`: - /// - /// ```dart - /// final myService = Provider((ref) => MyService()); - /// - /// runApp( - /// ProviderScope( - /// overrides: [ - /// myService.overrideWithValue( - /// // Replace the implementation of MyService with a fake implementation - /// MyFakeService(), - /// ), - /// ], - /// child: MyApp(), - /// ), - /// ); - /// ``` - /// {@endtemplate} - Override overrideWithValue(State value) { - return ProviderOverride( - origin: this, - override: ValueProvider(value), - ); - } -} - -/// A mixin to add `overrideWithProvider` capability to providers. -extension OverrideWithProviderExtension> on ProviderType { - /// {@template riverpod.overridewithprovider} - /// Overrides a provider with a value, ejecting the default behaviour. - /// - /// This will also disable the auto-scoping mechanism, meaning that if the - /// overridden provider specified `dependencies`, it will have no effect. - /// - /// The override must not specify a `dependencies`. - /// - /// Some common use-cases are: - /// - testing, by replacing a service with a fake implementation, or to reach - /// a very specific state easily. - /// - multiple environments, by changing the implementation of a class - /// based on the platform or other parameters. - /// - /// This function should be used in combination with `ProviderScope.overrides` - /// or `ProviderContainer.overrides`: - /// - /// ```dart - /// final myService = Provider((ref) => MyService()); - /// - /// runApp( - /// ProviderScope( - /// overrides: [ - /// myService.overrideWithProvider( - /// // Replace the implementation of the provider with a different one - /// Provider((ref) { - /// ref.watch('other'); - /// return MyFakeService(), - /// }), - /// ), - /// ], - /// child: MyApp(), - /// ), - /// ); - /// ``` - /// {@endtemplate} - @Deprecated('Will be removed in 3.0.0. Use overrideWith instead.') - Override overrideWithProvider(ProviderType override) { - assert( - override.dependencies == null, - 'When using overrideWithProvider, the override cannot specify `dependencies`.', - ); - - return ProviderOverride(origin: this, override: override); - } -} diff --git a/packages/riverpod/lib/src/framework/proxy_provider_listenable.dart b/packages/riverpod/lib/src/framework/proxy_provider_listenable.dart deleted file mode 100644 index 10980105b..000000000 --- a/packages/riverpod/lib/src/framework/proxy_provider_listenable.dart +++ /dev/null @@ -1,146 +0,0 @@ -part of '../framework.dart'; - -class _ProxySubscription extends ProviderSubscription { - _ProxySubscription( - super.source, - this._removeListeners, - this._read, { - required this.innerSubscription, - }); - - final ProviderSubscription innerSubscription; - final RemoveListener _removeListeners; - final T Function() _read; - - @override - T read() { - if (_closed) { - throw StateError( - 'called ProviderSubscription.read on a subscription that was closed', - ); - } - return _read(); - } - - @override - void close() { - if (!closed) { - innerSubscription.close(); - _removeListeners(); - } - - super.close(); - } -} - -/// An internal utility for reading alternate values of a provider. -/// -/// For example, this is used by [FutureProvider] to differentiate: -/// -/// ```dart -/// ref.watch(futureProvider); -/// ``` -/// -/// from: -/// -/// ```dart -/// ref.watch(futureProvider.future); -/// ``` -/// -/// This API is not meant for public consumption. -@internal -class ProviderElementProxy - with - ProviderListenable, - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderListenable - implements - // ignore: deprecated_member_use_from_same_package - AlwaysAliveRefreshable { - /// An internal utility for reading alternate values of a provider. - /// - /// For example, this is used by [FutureProvider] to differentiate: - /// - /// ```dart - /// ref.watch(futureProvider); - /// ``` - /// - /// from: - /// - /// ```dart - /// ref.watch(futureProvider.future); - /// ``` - /// - /// This API is not meant for public consumption. - const ProviderElementProxy(this._origin, this._lense); - - @override - final ProviderBase _origin; - final ProxyElementValueNotifier Function( - ProviderElementBase element, - ) _lense; - - @override - ProviderSubscription addListener( - Node node, - void Function(Output? previous, Output next) listener, { - required void Function(Object error, StackTrace stackTrace)? onError, - required void Function()? onDependencyMayHaveChanged, - required bool fireImmediately, - }) { - final element = node.readProviderElement(_origin); - - // While we don't care about changes to the element, calling _listenElement - // is necessary to tell the listened element that it is being listened. - // We do it at the top of the file to trigger a "flush" before adding - // a listener to the notifier. - // This avoids the listener from being immediately notified of a new - // future when adding the listener refreshes the future. - final innerSub = node.listen( - _origin, - (prev, next) {}, - ); - - final notifier = _lense(element); - if (fireImmediately) { - notifier.result?.when( - data: (data) { - runBinaryGuarded(listener, null, data); - }, - error: (err, stack) { - if (onError != null) { - runBinaryGuarded(onError, err, stack); - } - }, - ); - } - - final removeListener = notifier.addListener( - listener, - onError: onError, - onDependencyMayHaveChanged: onDependencyMayHaveChanged, - ); - - return _ProxySubscription( - node, - removeListener, - () => read(node), - innerSubscription: innerSub, - ); - } - - @override - Output read(Node node) { - final element = node.readProviderElement(_origin); - element.flush(); - element.mayNeedDispose(); - return _lense(element).value; - } - - @override - bool operator ==(Object other) => - other is ProviderElementProxy && other._origin == _origin; - - @override - int get hashCode => _origin.hashCode; -} diff --git a/packages/riverpod/lib/src/framework/selector.dart b/packages/riverpod/lib/src/framework/selector.dart deleted file mode 100644 index df4941a8e..000000000 --- a/packages/riverpod/lib/src/framework/selector.dart +++ /dev/null @@ -1,201 +0,0 @@ -part of '../framework.dart'; - -/// An abstraction of both [ProviderContainer] and [ProviderElement] used by -/// [ProviderListenable]. -abstract class Node { - /// Starts listening to a listenable - ProviderSubscription listen( - ProviderListenable listenable, - void Function(State? previous, State next) listener, { - void Function(Object error, StackTrace stackTrace)? onError, - bool fireImmediately = false, - }); - - /// Reads the state of a provider, potentially creating it in the process. - /// - /// It may throw if the provider requested threw when it was built. - /// - /// Do not use this in production code. This is exposed only for testing - /// and devtools, to be able to test if a provider has listeners or similar. - ProviderElementBase readProviderElement( - ProviderBase provider, - ); -} - -/// An internal class for `ProviderBase.select`. -@sealed -class _ProviderSelector with ProviderListenable { - /// An internal class for `ProviderBase.select`. - _ProviderSelector({ - required this.provider, - required this.selector, - }); - - /// The provider that was selected - final ProviderListenable provider; - - /// The selector applied - final Output Function(Input) selector; - - Result _select(Result value) { - assert( - () { - _debugIsRunningSelector = true; - return true; - }(), - '', - ); - - try { - return value.map( - data: (data) => Result.data(selector(data.state)), - // TODO test - error: (error) => Result.error(error.error, error.stackTrace), - ); - } catch (err, stack) { - // TODO test - return Result.error(err, stack); - } finally { - assert( - () { - _debugIsRunningSelector = false; - return true; - }(), - '', - ); - } - } - - void _selectOnChange({ - required Input newState, - required Result lastSelectedValue, - required void Function(Object error, StackTrace stackTrace) onError, - required void Function(Output? prev, Output next) listener, - required void Function(Result newState) onChange, - }) { - final newSelectedValue = _select(Result.data(newState)); - if (!lastSelectedValue.hasState || - !newSelectedValue.hasState || - lastSelectedValue.requireState != newSelectedValue.requireState) { - // TODO test events after selector exception correctly send `previous`s - - onChange(newSelectedValue); - // TODO test handle exception in listener - newSelectedValue.map( - data: (data) { - listener( - // TODO test from error - lastSelectedValue.stateOrNull, - data.state, - ); - }, - error: (error) => onError(error.error, error.stackTrace), - ); - } - } - - @override - _SelectorSubscription addListener( - Node node, - void Function(Output? previous, Output next) listener, { - required void Function(Object error, StackTrace stackTrace)? onError, - required void Function()? onDependencyMayHaveChanged, - required bool fireImmediately, - }) { - onError ??= Zone.current.handleUncaughtError; - - late Result lastSelectedValue; - - final sub = node.listen( - provider, - (prev, input) { - _selectOnChange( - newState: input, - lastSelectedValue: lastSelectedValue, - listener: listener, - onError: onError!, - onChange: (newState) => lastSelectedValue = newState, - ); - }, - onError: onError, - ); - - lastSelectedValue = _select(Result.guard(sub.read)); - - if (fireImmediately) { - handleFireImmediately( - lastSelectedValue, - listener: listener, - onError: onError, - ); - } - - return _SelectorSubscription( - node, - sub, - () { - return lastSelectedValue.map( - data: (data) => data.state, - error: (error) => throwErrorWithCombinedStackTrace( - error.error, - error.stackTrace, - ), - ); - }, - ); - } - - @override - Output read(Node node) { - final input = provider.read(node); - return selector(input); - } -} - -class _SelectorSubscription - extends ProviderSubscription { - _SelectorSubscription( - super.source, - this._internalSub, - this._read, { - this.onClose, - }); - - final ProviderSubscription _internalSub; - final Output Function() _read; - final void Function()? onClose; - - @override - void close() { - if (!closed) { - onClose?.call(); - _internalSub.close(); - } - super.close(); - } - - @override - Output read() { - if (closed) { - throw StateError( - 'called ProviderSubscription.read on a subscription that was closed', - ); - } - // flushes the provider - _internalSub.read(); - - return _read(); - } -} - -class _AlwaysAliveProviderSelector - extends _ProviderSelector - with - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderListenable { - /// An internal class for `ProviderBase.select`. - _AlwaysAliveProviderSelector({ - required super.provider, - required super.selector, - }); -} diff --git a/packages/riverpod/lib/src/framework/value_provider.dart b/packages/riverpod/lib/src/framework/value_provider.dart deleted file mode 100644 index cd684f0b5..000000000 --- a/packages/riverpod/lib/src/framework/value_provider.dart +++ /dev/null @@ -1,90 +0,0 @@ -part of '../framework.dart'; - -/// A provider that is driven by a value instead of a function. -/// -/// This is an implementation detail of `overrideWithValue`. -@sealed -@internal -class ValueProvider extends ProviderBase - with - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderBase { - /// Creates a [ValueProvider]. - ValueProvider(this._value) - : super( - name: null, - from: null, - argument: null, - debugGetCreateSourceHash: null, - allTransitiveDependencies: null, - dependencies: null, - ); - - final State _value; - - @override - Iterable? get dependencies => null; - - @override - Set? get allTransitiveDependencies => null; - - @override - ValueProviderElement createElement() { - return ValueProviderElement(this); - } -} - -/// The [ProviderElementBase] of a [ValueProvider] -@sealed -@internal -class ValueProviderElement extends ProviderElementBase { - /// The [ProviderElementBase] of a [ValueProvider] - ValueProviderElement(ValueProvider super._provider); - - /// A custom listener called when `overrideWithValue` changes - /// with a different value. - void Function(State value)? onChange; - - @override - void update(ProviderBase newProvider) { - super.update(newProvider); - final newValue = (provider as ValueProvider)._value; - - // `getState` will never be in error/loading state since there is no "create" - final previousState = getState()! as ResultData; - - if (newValue != previousState.state) { - assert( - () { - // Asserts would otherwise prevent a provider rebuild from updating - // other providers - _debugSkipNotifyListenersAsserts = true; - return true; - }(), - '', - ); - setState(newValue); - assert( - () { - // Asserts would otherwise prevent a provider rebuild from updating - // other providers - _debugSkipNotifyListenersAsserts = false; - return true; - }(), - '', - ); - onChange?.call(newValue); - } - } - - @override - void create({required bool didChangeDependency}) { - final provider = this.provider as ValueProvider; - setState(provider._value); - } - - @override - bool updateShouldNotify(State previous, State next) { - return true; - } -} diff --git a/packages/riverpod/lib/src/future_provider.dart b/packages/riverpod/lib/src/future_provider.dart deleted file mode 100644 index a1f7cf86c..000000000 --- a/packages/riverpod/lib/src/future_provider.dart +++ /dev/null @@ -1,128 +0,0 @@ -import 'dart:async'; - -import 'package:meta/meta.dart'; - -import 'async_notifier.dart'; -import 'builders.dart'; -import 'common.dart'; -import 'framework.dart'; -import 'provider.dart' show Provider; -import 'stream_provider.dart' show StreamProvider; - -part 'future_provider/auto_dispose.dart'; -part 'future_provider/base.dart'; - -ProviderElementProxy, Future> _future( - _FutureProviderBase that, -) { - return ProviderElementProxy, Future>( - that, - (element) { - return FutureHandlerProviderElementMixin.futureNotifierOf( - element as FutureHandlerProviderElementMixin, - ); - }, - ); -} - -/// {@template riverpod.future_provider} -/// A provider that asynchronously creates a value. -/// -/// [FutureProvider] can be considered as a combination of [Provider] and -/// `FutureBuilder`. -/// By using [FutureProvider], the UI will be able to read the state of the -/// future synchronously, handle the loading/error states, and rebuild when the -/// future completes. -/// -/// A common use-case for [FutureProvider] is to represent an asynchronous operation -/// such as reading a file or making an HTTP request, that is then listened to by the UI. -/// -/// It can then be combined with: -/// - [FutureProvider.family], to parameterize the http request based on external -/// parameters, such as fetching a `User` from its id. -/// - [FutureProvider.autoDispose], to cancel the HTTP request if the user -/// leaves the screen before the [Future] completed. -/// -/// ## Usage example: reading a configuration file -/// -/// [FutureProvider] can be a convenient way to expose a `Configuration` object -/// created by reading a JSON file. -/// -/// Creating the configuration would be done with your typical async/await -/// syntax, but inside the provider. -/// Using Flutter's asset system, this would be: -/// -/// ```dart -/// final configProvider = FutureProvider((ref) async { -/// final content = json.decode( -/// await rootBundle.loadString('assets/configurations.json'), -/// ) as Map; -/// -/// return Configuration.fromJson(content); -/// }); -/// ``` -/// -/// Then, the UI can listen to configurations like so: -/// -/// ```dart -/// Widget build(BuildContext context, WidgetRef ref) { -/// AsyncValue config = ref.watch(configProvider); -/// -/// return config.when( -/// loading: () => const CircularProgressIndicator(), -/// error: (err, stack) => Text('Error: $err'), -/// data: (config) { -/// return Text(config.host); -/// }, -/// ); -/// } -/// ``` -/// -/// This will automatically rebuild the UI when the [Future] completes. -/// -/// As you can see, listening to a [FutureProvider] inside a widget returns -/// an [AsyncValue] – which allows handling the error/loading states. -/// -/// 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. -/// - [FutureProvider.family], to create a [FutureProvider] from external parameters -/// - [FutureProvider.autoDispose], to destroy the state of a [FutureProvider] when no longer needed. -/// {@endtemplate} -abstract class _FutureProviderBase extends ProviderBase> { - const _FutureProviderBase({ - required super.dependencies, - required super.allTransitiveDependencies, - required super.name, - required super.from, - required super.argument, - required super.debugGetCreateSourceHash, - }); - - /// Obtains the [Future] associated with a [FutureProvider]. - /// - /// The instance of [Future] obtained may change over time, if the provider - /// was recreated (such as when using [Ref.watch]). - /// - /// This provider allows using `async`/`await` to easily combine - /// [FutureProvider] together: - /// - /// ```dart - /// final configsProvider = FutureProvider((ref) async => Configs()); - /// - /// final productsProvider = FutureProvider((ref) async { - /// // Wait for the configurations to resolve - /// final configs = await ref.watch(configsProvider.future); - /// - /// // Do something with the result - /// return await http.get('${configs.host}/products'); - /// }); - /// ``` - Refreshable> get future; - - FutureOr _create(covariant FutureProviderElement ref); -} diff --git a/packages/riverpod/lib/src/future_provider/auto_dispose.dart b/packages/riverpod/lib/src/future_provider/auto_dispose.dart deleted file mode 100644 index 7b34fda73..000000000 --- a/packages/riverpod/lib/src/future_provider/auto_dispose.dart +++ /dev/null @@ -1,138 +0,0 @@ -part of '../future_provider.dart'; - -/// {@macro riverpod.provider_ref_base} -/// - [FutureProviderRef.state], the value currently exposed by this provider. -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class AutoDisposeFutureProviderRef - extends FutureProviderRef - implements AutoDisposeRef> {} - -/// {@macro riverpod.future_provider} -class AutoDisposeFutureProvider extends _FutureProviderBase - with AsyncSelector { - /// {@macro riverpod.future_provider} - AutoDisposeFutureProvider( - this._createFn, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - AutoDisposeFutureProvider.internal( - this._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.family} - static const family = AutoDisposeFutureProviderFamily.new; - - // ignore: deprecated_member_use_from_same_package - final Create, AutoDisposeFutureProviderRef> _createFn; - - @override - FutureOr _create(AutoDisposeFutureProviderElement ref) => - _createFn(ref); - - @override - AutoDisposeFutureProviderElement createElement() { - return AutoDisposeFutureProviderElement(this); - } - - @override - late final Refreshable> future = _future(this); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - Create, AutoDisposeFutureProviderRef> create, - ) { - return ProviderOverride( - origin: this, - override: AutoDisposeFutureProvider.internal( - create, - from: from, - argument: argument, - debugGetCreateSourceHash: null, - dependencies: null, - allTransitiveDependencies: null, - name: null, - ), - ); - } -} - -/// The [ProviderElementBase] of [AutoDisposeFutureProvider] -class AutoDisposeFutureProviderElement extends FutureProviderElement - with - AutoDisposeProviderElementMixin> - implements - // ignore: deprecated_member_use_from_same_package - AutoDisposeFutureProviderRef { - /// The [ProviderElementBase] for [FutureProvider] - @internal - AutoDisposeFutureProviderElement( - AutoDisposeFutureProvider super._provider, - ) : super(); -} - -/// The [Family] of an [AutoDisposeFutureProvider] -class AutoDisposeFutureProviderFamily extends AutoDisposeFamilyBase< - // ignore: deprecated_member_use_from_same_package - AutoDisposeFutureProviderRef, - AsyncValue, - Arg, - FutureOr, - AutoDisposeFutureProvider> { - /// The [Family] of an [AutoDisposeFutureProvider] - AutoDisposeFutureProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: AutoDisposeFutureProvider.internal, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - debugGetCreateSourceHash: null, - ); - - /// Implementation detail of the code-generator. - @internal - AutoDisposeFutureProviderFamily.generator( - super._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - }) : super(providerFactory: AutoDisposeFutureProvider.internal); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - FutureOr Function(AutoDisposeFutureProviderRef ref, Arg arg) create, - ) { - return FamilyOverrideImpl, Arg, AutoDisposeFutureProvider>( - this, - (arg) => AutoDisposeFutureProvider.internal( - (ref) => create(ref, arg), - from: from, - argument: arg, - debugGetCreateSourceHash: null, - dependencies: null, - allTransitiveDependencies: null, - name: null, - ), - ); - } -} diff --git a/packages/riverpod/lib/src/future_provider/base.dart b/packages/riverpod/lib/src/future_provider/base.dart deleted file mode 100644 index c9c6d1984..000000000 --- a/packages/riverpod/lib/src/future_provider/base.dart +++ /dev/null @@ -1,167 +0,0 @@ -part of '../future_provider.dart'; - -/// {@macro riverpod.provider_ref_base} -/// - [state], the value currently exposed by this provider. -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class FutureProviderRef implements Ref> { - /// Obtains the state currently exposed by this provider. - /// - /// Mutating this property will notify the provider listeners. - /// - /// Cannot be called while a provider is creating, unless the setter was called first. - /// - /// Will return [AsyncLoading] if used during the first initialization. - /// Subsequent initializations will contain an [AsyncValue] with the previous - /// state and [AsyncValueX.isRefreshing]/[AsyncValueX.isReloading] set accordingly. - AsyncValue get state; - set state(AsyncValue newState); - - /// Obtains the [Future] associated to this provider. - /// - /// This is equivalent to doing `ref.read(myProvider.future)`. - /// See also [FutureProvider.future]. - Future get future; -} - -/// {@macro riverpod.future_provider} -class FutureProvider extends _FutureProviderBase - with - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderBase>, - AlwaysAliveAsyncSelector { - /// {@macro riverpod.future_provider} - FutureProvider( - this._createFn, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - FutureProvider.internal( - this._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.autoDispose} - static const autoDispose = AutoDisposeFutureProviderBuilder(); - - /// {@macro riverpod.family} - static const family = FutureProviderFamilyBuilder(); - - // ignore: deprecated_member_use_from_same_package - final Create, FutureProviderRef> _createFn; - - @override - // ignore: deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable> future = _future(this); - - @override - FutureOr _create(FutureProviderElement ref) => _createFn(ref); - - @override - FutureProviderElement createElement() => FutureProviderElement(this); - - /// {@macro riverpod.override_with} - // ignore: deprecated_member_use_from_same_package - Override overrideWith(Create, FutureProviderRef> create) { - return ProviderOverride( - origin: this, - override: FutureProvider.internal( - create, - from: from, - argument: argument, - debugGetCreateSourceHash: null, - dependencies: null, - allTransitiveDependencies: null, - name: null, - ), - ); - } -} - -/// The element of a [FutureProvider] -class FutureProviderElement extends ProviderElementBase> - with - FutureHandlerProviderElementMixin - implements - // ignore: deprecated_member_use_from_same_package - FutureProviderRef { - /// The element of a [FutureProvider] - @internal - // ignore: library_private_types_in_public_api - FutureProviderElement(_FutureProviderBase super._provider); - - @override - Future get future { - flush(); - return futureNotifier.value; - } - - @override - void create({required bool didChangeDependency}) { - final provider = this.provider as _FutureProviderBase; - - handleFuture( - () => provider._create(this), - didChangeDependency: didChangeDependency, - ); - } -} - -/// The [Family] of a [FutureProvider] -// ignore: deprecated_member_use_from_same_package -class FutureProviderFamily extends FamilyBase, - AsyncValue, Arg, FutureOr, FutureProvider> { - /// The [Family] of a [FutureProvider] - FutureProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: FutureProvider.internal, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - debugGetCreateSourceHash: null, - ); - - /// Implementation detail of the code-generator. - @internal - FutureProviderFamily.generator( - super._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - }) : super(providerFactory: FutureProvider.internal); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - FutureOr Function(FutureProviderRef ref, Arg arg) create, - ) { - return FamilyOverrideImpl, Arg, FutureProvider>( - this, - (arg) => FutureProvider.internal( - (ref) => create(ref, arg), - from: from, - argument: arg, - debugGetCreateSourceHash: null, - dependencies: null, - allTransitiveDependencies: null, - name: null, - ), - ); - } -} diff --git a/packages/riverpod/lib/src/internals.dart b/packages/riverpod/lib/src/internals.dart index 425a30feb..06592ceaa 100644 --- a/packages/riverpod/lib/src/internals.dart +++ b/packages/riverpod/lib/src/internals.dart @@ -5,17 +5,17 @@ /// It is used by the flutter and hooks implementation, but is not stable. library internals; -export 'async_notifier.dart'; -export 'builders.dart'; -export 'common.dart'; +export 'common/listenable.dart'; +export 'common/result.dart'; +export 'common/run_guarded.dart'; +export 'common/stack_trace.dart'; +export 'core/async_value.dart'; export 'framework.dart'; -export 'future_provider.dart'; -export 'listenable.dart'; -export 'notifier.dart'; -export 'provider.dart'; -export 'result.dart'; -export 'run_guarded.dart'; -export 'stack_trace.dart'; -export 'state_notifier_provider.dart'; -export 'state_provider.dart'; -export 'stream_provider.dart'; +export 'providers/async_notifier.dart'; +export 'providers/future_provider.dart'; +export 'providers/legacy/state_notifier_provider.dart'; +export 'providers/legacy/state_provider.dart'; +export 'providers/notifier.dart'; +export 'providers/provider.dart'; +export 'providers/stream_notifier.dart'; +export 'providers/stream_provider.dart'; diff --git a/packages/riverpod/lib/src/mutation.dart b/packages/riverpod/lib/src/mutation.dart new file mode 100644 index 000000000..1ac949fe1 --- /dev/null +++ b/packages/riverpod/lib/src/mutation.dart @@ -0,0 +1,513 @@ +import 'dart:async'; + +import 'package:meta/meta.dart'; +import 'package:meta/meta_meta.dart'; + +import 'internals.dart'; + +// Mutation code. This should be in riverpod_annotation, but has to be here +// for the sake of ProviderObserver. + +@internal +const mutationZoneKey = #_mutation; + +/// {@template mutation} +/// Declares a method of a notifier as a "mutation". +/// +/// ## What is a mutation? +/// +/// A mutation is a method that modifies the state of a [Notifier]. +/// For example, an `addTodo` mutation would add a new todo to a todo-list. +/// +/// The primary purpose of mutations is to enable Flutter's Widgets to listen +/// to the progress of an operation. +/// Specifically, by using mutations, a widget may: +/// - Disabling a button while the operation is in progress +/// - Show a button to start an operation +/// - Show a loading indicator when the mutation is in progress. +/// - Show a snackbar when the operation completes/fails +/// +/// Mutations also make it easier to separate the logic for "starting an operation" +/// from the logic for "handling the result of an operation". +/// Two widgets can rely on the same mutation, and the progress of the operation +/// will be shared between them. +/// This way, one widget can be responsible for showing a button, while another +/// widget can be responsible for showing a loading indicator. +/// +/// Although there are ways to handle such cases without mutations, using +/// mutations makes it simpler to deal with. +/// For example, there is no need to catch possible exceptions to allow the UI +/// to show an error message. By using mutations, Riverpod will automatically +/// take care of that. +/// +/// ## How to define a mutation +/// +/// To define a mutation, we must first define a [Notifier]. +/// For that, we need to define a class annotation by `@riverpod`, and that defines +/// a `build` method: +/// +/// ```dart +/// @riverpod +/// class TodoListNotifier extends $ExampleNotifier { +/// @override +/// Future> build() { +/// /* fetch the list of todos from your server here */ +/// } +/// } +/// ``` +/// +/// Once we have defined a notifier, we can add a mutation to it. +/// To do so, we define a method in the notifier class and annotate it with [Mutation]: +/// +/// **Note**: +/// That method **must** return a [Future] that resolves with a value of the same +/// type as the notifier's state. +/// In our above example, the state of our notifier is `List`, so the mutation +/// must return a `Future>`. +/// +/// ```dart +/// @riverpod +/// class TodoListNotifier extends $ExampleNotifier { +/// /* ... */ +/// +/// @mutation +/// Future> addTodo(Todo todo) async { +/// /* to-do: Make an HTTP post request to notify the server about the added todo */ +/// +/// // Mutations are expected to return the new state for our notifier. +/// // Riverpod will then assign this value to `this.state` +/// return [...await future, todo]; +/// } +/// } +/// ``` +/// +/// ## How to use mutations +/// +/// Now that we've defined a mutation, we need a way to invoke it from our UI. +/// +/// The way this is typically done is by using `ref.watch`/`ref.listen` to obtain +/// an object that enables us to interact with the mutation: +/// +/// ```dart +/// class AddTodoButton extends ConsumerWidget { +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// // We use `ref.watch` to obtain the mutation object. +/// // For every `@mutation` defined in our notifier, a corresponding +/// // `myProvider.myMutation` will be available ; which can be as followed: +/// final addTodo = ref.watch(todoListProvider.addTodo); +/// } +/// } +/// ``` +/// +/// Once we have obtained the mutation object, we have two main ways to use it: +/// +/// ### 1. Start the operation inside a button press +/// +/// Mutation objects are "callable". This means that we can call them like a function. +/// When we call a mutation, it will start the operation. +/// Of course, we will have to pass the required arguments that are expected by our mutation. +/// +/// The following code shows a button that, when pressed, will start the `addTodo` mutation: +/// +/// ```dart +/// final addTodo = ref.watch(todoListProvider.addTodo); +/// +/// return ElevatedButton( +/// // Pressing the button will call `TodoListNotifier.addTodo` +/// onPressed: () => addTodo(Todo('Buy milk')), +/// ); +/// ``` +/// +/// ### 2. Listen to the progress of the operation +/// +/// Alternatively, we can use the mutation object to track the progress of the operation. +/// This is useful for many reasons, including: +/// - Disabling a button while the operation is in progress +/// - Showing a loading indicator while the operation is pending +/// - Showing a snackbar or the button in red/green when the operation fails/completes +/// +/// {@macro mutation_states} +/// +/// You can switch over the different types using pattern matching: +/// +/// ```dart +/// final addTodo = ref.watch(todoListProvider.addTodo); +/// +/// switch (addTodo.state) { +/// case IdleMutationState(): +/// print('The mutation is idle'); +/// case PendingMutationState(): +/// print('The mutation is in progress'); +/// case ErrorMutationState(:final error): +/// print('The mutation has failed with $error'); +/// case SuccessMutationState(:final value): +/// print('The mutation has succeeded, and the new state is $value'); +/// } +/// ``` +/// +/// ### Example: Showing a loading indicator while the mutation is in progress +/// +/// You can check for the [PendingMutationState] to show a loading indicator. +/// The following code shows a loading indicator while the mutation is in progress. +/// The indicator will disappear when the mutation completes or fails. +/// +/// ```dart +/// class TodoListView extends ConsumerWidget { +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// final addTodo = ref.watch(todoListProvider.addTodo); +/// +/// return Scaffold( +/// body: Column( +/// children: [ +/// // If the mutation is in progress, show a loading indicator +/// if (addTodo.state is PendingMutationState) +/// const LinearProgressIndicator(), +/// // See above for how AddTodoButton is defined +/// AddTodoButton(), +/// ], +/// ), +/// ); +/// } +/// } +/// ``` +/// +/// Notice how the code that handles the loading state of our mutation +/// is separated from the code that starts the mutation. +/// In this example, even though `AddTodoButton` and `TodoListView` are separate, +/// both share the progress of the operation. This allows to easily separate +/// split the responsibilities of our widgets. +/// +/// ### Example: Showing a snackbar when the mutation completes/fails +/// +/// You can check for the [ErrorMutationState] and [SuccessMutationState] to show a snackbar. +/// +/// Since showing snackbars is done using `showSnackBar`, which is not a widget, +/// we cannot rely on `ref.watch` here. +/// Instead, we should use `ref.listen` to listen to the mutation state. +/// This will give us a callback where we can safely show a snackbar. +/// +/// ```dart +/// class TodoListView extends ConsumerWidget { +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// ref.listen(todoListProvider.addTodo, (_, addTodo) { +/// // We determine if the mutation succeeded or failed, and change +/// // the message accordingly. +/// String message; +/// if (addTodo.state is ErrorMutationState) { +/// message = 'Failed to add todo'; +/// } +/// else if (addTodo.state is SuccessMutationState) { +/// message = 'Todo added successfully'; +/// } +/// // We are neither in a success nor in an error state, so we do not show a snackbar. +/// else return; +/// +/// // We show a snackbar with the message. +/// ScaffoldMessenger.of(context).showSnackBar( +/// SnackBar(content: Text(message)), +/// ); +/// }); +/// } +/// } +/// ``` +/// +/// ## Example: Disabling a button while the operation is pending +/// +/// You can check for the [PendingMutationState] to know if an operation is +/// in progress. We can use this information to disable a button, by setting its +/// `onPressed` to `null`: +/// +/// ```dart +/// class AddTodoButton extends ConsumerWidget { +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// final addTodo = ref.watch(todoListProvider.addTodo); +/// +/// return ElevatedButton( +/// onPressed: addTodo.state is PendingMutationState +/// ? null // If the mutation is in progress, disable the button +/// : () => addTodo(Todo('Buy milk')), // Otherwise enable the button +/// ); +/// } +/// } +/// ``` +/// +/// A similar logic can be used for showing the button in red/green when the +/// operation fails/completes, by instead checking for [ErrorMutationState] and +/// [SuccessMutationState]. +/// +/// {@macro auto_reset} +/// {@endtemplate} +@Target({TargetKind.method}) +final class Mutation { + /// {@macro mutation} + const Mutation(); +} + +/// {@macro mutation} +const mutation = Mutation(); + +/// The current state of a mutation. +/// +/// {@template mutation_states} +/// A mutation can be in any of the following states: +/// - [IdleMutationState]: The mutation is not running. This is the default state. +/// - [PendingMutationState]: The mutation has been called and is in progress. +/// - [ErrorMutationState]: The mutation has failed with an error. +/// - [SuccessMutationState]: The mutation has completed successfully. +/// {@endtemplate} +sealed class MutationState { + const MutationState._(); +} + +/// The mutation is not running. +/// +/// This is the default state of a mutation. +/// A mutation can be reset to this state by calling [MutationBase.reset]. +/// +/// {@macro auto_reset} +/// +/// {@macro mutation_states} +final class IdleMutationState extends MutationState { + const IdleMutationState._() : super._(); + + @override + String toString() => 'IdleMutationState<$ResultT>()'; +} + +/// The mutation has been called and is in progress. +/// +/// {@macro mutation_states} +final class PendingMutationState extends MutationState { + const PendingMutationState._() : super._(); + + @override + String toString() => 'PendingMutationState<$ResultT>()'; +} + +/// The mutation has failed with an error. +/// +/// {@macro mutation_states} +final class ErrorMutationState extends MutationState { + ErrorMutationState._(this.error, this.stackTrace) : super._(); + + /// The error thrown by the mutation. + final Object error; + + /// The stack trace of the [error]. + final StackTrace stackTrace; + + @override + String toString() => 'ErrorMutationState<$ResultT>($error, $stackTrace)'; +} + +/// The mutation has completed successfully. +/// +/// {@macro mutation_states} +final class SuccessMutationState extends MutationState { + SuccessMutationState._(this.value) : super._(); + + /// The new state of the notifier after the mutation has completed. + final ResultT value; + + @override + String toString() => 'SuccessMutationState<$ResultT>($value)'; +} + +/// A base class that all mutations extends. +/// +/// See also [Mutation] for information on how to define a mutation. +@immutable +abstract class MutationBase { + /// The current state of the mutation. + /// + /// This defaults to [IdleMutationState]. + /// When the mutation starts, it will change to [PendingMutationState] and + /// then to either [ErrorMutationState] or [SuccessMutationState]. + /// + /// **Note**: + /// This property is immutable. The state will not change unless you + /// call `ref.watch(provider.myMutation)` again. + MutationState get state; + + /// Sets [state] back to [IdleMutationState]. + /// + /// Calling [reset] is useful when the mutation is actively listened to, + /// and you want to forcibly go back to the [IdleMutationState]. + /// + /// {@template auto_reset} + /// ## Automatic resets + /// + /// By default, mutations are automatically reset when they are no longer + /// being listened to. + /// This is similar to Riverpod's "auto-dispose" feature, for mutations. + /// If you remove all `watch`/`listen` calls to a mutation, the mutation + /// will automatically go-back to its [IdleMutationState]. + /// + /// If your mutation is always listened, you may want to call [reset] manually + /// to restore the mutation to its [IdleMutationState]. + /// {@endtemplate} + void reset(); +} + +@internal +abstract class $SyncMutationBase< + StateT, + MutationT extends $SyncMutationBase, + ClassT extends NotifierBase> + extends _MutationBase { + $SyncMutationBase({super.state, super.key}); + + @override + void setData(StateT value) { + element.setStateResult($Result.data(value)); + } +} + +@internal +abstract class $AsyncMutationBase< + StateT, + MutationT extends $AsyncMutationBase, + ClassT extends NotifierBase, Object?>> + extends _MutationBase, MutationT, ClassT> { + $AsyncMutationBase({super.state, super.key}); + + @override + void setData(StateT value) { + element.setStateResult($Result.data(AsyncData(value))); + } +} + +abstract class _MutationBase< + ValueT, + StateT, + MutationT extends _MutationBase, + ClassT extends NotifierBase> + implements MutationBase { + _MutationBase({MutationState? state, this.key}) + : state = state ?? IdleMutationState._() { + listenable.onCancel = _scheduleAutoReset; + } + + @override + final MutationState state; + final Object? key; + + ClassProviderElement get element; + $ElementLense get listenable; + + Object? get _currentKey => listenable.result?.stateOrNull?.key; + + MutationT copyWith(MutationState state, {Object? key}); + + void setData(ValueT value); + + void _scheduleAutoReset() { + Future.microtask(() { + if (listenable.hasListeners) return; + + reset(); + }); + } + + @override + void reset() { + if (state is IdleMutationState) return; + + listenable.result = ResultData(copyWith(IdleMutationState._())); + + final context = ProviderObserverContext(element.origin, element.container); + + _notifyObserver((obs) => obs.mutationReset(context)); + } + + void _notifyObserver(void Function(ProviderObserver obs) cb) { + for (final observer in element.container.observers) { + runUnaryGuarded(cb, observer); + } + } + + void _setState(MutationContext? mutationContext, MutationT mutation) { + listenable.result = $Result.data(mutation); + + final obsContext = ProviderObserverContext( + element.origin, + element.container, + mutation: mutationContext, + ); + + switch (mutation.state) { + case ErrorMutationState(:final error, :final stackTrace): + _notifyObserver( + (obs) => obs.mutationError( + obsContext, + mutationContext!, + error, + stackTrace, + ), + ); + + case SuccessMutationState(:final value): + _notifyObserver( + (obs) => obs.mutationSuccess(obsContext, mutationContext!, value), + ); + + case PendingMutationState(): + _notifyObserver( + (obs) => obs.mutationStart(obsContext, mutationContext!), + ); + + default: + } + } + + @protected + Future mutateAsync( + Invocation invocation, + FutureOr Function(ClassT clazz) cb, + ) { + element.flush(); + final notifier = element.classListenable.value; + final mutationContext = MutationContext(invocation, notifier); + + return runZoned( + zoneValues: {mutationZoneKey: mutationContext}, + () async { + // ! is safe because of the flush() above + final key = Object(); + try { + _setState( + mutationContext, + copyWith(PendingMutationState._(), key: key), + ); + + final result = await cb(notifier); + if (key == _currentKey) { + _setState( + mutationContext, + copyWith(SuccessMutationState._(result)), + ); + } + setData(result); + + return result; + } catch (err, stack) { + if (key == _currentKey) { + _setState( + mutationContext, + copyWith(ErrorMutationState._(err, stack)), + ); + } + + rethrow; + } + }, + ); + } + + @override + String toString() => '$runtimeType#${shortHash(this)}($state)'; +} diff --git a/packages/riverpod/lib/src/notifier.dart b/packages/riverpod/lib/src/notifier.dart deleted file mode 100644 index c7043c372..000000000 --- a/packages/riverpod/lib/src/notifier.dart +++ /dev/null @@ -1,175 +0,0 @@ -import 'package:meta/meta.dart'; - -import 'async_notifier.dart'; -import 'builders.dart'; -import 'framework.dart'; -import 'listenable.dart'; -import 'provider.dart'; -import 'result.dart'; - -part 'notifier/auto_dispose.dart'; -part 'notifier/auto_dispose_family.dart'; -part 'notifier/base.dart'; -part 'notifier/family.dart'; - -/// A base class for [NotifierBase]. -/// -/// Not meant for public consumption. -@internal -abstract class NotifierBase { - NotifierProviderElement, State> get _element; - - void _setElement(ProviderElementBase element); - - /// {@template notifier.listen} - /// Listens to changes on the value exposed by this provider. - /// - /// The listener will be called immediately after the provider completes building. - /// - /// As opposed to [Ref.listen], the listener will be called even if - /// [ProviderElementBase.updateShouldNotify] returns false, meaning that the previous - /// and new value can potentially be identical. - /// {@endtemplate} - void listenSelf( - void Function(State? previous, State next) listener, { - void Function(Object error, StackTrace stackTrace)? onError, - }) { - _element.listenSelf(listener, onError: onError); - } - - /// The value currently exposed by this [Notifier]. - /// - /// If used inside [Notifier.build], may throw if the notifier is not yet initialized. - /// - /// Invoking the setter will notify listeners if [updateShouldNotify] returns true. - /// By default, this will compare the previous and new value using [identical]. - /// - /// 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. - /// - /// If [Notifier.build] threw, reading [state] will rethrow the exception. - @protected - @visibleForTesting - State get state { - _element.flush(); - return _element.requireState; - } - - /// The value currently exposed by this [Notifier]. - /// - /// If used inside [Notifier.build], may return null if the notifier is not yet initialized. - /// It will also return null if [Notifier.build] threw. - /// - /// Invoking the setter will notify listeners if [updateShouldNotify] returns true. - /// By default, this will compare the previous and new value using [identical]. - /// - /// Reading [stateOrNull] if the provider is out of date (such as if one of its - /// dependency has changed) will trigger [Notifier.build] to be re-executed. - @protected - @visibleForTesting - State? get stateOrNull { - _element.flush(); - return _element.getState()?.stateOrNull; - } - - @protected - @visibleForTesting - set state(State value) { - // ignore: invalid_use_of_protected_member - _element.setState(value); - } - - /// The [Ref] from the provider associated with this [Notifier]. - @protected - Ref get ref; - - /// A method invoked when the state exposed by this [Notifier] 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 = 42; - /// state = 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 - /// value. - /// - /// 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". - /// - /// If you do not want that, you can override this method to perform a deep - /// comparison of the previous and new values. - @protected - bool updateShouldNotify(State previous, State next) { - return !identical(previous, next); - } -} - -ProviderElementProxy - _notifier, T>( - NotifierProviderBase that, -) { - return ProviderElementProxy( - that, - (element) { - return (element as NotifierProviderElement) - ._notifierNotifier; - }, - ); -} - -/// An internal base class for [Notifier]. -/// -/// Not meant for public consumption. -@internal -abstract class NotifierProviderBase, T> - extends ProviderBase { - /// An internal base class for [Notifier]. - /// - /// Not meant for public consumption. - const NotifierProviderBase( - this._createNotifier, { - required super.name, - required super.from, - required super.argument, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - }); - - /// Obtains the [Notifier] associated with this provider, without listening - /// to state changes. - /// - /// This is typically used to invoke methods on a [Notifier]. For example: - /// - /// ```dart - /// Button( - /// onTap: () => ref.read(stateNotifierProvider.notifier).increment(), - /// ) - /// ``` - /// - /// This listenable will notify its notifiers if the [Notifier] instance - /// changes. - /// This may happen if the provider is refreshed or one of its dependencies - /// has changes. - ProviderListenable get notifier; - - final NotifierT Function() _createNotifier; - - /// Runs the `build` method of a notifier. - /// - /// This is an implementation detail for differentiating [Notifier.build] - /// from [FamilyNotifier.build]. - @visibleForOverriding - T runNotifierBuild(NotifierBase notifier); -} diff --git a/packages/riverpod/lib/src/notifier/auto_dispose.dart b/packages/riverpod/lib/src/notifier/auto_dispose.dart deleted file mode 100644 index 8d4efe4ad..000000000 --- a/packages/riverpod/lib/src/notifier/auto_dispose.dart +++ /dev/null @@ -1,125 +0,0 @@ -part of '../notifier.dart'; - -/// An [AutoDisposeNotifier] base class shared between family and non-family notifiers. -/// -/// Not meant for public consumption outside of riverpod_generator -@internal -abstract class BuildlessAutoDisposeNotifier extends NotifierBase { - @override - late final AutoDisposeNotifierProviderElement, State> - _element; - - @override - void _setElement(ProviderElementBase element) { - _element = element - as AutoDisposeNotifierProviderElement, State>; - } - - @override - // ignore: deprecated_member_use_from_same_package - AutoDisposeNotifierProviderRef get ref => _element; -} - -/// {@macro riverpod.notifier} -/// -/// {@macro riverpod.notifier_provider_modifier} -abstract class AutoDisposeNotifier - extends BuildlessAutoDisposeNotifier { - /// {@macro riverpod.async_notifier.build} - @visibleForOverriding - State build(); -} - -/// {@macro riverpod.provider_ref_base} -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class AutoDisposeNotifierProviderRef - implements NotifierProviderRef, AutoDisposeRef {} - -/// {@macro riverpod.notifier_provider} -/// -/// {@macro riverpod.notifier_provider_modifier} -typedef AutoDisposeNotifierProvider, T> - = AutoDisposeNotifierProviderImpl; - -/// The implementation of [AutoDisposeNotifierProvider] but with loosened type constraints -/// that can be shared with [NotifierProvider]. -/// -/// This enables tests to execute on both [AutoDisposeNotifierProvider] and -/// [NotifierProvider] at the same time. -@internal -class AutoDisposeNotifierProviderImpl, T> - extends NotifierProviderBase { - /// {@macro riverpod.notifier_provider} - /// - /// {@macro riverpod.notifier_provider_modifier} - AutoDisposeNotifierProviderImpl( - super._createNotifier, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - AutoDisposeNotifierProviderImpl.internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.family} - static const family = AutoDisposeNotifierProviderFamily.new; - - @override - late final Refreshable notifier = _notifier(this); - - @override - AutoDisposeNotifierProviderElement createElement() { - return AutoDisposeNotifierProviderElement(this); - } - - @override - @mustBeOverridden - T runNotifierBuild(NotifierBase notifier) { - return (notifier as AutoDisposeNotifier).build(); - } - - /// {@macro riverpod.override_with} - @mustBeOverridden - Override overrideWith(NotifierT Function() create) { - return ProviderOverride( - origin: this, - override: AutoDisposeNotifierProviderImpl.internal( - create, - from: from, - argument: argument, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} - -/// The element of [AutoDisposeNotifierProvider] -class AutoDisposeNotifierProviderElement, T> - extends NotifierProviderElement - with - AutoDisposeProviderElementMixin - implements - // ignore: deprecated_member_use_from_same_package - AutoDisposeNotifierProviderRef { - /// The [ProviderElementBase] for [NotifierProvider] - @internal - AutoDisposeNotifierProviderElement(super._provider); -} diff --git a/packages/riverpod/lib/src/notifier/auto_dispose_family.dart b/packages/riverpod/lib/src/notifier/auto_dispose_family.dart deleted file mode 100644 index ce7da847a..000000000 --- a/packages/riverpod/lib/src/notifier/auto_dispose_family.dart +++ /dev/null @@ -1,124 +0,0 @@ -part of '../notifier.dart'; - -/// {@macro riverpod.notifier} -/// -/// {@macro riverpod.notifier_provider_modifier} -abstract class AutoDisposeFamilyNotifier - extends BuildlessAutoDisposeNotifier { - /// {@macro riverpod.notifier.family_arg} - late final Arg arg; - - @override - void _setElement(ProviderElementBase element) { - super._setElement(element); - arg = element.origin.argument as Arg; - } - - /// {@macro riverpod.async_notifier.build} - @visibleForOverriding - State build(Arg arg); -} - -/// {@macro riverpod.notifier_provider} -/// -/// {@macro riverpod.notifier_provider_modifier} -typedef AutoDisposeFamilyNotifierProvider< - NotifierT extends AutoDisposeFamilyNotifier, T, Arg> - = AutoDisposeFamilyNotifierProviderImpl; - -/// The implementation of [AutoDisposeNotifierProvider] but with loosened type constraints -/// that can be shared with [NotifierProvider]. -/// -/// This enables tests to execute on both [AutoDisposeNotifierProvider] and -/// [NotifierProvider] at the same time. -@internal -class AutoDisposeFamilyNotifierProviderImpl, - T, Arg> extends NotifierProviderBase { - /// {@macro riverpod.notifier_provider} - /// - /// {@macro riverpod.notifier_provider_modifier} - AutoDisposeFamilyNotifierProviderImpl( - super._createNotifier, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - AutoDisposeFamilyNotifierProviderImpl.internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - @override - late final Refreshable notifier = _notifier(this); - - @override - AutoDisposeNotifierProviderElement createElement() { - return AutoDisposeNotifierProviderElement(this); - } - - @override - T runNotifierBuild( - covariant AutoDisposeFamilyNotifier notifier, - ) { - return notifier.build(notifier.arg); - } -} - -/// The [Family] of [NotifierProvider]. -class AutoDisposeNotifierProviderFamily< - NotifierT extends AutoDisposeFamilyNotifier, T, Arg> - // ignore: deprecated_member_use_from_same_package - extends AutoDisposeNotifierFamilyBase, T, - Arg, NotifierT, AutoDisposeFamilyNotifierProvider> { - /// The [Family] of [AutoDisposeNotifierProvider]. - AutoDisposeNotifierProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: AutoDisposeFamilyNotifierProvider.internal, - debugGetCreateSourceHash: null, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - AutoDisposeNotifierProviderFamily.internal( - super._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - }) : super(providerFactory: AutoDisposeFamilyNotifierProvider.internal); - - /// {@macro riverpod.override_with} - Override overrideWith(NotifierT Function() create) { - return FamilyOverrideImpl>( - this, - (arg) => AutoDisposeFamilyNotifierProvider.internal( - create, - from: from, - argument: arg, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} diff --git a/packages/riverpod/lib/src/notifier/base.dart b/packages/riverpod/lib/src/notifier/base.dart deleted file mode 100644 index 44a0f9b58..000000000 --- a/packages/riverpod/lib/src/notifier/base.dart +++ /dev/null @@ -1,233 +0,0 @@ -part of '../notifier.dart'; - -/// A [Notifier] base class shared between family and non-family notifiers. -/// -/// Not meant for public consumption outside of riverpod_generator -@internal -abstract class BuildlessNotifier extends NotifierBase { - @override - late final NotifierProviderElement, State> _element; - - @override - void _setElement(ProviderElementBase element) { - _element = element as NotifierProviderElement, State>; - } - - @override - // ignore: deprecated_member_use_from_same_package - NotifierProviderRef get ref => _element; -} - -/// {@template riverpod.notifier} -/// A class which exposes a state that can change over time. -/// -/// For example, [Notifier] can be used to implement a counter by doing: -/// -/// ```dart -/// final counterProvider = NotifierProvider(Counter.new); -/// -/// class Counter extends Notifier { -/// @override -/// int build() { -/// // Inside "build", we return the initial state of the counter. -/// return 0; -/// } -/// -/// void increment() { -/// state++; -/// } -/// } -/// ``` -/// -/// We can then listen to the counter inside widgets by doing: -/// -/// ```dart -/// Consumer( -/// builder: (context, ref) { -/// return Text('count: ${ref.watch(counterProvider)}'); -/// }, -/// ) -/// ``` -/// -/// And finally, we can update the counter by doing: -/// -/// ```dart -/// Consumer( -/// builder: (context, ref) { -/// return ElevatedButton( -/// onTap: () => ref.read(counterProvider.notifier).increment(), -/// child: const Text('increment'), -/// ); -/// }, -/// ) -/// ``` -/// -/// 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]. - /// - /// It is safe to use [Ref.watch] or [Ref.listen] inside this method. - /// - /// If a dependency of this [Notifier] (when using [Ref.watch]) changes, - /// then [build] will be re-executed. On the other hand, the [Notifier] - /// will **not** be recreated. Its instance will be preserved between - /// executions of [build]. - /// - /// If this method throws, reading this provider will rethrow the error. - /// {@endtemplate} - @visibleForOverriding - State build(); -} - -/// {@macro riverpod.provider_ref_base} -@Deprecated('will be removed in 3.0.0. Use Ref instead') -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; - -/// The implementation of [NotifierProvider] but with loosened type constraints -/// that can be shared with [AutoDisposeNotifierProvider]. -/// -/// This enables tests to execute on both [NotifierProvider] and -/// [AutoDisposeNotifierProvider] at the same time. -@internal -class NotifierProviderImpl, T> - extends NotifierProviderBase - with - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderBase { - /// {@macro riverpod.notifier_provider} - /// - /// {@macro riverpod.notifier_provider_modifier} - NotifierProviderImpl( - super._createNotifier, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - NotifierProviderImpl.internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.autoDispose} - static const autoDispose = AutoDisposeNotifierProviderBuilder(); - - /// {@macro riverpod.family} - static const family = NotifierProviderFamilyBuilder(); - - @override - NotifierProviderElement createElement() { - return NotifierProviderElement(this); - } - - @override - // ignore: deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable notifier = - _notifier(this); - - @override - @mustBeOverridden - T runNotifierBuild(NotifierBase notifier) { - return (notifier as Notifier).build(); - } - - /// {@macro riverpod.override_with} - @mustBeOverridden - Override overrideWith(NotifierT Function() create) { - return ProviderOverride( - origin: this, - override: NotifierProviderImpl.internal( - create, - from: from, - argument: argument, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ), - ); - } -} - -/// The element of [NotifierProvider]. -class NotifierProviderElement, T> - extends ProviderElementBase - implements - // ignore: deprecated_member_use_from_same_package - NotifierProviderRef { - /// The element of [NotifierProvider]. - @internal - NotifierProviderElement(NotifierProviderBase super._provider); - - final _notifierNotifier = ProxyElementValueNotifier(); - - @override - void create({required bool didChangeDependency}) { - final provider = this.provider as NotifierProviderBase; - - final notifierResult = _notifierNotifier.result ??= Result.guard(() { - return provider._createNotifier().._setElement(this); - }); - - // If the Notifier failed to create (such as if the constructor has an assert exception), - // then we purposefully rethrow the error. - // This way, doing `watch(provider)` will rethrow the error. - final notifier = notifierResult.requireState; - - setState(provider.runNotifierBuild(notifier)); - } - - @override - void visitChildren({ - required void Function(ProviderElementBase element) elementVisitor, - required void Function(ProxyElementValueNotifier element) notifierVisitor, - }) { - super.visitChildren( - elementVisitor: elementVisitor, - notifierVisitor: notifierVisitor, - ); - notifierVisitor(_notifierNotifier); - } - - @override - bool updateShouldNotify(T previous, T next) { - return _notifierNotifier.result?.stateOrNull - ?.updateShouldNotify(previous, next) ?? - true; - } -} diff --git a/packages/riverpod/lib/src/notifier/family.dart b/packages/riverpod/lib/src/notifier/family.dart deleted file mode 100644 index 0d3f42318..000000000 --- a/packages/riverpod/lib/src/notifier/family.dart +++ /dev/null @@ -1,130 +0,0 @@ -part of '../notifier.dart'; - -/// {@macro riverpod.notifier} -/// -/// {@macro riverpod.notifier_provider_modifier} -abstract class FamilyNotifier extends BuildlessNotifier { - /// {@macro riverpod.notifier.family_arg} - late final Arg arg; - - @override - void _setElement(ProviderElementBase element) { - super._setElement(element); - arg = element.origin.argument as Arg; - } - - /// {@macro riverpod.async_notifier.build} - @visibleForOverriding - State build(Arg arg); -} - -/// The provider for [NotifierProviderFamily]. -typedef NotifierFamilyProvider, T, Arg> - = FamilyNotifierProviderImpl; - -/// The implementation of [NotifierFamilyProvider] but with loosened type constraints -/// that can be shared with [AutoDisposeNotifierProvider]. -/// -/// This enables tests to execute on both [NotifierProvider] and -/// [AutoDisposeNotifierProvider] at the same time. -@visibleForTesting -@internal -class FamilyNotifierProviderImpl, T, Arg> - extends NotifierProviderBase - with - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderBase { - /// {@macro riverpod.notifier} - FamilyNotifierProviderImpl( - super._createNotifier, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - FamilyNotifierProviderImpl.internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.autoDispose} - // ignore: prefer_const_declarations - static final autoDispose = AutoDisposeNotifierProviderFamily.new; - - // /// {@macro riverpod.family} - // static const family = NotifierProviderFamilyBuilder(); - - @override - // ignore: deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable notifier = - _notifier(this); - - @override - NotifierProviderElement createElement() { - return NotifierProviderElement(this); - } - - @override - T runNotifierBuild( - covariant FamilyNotifier notifier, - ) { - return notifier.build(notifier.arg); - } -} - -/// The [Family] of [NotifierProvider]. -class NotifierProviderFamily, T, Arg> - // ignore: deprecated_member_use_from_same_package - extends NotifierFamilyBase, T, Arg, NotifierT, - NotifierFamilyProvider> { - /// The [Family] of [NotifierProvider]. - NotifierProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: NotifierFamilyProvider.internal, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - debugGetCreateSourceHash: null, - ); - - /// An implementation detail of Riverpod - @internal - NotifierProviderFamily.internal( - super._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - }) : super(providerFactory: NotifierFamilyProvider.internal); - - /// {@macro riverpod.override_with} - Override overrideWith(NotifierT Function() create) { - return FamilyOverrideImpl>( - this, - (arg) => NotifierFamilyProvider.internal( - create, - from: from, - argument: arg, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} diff --git a/packages/riverpod/lib/src/provider.dart b/packages/riverpod/lib/src/provider.dart deleted file mode 100644 index 587bb40bd..000000000 --- a/packages/riverpod/lib/src/provider.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:meta/meta.dart'; - -import 'builders.dart'; -import 'framework.dart'; -import 'state_notifier_provider.dart' show StateNotifierProvider; -import 'stream_provider.dart' show StreamProvider; - -part 'provider/auto_dispose.dart'; -part 'provider/base.dart'; - -/// A base class for [Provider] -/// -/// Not meant for public consumption -@internal -abstract class InternalProvider extends ProviderBase - with OverrideWithValueMixin { - /// A base class for [Provider] - /// - /// Not meant for public consumption - const InternalProvider({ - required super.name, - required super.from, - required super.argument, - required super.debugGetCreateSourceHash, - required super.dependencies, - required super.allTransitiveDependencies, - }); - - State _create(covariant ProviderElement ref); -} diff --git a/packages/riverpod/lib/src/provider/auto_dispose.dart b/packages/riverpod/lib/src/provider/auto_dispose.dart deleted file mode 100644 index c6c5ac785..000000000 --- a/packages/riverpod/lib/src/provider/auto_dispose.dart +++ /dev/null @@ -1,119 +0,0 @@ -part of '../provider.dart'; - -/// {@macro riverpod.provider_ref_base} -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class AutoDisposeProviderRef extends ProviderRef - implements AutoDisposeRef {} - -/// {@macro riverpod.provider} -class AutoDisposeProvider extends InternalProvider { - /// {@macro riverpod.provider} - AutoDisposeProvider( - this._createFn, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - const AutoDisposeProvider.internal( - this._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.family} - static const family = AutoDisposeProviderFamily.new; - - // ignore: deprecated_member_use_from_same_package - final T Function(AutoDisposeProviderRef ref) _createFn; - - @override - T _create(AutoDisposeProviderElement ref) => _createFn(ref); - - @override - AutoDisposeProviderElement createElement() { - return AutoDisposeProviderElement(this); - } - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - Create> create, - ) { - return ProviderOverride( - origin: this, - override: AutoDisposeProvider.internal( - create, - from: from, - argument: argument, - allTransitiveDependencies: null, - dependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} - -/// The element of [AutoDisposeProvider] -class AutoDisposeProviderElement extends ProviderElement - with - AutoDisposeProviderElementMixin - implements - // ignore: deprecated_member_use_from_same_package - AutoDisposeProviderRef { - /// The [ProviderElementBase] for [Provider] - @internal - AutoDisposeProviderElement(AutoDisposeProvider super._provider); -} - -/// The [Family] of [AutoDisposeProvider] -class AutoDisposeProviderFamily extends AutoDisposeFamilyBase< - // ignore: deprecated_member_use_from_same_package - AutoDisposeProviderRef, - R, - Arg, - R, - AutoDisposeProvider> { - /// The [Family] of [AutoDisposeProvider] - AutoDisposeProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: AutoDisposeProvider.internal, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - debugGetCreateSourceHash: null, - ); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - R Function(AutoDisposeProviderRef ref, Arg arg) create, - ) { - return FamilyOverrideImpl>( - this, - (arg) => AutoDisposeProvider.internal( - (ref) => create(ref, arg), - from: from, - argument: arg, - name: null, - debugGetCreateSourceHash: null, - dependencies: null, - allTransitiveDependencies: null, - ), - ); - } -} diff --git a/packages/riverpod/lib/src/providers/async_notifier.dart b/packages/riverpod/lib/src/providers/async_notifier.dart new file mode 100644 index 000000000..a0ec15738 --- /dev/null +++ b/packages/riverpod/lib/src/providers/async_notifier.dart @@ -0,0 +1,77 @@ +import 'dart:async'; + +import 'package:meta/meta.dart'; + +import '../builder.dart'; +import '../core/async_value.dart'; +import '../framework.dart'; +import 'future_provider.dart' show FutureProvider; +import 'notifier.dart'; + +part 'async_notifier/orphan.dart'; +part 'async_notifier/family.dart'; + +/// Implementation detail of `riverpod_generator`. +/// Do not use. +abstract class $AsyncNotifier extends NotifierBase< // + AsyncValue, + FutureOr> // + with + $AsyncClassModifier> {} + +/// Implementation detail of `riverpod_generator`. +/// Do not use. +abstract base class $AsyncNotifierProvider< // + NotifierT extends $AsyncNotifier, + StateT> // + extends $ClassProvider< // + NotifierT, + AsyncValue, + FutureOr> // + with + $FutureModifier { + /// Implementation detail of `riverpod_generator`. + /// Do not use. + const $AsyncNotifierProvider({ + required super.name, + required super.from, + required super.argument, + required super.dependencies, + required super.allTransitiveDependencies, + required super.isAutoDispose, + required super.runNotifierBuildOverride, + required super.retry, + }); +} + +/// Implementation detail of `riverpod_generator`. +/// Do not use. +@internal +class $AsyncNotifierProviderElement< // + NotifierT extends $AsyncNotifier, + StateT> // + extends ClassProviderElement< // + NotifierT, + AsyncValue, + FutureOr> // + with + FutureModifierElement, + FutureModifierClassElement> { + /// Implementation detail of `riverpod_generator`. + /// Do not use. + $AsyncNotifierProviderElement(this.provider, super.pointer); + + @override + final $AsyncNotifierProvider provider; + + @override + void handleValue( + FutureOr created, { + required bool seamless, + }) { + handleFuture( + () => created, + seamless: seamless, + ); + } +} diff --git a/packages/riverpod/lib/src/providers/async_notifier/family.dart b/packages/riverpod/lib/src/providers/async_notifier/family.dart new file mode 100644 index 000000000..18cc23b73 --- /dev/null +++ b/packages/riverpod/lib/src/providers/async_notifier/family.dart @@ -0,0 +1,124 @@ +part of '../async_notifier.dart'; + +/// {@macro riverpod.async_notifier} +/// +/// {@macro riverpod.async_notifier_provider_modifier} +abstract class FamilyAsyncNotifier + extends $AsyncNotifier { + /// {@template riverpod.notifier.family_arg} + /// The argument that was passed to this family. + /// + /// For example, when doing: + /// + /// ```dart + /// ref.watch(provider(0)); + /// ``` + /// + /// then [arg] will be `0`. + /// {@endtemplate} + late final ArgT arg = ref.$arg as ArgT; + + /// {@macro riverpod.async_notifier.build} + @visibleForOverriding + FutureOr build(ArgT arg); + + @internal + @override + FutureOr runBuild() => build(arg); +} + +/// The [Family] of [AsyncNotifierProvider]. +class AsyncNotifierProviderFamily< // + NotifierT extends FamilyAsyncNotifier, + StateT, + ArgT> // + extends ClassFamily< // + NotifierT, + AsyncValue, + ArgT, + FutureOr, + FamilyAsyncNotifierProvider> { + /// The [Family] of [AsyncNotifierProvider]. + @internal + AsyncNotifierProviderFamily.internal( + super._createFn, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + providerFactory: FamilyAsyncNotifierProvider._, + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + ); +} + +/// The provider returned by [AsyncNotifierProviderFamily]. +final class FamilyAsyncNotifierProvider< // + NotifierT extends FamilyAsyncNotifier, + StateT, + ArgT> // + extends $AsyncNotifierProvider + with LegacyProviderMixin> { + /// An implementation detail of Riverpod + const FamilyAsyncNotifierProvider._( + this._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.from, + required super.argument, + required super.isAutoDispose, + required super.runNotifierBuildOverride, + required super.retry, + }); + + FamilyAsyncNotifierProvider _copyWith({ + NotifierT Function()? create, + RunNotifierBuild>? build, + }) { + return FamilyAsyncNotifierProvider._( + create ?? _createNotifier, + name: name, + dependencies: dependencies, + allTransitiveDependencies: allTransitiveDependencies, + from: from, + argument: argument, + isAutoDispose: isAutoDispose, + runNotifierBuildOverride: build ?? runNotifierBuildOverride, + retry: retry, + ); + } + + final NotifierT Function() _createNotifier; + + @internal + @override + NotifierT create() => _createNotifier(); + + @internal + @override + $AsyncNotifierProviderElement $createElement( + $ProviderPointer pointer, + ) { + return $AsyncNotifierProviderElement(this, pointer); + } + + @mustBeOverridden + @visibleForOverriding + @override + FamilyAsyncNotifierProvider $copyWithBuild( + RunNotifierBuild>? build, + ) { + return _copyWith(build: build); + } + + @mustBeOverridden + @visibleForOverriding + @override + FamilyAsyncNotifierProvider $copyWithCreate( + NotifierT Function() create, + ) { + return _copyWith(create: create); + } +} diff --git a/packages/riverpod/lib/src/providers/async_notifier/orphan.dart b/packages/riverpod/lib/src/providers/async_notifier/orphan.dart new file mode 100644 index 000000000..d76222ee1 --- /dev/null +++ b/packages/riverpod/lib/src/providers/async_notifier/orphan.dart @@ -0,0 +1,147 @@ +part of '../async_notifier.dart'; + +/// {@template riverpod.async_notifier} +/// 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". +/// And the [AsyncNotifier] could expose methods such as "setName", +/// to allow changing the current user name. +/// - 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} +abstract class AsyncNotifier extends $AsyncNotifier { + /// {@template riverpod.async_notifier.build} + /// Initialize an [AsyncNotifier]. + /// + /// It is safe to use [Ref.watch] or [Ref.listen] inside this method. + /// + /// If a dependency of this [AsyncNotifier] (when using [Ref.watch]) changes, + /// then [build] will be re-executed. On the other hand, the [AsyncNotifier] + /// will **not** be recreated. Its instance will be preserved between + /// executions of [build]. + /// + /// If this method throws or returns a future that fails, the error + /// will be caught and an [AsyncError] will be emitted. + /// {@endtemplate} + @visibleForOverriding + FutureOr build(); + + @internal + @override + FutureOr runBuild() => build(); +} + +/// {@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 `family`, your notifier type changes. +/// Instead of extending [AsyncNotifier], you should extend [FamilyAsyncNotifier]. +/// {@endtemplate} +final class AsyncNotifierProvider< // + NotifierT extends AsyncNotifier, + StateT> // + extends $AsyncNotifierProvider + with LegacyProviderMixin> { + /// {@macro riverpod.async_notifier_provider} + /// + /// {@macro riverpod.async_notifier_provider_modifier} + AsyncNotifierProvider( + this._createNotifier, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + from: null, + argument: null, + runNotifierBuildOverride: null, + ); + + /// An implementation detail of Riverpod + @internal + const AsyncNotifierProvider.internal( + this._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.from, + required super.argument, + required super.isAutoDispose, + required super.runNotifierBuildOverride, + required super.retry, + }); + + /// {@macro riverpod.autoDispose} + static const autoDispose = AutoDisposeAsyncNotifierProviderBuilder(); + + /// {@macro riverpod.family} + static const family = AsyncNotifierProviderFamilyBuilder(); + + final NotifierT Function() _createNotifier; + + @internal + @override + NotifierT create() => _createNotifier(); + + AsyncNotifierProvider _copyWith({ + NotifierT Function()? create, + RunNotifierBuild>? build, + }) { + return AsyncNotifierProvider.internal( + create ?? _createNotifier, + name: name, + dependencies: dependencies, + allTransitiveDependencies: allTransitiveDependencies, + from: from, + argument: argument, + isAutoDispose: isAutoDispose, + runNotifierBuildOverride: build ?? runNotifierBuildOverride, + retry: retry, + ); + } + + @internal + @override + $AsyncNotifierProviderElement $createElement( + $ProviderPointer pointer, + ) { + return $AsyncNotifierProviderElement(this, pointer); + } + + @mustBeOverridden + @visibleForOverriding + @override + AsyncNotifierProvider $copyWithBuild( + RunNotifierBuild>? build, + ) { + return _copyWith(build: build); + } + + @mustBeOverridden + @visibleForOverriding + @override + AsyncNotifierProvider $copyWithCreate( + NotifierT Function() create, + ) { + return _copyWith(create: create); + } +} diff --git a/packages/riverpod/lib/src/providers/future_provider.dart b/packages/riverpod/lib/src/providers/future_provider.dart new file mode 100644 index 000000000..bb2aba736 --- /dev/null +++ b/packages/riverpod/lib/src/providers/future_provider.dart @@ -0,0 +1,224 @@ +import 'dart:async'; + +import 'package:meta/meta.dart'; + +import '../builder.dart'; +import '../core/async_value.dart'; +import '../framework.dart'; +import 'async_notifier.dart'; +import 'provider.dart' show Provider; +import 'stream_provider.dart' show StreamProvider; + +/// Implementation detail of `riverpod_generator`. +/// Do not use, as this may be removed at any time. +@internal +base mixin $FutureProvider on ProviderBase> { + FutureOr create(Ref ref); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(AsyncValue value) { + return $ProviderOverride( + origin: this, + providerOverride: $AsyncValueProvider(value), + ); + } +} + +/// {@template riverpod.future_provider} +/// A provider that asynchronously creates a value. +/// +/// [FutureProvider] can be considered as a combination of [Provider] and +/// `FutureBuilder`. +/// By using [FutureProvider], the UI will be able to read the state of the +/// future synchronously, handle the loading/error states, and rebuild when the +/// future completes. +/// +/// A common use-case for [FutureProvider] is to represent an asynchronous operation +/// such as reading a file or making an HTTP request, that is then listened to by the UI. +/// +/// It can then be combined with: +/// - [FutureProvider.family], to parameterize the http request based on external +/// parameters, such as fetching a `User` from its id. +/// - [FutureProvider.autoDispose], to cancel the HTTP request if the user +/// leaves the screen before the [Future] completed. +/// +/// ## Usage example: reading a configuration file +/// +/// [FutureProvider] can be a convenient way to expose a `Configuration` object +/// created by reading a JSON file. +/// +/// Creating the configuration would be done with your typical async/await +/// syntax, but inside the provider. +/// Using Flutter's asset system, this would be: +/// +/// ```dart +/// final configProvider = FutureProvider((ref) async { +/// final content = json.decode( +/// await rootBundle.loadString('assets/configurations.json'), +/// ) as Map; +/// +/// return Configuration.fromJson(content); +/// }); +/// ``` +/// +/// Then, the UI can listen to configurations like so: +/// +/// ```dart +/// Widget build(BuildContext context, WidgetRef ref) { +/// AsyncValue config = ref.watch(configProvider); +/// +/// return config.when( +/// loading: () => const CircularProgressIndicator(), +/// error: (err, stack) => Text('Error: $err'), +/// data: (config) { +/// return Text(config.host); +/// }, +/// ); +/// } +/// ``` +/// +/// This will automatically rebuild the UI when the [Future] completes. +/// +/// As you can see, listening to a [FutureProvider] inside a widget returns +/// an [AsyncValue] – which allows handling the error/loading states. +/// +/// 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. +/// - [FutureProvider.family], to create a [FutureProvider] from external parameters +/// - [FutureProvider.autoDispose], to destroy the state of a [FutureProvider] when no longer needed. +/// {@endtemplate} +final class FutureProvider + extends $FunctionalProvider, FutureOr> + with + $FutureModifier, + $FutureProvider, + LegacyProviderMixin> { + /// {@macro riverpod.future_provider} + FutureProvider( + this._create, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + from: null, + argument: null, + ); + + /// An implementation detail of Riverpod + @internal + FutureProvider.internal( + this._create, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.from, + required super.argument, + required super.isAutoDispose, + required super.retry, + }); + + /// {@macro riverpod.autoDispose} + static const autoDispose = AutoDisposeFutureProviderBuilder(); + + /// {@macro riverpod.family} + static const family = FutureProviderFamilyBuilder(); + + final Create> _create; + + @override + FutureOr create(Ref ref) => this._create(ref); + + @internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) { + return $FutureProviderElement(this, pointer); + } + + @mustBeOverridden + @visibleForOverriding + @override + FutureProvider $copyWithCreate( + Create> create, + ) { + return FutureProvider.internal( + create, + name: name, + dependencies: dependencies, + allTransitiveDependencies: allTransitiveDependencies, + from: from, + argument: argument, + isAutoDispose: isAutoDispose, + retry: retry, + ); + } +} + +/// The element of a [FutureProvider] +/// Implementation detail of `riverpod_generator`. Do not use. +@internal +class $FutureProviderElement extends ProviderElement> + with FutureModifierElement { + /// The element of a [FutureProvider] + /// Implementation detail of `riverpod_generator`. Do not use. + $FutureProviderElement(this.provider, super.pointer); + + @override + final $FutureProvider provider; + + @override + WhenComplete create( + Ref ref, { + required bool didChangeDependency, + }) { + return handleFuture( + () => provider.create(ref), + seamless: !didChangeDependency, + ); + } + + @override + bool updateShouldNotify( + AsyncValue previous, + AsyncValue next, + ) { + return FutureModifierElement.handleUpdateShouldNotify( + previous, + next, + ); + } +} + +/// The [Family] of a [FutureProvider] +class FutureProviderFamily extends FunctionalFamily< + AsyncValue, ArgT, FutureOr, FutureProvider> { + FutureProviderFamily( + super._createFn, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + providerFactory: FutureProvider.internal, + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + ); + + /// Implementation detail of the code-generator. + @internal + FutureProviderFamily.internal( + super._createFn, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.isAutoDispose, + required super.retry, + }) : super(providerFactory: FutureProvider.internal); +} diff --git a/packages/riverpod/lib/src/state_controller.dart b/packages/riverpod/lib/src/providers/legacy/state_controller.dart similarity index 78% rename from packages/riverpod/lib/src/state_controller.dart rename to packages/riverpod/lib/src/providers/legacy/state_controller.dart index 32ada9304..7e8c3a8e6 100644 --- a/packages/riverpod/lib/src/state_controller.dart +++ b/packages/riverpod/lib/src/providers/legacy/state_controller.dart @@ -3,16 +3,16 @@ import 'package:state_notifier/state_notifier.dart'; /// A [StateNotifier] that allows modifying its [state] from outside. /// /// This avoids having to make a [StateNotifier] subclass for simple scenarios. -class StateController extends StateNotifier { +class StateController extends StateNotifier { /// Initialize the state of [StateController]. StateController(super._state); // Remove the protected status @override - T get state => super.state; + StateT get state => super.state; @override - set state(T value) => super.state = value; + set state(StateT value) => super.state = value; /// Calls a function with the current [state] and assigns the result as the /// new state. @@ -29,5 +29,5 @@ class StateController extends StateNotifier { /// ```dart /// ref.read(provider.notifier).update((state) => state + 1); /// ``` - T update(T Function(T state) cb) => state = cb(state); + StateT update(StateT Function(StateT state) cb) => state = cb(state); } diff --git a/packages/riverpod/lib/src/providers/legacy/state_notifier_provider.dart b/packages/riverpod/lib/src/providers/legacy/state_notifier_provider.dart new file mode 100644 index 000000000..1f723bf51 --- /dev/null +++ b/packages/riverpod/lib/src/providers/legacy/state_notifier_provider.dart @@ -0,0 +1,239 @@ +import 'package:meta/meta.dart'; +import 'package:state_notifier/state_notifier.dart'; + +import '../../builder.dart'; +import '../../internals.dart'; + +ProviderElementProxy + _notifier, StateT>( + StateNotifierProvider that, +) { + return ProviderElementProxy( + that, + (element) { + return (element as StateNotifierProviderElement) + ._notifierNotifier; + }, + ); +} + +/// Creates a [StateNotifier] and exposes its current state. +/// +/// This provider is used in combination with `package:state_notifier`. +/// +/// Combined with [StateNotifier], [StateNotifierProvider] can be used to manipulate +/// advanced states, that would otherwise be difficult to represent with simpler +/// providers such as [Provider] or [FutureProvider]. +/// +/// For example, you may have a todo-list, where you can add and remove +/// and complete a todo. +/// Using [StateNotifier], you could represent such state as: +/// +/// ```dart +/// class TodosNotifier extends StateNotifier> { +/// TodosNotifier(): super([]); +/// +/// void add(Todo todo) { +/// state = [...state, todo]; +/// } +/// +/// void remove(String todoId) { +/// state = [ +/// for (final todo in state) +/// if (todo.id != todoId) todo, +/// ]; +/// } +/// +/// void toggle(String todoId) { +/// state = [ +/// for (final todo in state) +/// if (todo.id == todoId) todo.copyWith(completed: !todo.completed), +/// ]; +/// } +/// } +/// ``` +/// +/// Which you can then pass to a [StateNotifierProvider] like so: +/// +/// ```dart +/// final todosProvider = StateNotifierProvider>((ref) => TodosNotifier()); +/// ``` +/// +/// And finally, you can interact with it inside your UI: +/// +/// ```dart +/// Widget build(BuildContext context, WidgetRef ref) { +/// // rebuild the widget when the todo list changes +/// List todos = ref.watch(todosProvider); +/// +/// return ListView( +/// children: [ +/// for (final todo in todos) +/// CheckboxListTile( +/// value: todo.completed, +/// // When tapping on the todo, change its completed status +/// onChanged: (value) => ref.read(todosProvider.notifier).toggle(todo.id), +/// title: Text(todo.description), +/// ), +/// ], +/// ); +/// } +/// ``` +final class StateNotifierProvider< // + NotifierT extends StateNotifier, + StateT> // + extends $FunctionalProvider< // + StateT, + NotifierT> with LegacyProviderMixin { + /// {@macro riverpod.statenotifierprovider} + StateNotifierProvider( + this._create, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + from: null, + argument: null, + ); + + /// An implementation detail of Riverpod + @internal + StateNotifierProvider.internal( + this._create, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.from, + required super.argument, + required super.isAutoDispose, + required super.retry, + }); + + /// {@macro riverpod.autoDispose} + static const autoDispose = AutoDisposeStateNotifierProviderBuilder(); + + /// {@macro riverpod.family} + static const family = StateNotifierProviderFamilyBuilder(); + + final NotifierT Function(Ref ref) _create; + + /// Obtains the [StateNotifier] associated with this provider, without listening + /// to state changes. + /// + /// This is typically used to invoke methods on a [StateNotifier]. For example: + /// + /// ```dart + /// Button( + /// onTap: () => ref.read(stateNotifierProvider.notifier).increment(), + /// ) + /// ``` + /// + /// This listenable will notify its notifiers if the [StateNotifier] instance + /// changes. + /// This may happen if the provider is refreshed or one of its dependencies + /// has changes. + Refreshable get notifier => _notifier(this); + + @internal + @override + StateNotifierProviderElement $createElement( + $ProviderPointer pointer, + ) { + return StateNotifierProviderElement._(this, pointer); + } + + @mustBeOverridden + @visibleForOverriding + @override + StateNotifierProvider $copyWithCreate( + Create create, + ) { + return StateNotifierProvider.internal( + create, + name: name, + dependencies: dependencies, + allTransitiveDependencies: allTransitiveDependencies, + from: from, + argument: argument, + isAutoDispose: isAutoDispose, + retry: retry, + ); + } +} + +/// The element of [StateNotifierProvider]. +class StateNotifierProviderElement, + StateT> extends ProviderElement { + StateNotifierProviderElement._(this.provider, super.pointer); + + @override + final StateNotifierProvider provider; + + final _notifierNotifier = $ElementLense(); + + void Function()? _removeListener; + + @override + WhenComplete create(Ref ref, {required bool didChangeDependency}) { + final notifier = _notifierNotifier.result = $Result.guard( + () => provider._create(ref), + ); + + _removeListener = notifier.requireState.addListener( + (newState) => setStateResult(ResultData(newState)), + fireImmediately: true, + ); + + return null; + } + + @override + bool updateShouldNotify(StateT previous, StateT next) { + return _notifierNotifier.result!.requireState + // ignore: invalid_use_of_protected_member + .updateShouldNotify(previous, next); + } + + @override + void runOnDispose() { + super.runOnDispose(); + + _removeListener?.call(); + _removeListener = null; + + final notifier = _notifierNotifier.result?.stateOrNull; + if (notifier != null) { + runGuarded(notifier.dispose); + } + _notifierNotifier.result = null; + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + listenableVisitor(_notifierNotifier); + } +} + +/// The [Family] of [StateNotifierProvider]. +class StateNotifierProviderFamily, T, Arg> + extends FunctionalFamily> { + /// The [Family] of [StateNotifierProvider]. + StateNotifierProviderFamily( + super._createFn, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + providerFactory: StateNotifierProvider.internal, + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + ); +} diff --git a/packages/riverpod/lib/src/providers/legacy/state_provider.dart b/packages/riverpod/lib/src/providers/legacy/state_provider.dart new file mode 100644 index 000000000..a31e09d89 --- /dev/null +++ b/packages/riverpod/lib/src/providers/legacy/state_provider.dart @@ -0,0 +1,204 @@ +import 'package:meta/meta.dart'; + +import '../../builder.dart'; +import '../../internals.dart'; +import 'state_controller.dart'; + +ProviderElementProxy, StateT> _notifier( + StateProvider that, +) { + return ProviderElementProxy, StateT>( + that, + (element) { + return (element as StateProviderElement)._controllerNotifier; + }, + ); +} + +/// {@template riverpod.state_provider} +/// A provider that exposes a value that can be modified from outside. +/// +/// It can be useful for very simple states, like a filter or the currently +/// selected item – which can then be combined with other providers or accessed +/// in multiple screens. +/// +/// The following code shows a list of products, and allows selecting +/// a product by tapping on it. +/// +/// ```dart +/// final selectedProductIdProvider = StateProvider((ref) => null); +/// final productsProvider = StateNotifierProvider>((ref) => ProductsNotifier()); +/// +/// Widget build(BuildContext context, WidgetRef ref) { +/// final List products = ref.watch(productsProvider); +/// final selectedProductId = ref.watch(selectedProductIdProvider); +/// +/// return ListView( +/// children: [ +/// for (final product in products) +/// GestureDetector( +/// onTap: () => ref.read(selectedProductIdProvider.notifier).state = product.id, +/// child: ProductItem( +/// product: product, +/// isSelected: selectedProductId.state == product.id, +/// ), +/// ), +/// ], +/// ); +/// } +/// ``` +/// {@endtemplate} +final class StateProvider extends $FunctionalProvider + with LegacyProviderMixin { + /// {@macro riverpod.state_provider} + StateProvider( + this._createFn, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + from: null, + argument: null, + ); + + /// An implementation detail of Riverpod + @internal + StateProvider.internal( + this._createFn, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.isAutoDispose, + required super.from, + required super.argument, + required super.retry, + }); + + /// {@macro riverpod.autoDispose} + static const autoDispose = AutoDisposeStateProviderBuilder(); + + /// {@macro riverpod.family} + static const family = StateProviderFamilyBuilder(); + + final StateT Function(Ref ref) _createFn; + + Refreshable> get notifier => _notifier(this); + + @internal + @override + StateProviderElement $createElement( + $ProviderPointer pointer, + ) { + return StateProviderElement._(this, pointer); + } + + @mustBeOverridden + @visibleForOverriding + @override + StateProvider $copyWithCreate(Create create) { + return StateProvider.internal( + create, + name: name, + dependencies: dependencies, + allTransitiveDependencies: allTransitiveDependencies, + from: from, + argument: argument, + isAutoDispose: isAutoDispose, + retry: retry, + ); + } +} + +/// The element of [StateProvider]. +class StateProviderElement extends ProviderElement { + StateProviderElement._(this.provider, super.pointer); + + @override + final StateProvider provider; + + final _controllerNotifier = $ElementLense>(); + + final _stateNotifier = $ElementLense>(); + + void Function()? _removeListener; + + @override + WhenComplete create( + Ref ref, { + required bool didChangeDependency, + }) { + final initialState = provider._createFn(ref); + + final controller = StateController(initialState); + _controllerNotifier.result = $Result.data(controller); + + _removeListener = controller.addListener( + fireImmediately: true, + (state) { + _stateNotifier.result = _controllerNotifier.result; + setStateResult(ResultData(state)); + }, + ); + + return null; + } + + @override + bool updateShouldNotify(T previous, T next) { + return !identical(previous, next); + } + + @override + void runOnDispose() { + super.runOnDispose(); + + _removeListener?.call(); + _removeListener = null; + + _controllerNotifier.result?.stateOrNull?.dispose(); + _controllerNotifier.result = null; + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + listenableVisitor(_stateNotifier); + listenableVisitor(_controllerNotifier); + } +} + +/// The [Family] of [StateProvider]. +class StateProviderFamily extends FunctionalFamily< // + StateT, + Arg, + StateT, + StateProvider> { + /// The [Family] of [AsyncNotifierProvider]. + StateProviderFamily( + super._createFn, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + providerFactory: StateProvider.internal, + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + ); + + /// The [Family] of [AsyncNotifierProvider]. + @internal + StateProviderFamily.internal( + super._createFn, { + super.name, + super.dependencies, + required super.allTransitiveDependencies, + required super.isAutoDispose, + required super.retry, + }) : super(providerFactory: StateProvider.internal); +} diff --git a/packages/riverpod/lib/src/providers/notifier.dart b/packages/riverpod/lib/src/providers/notifier.dart new file mode 100644 index 000000000..d2ced3d23 --- /dev/null +++ b/packages/riverpod/lib/src/providers/notifier.dart @@ -0,0 +1,136 @@ +import 'package:meta/meta.dart'; + +import '../builder.dart'; +import '../common/result.dart'; +import '../framework.dart'; +import 'async_notifier.dart'; + +part 'notifier/orphan.dart'; +part 'notifier/family.dart'; + +/// A base class for [$Notifier]. +/// Not meant for public consumption. +abstract class $Notifier extends NotifierBase { + /// The value currently exposed by this [Notifier]. + /// + /// If used inside [Notifier.build], may throw if the notifier is not yet initialized. + /// + /// Invoking the setter will notify listeners if [updateShouldNotify] returns true. + /// By default, this will compare the previous and new value using [identical]. + /// + /// 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. + /// + /// If [Notifier.build] threw, reading [state] will rethrow the exception. + @override + @protected + @visibleForTesting + StateT get state; + + /// The value currently exposed by this [Notifier]. + /// + /// If used inside [Notifier.build], may return null if the notifier is not yet initialized. + /// It will also return null if [Notifier.build] threw. + /// + /// Invoking the setter will notify listeners if [updateShouldNotify] returns true. + /// By default, this will compare the previous and new value using [identical]. + /// + /// Reading [stateOrNull] if the provider is out of date (such as if one of its + /// dependency has changed) will trigger [Notifier.build] to be re-executed. + @protected + @visibleForTesting + StateT? get stateOrNull { + final element = this.element; + if (element == null) throw StateError(uninitializedElementError); + + element.flush(); + return element.stateResult?.stateOrNull; + } + + /// A method invoked when the state exposed by this [Notifier] 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 = 42; + /// state = 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 + /// value. + /// + /// 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". + /// + /// If you do not want that, you can override this method to perform a deep + /// comparison of the previous and new values. + @override + @visibleForOverriding + bool updateShouldNotify(StateT previous, StateT next) { + return !identical(previous, next); + } +} + +/// An implementation detail of `riverpod_generator`. +/// Do not use. +abstract base class $NotifierProvider // + , StateT> + extends $ClassProvider { + /// An internal base class for [Notifier]. + /// + /// Not meant for public consumption. + const $NotifierProvider({ + required super.name, + required super.from, + required super.argument, + required super.dependencies, + required super.allTransitiveDependencies, + required super.isAutoDispose, + required super.runNotifierBuildOverride, + required super.retry, + }); +} + +/// An implementation detail of `riverpod_generator`. +/// Do not use. +@internal +class $NotifierProviderElement< // + NotifierT extends $Notifier, + StateT> // + extends ClassProviderElement< // + NotifierT, + StateT, + StateT> { + /// An implementation detail of `riverpod_generator`. + /// Do not use. + $NotifierProviderElement(this.provider, super.pointer); + + @override + final $NotifierProvider provider; + + @override + void handleError( + Object error, + StackTrace stackTrace, { + required bool seamless, + }) { + setStateResult(ResultError(error, stackTrace)); + } + + @override + void handleValue( + StateT created, { + required bool seamless, + }) { + setStateResult(ResultData(created)); + } +} diff --git a/packages/riverpod/lib/src/providers/notifier/family.dart b/packages/riverpod/lib/src/providers/notifier/family.dart new file mode 100644 index 000000000..a1a51732d --- /dev/null +++ b/packages/riverpod/lib/src/providers/notifier/family.dart @@ -0,0 +1,108 @@ +part of '../notifier.dart'; + +/// {@macro riverpod.notifier} +/// +/// {@macro riverpod.notifier_provider_modifier} +abstract class FamilyNotifier extends $Notifier { + /// {@macro riverpod.notifier.family_arg} + late final ArgT arg = ref.$arg as ArgT; + + /// {@macro riverpod.async_notifier.build} + @visibleForOverriding + StateT build(ArgT arg); + + @internal + @override + StateT runBuild() => build(arg); +} + +final class FamilyNotifierProvider // + , StateT, ArgT> + extends $NotifierProvider + with LegacyProviderMixin { + /// An implementation detail of Riverpod + const FamilyNotifierProvider._( + this._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.from, + required super.argument, + required super.isAutoDispose, + required super.runNotifierBuildOverride, + required super.retry, + }); + + final NotifierT Function() _createNotifier; + + @internal + @override + NotifierT create() => _createNotifier(); + + @internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer, + ) { + return $NotifierProviderElement(this, pointer); + } + + FamilyNotifierProvider _copyWith({ + NotifierT Function()? create, + RunNotifierBuild? build, + }) { + return FamilyNotifierProvider._( + create ?? _createNotifier, + name: name, + dependencies: dependencies, + allTransitiveDependencies: allTransitiveDependencies, + isAutoDispose: isAutoDispose, + runNotifierBuildOverride: build ?? runNotifierBuildOverride, + from: from, + argument: argument, + retry: retry, + ); + } + + @override + @mustBeOverridden + @visibleForOverriding + FamilyNotifierProvider $copyWithBuild( + RunNotifierBuild build, + ) { + return _copyWith(build: build); + } + + @mustBeOverridden + @visibleForOverriding + @override + FamilyNotifierProvider $copyWithCreate( + NotifierT Function() create, + ) { + return _copyWith(create: create); + } +} + +/// The [Family] of [NotifierProvider]. +class NotifierProviderFamily< + NotifierT extends FamilyNotifier, StateT, ArgT> + extends ClassFamily< // + NotifierT, + StateT, + ArgT, + StateT, + FamilyNotifierProvider> { + /// The [Family] of [AsyncNotifierProvider]. + @internal + NotifierProviderFamily.internal( + super._createFn, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + providerFactory: FamilyNotifierProvider._, + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + ); +} diff --git a/packages/riverpod/lib/src/providers/notifier/orphan.dart b/packages/riverpod/lib/src/providers/notifier/orphan.dart new file mode 100644 index 000000000..b63a988f3 --- /dev/null +++ b/packages/riverpod/lib/src/providers/notifier/orphan.dart @@ -0,0 +1,164 @@ +part of '../notifier.dart'; + +/// {@template riverpod.notifier} +/// A class which exposes a state that can change over time. +/// +/// For example, [Notifier] can be used to implement a counter by doing: +/// +/// ```dart +/// final counterProvider = NotifierProvider(Counter.new); +/// +/// class Counter extends Notifier { +/// @override +/// int build() { +/// // Inside "build", we return the initial state of the counter. +/// return 0; +/// } +/// +/// void increment() { +/// state++; +/// } +/// } +/// ``` +/// +/// We can then listen to the counter inside widgets by doing: +/// +/// ```dart +/// Consumer( +/// builder: (context, ref) { +/// return Text('count: ${ref.watch(counterProvider)}'); +/// }, +/// ) +/// ``` +/// +/// And finally, we can update the counter by doing: +/// +/// ```dart +/// Consumer( +/// builder: (context, ref) { +/// return ElevatedButton( +/// onTap: () => ref.read(counterProvider.notifier).increment(), +/// child: const Text('increment'), +/// ); +/// }, +/// ) +/// ``` +/// +/// The state of [Notifier] is expected to be initialized synchronously. +/// For asynchronous initializations, see [AsyncNotifier]. +/// {@endtemplate} +/// +/// {@template riverpod.notifier_provider_modifier} +/// When using `family`, your notifier type changes. +/// Instead of extending [Notifier], you should extend [FamilyNotifier]. +/// {@endtemplate} +abstract class Notifier extends $Notifier { + /// {@template riverpod.notifier.build} + /// Initialize a [Notifier]. + /// + /// It is safe to use [Ref.watch] or [Ref.listen] inside this method. + /// + /// If a dependency of this [Notifier] (when using [Ref.watch]) changes, + /// then [build] will be re-executed. On the other hand, the [Notifier] + /// will **not** be recreated. Its instance will be preserved between + /// executions of [build]. + /// + /// If this method throws, reading this provider will rethrow the error. + /// {@endtemplate} + @visibleForOverriding + StateT build(); + + @internal + @override + StateT runBuild() => build(); +} + +final class NotifierProvider, StateT> + extends $NotifierProvider + with LegacyProviderMixin { + /// {@macro riverpod.notifier_provider} + /// + /// {@macro riverpod.notifier_provider_modifier} + NotifierProvider( + this._createNotifier, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + from: null, + argument: null, + runNotifierBuildOverride: null, + ); + + /// An implementation detail of Riverpod + @internal + NotifierProvider.internal( + this._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.from, + required super.argument, + required super.isAutoDispose, + required super.runNotifierBuildOverride, + required super.retry, + }); + + /// {@macro riverpod.autoDispose} + static const autoDispose = AutoDisposeNotifierProviderBuilder(); + + /// {@macro riverpod.family} + static const family = NotifierProviderFamilyBuilder(); + + final NotifierT Function() _createNotifier; + + @internal + @override + NotifierT create() => _createNotifier(); + + @internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer, + ) { + return $NotifierProviderElement(this, pointer); + } + + NotifierProvider _copyWith({ + NotifierT Function()? create, + RunNotifierBuild? build, + }) { + return NotifierProvider.internal( + create ?? _createNotifier, + name: name, + dependencies: dependencies, + allTransitiveDependencies: allTransitiveDependencies, + from: from, + argument: argument, + isAutoDispose: isAutoDispose, + runNotifierBuildOverride: build ?? runNotifierBuildOverride, + retry: retry, + ); + } + + @mustBeOverridden + @visibleForOverriding + @override + NotifierProvider $copyWithBuild( + RunNotifierBuild? build, + ) { + return _copyWith(build: build); + } + + @mustBeOverridden + @visibleForOverriding + @override + NotifierProvider $copyWithCreate( + NotifierT Function() create, + ) { + return _copyWith(create: create); + } +} diff --git a/packages/riverpod/lib/src/provider/base.dart b/packages/riverpod/lib/src/providers/provider.dart similarity index 69% rename from packages/riverpod/lib/src/provider/base.dart rename to packages/riverpod/lib/src/providers/provider.dart index ca4d77b24..d61d409ca 100644 --- a/packages/riverpod/lib/src/provider/base.dart +++ b/packages/riverpod/lib/src/providers/provider.dart @@ -1,115 +1,131 @@ -part of '../provider.dart'; +import 'package:meta/meta.dart'; -/// {@macro riverpod.provider_ref_base} -/// - [state], the value currently exposed by this provider. -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class ProviderRef implements Ref { - /// Obtains the state currently exposed by this provider. - /// - /// Mutating this property will notify the provider listeners. - /// - /// Cannot be called while a provider is creating, unless the setter was called first. - /// - /// Will throw if the provider threw during creation. - State get state; - set state(State newState); +import '../builder.dart'; +import '../common/result.dart'; +import '../framework.dart'; +import 'legacy/state_notifier_provider.dart' show StateNotifierProvider; +import 'stream_provider.dart' show StreamProvider; + +/// Implementation detail of `riverpod_generator`. +/// Do not use, as this may be removed at any time. +@internal +base mixin $Provider on ProviderBase { + StateT create(Ref ref); } /// {@macro riverpod.provider} -@sealed -class Provider extends InternalProvider - with - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderBase { +base class Provider extends $FunctionalProvider + with $Provider, LegacyProviderMixin { /// {@macro riverpod.provider} Provider( - this._createFn, { + this._create, { super.name, super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, + super.isAutoDispose = false, + super.retry, }) : super( allTransitiveDependencies: computeAllTransitiveDependencies(dependencies), + from: null, + argument: null, ); /// An implementation detail of Riverpod @internal - Provider.internal( - this._createFn, { + const Provider.internal( + this._create, { required super.name, required super.dependencies, required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, + required super.from, + required super.argument, + required super.isAutoDispose, + required super.retry, }); + /// {@macro riverpod.autoDispose} + static const autoDispose = AutoDisposeProviderBuilder(); + /// {@macro riverpod.family} static const family = ProviderFamilyBuilder(); - /// {@macro riverpod.autoDispose} - static const autoDispose = AutoDisposeProviderBuilder(); + final Create _create; - // ignore: deprecated_member_use_from_same_package - final Create> _createFn; + @override + StateT create(Ref ref) => _create(ref); + @internal @override - State _create(ProviderElement ref) => _createFn(ref); + $ProviderElement $createElement($ProviderPointer pointer) { + return $ProviderElement(this, pointer); + } + @mustBeOverridden + @visibleForOverriding @override - ProviderElement createElement() => ProviderElement(this); + Provider $copyWithCreate(Create create) { + return Provider.internal( + create, + from: from, + argument: argument, + isAutoDispose: isAutoDispose, + retry: retry, + allTransitiveDependencies: null, + dependencies: null, + name: null, + ); + } - /// {@template riverpod.override_with} - /// Override the provider with a new initialization function. + /// {@template riverpod.override_with_value} + /// Overrides a provider with a value, ejecting the default behavior. /// /// This will also disable the auto-scoping mechanism, meaning that if the /// overridden provider specified `dependencies`, it will have no effect. /// - /// The override must not specify a `dependencies`. + /// The main difference between [overrideWith] and [overrideWithValue] is: + /// - [overrideWith] allows you to replace the implementation of a provider. + /// This gives full access to [Ref], and the result will be cached by Riverpod. + /// The override can never change. + /// - [overrideWithValue] allows you to replace the result of a provider. + /// If the overridden value ever changes, notifiers will be updated. + /// /// - /// Some common use-cases are: - /// - testing, by replacing a service with a fake implementation, or to reach - /// a very specific state easily. - /// - multiple environments, by changing the implementation of a class - /// based on the platform or other parameters. + /// Alongside the typical use-cases of [overrideWith], [overrideWithValue] + /// has is particularly useful for converting `BuildContext` values to providers. /// - /// This function should be used in combination with `ProviderScope.overrides` - /// or `ProviderContainer.overrides`: + /// For example, we can make a provider that represents `ThemeData` from Flutter: /// /// ```dart - /// final myService = Provider((ref) => MyService()); + /// final themeProvider = Provider((ref) => throw UnimplementedError()); + /// ``` + /// + /// We can then override this provider with the current theme of the app: /// - /// runApp( - /// ProviderScope( - /// overrides: [ - /// // Replace the implementation of the provider with a different one - /// myService.overrideWithProvider((ref) { - /// ref.watch('other'); - /// return MyFakeService(), - /// })), - /// ], - /// child: MyApp(), - /// ), + /// ```dart + /// MaterialApp( + /// builder: (context, child) { + /// final theme = Theme.of(context); + /// + /// return ProviderScope( + /// overrides: [ + /// /// We override "themeProvider" with a valid theme instance. + /// /// This allows providers such as "tagThemeProvider" to read the + /// /// current theme, without having a BuildContext. + /// themeProvider.overrideWithValue(theme), + /// ], + /// child: MyApp(), + /// ); + /// }, /// ); /// ``` + /// + /// The benefit of using [overrideWithValue] over [overrideWith] in this scenario + /// is that if the theme ever changes, then `themeProvider` will be updated. /// {@endtemplate} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - Create> create, - ) { - return ProviderOverride( + Override overrideWithValue(StateT value) { + return $ProviderOverride( origin: this, - override: Provider.internal( - create, - from: from, - argument: argument, - allTransitiveDependencies: null, - dependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), + providerOverride: $ValueProvider(value), ); } } @@ -324,47 +340,40 @@ class Provider extends InternalProvider /// when that provider is no longer listened to. /// - [Provider.family], to allow providers to create a value from external parameters. /// {@endtemplate} -class ProviderElement extends ProviderElementBase - implements - // ignore: deprecated_member_use_from_same_package - ProviderRef { - /// A [ProviderElementBase] for [Provider] - @internal - ProviderElement(super._provider); - - @override - State get state => requireState; +@internal +class $ProviderElement extends ProviderElement { + /// A [ProviderElement] for [Provider] + $ProviderElement(this.provider, super.pointer); @override - set state(State newState) => setState(newState); + final $Provider provider; @override - void create({required bool didChangeDependency}) { - final provider = this.provider as InternalProvider; + WhenComplete create(Ref ref, {required bool didChangeDependency}) { + setStateResult(ResultData(provider.create(ref))); - setState(provider._create(this)); + return null; } @override - bool updateShouldNotify(State previous, State next) { + bool updateShouldNotify(StateT previous, StateT next) { return previous != next; } } /// The [Family] of [Provider] -class ProviderFamily - // ignore: deprecated_member_use_from_same_package - extends FamilyBase, R, Arg, R, Provider> { - /// The [Family] of [ProviderFamily] +class ProviderFamily + extends FunctionalFamily> { ProviderFamily( super._createFn, { super.name, super.dependencies, + super.isAutoDispose = false, + super.retry, }) : super( providerFactory: Provider.internal, allTransitiveDependencies: computeAllTransitiveDependencies(dependencies), - debugGetCreateSourceHash: null, ); /// An implementation detail of Riverpod @@ -374,25 +383,7 @@ class ProviderFamily required super.name, required super.dependencies, required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, + required super.isAutoDispose, + required super.retry, }) : super(providerFactory: Provider.internal); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - R Function(ProviderRef ref, Arg arg) create, - ) { - return FamilyOverrideImpl>( - this, - (arg) => Provider.internal( - (ref) => create(ref, arg), - from: from, - argument: arg, - name: name, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ), - ); - } } diff --git a/packages/riverpod/lib/src/providers/stream_notifier.dart b/packages/riverpod/lib/src/providers/stream_notifier.dart new file mode 100644 index 000000000..c84e097e5 --- /dev/null +++ b/packages/riverpod/lib/src/providers/stream_notifier.dart @@ -0,0 +1,75 @@ +import 'dart:async'; + +import 'package:meta/meta.dart'; + +import '../builder.dart'; +import '../core/async_value.dart'; +import '../framework.dart'; +import 'async_notifier.dart'; +import 'future_provider.dart' show FutureProvider; +import 'stream_provider.dart'; + +part 'stream_notifier/family.dart'; +part 'stream_notifier/orphan.dart'; + +/// Implementation detail of `riverpod_generator`. +/// Do not use. +abstract class $StreamNotifier extends NotifierBase< // + AsyncValue, + Stream> // + with + $AsyncClassModifier> {} + +/// Implementation detail of `riverpod_generator`. +/// Do not use. +abstract base class $StreamNotifierProvider< + NotifierT extends $StreamNotifier, // + StateT> // + extends $ClassProvider< // + NotifierT, + AsyncValue, + Stream> // + with + $FutureModifier { + /// Implementation detail of `riverpod_generator`. + /// Do not use. + const $StreamNotifierProvider({ + required super.name, + required super.from, + required super.argument, + required super.dependencies, + required super.allTransitiveDependencies, + required super.isAutoDispose, + required super.runNotifierBuildOverride, + required super.retry, + }); +} + +/// Implementation detail of `riverpod_generator`. +/// Do not use. +@internal +class $StreamNotifierProviderElement< // + NotifierT extends $StreamNotifier, + StateT> // + extends ClassProviderElement< // + NotifierT, + AsyncValue, + Stream> // + with + FutureModifierElement, + FutureModifierClassElement> { + /// Implementation detail of `riverpod_generator`. + /// Do not use. + $StreamNotifierProviderElement(this.provider, super.pointer); + + @override + final $StreamNotifierProvider provider; + + @override + void handleValue( + Stream created, { + required bool seamless, + }) { + handleStream(() => created, seamless: seamless); + } +} diff --git a/packages/riverpod/lib/src/providers/stream_notifier/family.dart b/packages/riverpod/lib/src/providers/stream_notifier/family.dart new file mode 100644 index 000000000..7196ca061 --- /dev/null +++ b/packages/riverpod/lib/src/providers/stream_notifier/family.dart @@ -0,0 +1,126 @@ +part of '../stream_notifier.dart'; + +/// {@macro riverpod.async_notifier} +/// +/// {@macro riverpod.async_notifier_provider_modifier} +abstract class FamilyStreamNotifier + extends $StreamNotifier { + /// {@template riverpod.notifier.family_arg} + /// The argument that was passed to this family. + /// + /// For example, when doing: + /// + /// ```dart + /// ref.watch(provider(0)); + /// ``` + /// + /// then [arg] will be `0`. + /// {@endtemplate} + late final ArgT arg = ref.$arg as ArgT; + + /// {@macro riverpod.async_notifier.build} + @visibleForOverriding + Stream build(ArgT arg); + + @internal + @override + Stream runBuild() => build(arg); +} + +final class FamilyStreamNotifierProvider< // + NotifierT extends FamilyStreamNotifier, + StateT, + ArgT> // + extends $StreamNotifierProvider + with LegacyProviderMixin> { + /// An implementation detail of Riverpod + const FamilyStreamNotifierProvider._( + this._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.from, + required super.argument, + required super.isAutoDispose, + required super.runNotifierBuildOverride, + required super.retry, + }); + + final NotifierT Function() _createNotifier; + + @internal + @override + NotifierT create() => _createNotifier(); + + @internal + @override + $StreamNotifierProviderElement $createElement( + $ProviderPointer pointer, + ) { + return $StreamNotifierProviderElement(this, pointer); + } + + FamilyStreamNotifierProvider _copyWith({ + NotifierT Function()? create, + RunNotifierBuild< // + NotifierT, + Stream>? + build, + }) { + return FamilyStreamNotifierProvider._( + create ?? _createNotifier, + name: name, + dependencies: dependencies, + allTransitiveDependencies: allTransitiveDependencies, + isAutoDispose: isAutoDispose, + runNotifierBuildOverride: build ?? runNotifierBuildOverride, + from: from, + argument: argument, + retry: retry, + ); + } + + @override + @mustBeOverridden + @visibleForOverriding + FamilyStreamNotifierProvider $copyWithBuild( + RunNotifierBuild> build, + ) { + return _copyWith(build: build); + } + + @override + @mustBeOverridden + @visibleForOverriding + FamilyStreamNotifierProvider $copyWithCreate( + NotifierT Function() create, + ) { + return _copyWith(create: create); + } +} + +/// The [Family] of [StreamNotifierProvider]. +class StreamNotifierProviderFamily< // + NotifierT extends FamilyStreamNotifier, + StateT, + ArgT> // + extends ClassFamily< // + NotifierT, + AsyncValue, + ArgT, + Stream, + FamilyStreamNotifierProvider> { + /// The [Family] of [StreamNotifierProvider]. + @internal + StreamNotifierProviderFamily.internal( + super._createFn, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + providerFactory: FamilyStreamNotifierProvider._, + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + ); +} diff --git a/packages/riverpod/lib/src/providers/stream_notifier/orphan.dart b/packages/riverpod/lib/src/providers/stream_notifier/orphan.dart new file mode 100644 index 000000000..d2ce95a05 --- /dev/null +++ b/packages/riverpod/lib/src/providers/stream_notifier/orphan.dart @@ -0,0 +1,134 @@ +part of '../stream_notifier.dart'; + +/// {@template 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 `family`, your notifier type changes. Instead of extending +/// [StreamNotifier], you should extend [FamilyStreamNotifier]. +/// {@endtemplate} +abstract class StreamNotifier extends $StreamNotifier { + /// {@macro riverpod.async_notifier.build} + @visibleForOverriding + Stream build(); + + @internal + @override + Stream runBuild() => build(); +} + +/// {@template riverpod.async_notifier_provider} +/// A provider which creates and listen to an [StreamNotifier]. +/// +/// 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 +/// [StreamNotifier]. +/// {@endtemplate} +/// +/// {@template riverpod.async_notifier_provider_modifier} +/// When using your notifier type changes. +/// Instead of extending [StreamNotifier], you should extend [FamilyStreamNotifier]. +/// {@endtemplate} +final class StreamNotifierProvider< // + NotifierT extends StreamNotifier, + StateT> // + extends $StreamNotifierProvider + with LegacyProviderMixin> { + /// {@macro riverpod.async_notifier_provider} + /// + /// {@macro riverpod.async_notifier_provider_modifier} + StreamNotifierProvider( + this._createNotifier, { + super.name, + super.dependencies, + super.runNotifierBuildOverride, + super.isAutoDispose = false, + super.retry, + }) : super( + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + from: null, + argument: null, + ); + + /// An implementation detail of Riverpod + @internal + const StreamNotifierProvider.internal( + this._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.from, + required super.argument, + required super.isAutoDispose, + required super.runNotifierBuildOverride, + required super.retry, + }); + + /// {@macro riverpod.autoDispose} + static const autoDispose = AutoDisposeStreamNotifierProviderBuilder(); + + /// {@macro riverpod.family} + static const family = StreamNotifierProviderFamilyBuilder(); + + final NotifierT Function() _createNotifier; + + @internal + @override + NotifierT create() => _createNotifier(); + + StreamNotifierProvider _copyWith({ + NotifierT Function()? create, + RunNotifierBuild>? build, + }) { + return StreamNotifierProvider.internal( + create ?? _createNotifier, + name: name, + dependencies: dependencies, + allTransitiveDependencies: allTransitiveDependencies, + from: from, + argument: argument, + isAutoDispose: isAutoDispose, + runNotifierBuildOverride: build ?? runNotifierBuildOverride, + retry: retry, + ); + } + + @internal + @override + $StreamNotifierProviderElement $createElement( + $ProviderPointer pointer, + ) { + return $StreamNotifierProviderElement(this, pointer); + } + + @mustBeOverridden + @visibleForOverriding + @override + StreamNotifierProvider $copyWithBuild( + RunNotifierBuild>? build, + ) { + return _copyWith(build: build); + } + + @mustBeOverridden + @visibleForOverriding + @override + StreamNotifierProvider $copyWithCreate( + NotifierT Function() create, + ) { + return _copyWith(create: create); + } +} diff --git a/packages/riverpod/lib/src/providers/stream_provider.dart b/packages/riverpod/lib/src/providers/stream_provider.dart new file mode 100644 index 000000000..b636be857 --- /dev/null +++ b/packages/riverpod/lib/src/providers/stream_provider.dart @@ -0,0 +1,253 @@ +import 'dart:async'; + +import 'package:meta/meta.dart'; + +import '../builder.dart'; +import '../common/listenable.dart'; +import '../common/result.dart'; +import '../core/async_value.dart'; +import '../framework.dart'; +import 'future_provider.dart' show FutureProvider; +import 'provider.dart' show Provider; + +/// Implementation detail of `riverpod_generator`. +/// Do not use, as this may be removed at any time. +@internal +base mixin $StreamProvider on ProviderBase> { + Stream create(Ref ref); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(AsyncValue value) { + return $ProviderOverride( + origin: this, + providerOverride: $AsyncValueProvider(value), + ); + } +} + +/// {@template riverpod.stream_provider} +/// Creates a stream and exposes its latest event. +/// +/// [StreamProvider] is identical in behavior/usage to [FutureProvider], modulo +/// the fact that the value created is a [Stream] instead of a [Future]. +/// +/// It can be used to express a value asynchronously loaded that can change over +/// time, such as an editable `Message` coming from a web socket: +/// +/// ```dart +/// final messageProvider = StreamProvider.autoDispose((ref) async* { +/// // Open the connection +/// final channel = IOWebSocketChannel.connect('ws://echo.websocket.org'); +/// +/// // Close the connection when the stream is destroyed +/// ref.onDispose(() => channel.sink.close()); +/// +/// // Parse the value received and emit a Message instance +/// await for (final value in channel.stream) { +/// yield value.toString(); +/// } +/// }); +/// ``` +/// +/// Which the UI can then listen: +/// +/// ```dart +/// Widget build(BuildContext context, WidgetRef ref) { +/// AsyncValue message = ref.watch(messageProvider); +/// +/// return message.when( +/// loading: () => const CircularProgressIndicator(), +/// error: (err, stack) => Text('Error: $err'), +/// data: (message) { +/// return Text(message); +/// }, +/// ); +/// } +/// ``` +/// +/// **Note**: +/// When listening to web sockets, firebase, or anything that consumes resources, +/// it is important to use [StreamProvider.autoDispose] instead of simply [StreamProvider]. +/// +/// This ensures that the resources are released when no longer needed as, +/// by default, a [StreamProvider] is almost never destroyed. +/// +/// See also: +/// +/// - [Provider], a provider that synchronously creates a value +/// - [FutureProvider], a provider that asynchronously exposes a value that +/// can change over time. +/// - [future], to obtain the last value emitted by a [Stream]. +/// - [StreamProvider.family], to create a [StreamProvider] from external parameters +/// - [StreamProvider.autoDispose], to destroy the state of a [StreamProvider] when no longer needed. +/// {@endtemplate} +base class StreamProvider + extends $FunctionalProvider, Stream> + with + $FutureModifier, + $StreamProvider, + LegacyProviderMixin> { + /// {@macro riverpod.stream_provider} + StreamProvider( + this._create, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + from: null, + argument: null, + ); + + /// An implementation detail of Riverpod + @internal + StreamProvider.internal( + this._create, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.from, + required super.argument, + required super.isAutoDispose, + required super.retry, + }); + + /// {@macro riverpod.autoDispose} + static const autoDispose = AutoDisposeStreamProviderBuilder(); + + /// {@macro riverpod.family} + static const family = StreamProviderFamilyBuilder(); + + final Create> _create; + + @override + Stream create(Ref ref) => _create(ref); + + @internal + @override + $StreamProviderElement $createElement($ProviderPointer pointer) { + return $StreamProviderElement(this, pointer); + } + + @mustBeOverridden + @visibleForOverriding + @override + $FunctionalProvider, Stream> $copyWithCreate( + Create> create, + ) { + return StreamProvider.internal( + create, + name: name, + dependencies: null, + allTransitiveDependencies: null, + from: from, + argument: argument, + isAutoDispose: isAutoDispose, + retry: retry, + ); + } +} + +/// The element of [StreamProvider]. +@internal +class $StreamProviderElement extends ProviderElement> + with FutureModifierElement { + /// The element of [StreamProvider]. + $StreamProviderElement(this.provider, super.pointer); + + @override + final $StreamProvider provider; + + final _streamNotifier = $ElementLense>(); + final StreamController _streamController = + StreamController.broadcast(); + + @override + WhenComplete create( + Ref ref, { + required bool didChangeDependency, + }) { + asyncTransition(AsyncLoading(), seamless: !didChangeDependency); + _streamNotifier.result ??= $Result.data(_streamController.stream); + + return handleStream( + () => provider.create(ref), + seamless: !didChangeDependency, + ); + } + + @override + void dispose() { + super.dispose(); + + /// The controller isn't recreated on provider rebuild. So we only close it + /// when the element is destroyed, not on "ref.onDispose". + _streamController.close(); + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + listenableVisitor(_streamNotifier); + } + + @override + void onData(AsyncData value, {bool seamless = false}) { + if (!_streamController.isClosed) { + // The controller might be closed if onData is executed post dispose. Cf onData + _streamController.add(value.value); + } + super.onData(value, seamless: seamless); + } + + @override + void onError(AsyncError value, {bool seamless = false}) { + if (!_streamController.isClosed) { + // The controller might be closed if onError is executed post dispose. Cf onError + _streamController.addError(value.error, value.stackTrace); + } + super.onError(value, seamless: seamless); + } + + @override + bool updateShouldNotify( + AsyncValue previous, + AsyncValue next, + ) { + return FutureModifierElement.handleUpdateShouldNotify( + previous, + next, + ); + } +} + +/// The [Family] of a [StreamProvider] +class StreamProviderFamily extends FunctionalFamily< + AsyncValue, ArgT, Stream, StreamProvider> { + StreamProviderFamily( + super._createFn, { + super.name, + super.dependencies, + super.isAutoDispose = false, + super.retry, + }) : super( + providerFactory: StreamProvider.internal, + allTransitiveDependencies: + computeAllTransitiveDependencies(dependencies), + ); + + /// Implementation detail of the code-generator. + @internal + StreamProviderFamily.internal( + super._createFn, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.isAutoDispose, + required super.retry, + }) : super(providerFactory: StreamProvider.internal); +} diff --git a/packages/riverpod/lib/src/state_notifier_provider.dart b/packages/riverpod/lib/src/state_notifier_provider.dart deleted file mode 100644 index 79784d3df..000000000 --- a/packages/riverpod/lib/src/state_notifier_provider.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:meta/meta.dart'; -import 'package:state_notifier/state_notifier.dart'; - -import 'internals.dart'; - -part 'state_notifier_provider/auto_dispose.dart'; -part 'state_notifier_provider/base.dart'; - -ProviderElementProxy - _notifier, T>( - _StateNotifierProviderBase that, -) { - return ProviderElementProxy( - that, - (element) { - return (element as StateNotifierProviderElement) - ._notifierNotifier; - }, - ); -} - -abstract class _StateNotifierProviderBase, T> - extends ProviderBase { - const _StateNotifierProviderBase({ - required super.name, - required super.from, - required super.argument, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - }); - - /// Obtains the [StateNotifier] associated with this provider, without listening - /// to state changes. - /// - /// This is typically used to invoke methods on a [StateNotifier]. For example: - /// - /// ```dart - /// Button( - /// onTap: () => ref.read(stateNotifierProvider.notifier).increment(), - /// ) - /// ``` - /// - /// This listenable will notify its notifiers if the [StateNotifier] instance - /// changes. - /// This may happen if the provider is refreshed or one of its dependencies - /// has changes. - ProviderListenable get notifier; - - NotifierT _create(covariant StateNotifierProviderElement ref); -} diff --git a/packages/riverpod/lib/src/state_notifier_provider/auto_dispose.dart b/packages/riverpod/lib/src/state_notifier_provider/auto_dispose.dart deleted file mode 100644 index b00185b72..000000000 --- a/packages/riverpod/lib/src/state_notifier_provider/auto_dispose.dart +++ /dev/null @@ -1,137 +0,0 @@ -part of '../state_notifier_provider.dart'; - -/// {@macro riverpod.provider_ref_base} -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class AutoDisposeStateNotifierProviderRef< - NotifierT extends StateNotifier, - T> extends StateNotifierProviderRef - implements AutoDisposeRef {} - -/// {@macro riverpod.statenotifierprovider} -class AutoDisposeStateNotifierProvider, T> - extends _StateNotifierProviderBase { - /// {@macro riverpod.statenotifierprovider} - AutoDisposeStateNotifierProvider( - this._createFn, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - AutoDisposeStateNotifierProvider.internal( - this._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.family} - static const family = AutoDisposeStateNotifierProviderFamily.new; - - final NotifierT Function( - // ignore: deprecated_member_use_from_same_package - AutoDisposeStateNotifierProviderRef ref, - ) _createFn; - - @override - NotifierT _create(AutoDisposeStateNotifierProviderElement ref) { - return _createFn(ref); - } - - @override - AutoDisposeStateNotifierProviderElement createElement() { - return AutoDisposeStateNotifierProviderElement._(this); - } - - @override - late final Refreshable notifier = _notifier(this); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - Create> create, - ) { - return ProviderOverride( - origin: this, - override: AutoDisposeStateNotifierProvider.internal( - create, - from: from, - argument: argument, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} - -/// The element of [AutoDisposeStateNotifierProvider]. -class AutoDisposeStateNotifierProviderElement< - NotifierT extends StateNotifier, - T> extends StateNotifierProviderElement - with - AutoDisposeProviderElementMixin - implements - // ignore: deprecated_member_use_from_same_package - AutoDisposeStateNotifierProviderRef { - /// The [ProviderElementBase] for [StateNotifierProvider] - AutoDisposeStateNotifierProviderElement._( - AutoDisposeStateNotifierProvider super._provider, - ) : super._(); -} - -/// The [Family] of [AutoDisposeStateNotifierProvider]. -class AutoDisposeStateNotifierProviderFamily, T, Arg> - extends AutoDisposeFamilyBase< - // ignore: deprecated_member_use_from_same_package - AutoDisposeStateNotifierProviderRef, - T, - Arg, - NotifierT, - AutoDisposeStateNotifierProvider> { - /// The [Family] of [AutoDisposeStateNotifierProvider]. - AutoDisposeStateNotifierProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: AutoDisposeStateNotifierProvider.internal, - debugGetCreateSourceHash: null, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// {@macro riverpod.override_with} - Override overrideWith( - NotifierT Function( - // ignore: deprecated_member_use_from_same_package - AutoDisposeStateNotifierProviderRef ref, - Arg arg, - ) create, - ) { - return FamilyOverrideImpl>( - this, - (arg) => AutoDisposeStateNotifierProvider.internal( - (ref) => create(ref, arg), - from: from, - argument: arg, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} diff --git a/packages/riverpod/lib/src/state_notifier_provider/base.dart b/packages/riverpod/lib/src/state_notifier_provider/base.dart deleted file mode 100644 index c9de539b0..000000000 --- a/packages/riverpod/lib/src/state_notifier_provider/base.dart +++ /dev/null @@ -1,252 +0,0 @@ -part of '../state_notifier_provider.dart'; - -/// {@macro riverpod.provider_ref_base} -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class StateNotifierProviderRef, T> - implements Ref { - /// The [StateNotifier] currently exposed by this provider. - /// - /// Cannot be accessed while creating the provider. - NotifierT get notifier; -} - -/// {@template riverpod.statenotifierprovider} -/// Creates a [StateNotifier] and exposes its current state. -/// -/// This provider is used in combination with `package:state_notifier`. -/// -/// Combined with [StateNotifier], [StateNotifierProvider] can be used to manipulate -/// advanced states, that would otherwise be difficult to represent with simpler -/// providers such as [Provider] or [FutureProvider]. -/// -/// For example, you may have a todo-list, where you can add and remove -/// and complete a todo. -/// Using [StateNotifier], you could represent such state as: -/// -/// ```dart -/// class TodosNotifier extends StateNotifier> { -/// TodosNotifier(): super([]); -/// -/// void add(Todo todo) { -/// state = [...state, todo]; -/// } -/// -/// void remove(String todoId) { -/// state = [ -/// for (final todo in state) -/// if (todo.id != todoId) todo, -/// ]; -/// } -/// -/// void toggle(String todoId) { -/// state = [ -/// for (final todo in state) -/// if (todo.id == todoId) todo.copyWith(completed: !todo.completed), -/// ]; -/// } -/// } -/// ``` -/// -/// Which you can then pass to a [StateNotifierProvider] like so: -/// -/// ```dart -/// final todosProvider = StateNotifierProvider>((ref) => TodosNotifier()); -/// ``` -/// -/// And finally, you can interact with it inside your UI: -/// -/// ```dart -/// Widget build(BuildContext context, WidgetRef ref) { -/// // rebuild the widget when the todo list changes -/// List todos = ref.watch(todosProvider); -/// -/// return ListView( -/// children: [ -/// for (final todo in todos) -/// CheckboxListTile( -/// value: todo.completed, -/// // When tapping on the todo, change its completed status -/// onChanged: (value) => ref.read(todosProvider.notifier).toggle(todo.id), -/// title: Text(todo.description), -/// ), -/// ], -/// ); -/// } -/// ``` -/// {@endtemplate} -class StateNotifierProvider, T> - extends _StateNotifierProviderBase - with - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderBase { - /// {@macro riverpod.statenotifierprovider} - StateNotifierProvider( - this._createFn, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - StateNotifierProvider.internal( - this._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.autoDispose} - static const autoDispose = AutoDisposeStateNotifierProviderBuilder(); - - /// {@macro riverpod.family} - static const family = StateNotifierProviderFamilyBuilder(); - - // ignore: deprecated_member_use_from_same_package - final NotifierT Function(StateNotifierProviderRef ref) - _createFn; - - @override - NotifierT _create(StateNotifierProviderElement ref) { - return _createFn(ref); - } - - @override - StateNotifierProviderElement createElement() { - return StateNotifierProviderElement._(this); - } - - @override - // ignore: deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable notifier = _notifier(this); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - Create> create, - ) { - return ProviderOverride( - origin: this, - override: StateNotifierProvider.internal( - create, - from: from, - argument: argument, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} - -/// The element of [StateNotifierProvider]. -class StateNotifierProviderElement, T> - extends ProviderElementBase - implements - // ignore: deprecated_member_use_from_same_package - StateNotifierProviderRef { - StateNotifierProviderElement._( - _StateNotifierProviderBase super._provider, - ); - - @override - NotifierT get notifier => _notifierNotifier.value; - final _notifierNotifier = ProxyElementValueNotifier(); - - void Function()? _removeListener; - - @override - void create({required bool didChangeDependency}) { - final provider = this.provider as _StateNotifierProviderBase; - - final notifier = - _notifierNotifier.result = Result.guard(() => provider._create(this)); - - _removeListener = notifier - // TODO test requireState, as ref.read(p) is expected to throw if notifier creation failed - .requireState - .addListener(setState, fireImmediately: true); - } - - @override - bool updateShouldNotify(T previous, T next) { - // TODO test that updateShouldNotify is applied - return _notifierNotifier.result!.requireState - // ignore: invalid_use_of_protected_member - .updateShouldNotify(previous, next); - } - - @override - void runOnDispose() { - super.runOnDispose(); - - _removeListener?.call(); - _removeListener = null; - - final notifier = _notifierNotifier.result?.stateOrNull; - if (notifier != null) { - // TODO test STateNotifier.dispose is guarded - runGuarded(notifier.dispose); - } - _notifierNotifier.result = null; - } - - @override - void visitChildren({ - required void Function(ProviderElementBase element) elementVisitor, - required void Function(ProxyElementValueNotifier element) notifierVisitor, - }) { - super.visitChildren( - elementVisitor: elementVisitor, - notifierVisitor: notifierVisitor, - ); - notifierVisitor(_notifierNotifier); - } -} - -/// The [Family] of [StateNotifierProvider]. -class StateNotifierProviderFamily, T, Arg> - // ignore: deprecated_member_use_from_same_package - extends FamilyBase, T, Arg, - NotifierT, StateNotifierProvider> { - /// The [Family] of [StateNotifierProvider]. - StateNotifierProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: StateNotifierProvider.internal, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - debugGetCreateSourceHash: null, - ); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - NotifierT Function(StateNotifierProviderRef ref, Arg arg) - create, - ) { - return FamilyOverrideImpl>( - this, - (arg) => StateNotifierProvider.internal( - (ref) => create(ref, arg), - from: from, - argument: arg, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} diff --git a/packages/riverpod/lib/src/state_provider.dart b/packages/riverpod/lib/src/state_provider.dart deleted file mode 100644 index bac6be04f..000000000 --- a/packages/riverpod/lib/src/state_provider.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:meta/meta.dart'; - -import 'internals.dart'; -import 'state_controller.dart'; - -part 'state_provider/auto_dispose.dart'; -part 'state_provider/base.dart'; - -ProviderElementProxy> _notifier( - _StateProviderBase that, -) { - return ProviderElementProxy>( - that, - (element) { - return (element as StateProviderElement)._controllerNotifier; - }, - ); -} - -ProviderElementProxy> _state( - _StateProviderBase that, -) { - return ProviderElementProxy>( - that, - (element) { - return (element as StateProviderElement)._stateNotifier; - }, - ); -} - -abstract class _StateProviderBase extends ProviderBase { - const _StateProviderBase({ - required super.name, - required super.from, - required super.argument, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - }); - - ProviderListenable> get notifier; - ProviderListenable> get state; - - T _create(covariant StateProviderElement ref); -} diff --git a/packages/riverpod/lib/src/state_provider/auto_dispose.dart b/packages/riverpod/lib/src/state_provider/auto_dispose.dart deleted file mode 100644 index a79949a3c..000000000 --- a/packages/riverpod/lib/src/state_provider/auto_dispose.dart +++ /dev/null @@ -1,130 +0,0 @@ -part of '../state_provider.dart'; - -/// {@macro riverpod.provider_ref_base} -/// - [controller], the [StateController] currently exposed by this provider. -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class AutoDisposeStateProviderRef - extends StateProviderRef implements AutoDisposeRef {} - -/// {@macro riverpod.stateprovider} -class AutoDisposeStateProvider extends _StateProviderBase { - /// {@macro riverpod.stateprovider} - AutoDisposeStateProvider( - this._createFn, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - AutoDisposeStateProvider.internal( - this._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.family} - static const family = AutoDisposeStateProviderFamily.new; - - // ignore: deprecated_member_use_from_same_package - final T Function(AutoDisposeStateProviderRef ref) _createFn; - - @override - T _create(AutoDisposeStateProviderElement ref) => _createFn(ref); - - @override - AutoDisposeStateProviderElement createElement() { - return AutoDisposeStateProviderElement._(this); - } - - @override - late final Refreshable> notifier = _notifier(this); - - @Deprecated( - 'Will be removed in 3.0.0. ' - 'Use either `ref.watch(provider)` or `ref.read(provider.notifier)` instead', - ) - @override - late final Refreshable> state = _state(this); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - Create> create, - ) { - return ProviderOverride( - origin: this, - override: AutoDisposeStateProvider.internal( - create, - from: from, - argument: argument, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} - -/// The element of [StateProvider]. -class AutoDisposeStateProviderElement extends StateProviderElement - with - AutoDisposeProviderElementMixin - implements - // ignore: deprecated_member_use_from_same_package - AutoDisposeStateProviderRef { - /// The [ProviderElementBase] for [StateProvider] - AutoDisposeStateProviderElement._(AutoDisposeStateProvider super._provider) - : super._(); -} - -/// The [Family] of [StateProvider]. -class AutoDisposeStateProviderFamily extends AutoDisposeFamilyBase< - // ignore: deprecated_member_use_from_same_package - AutoDisposeStateProviderRef, - R, - Arg, - R, - AutoDisposeStateProvider> { - /// The [Family] of [StateProvider]. - AutoDisposeStateProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: AutoDisposeStateProvider.internal, - debugGetCreateSourceHash: null, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - R Function(AutoDisposeStateProviderRef ref, Arg arg) create, - ) { - return FamilyOverrideImpl>( - this, - (arg) => AutoDisposeStateProvider.internal( - (ref) => create(ref, arg), - from: from, - argument: arg, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} diff --git a/packages/riverpod/lib/src/state_provider/base.dart b/packages/riverpod/lib/src/state_provider/base.dart deleted file mode 100644 index 4ea49f466..000000000 --- a/packages/riverpod/lib/src/state_provider/base.dart +++ /dev/null @@ -1,218 +0,0 @@ -part of '../state_provider.dart'; - -/// {@macro riverpod.provider_ref_base} -/// - [controller], the [StateController] currently exposed by this provider. -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class StateProviderRef implements Ref { - /// The [StateController] currently exposed by this provider. - /// - /// Cannot be accessed while creating the provider. - StateController get controller; -} - -/// {@template riverpod.stateprovider} -/// A provider that exposes a value that can be modified from outside. -/// -/// It can be useful for very simple states, like a filter or the currently -/// selected item – which can then be combined with other providers or accessed -/// in multiple screens. -/// -/// The following code shows a list of products, and allows selecting -/// a product by tapping on it. -/// -/// ```dart -/// final selectedProductIdProvider = StateProvider((ref) => null); -/// final productsProvider = StateNotifierProvider>((ref) => ProductsNotifier()); -/// -/// Widget build(BuildContext context, WidgetRef ref) { -/// final List products = ref.watch(productsProvider); -/// final selectedProductId = ref.watch(selectedProductIdProvider); -/// -/// return ListView( -/// children: [ -/// for (final product in products) -/// GestureDetector( -/// onTap: () => ref.read(selectedProductIdProvider.notifier).state = product.id, -/// child: ProductItem( -/// product: product, -/// isSelected: selectedProductId.state == product.id, -/// ), -/// ), -/// ], -/// ); -/// } -/// ``` -/// {@endtemplate} -class StateProvider extends _StateProviderBase - with - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderBase { - /// {@macro riverpod.stateprovider} - StateProvider( - this._createFn, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - StateProvider.internal( - this._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.autoDispose} - static const autoDispose = AutoDisposeStateProviderBuilder(); - - /// {@macro riverpod.family} - static const family = StateProviderFamilyBuilder(); - - // ignore: deprecated_member_use_from_same_package - final T Function(StateProviderRef ref) _createFn; - - @override - T _create(StateProviderElement ref) => _createFn(ref); - - @override - StateProviderElement createElement() => StateProviderElement._(this); - - @override - // ignore: deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable> notifier = - _notifier(this); - - @Deprecated( - 'Will be removed in 3.0.0. ' - 'Use either `ref.watch(provider)` or `ref.read(provider.notifier)` instead', - ) - @override - late final AlwaysAliveRefreshable> state = _state(this); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - Create> create, - ) { - return ProviderOverride( - origin: this, - override: StateProvider.internal( - create, - from: from, - argument: argument, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} - -/// The element of [StateProvider]. -class StateProviderElement extends ProviderElementBase - implements - // ignore: deprecated_member_use_from_same_package - StateProviderRef { - StateProviderElement._(_StateProviderBase super._provider); - - @override - StateController get controller => _controllerNotifier.value; - final _controllerNotifier = ProxyElementValueNotifier>(); - - final _stateNotifier = ProxyElementValueNotifier>(); - - void Function()? _removeListener; - - @override - void create({required bool didChangeDependency}) { - final provider = this.provider as _StateProviderBase; - final initialState = provider._create(this); - - final controller = StateController(initialState); - _controllerNotifier.result = Result.data(controller); - - _removeListener = controller.addListener( - fireImmediately: true, - (state) { - _stateNotifier.result = _controllerNotifier.result; - setState(state); - }, - ); - } - - @override - bool updateShouldNotify(T previous, T next) { - return !identical(previous, next); - } - - @override - void runOnDispose() { - super.runOnDispose(); - - _removeListener?.call(); - _removeListener = null; - - _controllerNotifier.result?.stateOrNull?.dispose(); - _controllerNotifier.result = null; - } - - @override - void visitChildren({ - required void Function(ProviderElementBase element) elementVisitor, - required void Function(ProxyElementValueNotifier element) notifierVisitor, - }) { - super.visitChildren( - elementVisitor: elementVisitor, - notifierVisitor: notifierVisitor, - ); - notifierVisitor(_stateNotifier); - notifierVisitor(_controllerNotifier); - } -} - -/// The [Family] of [StateProvider]. -class StateProviderFamily - // ignore: deprecated_member_use_from_same_package - extends FamilyBase, R, Arg, R, StateProvider> { - /// The [Family] of [StateProvider]. - StateProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: StateProvider.internal, - debugGetCreateSourceHash: null, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - R Function(StateProviderRef ref, Arg arg) create, - ) { - return FamilyOverrideImpl>( - this, - (arg) => StateProvider.internal( - (ref) => create(ref, arg), - from: from, - argument: arg, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} diff --git a/packages/riverpod/lib/src/stream_notifier.dart b/packages/riverpod/lib/src/stream_notifier.dart deleted file mode 100644 index 874aac4eb..000000000 --- a/packages/riverpod/lib/src/stream_notifier.dart +++ /dev/null @@ -1,80 +0,0 @@ -part of 'async_notifier.dart'; - -ProviderElementProxy, NotifierT> - _streamNotifier, T>( - StreamNotifierProviderBase that, -) { - return ProviderElementProxy, NotifierT>( - that, - (element) { - return (element as StreamNotifierProviderElement) - ._notifierNotifier; - }, - ); -} - -ProviderElementProxy, Future> _streamFuture( - StreamNotifierProviderBase, T> that, -) { - return ProviderElementProxy, Future>( - that, - (element) { - return (element as StreamNotifierProviderElement, T>) - .futureNotifier; - }, - ); -} - -/// A base class for [StreamNotifierProvider] -/// -/// Not meant for public consumption -@visibleForTesting -@internal -abstract class StreamNotifierProviderBase< - NotifierT extends AsyncNotifierBase, - T> extends ProviderBase> { - /// A base class for [StreamNotifierProvider] - /// - /// Not meant for public consumption - const StreamNotifierProviderBase( - this._createNotifier, { - required super.name, - required super.from, - required super.argument, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - }); - - /// Obtains the [StreamNotifier] associated with this provider, without listening - /// to state changes. - /// - /// This is typically used to invoke methods on a [StreamNotifier]. For example: - /// - /// ```dart - /// Button( - /// onTap: () => ref.read(streamNotifierProvider.notifier).increment(), - /// ) - /// ``` - /// - /// This listenable will notify its notifiers if the [StreamNotifier] instance - /// changes. - /// This may happen if the provider is refreshed or one of its dependencies - /// has changes. - Refreshable get notifier; - - /// {@macro riverpod.async_notifier.future} - /// - /// Listening to this using [Ref.watch] will rebuild the widget/provider - /// when the [StreamNotifier] emits a new value. - /// This will then return a new [Future] that resoles with the latest "state". - Refreshable> get future; - - final NotifierT Function() _createNotifier; - - /// Runs the `build` method of a notifier. - /// - /// This is an implementation detail for differentiating [StreamNotifier.build] - /// from [FamilyStreamNotifier.build]. - Stream runNotifierBuild(AsyncNotifierBase notifier); -} diff --git a/packages/riverpod/lib/src/stream_notifier/auto_dispose.dart b/packages/riverpod/lib/src/stream_notifier/auto_dispose.dart deleted file mode 100644 index 6395d405e..000000000 --- a/packages/riverpod/lib/src/stream_notifier/auto_dispose.dart +++ /dev/null @@ -1,128 +0,0 @@ -part of '../async_notifier.dart'; - -/// A [AutoDisposeStreamNotifier] base class shared between family and non-family notifiers. -/// -/// Not meant for public consumption outside of riverpod_generator -@internal -abstract class BuildlessAutoDisposeStreamNotifier - extends AsyncNotifierBase { - @override - late final AutoDisposeStreamNotifierProviderElement, - State> _element; - - @override - void _setElement(ProviderElementBase> element) { - _element = element as AutoDisposeStreamNotifierProviderElement< - AsyncNotifierBase, State>; - } - - @override - // ignore: deprecated_member_use_from_same_package - AutoDisposeStreamNotifierProviderRef get ref => _element; -} - -/// {@macro riverpod.streamNotifier} -abstract class AutoDisposeStreamNotifier - extends BuildlessAutoDisposeStreamNotifier { - /// {@macro riverpod.StreamNotifier.build} - @visibleForOverriding - Stream build(); -} - -/// {@macro riverpod.provider_ref_base} -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class AutoDisposeStreamNotifierProviderRef - implements StreamNotifierProviderRef, AutoDisposeRef> {} - -/// {@macro riverpod.StreamNotifier} -typedef AutoDisposeStreamNotifierProvider< - NotifierT extends AutoDisposeStreamNotifier, T> - = AutoDisposeStreamNotifierProviderImpl; - -/// The implementation of [AutoDisposeStreamNotifierProvider] but with loosened type constraints -/// that can be shared with [StreamNotifierProvider]. -/// -/// This enables tests to execute on both [AutoDisposeStreamNotifierProvider] and -/// [StreamNotifierProvider] at the same time. -@internal -class AutoDisposeStreamNotifierProviderImpl< - NotifierT extends AsyncNotifierBase, - T> extends StreamNotifierProviderBase with AsyncSelector { - /// {@macro riverpod.streamNotifier} - AutoDisposeStreamNotifierProviderImpl( - super._createNotifier, { - super.name, - super.dependencies, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - from: null, - argument: null, - debugGetCreateSourceHash: null, - ); - - /// An implementation detail of Riverpod - @internal - AutoDisposeStreamNotifierProviderImpl.internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.family} - static const family = AutoDisposeStreamNotifierProviderFamily.new; - - @override - late final Refreshable notifier = - _streamNotifier(this); - - @override - late final Refreshable> future = _streamFuture(this); - - @override - AutoDisposeStreamNotifierProviderElement createElement() { - return AutoDisposeStreamNotifierProviderElement(this); - } - - @override - Stream runNotifierBuild(AsyncNotifierBase notifier) { - // Not using "covariant" as riverpod_generator subclasses this with a - // different notifier type - return (notifier as AutoDisposeStreamNotifier).build(); - } - - /// {@macro riverpod.override_with} - @mustBeOverridden - Override overrideWith(NotifierT Function() create) { - return ProviderOverride( - origin: this, - override: AutoDisposeStreamNotifierProviderImpl.internal( - create, - from: from, - argument: argument, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} - -/// The element of [AutoDisposeStreamNotifierProvider]. -class AutoDisposeStreamNotifierProviderElement< - NotifierT extends AsyncNotifierBase, - T> extends StreamNotifierProviderElement - with - AutoDisposeProviderElementMixin> - implements - // ignore: deprecated_member_use_from_same_package - AutoDisposeStreamNotifierProviderRef { - /// The [ProviderElementBase] for [StreamNotifierProvider] - @internal - AutoDisposeStreamNotifierProviderElement(super._provider); -} diff --git a/packages/riverpod/lib/src/stream_notifier/auto_dispose_family.dart b/packages/riverpod/lib/src/stream_notifier/auto_dispose_family.dart deleted file mode 100644 index d6e75f74e..000000000 --- a/packages/riverpod/lib/src/stream_notifier/auto_dispose_family.dart +++ /dev/null @@ -1,118 +0,0 @@ -part of '../async_notifier.dart'; - -/// {@macro riverpod.streamNotifier} -abstract class AutoDisposeFamilyStreamNotifier - extends BuildlessAutoDisposeStreamNotifier { - /// {@macro riverpod.notifier.family_arg} - late final Arg arg; - - @override - void _setElement(ProviderElementBase> element) { - super._setElement(element); - arg = element.origin.argument as Arg; - } - - /// {@macro riverpod.StreamNotifier.build} - @visibleForOverriding - Stream build(Arg arg); -} - -/// {@macro riverpod.StreamNotifier} -typedef AutoDisposeFamilyStreamNotifierProvider< - NotifierT extends AutoDisposeFamilyStreamNotifier, T, Arg> - = AutoDisposeFamilyStreamNotifierProviderImpl; - -/// The implementation of [AutoDisposeStreamNotifierProvider] but with loosened type constraints -/// that can be shared with [StreamNotifierProvider]. -/// -/// This enables tests to execute on both [AutoDisposeStreamNotifierProvider] and -/// [StreamNotifierProvider] at the same time. -@internal -class AutoDisposeFamilyStreamNotifierProviderImpl< - NotifierT extends AsyncNotifierBase, T, Arg> - extends StreamNotifierProviderBase with AsyncSelector { - /// {@macro riverpod.streamNotifier} - AutoDisposeFamilyStreamNotifierProviderImpl( - super._createNotifier, { - super.name, - super.dependencies, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - from: null, - argument: null, - debugGetCreateSourceHash: null, - ); - - /// An implementation detail of Riverpod - @internal - AutoDisposeFamilyStreamNotifierProviderImpl.internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - @override - late final Refreshable notifier = - _streamNotifier(this); - - @override - late final Refreshable> future = _streamFuture(this); - - @override - AutoDisposeStreamNotifierProviderElement createElement() { - return AutoDisposeStreamNotifierProviderElement(this); - } - - @override - Stream runNotifierBuild( - covariant AutoDisposeFamilyStreamNotifier notifier, - ) { - return notifier.build(notifier.arg); - } -} - -/// The [Family] of [StreamNotifierProvider]. -class AutoDisposeStreamNotifierProviderFamily< - NotifierT extends AutoDisposeFamilyStreamNotifier, T, Arg> - extends AutoDisposeNotifierFamilyBase< - // ignore: deprecated_member_use_from_same_package - AutoDisposeStreamNotifierProviderRef, - AsyncValue, - Arg, - NotifierT, - AutoDisposeFamilyStreamNotifierProvider> { - /// The [Family] of [AutoDisposeStreamNotifierProvider]. - AutoDisposeStreamNotifierProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: AutoDisposeFamilyStreamNotifierProvider.internal, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - debugGetCreateSourceHash: null, - ); - - /// {@macro riverpod.override_with} - Override overrideWith(NotifierT Function() create) { - return FamilyOverrideImpl, Arg, - AutoDisposeFamilyStreamNotifierProvider>( - this, - (arg) => - AutoDisposeFamilyStreamNotifierProvider.internal( - create, - from: from, - argument: arg, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} diff --git a/packages/riverpod/lib/src/stream_notifier/base.dart b/packages/riverpod/lib/src/stream_notifier/base.dart deleted file mode 100644 index 60adec31b..000000000 --- a/packages/riverpod/lib/src/stream_notifier/base.dart +++ /dev/null @@ -1,171 +0,0 @@ -part of '../async_notifier.dart'; - -/// A [StreamNotifier] base class shared between family and non-family notifiers. -/// -/// Not meant for public consumption outside of riverpod_generator -@internal -abstract class BuildlessStreamNotifier extends AsyncNotifierBase { - @override - late final StreamNotifierProviderElement, State> - _element; - - @override - void _setElement(ProviderElementBase> element) { - _element = element - as StreamNotifierProviderElement, State>; - } - - @override - // ignore: deprecated_member_use_from_same_package - StreamNotifierProviderRef get ref => _element; -} - -/// {@template 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 { - /// {@macro riverpod.async_notifier.build} - @visibleForOverriding - Stream build(); -} - -/// {@macro riverpod.provider_ref_base} -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class StreamNotifierProviderRef implements Ref> {} - -/// {@macro riverpod.streamNotifier} -typedef StreamNotifierProvider, T> - = StreamNotifierProviderImpl; - -/// The implementation of [StreamNotifierProvider] but with loosened type constraints -/// that can be shared with [AutoDisposeStreamNotifierProvider]. -/// -/// This enables tests to execute on both [StreamNotifierProvider] and -/// [AutoDisposeStreamNotifierProvider] at the same time. -@visibleForTesting -@internal -class StreamNotifierProviderImpl, T> - extends StreamNotifierProviderBase - with - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderBase>, - AlwaysAliveAsyncSelector { - /// {@macro riverpod.streamNotifier} - StreamNotifierProviderImpl( - super._createNotifier, { - super.name, - super.dependencies, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - from: null, - argument: null, - debugGetCreateSourceHash: null, - ); - - /// An implementation detail of Riverpod - @internal - StreamNotifierProviderImpl.internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.autoDispose} - static const autoDispose = AutoDisposeStreamNotifierProviderBuilder(); - - /// {@macro riverpod.family} - static const family = StreamNotifierProviderFamilyBuilder(); - - @override - // ignore: deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable notifier = - _streamNotifier(this); - - @override - // ignore: deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable> future = _streamFuture(this); - - @override - StreamNotifierProviderElement createElement() { - return StreamNotifierProviderElement(this); - } - - @override - Stream runNotifierBuild(AsyncNotifierBase notifier) { - // Not using "covariant" as riverpod_generator subclasses this with a - // different notifier type - return (notifier as StreamNotifier).build(); - } - - /// {@macro riverpod.override_with} - @mustBeOverridden - Override overrideWith(NotifierT Function() create) { - return ProviderOverride( - origin: this, - override: StreamNotifierProviderImpl.internal( - create, - from: from, - argument: argument, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ), - ); - } -} - -/// The element of [StreamNotifierProvider]. -class StreamNotifierProviderElement, T> - extends AsyncNotifierProviderElementBase - implements - // ignore: deprecated_member_use_from_same_package - StreamNotifierProviderRef { - /// The element of [StreamNotifierProvider]. - @internal - StreamNotifierProviderElement( - StreamNotifierProviderBase super._provider, - ); - - @override - void create({required bool didChangeDependency}) { - final provider = this.provider as StreamNotifierProviderBase; - - final notifierResult = _notifierNotifier.result ??= Result.guard(() { - return provider._createNotifier().._setElement(this); - }); - - notifierResult.when( - error: (error, stackTrace) { - onError(AsyncError(error, stackTrace), seamless: !didChangeDependency); - }, - data: (notifier) { - handleStream( - () => provider.runNotifierBuild(notifier), - didChangeDependency: didChangeDependency, - ); - }, - ); - } -} diff --git a/packages/riverpod/lib/src/stream_notifier/family.dart b/packages/riverpod/lib/src/stream_notifier/family.dart deleted file mode 100644 index d04944824..000000000 --- a/packages/riverpod/lib/src/stream_notifier/family.dart +++ /dev/null @@ -1,130 +0,0 @@ -part of '../async_notifier.dart'; - -/// {@macro riverpod.streamNotifier} -abstract class FamilyStreamNotifier - extends BuildlessStreamNotifier { - /// {@template riverpod.notifier.family_arg} - /// The argument that was passed to this family. - /// - /// For example, when doing: - /// - /// ```dart - /// ref.watch(provider(0)); - /// ``` - /// - /// then [arg] will be `0`. - /// {@endtemplate} - late final Arg arg; - - @override - void _setElement(ProviderElementBase> element) { - super._setElement(element); - arg = element.origin.argument as Arg; - } - - /// {@macro riverpod.StreamNotifier.build} - @visibleForOverriding - Stream build(Arg arg); -} - -/// {@macro riverpod.streamNotifier} -typedef StreamNotifierFamilyProvider< - NotifierT extends FamilyStreamNotifier, T, Arg> - = FamilyStreamNotifierProviderImpl; - -/// An internal implementation of [StreamNotifierFamilyProvider] for testing purpose. -/// -/// Not meant for public consumption. -@visibleForTesting -@internal -class FamilyStreamNotifierProviderImpl, - T, Arg> extends StreamNotifierProviderBase - with - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderBase>, - AlwaysAliveAsyncSelector { - /// {@macro riverpod.streamNotifier} - FamilyStreamNotifierProviderImpl( - super._createNotifier, { - super.name, - super.dependencies, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - from: null, - argument: null, - debugGetCreateSourceHash: null, - ); - - /// An implementation detail of Riverpod - @internal - FamilyStreamNotifierProviderImpl.internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.autoDispose} - static const autoDispose = AutoDisposeStreamNotifierProviderFamily.new; - - @override - // ignore: deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable notifier = - _streamNotifier(this); - - @override - // ignore: deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable> future = _streamFuture(this); - - @override - StreamNotifierProviderElement createElement() { - return StreamNotifierProviderElement(this); - } - - @override - Stream runNotifierBuild( - covariant FamilyStreamNotifier notifier, - ) { - return notifier.build(notifier.arg); - } -} - -/// The [Family] of [StreamNotifierProvider]. -class StreamNotifierProviderFamily< - NotifierT extends FamilyStreamNotifier, T, Arg> - // ignore: deprecated_member_use_from_same_package - extends NotifierFamilyBase, AsyncValue, Arg, - NotifierT, StreamNotifierFamilyProvider> { - /// The [Family] of [StreamNotifierProvider]. - StreamNotifierProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: StreamNotifierFamilyProvider.internal, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - debugGetCreateSourceHash: null, - ); - - /// {@macro riverpod.override_with} - Override overrideWith(NotifierT Function() create) { - return FamilyOverrideImpl, Arg, - StreamNotifierFamilyProvider>( - this, - (arg) => StreamNotifierFamilyProvider.internal( - create, - from: from, - argument: arg, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} diff --git a/packages/riverpod/lib/src/stream_provider.dart b/packages/riverpod/lib/src/stream_provider.dart deleted file mode 100644 index 6eadcab2d..000000000 --- a/packages/riverpod/lib/src/stream_provider.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'dart:async'; - -import 'package:meta/meta.dart'; - -import 'async_notifier.dart'; -import 'builders.dart'; -import 'common.dart'; -import 'framework.dart'; -import 'future_provider.dart' show FutureProvider; -import 'listenable.dart'; -import 'provider.dart' show Provider; -import 'result.dart'; - -part 'stream_provider/auto_dispose.dart'; -part 'stream_provider/base.dart'; - -ProviderElementProxy, Future> _future( - _StreamProviderBase that, -) { - return ProviderElementProxy, Future>( - that, - (element) => FutureHandlerProviderElementMixin.futureNotifierOf( - element as FutureHandlerProviderElementMixin, - ), - ); -} - -ProviderElementProxy, Stream> _stream( - _StreamProviderBase that, -) { - return ProviderElementProxy, Stream>( - that, - (element) { - return (element as StreamProviderElement)._streamNotifier; - }, - ); -} - -abstract class _StreamProviderBase extends ProviderBase> { - const _StreamProviderBase({ - required super.allTransitiveDependencies, - required super.dependencies, - required super.name, - required super.from, - required super.argument, - required super.debugGetCreateSourceHash, - }); - - ProviderListenable> get future; - - @Deprecated( - '.stream will be removed in 3.0.0. As a replacement, either listen to the ' - 'provider itself (AsyncValue) or .future.', - ) - ProviderListenable> get stream; - - Stream _create(covariant StreamProviderElement ref); -} diff --git a/packages/riverpod/lib/src/stream_provider/auto_dispose.dart b/packages/riverpod/lib/src/stream_provider/auto_dispose.dart deleted file mode 100644 index 61352f895..000000000 --- a/packages/riverpod/lib/src/stream_provider/auto_dispose.dart +++ /dev/null @@ -1,132 +0,0 @@ -part of '../stream_provider.dart'; - -/// {@macro riverpod.provider_ref_base} -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class AutoDisposeStreamProviderRef - extends StreamProviderRef - implements AutoDisposeRef> {} - -/// {@macro riverpod.stream_provider} -class AutoDisposeStreamProvider extends _StreamProviderBase - with AsyncSelector { - /// {@macro riverpod.stream_provider} - AutoDisposeStreamProvider( - this._createFn, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - AutoDisposeStreamProvider.internal( - this._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.family} - static const family = AutoDisposeStreamProviderFamily.new; - - // ignore: deprecated_member_use_from_same_package - final Stream Function(AutoDisposeStreamProviderRef ref) _createFn; - - @override - Stream _create(AutoDisposeStreamProviderElement ref) => _createFn(ref); - - @override - AutoDisposeStreamProviderElement createElement() { - return AutoDisposeStreamProviderElement(this); - } - - @override - late final Refreshable> future = _future(this); - - @Deprecated( - '.stream will be removed in 3.0.0. As a replacement, either listen to the ' - 'provider itself (AsyncValue) or .future.', - ) - @override - late final Refreshable> stream = _stream(this); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - Create, AutoDisposeStreamProviderRef> create, - ) { - return ProviderOverride( - origin: this, - override: AutoDisposeStreamProvider.internal( - create, - from: from, - argument: argument, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} - -/// The element of [AutoDisposeStreamProvider]. -class AutoDisposeStreamProviderElement extends StreamProviderElement - with - AutoDisposeProviderElementMixin> - implements - // ignore: deprecated_member_use_from_same_package - AutoDisposeStreamProviderRef { - /// The [ProviderElementBase] for [StreamProvider] - AutoDisposeStreamProviderElement( - AutoDisposeStreamProvider super._provider, - ); -} - -/// The [Family] of [AutoDisposeStreamProvider]. -class AutoDisposeStreamProviderFamily extends AutoDisposeFamilyBase< - // ignore: deprecated_member_use_from_same_package - AutoDisposeStreamProviderRef, - AsyncValue, - Arg, - Stream, - AutoDisposeStreamProvider> { - /// The [Family] of [AutoDisposeStreamProvider]. - AutoDisposeStreamProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: AutoDisposeStreamProvider.internal, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - debugGetCreateSourceHash: null, - ); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - Stream Function(AutoDisposeStreamProviderRef ref, Arg arg) create, - ) { - return FamilyOverrideImpl, Arg, AutoDisposeStreamProvider>( - this, - (arg) => AutoDisposeStreamProvider.internal( - (ref) => create(ref, arg), - from: from, - argument: arg, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} diff --git a/packages/riverpod/lib/src/stream_provider/base.dart b/packages/riverpod/lib/src/stream_provider/base.dart deleted file mode 100644 index 88784a40c..000000000 --- a/packages/riverpod/lib/src/stream_provider/base.dart +++ /dev/null @@ -1,252 +0,0 @@ -part of '../stream_provider.dart'; - -/// {@macro riverpod.provider_ref_base} -/// - [StreamProviderRef.state], the value currently exposed by this provider. -@Deprecated('will be removed in 3.0.0. Use Ref instead') -abstract class StreamProviderRef implements Ref> { - /// Obtains the state currently exposed by this provider. - /// - /// Mutating this property will notify the provider listeners. - /// - /// Cannot be called while a provider is creating, unless the setter was called first. - /// - /// Will throw if the provider threw during creation. - AsyncValue get state; - set state(AsyncValue newState); -} - -/// {@template riverpod.stream_provider} -/// Creates a stream and exposes its latest event. -/// -/// [StreamProvider] is identical in behavior/usage to [FutureProvider], modulo -/// the fact that the value created is a [Stream] instead of a [Future]. -/// -/// It can be used to express a value asynchronously loaded that can change over -/// time, such as an editable `Message` coming from a web socket: -/// -/// ```dart -/// final messageProvider = StreamProvider.autoDispose((ref) async* { -/// // Open the connection -/// final channel = IOWebSocketChannel.connect('ws://echo.websocket.org'); -/// -/// // Close the connection when the stream is destroyed -/// ref.onDispose(() => channel.sink.close()); -/// -/// // Parse the value received and emit a Message instance -/// await for (final value in channel.stream) { -/// yield value.toString(); -/// } -/// }); -/// ``` -/// -/// Which the UI can then listen: -/// -/// ```dart -/// Widget build(BuildContext context, WidgetRef ref) { -/// AsyncValue message = ref.watch(messageProvider); -/// -/// return message.when( -/// loading: () => const CircularProgressIndicator(), -/// error: (err, stack) => Text('Error: $err'), -/// data: (message) { -/// return Text(message); -/// }, -/// ); -/// } -/// ``` -/// -/// **Note**: -/// When listening to web sockets, firebase, or anything that consumes resources, -/// it is important to use [StreamProvider.autoDispose] instead of simply [StreamProvider]. -/// -/// This ensures that the resources are released when no longer needed as, -/// by default, a [StreamProvider] is almost never destroyed. -/// -/// See also: -/// -/// - [Provider], a provider that synchronously creates a value -/// - [FutureProvider], a provider that asynchronously exposes a value that -/// can change over time. -/// - [future], to obtain the last value emitted by a [Stream]. -/// - [StreamProvider.family], to create a [StreamProvider] from external parameters -/// - [StreamProvider.autoDispose], to destroy the state of a [StreamProvider] when no longer needed. -/// {@endtemplate} -class StreamProvider extends _StreamProviderBase - with - // ignore: deprecated_member_use_from_same_package - AlwaysAliveProviderBase>, - AlwaysAliveAsyncSelector { - /// {@macro riverpod.stream_provider} - StreamProvider( - this._createFn, { - super.name, - super.dependencies, - @Deprecated('Will be removed in 3.0.0') super.from, - @Deprecated('Will be removed in 3.0.0') super.argument, - @Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash, - }) : super( - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// An implementation detail of Riverpod - @internal - StreamProvider.internal( - this._createFn, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - super.from, - super.argument, - }); - - /// {@macro riverpod.autoDispose} - static const autoDispose = AutoDisposeStreamProviderBuilder(); - - /// {@macro riverpod.family} - static const family = StreamProviderFamilyBuilder(); - - // ignore: deprecated_member_use_from_same_package - final Stream Function(StreamProviderRef ref) _createFn; - - @override - // ignore: deprecated_member_use_from_same_package - late final AlwaysAliveRefreshable> future = _future(this); - - @Deprecated( - '.stream will be removed in 3.0.0. As a replacement, either listen to the ' - 'provider itself (AsyncValue) or .future.', - ) - @override - late final AlwaysAliveRefreshable> stream = _stream(this); - - @override - Stream _create(StreamProviderElement ref) => _createFn(ref); - - @override - StreamProviderElement createElement() => StreamProviderElement(this); - - /// {@macro riverpod.override_with} - @mustBeOverridden - // ignore: deprecated_member_use_from_same_package - Override overrideWith(Create, StreamProviderRef> create) { - return ProviderOverride( - origin: this, - override: StreamProvider.internal( - create, - from: from, - argument: argument, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} - -/// The element of [StreamProvider]. -class StreamProviderElement extends ProviderElementBase> - with - FutureHandlerProviderElementMixin - implements - // ignore: deprecated_member_use_from_same_package - StreamProviderRef { - /// The element of [StreamProvider]. - @internal - // ignore: library_private_types_in_public_api - StreamProviderElement(_StreamProviderBase super._provider); - - final _streamNotifier = ProxyElementValueNotifier>(); - final StreamController _streamController = StreamController.broadcast(); - - @override - void create({required bool didChangeDependency}) { - asyncTransition(AsyncLoading(), seamless: !didChangeDependency); - _streamNotifier.result ??= Result.data(_streamController.stream); - - handleStream( - () { - final provider = this.provider as _StreamProviderBase; - return provider._create(this); - }, - didChangeDependency: didChangeDependency, - ); - } - - @override - void dispose() { - super.dispose(); - - /// The controller isn't recreated on provider rebuild. So we only close it - /// when the element is destroyed, not on "ref.onDispose". - _streamController.close(); - } - - @override - void visitChildren({ - required void Function(ProviderElementBase element) elementVisitor, - required void Function(ProxyElementValueNotifier element) notifierVisitor, - }) { - super.visitChildren( - elementVisitor: elementVisitor, - notifierVisitor: notifierVisitor, - ); - notifierVisitor(_streamNotifier); - } - - @override - void onData(AsyncData value, {bool seamless = false}) { - if (!_streamController.isClosed) { - // The controller might be closed if onData is executed post dispose. Cf onData - _streamController.add(value.value); - } - super.onData(value, seamless: seamless); - } - - @override - void onError(AsyncError value, {bool seamless = false}) { - if (!_streamController.isClosed) { - // The controller might be closed if onError is executed post dispose. Cf onError - _streamController.addError(value.error, value.stackTrace); - } - super.onError(value, seamless: seamless); - } -} - -/// The [Family] of [StreamProvider]. -// ignore: deprecated_member_use_from_same_package -class StreamProviderFamily extends FamilyBase, - AsyncValue, Arg, Stream, StreamProvider> { - /// The [Family] of [StreamProvider]. - StreamProviderFamily( - super._createFn, { - super.name, - super.dependencies, - }) : super( - providerFactory: StreamProvider.internal, - debugGetCreateSourceHash: null, - allTransitiveDependencies: - computeAllTransitiveDependencies(dependencies), - ); - - /// {@macro riverpod.override_with} - Override overrideWith( - // ignore: deprecated_member_use_from_same_package - Stream Function(StreamProviderRef ref, Arg arg) create, - ) { - return FamilyOverrideImpl, Arg, StreamProvider>( - this, - (arg) => StreamProvider.internal( - (ref) => create(ref, arg), - from: from, - argument: arg, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - name: null, - ), - ); - } -} diff --git a/packages/riverpod/pubspec.yaml b/packages/riverpod/pubspec.yaml index 64bce22f8..c246419aa 100644 --- a/packages/riverpod/pubspec.yaml +++ b/packages/riverpod/pubspec.yaml @@ -2,7 +2,7 @@ name: riverpod description: > A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze. -version: 2.6.1 +version: 3.0.0-dev.3 homepage: https://riverpod.dev repository: https://github.com/rrousselGit/riverpod issue_tracker: https://github.com/rrousselGit/riverpod/issues @@ -10,17 +10,18 @@ funding: - https://github.com/sponsors/rrousselGit/ environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.3.0-0.0.dev <4.0.0" dependencies: collection: ^1.18.0 meta: ^1.9.0 stack_trace: ^1.10.0 state_notifier: ">=0.7.2 <2.0.0" + test: ^1.0.0 dev_dependencies: analyzer: ^7.0.0 expect_error: ^1.0.0 mockito: ^5.0.0 - test: ^1.16.0 + path: ^1.9.0 trotter: ^2.0.0-dev.1 diff --git a/packages/riverpod/test/analysis_options.yaml b/packages/riverpod/test/analysis_options.yaml index 38c1cf1a8..99904038d 100644 --- a/packages/riverpod/test/analysis_options.yaml +++ b/packages/riverpod/test/analysis_options.yaml @@ -1,5 +1,10 @@ -import: "../../analysis_options.yaml" - +include: ../../../analysis_options.yaml analyzer: errors: - deprecated_member_use_from_same_package: false + unused_local_variable: ignore + +linter: + rules: + # Some tests may want to explicitly create an unused variable without type inference, + # for the sake of testing that that two types are assignable + omit_local_variable_types: false diff --git a/packages/riverpod/test/framework/auto_dispose_test.dart b/packages/riverpod/test/framework/auto_dispose_test.dart deleted file mode 100644 index 79f5e0fd3..000000000 --- a/packages/riverpod/test/framework/auto_dispose_test.dart +++ /dev/null @@ -1,820 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'package:mockito/mockito.dart'; -import 'package:riverpod/src/internals.dart'; -import 'package:test/test.dart'; - -import '../utils.dart'; - -Future main() async { - test( - 'When a non-overridden autoDispose provider is disposed ' - 'and the associated ProviderContainer has a child ProviderContainer which overrides said provider, ' - 'the child container keeps its override', () async { -// Regression test for https://github.com/rrousselGit/riverpod/issues/1519 - - final root = createContainer(); - final provider = Provider.autoDispose((ref) => 0); - final child = createContainer( - parent: root, - overrides: [provider.overrideWithValue(42)], - ); - - root.read(provider); - - await root.pump(); - - child.updateOverrides([ - provider.overrideWithValue(21), - ]); - - expect(child.read(provider), 21); - }); - - test( - 'Handles cases where the ProviderContainer is disposed yet Scheduler.performDispose is invoked anyway', - () async { - // regression test for https://github.com/rrousselGit/riverpod/issues/1400 - final provider = Provider.autoDispose((ref) => 0); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); - - container.read(provider); - container.dispose(); - - await root.pump(); - }); - - group('ref.keepAlive', () { - test('Does not cause an infinite loop if aborted directly in the callback', - () async { - final container = createContainer(); - var buildCount = 0; - var disposeCount = 0; - final provider = Provider.autoDispose((ref) { - buildCount++; - ref.onDispose(() => disposeCount++); - final link = ref.keepAlive(); - link.close(); - return 'value'; - }); - - container.read(provider); - - expect(buildCount, 1); - expect(disposeCount, 0); - expect( - container.getAllProviderElements().map((e) => e.provider), - [provider], - ); - - await container.pump(); - - expect(buildCount, 1); - expect(disposeCount, 1); - expect( - container.getAllProviderElements().map((e) => e.provider), - isEmpty, - ); - }); - - test('when the provider rebuilds, links are cleared', () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - KeepAliveLink? a; - - final provider = Provider.autoDispose((ref) { - ref.watch(dep); - a ??= ref.keepAlive(); - }); - - container.read(provider); - await container.pump(); - - expect( - container.getAllProviderElements().map((e) => e.provider), - contains(provider), - ); - - container.read(dep.notifier).state++; - // manually trigger rebuild, as the provider is not listened - container.read(provider); - await container.pump(); - - expect( - container.getAllProviderElements().map((e) => e.provider), - isNot(contains(provider)), - ); - }); - - test('maintains the state of the provider until all links are closed', - () async { - final container = createContainer(); - late KeepAliveLink a; - late KeepAliveLink b; - - final provider = Provider.autoDispose((ref) { - a = ref.keepAlive(); - b = ref.keepAlive(); - }); - - container.read(provider); - - expect( - container.getAllProviderElements().map((e) => e.provider), - [provider], - ); - - await container.pump(); - - expect( - container.getAllProviderElements().map((e) => e.provider), - [provider], - ); - - a.close(); - await container.pump(); - - expect( - container.getAllProviderElements().map((e) => e.provider), - [provider], - ); - - b.close(); - await container.pump(); - - expect( - container.getAllProviderElements(), - isEmpty, - ); - }); - - test( - 'when closing KeepAliveLink, does not dispose the provider if it is still being listened to', - () async { - final container = createContainer(); - late KeepAliveLink a; - - final provider = Provider.autoDispose((ref) { - a = ref.keepAlive(); - }); - - final sub = container.listen(provider, (previous, next) {}); - - a.close(); - await container.pump(); - - expect( - container.getAllProviderElements().map((e) => e.provider), - [provider], - ); - - sub.close(); - await container.pump(); - - expect( - container.getAllProviderElements().map((e) => e.provider), - isEmpty, - ); - }); - - test( - 'when closing KeepAliveLink, does not dispose the provider maintainState=true', - () async { - final container = createContainer(); - late KeepAliveLink a; - late AutoDisposeRef ref; - - final provider = Provider.autoDispose((r) { - ref = r; - r.maintainState = true; - a = ref.keepAlive(); - }); - - container.read(provider); - - a.close(); - await container.pump(); - - expect( - container.getAllProviderElements().map((e) => e.provider), - [provider], - ); - - ref.maintainState = false; - await container.pump(); - - expect( - container.getAllProviderElements().map((e) => e.provider), - isEmpty, - ); - }); - - test( - 'when closing the last KeepAliveLink, then immediately adding a new link, ' - 'the provider will not be disposed.', () async { - final container = createContainer(); - late KeepAliveLink a; - late AutoDisposeRef ref; - - final provider = Provider.autoDispose((r) { - ref = r; - a = ref.keepAlive(); - }); - - container.read(provider); - - a.close(); - final b = ref.keepAlive(); - await container.pump(); - - expect( - container.getAllProviderElements().map((e) => e.provider), - [provider], - ); - - b.close(); - await container.pump(); - - expect( - container.getAllProviderElements().map((e) => e.provider), - isEmpty, - ); - }); - }); - - test('Can ref.read autoDispose selectors inside non-autoDispose providers', - () { - final autoDispose = Provider.autoDispose((ref) => 0); - - Provider((ref) { - ref.read( - autoDispose.select((value) => value), - ); - }); - }); - - test( - 'if a dependency changed, the element is still disposed, ' - 'but without calling ref.onDispose again', () async { - final container = createContainer(); - final onDispose = OnDisposeMock(); - final dep = StateProvider((ref) => 0); - final provider = Provider.autoDispose((ref) { - ref.onDispose(onDispose.call); - return ref.watch(dep); - }); - - container.read(provider); - - verifyZeroInteractions(onDispose); - expect( - container.getAllProviderElements().map((e) => e.origin), - contains(provider), - ); - - container.read(dep.notifier).state++; - - await container.pump(); - - verify(onDispose()).called(1); - - expect( - container.getAllProviderElements().map((e) => e.origin), - isNot(contains(provider)), - ); - }); - - test( - 'when a provider conditionally depends on another provider, rebuilding without the dependency can dispose the dependency', - () async { - final container = createContainer(); - var dependencyDisposeCount = 0; - final dependency = Provider.autoDispose( - name: 'dependency', - (ref) { - ref.onDispose(() => dependencyDisposeCount++); - return 0; - }, - ); - final isDependingOnDependency = StateProvider( - name: 'isDependingOnDependency', - (ref) => true, - ); - final provider = Provider.autoDispose( - name: 'provider', - (ref) { - ref.maintainState = true; - if (ref.watch(isDependingOnDependency)) { - ref.watch(dependency); - } - }, - ); - - container.listen(provider, (_, __) {}); - - expect(dependencyDisposeCount, 0); - expect( - container.getAllProviderElements().map((e) => e.provider), - unorderedEquals([ - dependency, - provider, - isDependingOnDependency, - ]), - ); - - container.read(isDependingOnDependency.notifier).state = false; - await container.pump(); - - expect(dependencyDisposeCount, 1); - expect( - container.getAllProviderElements().map((e) => e.provider), - unorderedEquals([ - provider, - isDependingOnDependency, - ]), - ); - }); - - test('works if used across a ProviderContainer', () async { - var value = 0; - var buildCount = 0; - var disposeCount = 0; - final listener = Listener(); - final provider = Provider.autoDispose((ref) { - buildCount++; - ref.onDispose(() => disposeCount++); - return value; - }); - - final root = createContainer(); - final container = createContainer(parent: root); - - final sub = - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 0)); - expect(buildCount, 1); - expect(disposeCount, 0); - - sub.close(); - await container.pump(); - - expect(buildCount, 1); - expect(disposeCount, 1); - verifyNoMoreInteractions(listener); - expect(root.getAllProviderElements(), isEmpty); - expect(container.getAllProviderElements(), isEmpty); - - value = 42; - container.listen(provider, listener.call, fireImmediately: true); - - expect(buildCount, 2); - expect(disposeCount, 1); - verifyOnly(listener, listener(null, 42)); - }); - - test('scoped autoDispose override preserve the override after one disposal', - () async { - final provider = Provider.autoDispose((ref) => 0); - - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); - - container.read(provider); - expect(root.getAllProviderElements(), isEmpty); - expect(container.getAllProviderElements(), isNotEmpty); - - await container.pump(); - - expect(root.getAllProviderElements(), isEmpty); - expect(container.getAllProviderElements(), isEmpty); - - container.read(provider); - - expect(root.getAllProviderElements(), isEmpty); - expect(container.getAllProviderElements(), isNotEmpty); - }); - - test( - 'scoped autoDispose override through intermediary unused container preserve the override after one disposal', - () async { - final provider = Provider.autoDispose((ref) => 0); - - final root = createContainer(); - final mid = createContainer(parent: root, overrides: [provider]); - final container = createContainer(parent: mid); - - container.read(provider); - expect(root.getAllProviderElements(), isEmpty); - expect(mid.getAllProviderElements(), isNotEmpty); - expect(container.getAllProviderElements(), isEmpty); - - await container.pump(); - - expect(root.getAllProviderElements(), isEmpty); - expect(mid.getAllProviderElements(), isEmpty); - expect(container.getAllProviderElements(), isEmpty); - - container.read(provider); - - expect(root.getAllProviderElements(), isEmpty); - expect(mid.getAllProviderElements(), isNotEmpty); - expect(container.getAllProviderElements(), isEmpty); - }); - - test( - 'scoped autoDispose override preserve family override after one disposal', - () async { - final provider = Provider.autoDispose.family((ref, _) => 0); - - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); - - container.read(provider(0)); - expect(root.getAllProviderElements(), isEmpty); - expect(container.getAllProviderElements(), isNotEmpty); - - await container.pump(); - - expect(root.getAllProviderElements(), isEmpty); - expect(container.getAllProviderElements(), isEmpty); - - container.read(provider(0)); - - expect(root.getAllProviderElements(), isEmpty); - expect(container.getAllProviderElements(), isNotEmpty); - }); - - test( - 'scoped autoDispose override through intermediary unused container preserve family override after one disposal', - () async { - final provider = Provider.autoDispose.family((ref, _) => 0); - - final root = createContainer(); - final mid = createContainer(parent: root, overrides: [provider]); - final container = createContainer(parent: mid); - - container.read(provider(0)); - expect(root.getAllProviderElements(), isEmpty); - expect(mid.getAllProviderElements(), isNotEmpty); - expect(container.getAllProviderElements(), isEmpty); - - await container.pump(); - - expect(root.getAllProviderElements(), isEmpty); - expect(mid.getAllProviderElements(), isEmpty); - expect(container.getAllProviderElements(), isEmpty); - - container.read(provider(0)); - - expect(root.getAllProviderElements(), isEmpty); - expect(mid.getAllProviderElements(), isNotEmpty); - expect(container.getAllProviderElements(), isEmpty); - }); - - test('supports disposing of overridden families', () async { - // Regression test for https://github.com/rrousselGit/riverpod/issues/2480 - final provider = Provider.autoDispose.family((ref, _) => -1); - - var constructionCount = 0; - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [provider.overrideWith((ref, arg) => ++constructionCount)], - ); - - var count = container.read(provider(0)); - expect(count, 1); - - await container.pump(); - - count = container.read(provider(0)); - expect(count, 2); - }); - - test( - 'can select auto-dispose providers if the selecting provider is auto-dispose too', - () { - final container = createContainer(); - final selected = Provider.autoDispose((ref) => 0); - final isEven = Provider.autoDispose((ref) { - return ref.watch(selected.select((c) => c.isEven)); - }); - - expect(container.read(isEven), true); - }); - - test('setting maintainState to false destroys the state when not listened to', - () async { - final onDispose = OnDisposeMock(); - late AutoDisposeRef ref; - final provider = Provider.autoDispose((r) { - ref = r; - ref.onDispose(onDispose.call); - ref.maintainState = true; - }); - final container = createContainer(); - - final sub = container.listen(provider, (prev, value) {}); - sub.close(); - - await container.pump(); - - verifyZeroInteractions(onDispose); - - ref.maintainState = false; - - verifyZeroInteractions(onDispose); - - await container.pump(); - - verify(onDispose()).called(1); - verifyNoMoreInteractions(onDispose); - }); - - test( - "maintainState to true don't dispose the state when no longer listened to", - () async { - var value = 42; - final onDispose = OnDisposeMock(); - final provider = Provider.autoDispose((ref) { - ref.onDispose(onDispose.call); - ref.maintainState = true; - return value; - }); - final container = createContainer(); - final listener = Listener(); - - final sub = - container.listen(provider, listener.call, fireImmediately: true); - verify(listener(null, 42)).called(1); - verifyNoMoreInteractions(listener); - sub.close(); - - await container.pump(); - - verifyZeroInteractions(onDispose); - - value = 21; - container.listen(provider, listener.call, fireImmediately: true); - - verify(listener(null, 42)).called(1); - verifyNoMoreInteractions(listener); - }); - - test('maintainState defaults to false', () { - late bool maintainState; - final provider = Provider.autoDispose((ref) { - maintainState = ref.maintainState; - return 42; - }); - final container = createContainer(); - - container.listen(provider, (prev, value) {}); - - expect(maintainState, false); - }); - - test('unsub to A then make B sub to A then unsub to B disposes B before A', - () async { - final container = createContainer(); - final aDispose = OnDisposeMock(); - final a = Provider.autoDispose((ref) { - ref.onDispose(aDispose.call); - return 42; - }); - final bDispose = OnDisposeMock(); - final b = Provider.autoDispose((ref) { - ref.onDispose(bDispose.call); - ref.watch(a); - return '42'; - }); - - final subA = container.listen(a, (prev, value) {}); - subA.close(); - - final subB = container.listen(b, (prev, value) {}); - subB.close(); - - verifyNoMoreInteractions(aDispose); - verifyNoMoreInteractions(bDispose); - - await container.pump(); - - verifyInOrder([ - bDispose(), - aDispose(), - ]); - verifyNoMoreInteractions(aDispose); - verifyNoMoreInteractions(bDispose); - }); - - test('chain', () async { - final container = createContainer(); - final onDispose = OnDisposeMock(); - var value = 42; - final provider = Provider.autoDispose((ref) { - ref.onDispose(onDispose.call); - return value; - }); - final onDispose2 = OnDisposeMock(); - final provider2 = Provider.autoDispose((ref) { - ref.onDispose(onDispose2.call); - return ref.watch(provider); - }); - final listener = Listener(); - - var sub = container.listen(provider2, listener.call, fireImmediately: true); - - verify(listener(null, 42)).called(1); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(onDispose); - verifyNoMoreInteractions(onDispose2); - - sub.close(); - - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(onDispose); - verifyNoMoreInteractions(onDispose2); - - await container.pump(); - - verifyNoMoreInteractions(listener); - verifyInOrder([ - onDispose2(), - onDispose(), - ]); - verifyNoMoreInteractions(onDispose); - verifyNoMoreInteractions(onDispose2); - - value = 21; - sub = container.listen(provider2, listener.call, fireImmediately: true); - - verify(listener(null, 21)).called(1); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(onDispose); - verifyNoMoreInteractions(onDispose2); - }); - - test("auto dispose A then auto dispose B doesn't dispose A again", () async { - final container = createContainer(); - final aDispose = OnDisposeMock(); - final a = Provider.autoDispose((ref) { - ref.onDispose(aDispose.call); - return 42; - }); - final bDispose = OnDisposeMock(); - final b = Provider.autoDispose((ref) { - ref.onDispose(bDispose.call); - return 42; - }); - - var subA = container.listen(a, (prev, value) {}); - verifyNoMoreInteractions(aDispose); - verifyNoMoreInteractions(bDispose); - subA.close(); - - await container.pump(); - - verify(aDispose()).called(1); - verifyNoMoreInteractions(aDispose); - verifyNoMoreInteractions(bDispose); - - subA = container.listen(a, (prev, value) {}); - final subB = container.listen(b, (prev, value) {}); - - subB.close(); - - await container.pump(); - - verify(bDispose()).called(1); - verifyNoMoreInteractions(aDispose); - verifyNoMoreInteractions(bDispose); - }); - - test('ProviderContainer was disposed before AutoDisposer handled the dispose', - () async { - final container = createContainer(); - final onDispose = OnDisposeMock(); - final provider = Provider.autoDispose((ref) { - ref.onDispose(onDispose.call); - return 42; - }); - - final sub = container.listen(provider, (prev, value) {}); - - verifyNoMoreInteractions(onDispose); - - sub.close(); - verifyNoMoreInteractions(onDispose); - - container.dispose(); - - verify(onDispose()).called(1); - verifyNoMoreInteractions(onDispose); - - await container.pump(); - - verifyNoMoreInteractions(onDispose); - }); - - test('unsub no-op if another sub is added before event-loop', () async { - final container = createContainer(); - final onDispose = OnDisposeMock(); - final provider = Provider.autoDispose((ref) { - ref.onDispose(onDispose.call); - return 42; - }); - - final sub = container.listen(provider, (prev, value) {}); - - verifyNoMoreInteractions(onDispose); - - sub.close(); - verifyNoMoreInteractions(onDispose); - - final sub2 = container.listen(provider, (prev, value) {}); - - await container.pump(); - - verifyNoMoreInteractions(onDispose); - - sub2.close(); - await container.pump(); - - verify(onDispose()).called(1); - verifyNoMoreInteractions(onDispose); - }); - - test('no-op if when removing listener if there is still a listener', - () async { - final container = createContainer(); - final onDispose = OnDisposeMock(); - final provider = Provider.autoDispose((ref) { - ref.onDispose(onDispose.call); - return 42; - }); - - final sub = container.listen(provider, (prev, value) {}); - final sub2 = container.listen(provider, (prev, value) {}); - - verifyNoMoreInteractions(onDispose); - - sub.close(); - await container.pump(); - - verifyNoMoreInteractions(onDispose); - - sub2.close(); - await container.pump(); - - verify(onDispose()).called(1); - verifyNoMoreInteractions(onDispose); - }); - - test('Do not dispose twice when ProviderContainer is disposed first', - () async { - final onDispose = OnDisposeMock(); - final provider = Provider.autoDispose((ref) { - ref.onDispose(onDispose.call); - return 42; - }); - final container = createContainer(); - - final sub = container.listen(provider, (_, __) {}); - sub.close(); - - container.dispose(); - - verify(onDispose()).called(1); - verifyNoMoreInteractions(onDispose); - - await container.pump(); - - verifyNoMoreInteractions(onDispose); - }); - - test('providers with only a "listen" as subscribers are kept alive', - () async { - final container = createContainer(); - var mounted = true; - final listened = Provider.autoDispose((ref) { - ref.onDispose(() => mounted = false); - return 0; - }); - final provider = Provider.autoDispose((ref) { - ref.listen(listened, (prev, value) {}); - return 0; - }); - - container.listen(provider, (prev, value) {}); - final sub = container.listen(listened, (prev, value) {}); - - sub.close(); - - await container.pump(); - - expect(mounted, true); - }); -} diff --git a/packages/riverpod/test/framework/listen_test.dart b/packages/riverpod/test/framework/listen_test.dart deleted file mode 100644 index 9c5cf04c6..000000000 --- a/packages/riverpod/test/framework/listen_test.dart +++ /dev/null @@ -1,1645 +0,0 @@ -import 'dart:async'; - -import 'package:mockito/mockito.dart'; -import 'package:riverpod/riverpod.dart' hide ErrorListener; -import 'package:test/expect.dart'; -import 'package:test/scaffolding.dart'; - -import '../utils.dart'; - -void main() { - group('Ref.listenSelf', () { - test('does not break autoDispose', () async { - final container = createContainer(); - final provider = Provider.autoDispose((ref) { - ref.listenSelf((previous, next) {}); - }); - - container.read(provider); - expect(container.getAllProviderElements(), [anything]); - - await container.pump(); - - expect(container.getAllProviderElements(), isEmpty); - }); - - test('listens to mutations post build', () async { - final container = createContainer(); - final listener = Listener(); - final listener2 = Listener(); - - late ProviderRef ref; - final provider = Provider((r) { - ref = r; - ref.listenSelf(listener.call); - ref.listenSelf(listener2.call); - - return 0; - }); - - container.read(provider); - - verifyInOrder([ - listener(null, 0), - listener2(null, 0), - ]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - ref.state = 42; - - verifyInOrder([ - listener(0, 42), - listener2(0, 42), - ]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('listens to rebuild', () async { - final container = createContainer(); - final listener = Listener(); - final listener2 = Listener(); - var result = 0; - final provider = Provider((ref) { - ref.listenSelf(listener.call); - ref.listenSelf(listener2.call); - - return result; - }); - - container.read(provider); - - verifyInOrder([ - listener(null, 0), - listener2(null, 0), - ]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - result = 42; - container.refresh(provider); - - verifyInOrder([ - listener(0, 42), - listener2(0, 42), - ]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('notify listeners independently from updateShouldNotify', () async { - final container = createContainer(); - final listener = Listener(); - final listener2 = Listener(); - final provider = Provider((ref) { - ref.listenSelf(listener.call); - ref.listenSelf(listener2.call); - - return 0; - }); - - container.read(provider); - - verifyInOrder([ - listener(null, 0), - listener2(null, 0), - ]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - container.refresh(provider); - - verifyInOrder([ - listener(0, 0), - listener2(0, 0), - ]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('clears state listeners on rebuild', () async { - final container = createContainer(); - final listener = Listener(); - final listener2 = Listener(); - var result = 0; - final provider = Provider((ref) { - if (result == 0) { - ref.listenSelf(listener.call); - } else { - ref.listenSelf(listener2.call); - } - - return result; - }); - - container.read(provider); - - verifyOnly(listener, listener(null, 0)); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - result = 42; - container.refresh(provider); - - verifyOnly(listener2, listener2(0, 42)); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('listens to errors', () { - final container = createContainer(); - final listener = Listener(); - final errorListener = ErrorListener(); - final errorListener2 = ErrorListener(); - var error = 42; - final provider = Provider((ref) { - ref.listenSelf(listener.call, onError: errorListener.call); - ref.listenSelf((prev, next) {}, onError: errorListener2.call); - - Error.throwWithStackTrace(error, StackTrace.empty); - }); - - expect(() => container.read(provider), throwsA(42)); - - verifyZeroInteractions(listener); - verifyInOrder([ - errorListener(42, StackTrace.empty), - errorListener2(42, StackTrace.empty), - ]); - verifyNoMoreInteractions(errorListener); - verifyNoMoreInteractions(errorListener2); - - error = 21; - expect(() => container.refresh(provider), throwsA(21)); - - verifyZeroInteractions(listener); - - verifyInOrder([ - errorListener(21, StackTrace.empty), - errorListener2(21, StackTrace.empty), - ]); - verifyNoMoreInteractions(errorListener); - verifyNoMoreInteractions(errorListener2); - }); - - test('executes error listener before other listeners', () { - final container = createContainer(); - final errorListener = ErrorListener(); - final errorListener2 = ErrorListener(); - Exception? error; - final provider = Provider((ref) { - ref.listenSelf((prev, next) {}, onError: errorListener.call); - - if (error != null) Error.throwWithStackTrace(error, StackTrace.empty); - - return 0; - }); - - container.listen(provider, (prev, next) {}, onError: errorListener2.call); - - verifyZeroInteractions(errorListener); - verifyZeroInteractions(errorListener2); - - error = Exception(); - expect(() => container.refresh(provider), throwsA(error)); - - verifyInOrder([ - errorListener(error, StackTrace.empty), - errorListener2(error, StackTrace.empty), - ]); - verifyNoMoreInteractions(errorListener); - verifyNoMoreInteractions(errorListener2); - }); - - test('executes state listener before other listeners', () { - final container = createContainer(); - final listener = Listener(); - final listener2 = Listener(); - var result = 0; - final provider = Provider((ref) { - ref.listenSelf(listener.call); - return result; - }); - - container.listen(provider, listener2.call, fireImmediately: true); - - verifyInOrder([ - listener(null, 0), - listener2(null, 0), - ]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - result = 42; - container.refresh(provider); - - verifyInOrder([ - listener(0, 42), - listener2(0, 42), - ]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('listeners are not allowed to modify the state', () {}); - }); - - group('Ref.listen', () { - test( - 'when rebuild throws identical error/stack, listeners are still notified', - () { - final container = createContainer(); - const stack = StackTrace.empty; - final listener = Listener(); - final errorListener = ErrorListener(); - final provider = Provider((ref) { - Error.throwWithStackTrace(42, stack); - }); - - container.listen( - provider, - listener.call, - onError: errorListener.call, - fireImmediately: true, - ); - - verifyZeroInteractions(listener); - verifyOnly(errorListener, errorListener(42, stack)); - - expect(() => container.refresh(provider), throwsA(42)); - - verifyZeroInteractions(listener); - verifyOnly(errorListener, errorListener(42, stack)); - }); - - test('cannot listen itself', () { - final container = createContainer(); - final listener = Listener(); - late ProviderRef ref; - late Provider provider; - provider = Provider((r) { - ref = r; - ref.listen(provider, (previous, next) {}); - return 0; - }); - - expect(() => container.read(provider), throwsA(isAssertionError)); - - ref.state = 42; - - verifyZeroInteractions(listener); - }); - - test('expose previous and new value on change', () { - final container = createContainer(); - final dep = StateNotifierProvider, int>( - (ref) => StateController(0), - ); - final listener = Listener(); - final provider = Provider((ref) { - ref.listen(dep, listener.call, fireImmediately: true); - }); - - container.read(provider); - - verifyOnly(listener, listener(null, 0)); - - container.read(dep.notifier).state++; - - verifyOnly(listener, listener(0, 1)); - }); - - test( - 'calling ref.listen on a provider with an outdated dependency flushes it, then add the listener', - () { - final container = createContainer(); - var buildCount = 0; - final dep2 = StateNotifierProvider, int>( - (ref) => StateController(0), - ); - final dep = Provider((ref) { - buildCount++; - return ref.watch(dep2); - }); - final listener = Listener(); - final provider = Provider((ref) { - ref.listen(dep, listener.call); - }); - - container.read(dep); - container.read(dep2.notifier).state++; // mark `dep` as outdated - - expect(buildCount, 1); - verifyZeroInteractions(listener); - - container.read(provider); - - expect(buildCount, 2); - verifyZeroInteractions(listener); - }); - - test( - 'when using selectors, `previous` is the latest notification instead of latest event', - () { - final container = createContainer(); - final dep = StateNotifierProvider, int>( - (ref) => StateController(0), - ); - final listener = Listener(); - final provider = Provider((ref) { - ref.listen( - dep.select((value) => value.isEven), - listener.call, - fireImmediately: true, - ); - }); - - container.read(provider); - verifyOnly(listener, listener(null, true)); - - container.read(dep.notifier).state += 2; - - verifyNoMoreInteractions(listener); - - container.read(dep.notifier).state++; - - verifyOnly(listener, listener(true, false)); - }); - - test('when no onError is specified, fallbacks to handleUncaughtError', - () async { - final container = createContainer(); - final isErrored = StateProvider((ref) => false); - final dep = Provider((ref) { - if (ref.watch(isErrored)) throw UnimplementedError(); - return 0; - }); - final listener = Listener(); - final errors = []; - final provider = Provider((ref) { - runZonedGuarded( - () => ref.listen(dep, listener.call), - (err, stack) => errors.add(err), - ); - }); - - container.read(provider); - - verifyZeroInteractions(listener); - expect(errors, isEmpty); - - container.read(isErrored.notifier).state = true; - - await container.pump(); - - verifyZeroInteractions(listener); - expect(errors, [isUnimplementedError]); - }); - - test( - 'when no onError is specified, selectors fallbacks to handleUncaughtError', - () async { - final container = createContainer(); - final isErrored = StateProvider((ref) => false); - final dep = Provider((ref) { - if (ref.watch(isErrored)) throw UnimplementedError(); - return 0; - }); - final listener = Listener(); - final errors = []; - final provider = Provider((ref) { - runZonedGuarded( - () => ref.listen(dep.select((value) => value), listener.call), - (err, stack) => errors.add(err), - ); - }); - - container.read(provider); - - verifyZeroInteractions(listener); - expect(errors, isEmpty); - - container.read(isErrored.notifier).state = true; - - await container.pump(); - - verifyZeroInteractions(listener); - expect(errors, [isUnimplementedError]); - }); - - test('when rebuild throws, calls onError', () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final provider = Provider((ref) { - if (ref.watch(dep) != 0) { - throw UnimplementedError(); - } - return 0; - }); - final errorListener = ErrorListener(); - final listener = Listener(); - - final a = Provider((ref) { - ref.listen(provider, listener.call, onError: errorListener.call); - }); - - container.read(a); - - verifyZeroInteractions(errorListener); - verifyZeroInteractions(listener); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyZeroInteractions(listener); - verifyOnly( - errorListener, - errorListener(isUnimplementedError, any), - ); - }); - - test('when rebuild throws on selector, calls onError', () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final provider = Provider((ref) { - if (ref.watch(dep) != 0) { - throw UnimplementedError(); - } - return 0; - }); - final errorListener = ErrorListener(); - final listener = Listener(); - - final a = Provider((ref) { - ref.listen( - provider.select((value) => value), - listener.call, - onError: errorListener.call, - ); - }); - - container.read(a); - - verifyZeroInteractions(errorListener); - verifyZeroInteractions(listener); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyZeroInteractions(listener); - verifyOnly( - errorListener, - errorListener(isUnimplementedError, any), - ); - }); - - group('fireImmediately', () { - test('when no onError is specified, fallbacks to handleUncaughtError', - () { - final container = createContainer(); - final dep = Provider((ref) => throw UnimplementedError()); - final listener = Listener(); - final errors = []; - final provider = Provider((ref) { - runZonedGuarded( - () { - ref.listen( - dep, - listener.call, - fireImmediately: true, - ); - }, - (err, stack) => errors.add(err), - ); - }); - - container.read(provider); - - verifyZeroInteractions(listener); - expect(errors, [ - isUnimplementedError, - ]); - }); - - test( - 'when no onError is specified on selectors, fallbacks to handleUncaughtError', - () { - final container = createContainer(); - final dep = Provider((ref) => throw UnimplementedError()); - final listener = Listener(); - final errors = []; - final provider = Provider((ref) { - runZonedGuarded( - () { - ref.listen( - dep.select((value) => value), - listener.call, - fireImmediately: true, - ); - }, - (err, stack) => errors.add(err), - ); - }); - - container.read(provider); - - verifyZeroInteractions(listener); - expect(errors, [ - isUnimplementedError, - ]); - }); - - test('on provider that threw, fireImmediately calls onError', () { - final container = createContainer(); - final dep = Provider((ref) => throw UnimplementedError()); - final listener = Listener(); - final errorListener = ErrorListener(); - final provider = Provider((ref) { - ref.listen( - dep, - listener.call, - onError: errorListener.call, - fireImmediately: true, - ); - }); - - container.read(provider); - - verifyZeroInteractions(listener); - verifyOnly( - errorListener, - errorListener(isUnimplementedError, argThat(isNotNull)), - ); - }); - - test('when selecting provider that threw, fireImmediately calls onError', - () { - final container = createContainer(); - final dep = Provider((ref) => throw UnimplementedError()); - final listener = Listener(); - final errorListener = ErrorListener(); - final provider = Provider((ref) { - ref.listen( - dep.select((value) => 0), - listener.call, - onError: errorListener.call, - fireImmediately: true, - ); - }); - - container.read(provider); - - verifyZeroInteractions(listener); - verifyOnly( - errorListener, - errorListener(isUnimplementedError, argThat(isNotNull)), - ); - }); - - test('correctly listens to the provider if selector listener throws', () { - final dep = StateProvider((ref) => 0); - final listener = Listener(); - var isFirstCall = true; - - final container = createContainer(); - final errors = []; - - ProviderSubscription? sub; - - final provider = Provider((ref) { - sub = runZonedGuarded( - () => ref.listen( - dep.select((value) => value), - (prev, value) { - listener(prev, value); - if (isFirstCall) { - isFirstCall = false; - throw StateError('Some error'); - } - }, - fireImmediately: true, - ), - (err, stack) => errors.add(err), - ); - }); - - container.listen(provider, (prev, value) {}); - - expect(sub, isNotNull); - verifyOnly(listener, listener(null, 0)); - expect(errors, [isStateError]); - - container.read(dep.notifier).state++; - verifyOnly(listener, listener(0, 1)); - }); - - test('correctly listens to the provider if normal listener throws', () { - final dep = StateProvider((ref) => 0); - final listener = Listener(); - var isFirstCall = true; - - final container = createContainer(); - final errors = []; - - ProviderSubscription? sub; - - final provider = Provider((ref) { - sub = runZonedGuarded( - () => ref.listen( - dep, - (prev, value) { - listener(prev, value); - if (isFirstCall) { - isFirstCall = false; - throw StateError('Some error'); - } - }, - fireImmediately: true, - ), - (err, stack) => errors.add(err), - ); - }); - - container.listen(provider, (prev, value) {}); - - expect(sub, isNotNull); - verifyOnly(listener, listener(null, 0)); - expect(errors, [isStateError]); - - container.read(dep.notifier).state++; - verifyOnly(listener, listener(0, 1)); - }); - - test( - 'correctly listens to the provider if selector onError listener throws', - () async { - final dep = StateProvider((ref) => 0); - final dep2 = Provider((ref) { - if (ref.watch(dep) == 0) { - throw UnimplementedError(); - } - return ref.watch(dep); - }); - final listener = Listener(); - final errorListener = ErrorListener(); - var isFirstCall = true; - - final container = createContainer(); - final errors = []; - - ProviderSubscription? sub; - - final provider = Provider((ref) { - sub = runZonedGuarded( - () => ref.listen( - dep2.select((value) => value), - listener.call, - onError: (err, stack) { - errorListener(err, stack); - if (isFirstCall) { - isFirstCall = false; - throw StateError('Some error'); - } - }, - fireImmediately: true, - ), - (err, stack) => errors.add(err), - ); - }); - - container.read(provider); - - expect(sub, isNotNull); - verifyZeroInteractions(listener); - verifyOnly( - errorListener, - errorListener(argThat(isUnimplementedError), argThat(isNotNull)), - ); - expect(errors, [isStateError]); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyNoMoreInteractions(errorListener); - verifyOnly(listener, listener(null, 1)); - }); - - test( - 'correctly listens to the provider if normal onError listener throws', - () async { - final dep = StateProvider((ref) => 0); - final dep2 = Provider((ref) { - if (ref.watch(dep) == 0) { - throw UnimplementedError(); - } - return ref.watch(dep); - }); - final listener = Listener(); - final errorListener = ErrorListener(); - var isFirstCall = true; - - final container = createContainer(); - final errors = []; - - ProviderSubscription? sub; - - final provider = Provider((ref) { - sub = runZonedGuarded( - () => ref.listen( - dep2, - listener.call, - onError: (err, stack) { - errorListener(err, stack); - if (isFirstCall) { - isFirstCall = false; - throw StateError('Some error'); - } - }, - fireImmediately: true, - ), - (err, stack) => errors.add(err), - ); - }); - - container.read(provider); - - expect(sub, isNotNull); - verifyZeroInteractions(listener); - verifyOnly( - errorListener, - errorListener(argThat(isUnimplementedError), argThat(isNotNull)), - ); - expect(errors, [isStateError]); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyNoMoreInteractions(errorListener); - verifyOnly(listener, listener(null, 1)); - }); - }); - }); - - group('ProviderContainer.listen', () { - test('when no onError is specified, fallbacks to handleUncaughtError', - () async { - final container = createContainer(); - final isErrored = StateProvider((ref) => false); - final dep = Provider((ref) { - if (ref.watch(isErrored)) throw UnimplementedError(); - return 0; - }); - final listener = Listener(); - final errors = []; - - runZonedGuarded( - () => container.listen(dep, listener.call), - (err, stack) => errors.add(err), - ); - - verifyZeroInteractions(listener); - expect(errors, isEmpty); - - container.read(isErrored.notifier).state = true; - - await container.pump(); - - verifyZeroInteractions(listener); - expect(errors, [isUnimplementedError]); - }); - - test( - 'when no onError is specified, selectors fallbacks to handleUncaughtError', - () async { - final container = createContainer(); - final isErrored = StateProvider((ref) => false); - final dep = Provider((ref) { - if (ref.watch(isErrored)) throw UnimplementedError(); - return 0; - }); - final listener = Listener(); - final errors = []; - - runZonedGuarded( - () => container.listen(dep.select((value) => value), listener.call), - (err, stack) => errors.add(err), - ); - - verifyZeroInteractions(listener); - expect(errors, isEmpty); - - container.read(isErrored.notifier).state = true; - - await container.pump(); - - verifyZeroInteractions(listener); - expect(errors, [isUnimplementedError]); - }); - - test('when rebuild throws, calls onError', () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final provider = Provider((ref) { - if (ref.watch(dep) != 0) { - throw UnimplementedError(); - } - return 0; - }); - final errorListener = ErrorListener(); - final listener = Listener(); - - container.listen(provider, listener.call, onError: errorListener.call); - - verifyZeroInteractions(errorListener); - verifyZeroInteractions(listener); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyZeroInteractions(listener); - verifyOnly( - errorListener, - errorListener(isUnimplementedError, any), - ); - }); - - test('when rebuild throws on selector, calls onError', () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final provider = Provider((ref) { - if (ref.watch(dep) != 0) { - throw UnimplementedError(); - } - return 0; - }); - final errorListener = ErrorListener(); - final listener = Listener(); - - container.listen( - provider.select((value) => value), - listener.call, - onError: errorListener.call, - ); - - verifyZeroInteractions(errorListener); - verifyZeroInteractions(listener); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyZeroInteractions(listener); - verifyOnly( - errorListener, - errorListener(isUnimplementedError, any), - ); - }); - - test( - 'when using selectors, `previous` is the latest notification instead of latest event', - () { - final container = createContainer(); - final provider = StateNotifierProvider, int>( - (ref) => StateController(0), - ); - final listener = Listener(); - - container.listen( - provider.select((value) => value.isEven), - listener.call, - fireImmediately: true, - ); - - verifyOnly(listener, listener(null, true)); - - container.read(provider.notifier).state += 2; - - verifyNoMoreInteractions(listener); - - container.read(provider.notifier).state++; - - verifyOnly(listener, listener(true, false)); - }); - - test('expose previous and new value on change', () { - final container = createContainer(); - final provider = StateNotifierProvider, int>( - (ref) => StateController(0), - ); - final listener = Listener(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 0)); - - container.read(provider.notifier).state++; - - verifyOnly(listener, listener(0, 1)); - }); - - test('can downcast the value', () async { - final listener = Listener(); - final dep = StateProvider((ref) => 0); - - final container = createContainer(); - - container.listen(dep, listener.call); - - verifyZeroInteractions(listener); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyOnly(listener, listener(0, 1)); - }); - - test( - 'if a listener adds a container.listen, the new listener is not called immediately', - () { - final provider = StateProvider((ref) => 0); - final container = createContainer(); - - final listener = Listener(); - - container.listen(provider, (prev, value) { - listener(prev, value); - container.listen(provider, listener.call); - }); - - verifyZeroInteractions(listener); - - container.read(provider.notifier).state++; - - verify(listener(0, 1)).called(1); - - container.read(provider.notifier).state++; - - verify(listener(1, 2)).called(2); - }); - - test( - 'if a listener removes another provider.listen, the removed listener is still called', - () { - final provider = StateProvider((ref) => 0); - final container = createContainer(); - - final listener = Listener(); - final listener2 = Listener(); - - final p = Provider((ref) { - ProviderSubscription? a; - ref.listen(provider, (prev, value) { - listener(prev, value); - a?.close(); - a = null; - }); - - a = ref.listen(provider, listener2.call); - }); - container.read(p); - - verifyZeroInteractions(listener); - verifyZeroInteractions(listener2); - - container.read(provider.notifier).state++; - - verifyInOrder([ - listener(0, 1), - listener2(0, 1), - ]); - - container.read(provider.notifier).state++; - - verify(listener(1, 2)).called(1); - verifyNoMoreInteractions(listener2); - }); - - test( - 'if a listener removes another provider.listen, the removed listener is still called (ProviderListenable)', - skip: true, - () { - final provider = StateProvider((ref) => 0); - final container = createContainer(); - - final listener = Listener(); - final listener2 = Listener(); - - final p = Provider((ref) { - ProviderSubscription? a; - ref.listen(provider, (prev, value) { - listener(prev, value); - a?.close(); - a = null; - }); - - a = ref.listen(provider, listener2.call); - }); - container.read(p); - - verifyZeroInteractions(listener); - verifyZeroInteractions(listener2); - - container.read(provider.notifier).state++; - - verifyInOrder([ - listener(1, 1), - listener2(1, 1), - ]); - - container.read(provider.notifier).state++; - - verify(listener(2, 2)).called(1); - verifyNoMoreInteractions(listener2); - // TODO the problem is that ProviderListenable subscriptions are separate from - // ProviderElement subscriptions. So the ProviderElement.notifyListeners - // making a local copy of the list of subscriptions before notifying listeners - // does not apply to ProviderListenables - // Support for modifying listeners within a listener probably should be dropped anyway for performance. - // This would remove a list copy - }, - ); - - test( - 'if a listener adds a provider.listen, the new listener is not called immediately', - () { - final provider = StateProvider((ref) => 0); - final container = createContainer(); - - final listener = Listener(); - - final p = Provider((ref) { - ref.listen(provider, (prev, value) { - listener(prev, value); - ref.listen(provider, listener.call); - }); - }); - container.read(p); - - verifyZeroInteractions(listener); - - container.read(provider.notifier).state++; - - verify(listener(0, 1)).called(1); - - container.read(provider.notifier).state++; - - verify(listener(1, 2)).called(2); - }); - - test( - 'if a listener removes another container.listen, the removed listener is still called (ProviderListenable)', - skip: true, - () { - final provider = StateProvider((ref) => 0); - final container = createContainer(); - - final listener = Listener(); - final listener2 = Listener(); - - ProviderSubscription? a; - container.listen(provider, (prev, value) { - listener(prev, value); - a?.close(); - a = null; - }); - - a = container.listen(provider, listener2.call); - - verifyZeroInteractions(listener); - verifyZeroInteractions(listener2); - - container.read(provider.notifier).state++; - - verifyInOrder([ - listener(1, 1), - listener2(1, 1), - ]); - - container.read(provider.notifier).state++; - - verify(listener(2, 2)).called(1); - verifyNoMoreInteractions(listener2); - // TODO the problem is that ProviderListenable subscriptions are separate from - // ProviderElement subscriptions. So the ProviderElement.notifyListeners - // making a local copy of the list of subscriptions before notifying listeners - // does not apply to ProviderListenables - // Support for modifying listeners within a listener probably should be dropped anyway for performance. - // This would remove a list copy - }, - ); - - test( - 'if a listener removes another container.listen, the removed listener is still called', - () { - final provider = StateProvider((ref) => 0); - final container = createContainer(); - - final listener = Listener(); - final listener2 = Listener(); - - ProviderSubscription? a; - container.listen(provider, (prev, value) { - listener(prev, value); - a?.close(); - a = null; - }); - - a = container.listen(provider, listener2.call); - - verifyZeroInteractions(listener); - verifyZeroInteractions(listener2); - - container.read(provider.notifier).state++; - - verifyInOrder([ - listener(0, 1), - listener2(0, 1), - ]); - - container.read(provider.notifier).state++; - - verify(listener(1, 2)).called(1); - verifyNoMoreInteractions(listener2); - }); - - group('fireImmediately', () { - test('when no onError is specified, fallbacks to handleUncaughtError', - () { - final container = createContainer(); - final dep = Provider((ref) => throw UnimplementedError()); - final listener = Listener(); - final errors = []; - - runZonedGuarded( - () { - container.listen( - dep, - listener.call, - fireImmediately: true, - ); - }, - (err, stack) => errors.add(err), - ); - - verifyZeroInteractions(listener); - expect(errors, [ - isUnimplementedError, - ]); - }); - - test( - 'when no onError is specified on selectors, fallbacks to handleUncaughtError', - () { - final container = createContainer(); - final dep = Provider((ref) => throw UnimplementedError()); - final listener = Listener(); - final errors = []; - - runZonedGuarded( - () { - container.listen( - dep.select((value) => value), - listener.call, - fireImmediately: true, - ); - }, - (err, stack) => errors.add(err), - ); - - verifyZeroInteractions(listener); - expect(errors, [ - isUnimplementedError, - ]); - }); - - test('on provider that threw, fireImmediately calls onError', () { - final container = createContainer(); - final provider = Provider((ref) => throw UnimplementedError()); - final listener = Listener(); - final errorListener = ErrorListener(); - - container.listen( - provider, - listener.call, - onError: errorListener.call, - fireImmediately: true, - ); - - verifyZeroInteractions(listener); - verifyOnly( - errorListener, - errorListener(isUnimplementedError, argThat(isNotNull)), - ); - }); - - test('supports selectors', () { - final container = createContainer(); - final provider = - StateNotifierProvider((ref) => Counter()); - final listener = Listener(); - final listener2 = Listener(); - - container.listen( - provider.select((v) => v.isEven), - listener.call, - fireImmediately: true, - ); - container.listen(provider.select((v) => v.isEven), listener2.call); - - verifyOnly(listener, listener(null, true)); - verifyZeroInteractions(listener2); - - container.read(provider.notifier).state = 21; - - verifyOnly(listener, listener(true, false)); - verifyOnly(listener2, listener2(true, false)); - }); - - test('passing fireImmediately: false skips the initial value', () { - final provider = StateProvider((ref) => 0); - final listener = Listener(); - - final container = createContainer(); - - container.listen(provider, listener.call); - - verifyZeroInteractions(listener); - }); - - test( - 'correctly listens to the provider if selector onError listener throws', - () async { - final dep = StateProvider((ref) => 0); - final provider = Provider((ref) { - if (ref.watch(dep) == 0) { - throw UnimplementedError(); - } - return ref.watch(dep); - }); - final listener = Listener(); - final errorListener = ErrorListener(); - var isFirstCall = true; - - final container = createContainer(); - final errors = []; - - final sub = runZonedGuarded( - () => container.listen( - provider.select((value) => value), - listener.call, - onError: (err, stack) { - errorListener(err, stack); - if (isFirstCall) { - isFirstCall = false; - throw StateError('Some error'); - } - }, - fireImmediately: true, - ), - (err, stack) => errors.add(err), - ); - - container.listen(provider, (prev, value) {}); - - expect(sub, isNotNull); - verifyZeroInteractions(listener); - verifyOnly( - errorListener, - errorListener(argThat(isUnimplementedError), argThat(isNotNull)), - ); - expect(errors, [isStateError]); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyNoMoreInteractions(errorListener); - verifyOnly(listener, listener(null, 1)); - }); - - test( - 'correctly listens to the provider if normal onError listener throws', - () async { - final dep = StateProvider((ref) => 0); - final provider = Provider((ref) { - if (ref.watch(dep) == 0) { - throw UnimplementedError(); - } - return ref.watch(dep); - }); - final listener = Listener(); - final errorListener = ErrorListener(); - var isFirstCall = true; - - final container = createContainer(); - final errors = []; - - final sub = runZonedGuarded( - () => container.listen( - provider, - listener.call, - onError: (err, stack) { - errorListener(err, stack); - if (isFirstCall) { - isFirstCall = false; - throw StateError('Some error'); - } - }, - fireImmediately: true, - ), - (err, stack) => errors.add(err), - ); - - container.listen(provider, (prev, value) {}); - - expect(sub, isNotNull); - verifyZeroInteractions(listener); - verifyOnly( - errorListener, - errorListener(argThat(isUnimplementedError), argThat(isNotNull)), - ); - expect(errors, [isStateError]); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyNoMoreInteractions(errorListener); - verifyOnly(listener, listener(null, 1)); - }); - - test('correctly listens to the provider if selector listener throws', () { - final provider = StateProvider((ref) => 0); - final listener = Listener(); - var isFirstCall = true; - - final container = createContainer(); - final errors = []; - - final sub = runZonedGuarded( - () => container.listen( - provider.select((value) => value), - (prev, value) { - listener(prev, value); - if (isFirstCall) { - isFirstCall = false; - throw StateError('Some error'); - } - }, - fireImmediately: true, - ), - (err, stack) => errors.add(err), - ); - - expect(sub, isNotNull); - verifyOnly(listener, listener(null, 0)); - expect(errors, [isStateError]); - - container.read(provider.notifier).state++; - - verifyOnly(listener, listener(0, 1)); - }); - - test('correctly listens to the provider if normal listener throws', () { - final provider = StateProvider((ref) => 0); - final listener = Listener(); - var isFirstCall = true; - - final container = createContainer(); - final errors = []; - - final sub = runZonedGuarded( - () => container.listen( - provider, - (prev, value) { - listener(prev, value); - if (isFirstCall) { - isFirstCall = false; - throw StateError('Some error'); - } - }, - fireImmediately: true, - ), - (err, stack) => errors.add(err), - ); - - expect(sub, isNotNull); - verifyOnly(listener, listener(null, 0)); - expect(errors, [isStateError]); - - container.read(provider.notifier).state++; - - verifyOnly(listener, listener(0, 1)); - }); - - test('correctly listens to the provider if normal listener throws', () { - final provider = StateProvider((ref) => 0); - final listener = Listener(); - var isFirstCall = true; - - final container = createContainer(); - final errors = []; - - final sub = runZonedGuarded( - () => container.listen( - provider, - (prev, notifier) { - listener(prev, notifier); - if (isFirstCall) { - isFirstCall = false; - throw StateError('Some error'); - } - }, - fireImmediately: true, - ), - (err, stack) => errors.add(err), - ); - - expect(sub, isNotNull); - verifyOnly(listener, listener(null, 0)); - expect(errors, [isStateError]); - - container.read(provider.notifier).state++; - - verifyOnly(listener, listener(0, 1)); - }); - }); - - test('.read on closed subscription throws', () { - final notifier = Counter(); - final provider = StateNotifierProvider((_) => notifier); - final container = createContainer(); - final listener = Listener(); - - final sub = container.listen( - provider, - listener.call, - fireImmediately: true, - ); - - verify(listener(null, 0)).called(1); - verifyNoMoreInteractions(listener); - - sub.close(); - notifier.increment(); - - expect(sub.read, throwsStateError); - - verifyNoMoreInteractions(listener); - }); - - test('.read on closed selector subscription throws', () { - final notifier = Counter(); - final provider = StateNotifierProvider((_) => notifier); - final container = createContainer(); - final listener = Listener(); - - final sub = container.listen( - provider.select((value) => value * 2), - listener.call, - fireImmediately: true, - ); - - verify(listener(null, 0)).called(1); - verifyNoMoreInteractions(listener); - - sub.close(); - notifier.increment(); - - expect(sub.read, throwsStateError); - verifyNoMoreInteractions(listener); - }); - - test("doesn't trow when creating a provider that failed", () { - final container = createContainer(); - final provider = Provider((ref) { - throw Error(); - }); - - final sub = container.listen(provider, (_, __) {}); - - expect(sub, isA>()); - }); - - test('selectors can close listeners', () { - final container = createContainer(); - final provider = StateNotifierProvider((ref) => Counter()); - - expect(container.readProviderElement(provider).hasListeners, false); - - final sub = container.listen( - provider.select((count) => count.isEven), - (prev, isEven) {}, - ); - - expect(container.readProviderElement(provider).hasListeners, true); - - sub.close(); - - expect(container.readProviderElement(provider).hasListeners, false); - }); - - test('can watch selectors', () async { - final container = createContainer(); - final provider = StateNotifierProvider((ref) => Counter()); - final isAdultSelector = Selector(false, (c) => c >= 18); - final isAdultListener = Listener(); - - final controller = container.read(provider.notifier); - container.listen( - provider.select(isAdultSelector.call), - isAdultListener.call, - fireImmediately: true, - ); - - verifyOnly(isAdultSelector, isAdultSelector(0)); - verifyOnly(isAdultListener, isAdultListener(null, false)); - - controller.state += 10; - - verifyOnly(isAdultSelector, isAdultSelector(10)); - verifyNoMoreInteractions(isAdultListener); - - controller.state += 10; - - verifyOnly(isAdultSelector, isAdultSelector(20)); - verifyOnly(isAdultListener, isAdultListener(false, true)); - - controller.state += 10; - - verifyOnly(isAdultSelector, isAdultSelector(30)); - verifyNoMoreInteractions(isAdultListener); - }); - - test('calls immediately the listener with the current value', () { - final provider = Provider((ref) => 0); - final listener = Listener(); - - final container = createContainer(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 0)); - }); - - test('call listener when provider rebuilds', () async { - final controller = StreamController(); - addTearDown(controller.close); - final container = createContainer(); - - final count = StateProvider((ref) => 0); - final provider = Provider((ref) => ref.watch(count)); - - container.listen( - provider, - (prev, value) => controller.add(value), - fireImmediately: true, - ); - - container.read(count.notifier).state++; - - await expectLater( - controller.stream, - emitsInOrder([0, 1]), - ); - }); - - test('call listener when provider emits an update', () async { - final container = createContainer(); - - final count = StateProvider((ref) => 0); - final listener = Listener(); - - container.listen(count, listener.call); - - container.read(count.notifier).state++; - - verifyOnly(listener, listener(0, 1)); - - container.read(count.notifier).state++; - - verifyOnly(listener, listener(1, 2)); - }); - - test('supports selectors', () { - final container = createContainer(); - - final count = StateProvider((ref) => 0); - final listener = Listener(); - - container.listen( - count.select((value) => value.isEven), - listener.call, - fireImmediately: true, - ); - - verifyOnly(listener, listener(null, true)); - - container.read(count.notifier).state = 2; - - verifyNoMoreInteractions(listener); - - container.read(count.notifier).state = 3; - - verifyOnly(listener, listener(true, false)); - }); - }); -} diff --git a/packages/riverpod/test/framework/provider_container_test.dart b/packages/riverpod/test/framework/provider_container_test.dart deleted file mode 100644 index b9a44e9a7..000000000 --- a/packages/riverpod/test/framework/provider_container_test.dart +++ /dev/null @@ -1,749 +0,0 @@ -import 'package:mockito/mockito.dart'; -import 'package:riverpod/riverpod.dart'; -import 'package:riverpod/src/internals.dart'; -import 'package:test/test.dart'; - -import '../utils.dart'; - -void main() { - group('ProviderContainer', () { - group('invalidate', () { - test('can disposes of the element if not used anymore', () async { - final provider = Provider.autoDispose((r) { - r.keepAlive(); - return 0; - }); - final container = createContainer(); - - container.read(provider); - container.invalidate(provider); - - await container.pump(); - - expect(container.getAllProviderElements(), isEmpty); - }); - }); - - test('Supports unmounting containers in reverse order', () { - final container = createContainer(); - - final child = createContainer(parent: container); - - container.dispose(); - child.dispose(); - }); - - group('when unmounting providers', () { - test( - 'cleans up all the StateReaders of a provider in the entire ProviderContainer tree', - () async { - // Regression test for https://github.com/rrousselGit/riverpod/issues/1943 - final a = createContainer(); - // b/c voluntarily do not use the Provider, but a/d do. This is to test - // that the disposal logic correctly cleans up the StateReaders - // in all ProviderContainers associated with the provider, even if - // some links between two ProviderContainers are not using the provider. - final b = createContainer(parent: a); - final c = createContainer(parent: b); - final d = createContainer(parent: c); - - final provider = Provider.autoDispose((ref) => 3); - - final subscription = d.listen( - provider, - (previous, next) {}, - fireImmediately: true, - ); - - expect(a.hasStateReaderFor(provider), true); - expect(b.hasStateReaderFor(provider), false); - expect(c.hasStateReaderFor(provider), false); - expect(d.hasStateReaderFor(provider), true); - - subscription.close(); - - expect(a.hasStateReaderFor(provider), true); - expect(b.hasStateReaderFor(provider), false); - expect(c.hasStateReaderFor(provider), false); - expect(d.hasStateReaderFor(provider), true); - - await a.pump(); - - expect(a.hasStateReaderFor(provider), false); - expect(b.hasStateReaderFor(provider), false); - expect(c.hasStateReaderFor(provider), false); - expect(d.hasStateReaderFor(provider), false); - - d.listen( - provider, - (previous, next) {}, - fireImmediately: true, - ); - - expect(a.hasStateReaderFor(provider), true); - expect(b.hasStateReaderFor(provider), false); - expect(c.hasStateReaderFor(provider), false); - expect(d.hasStateReaderFor(provider), true); - }); - }); - - group('exists', () { - test('simple use-case', () { - final container = createContainer(); - final provider = Provider((ref) => 0); - - expect(container.exists(provider), false); - expect(container.getAllProviderElements(), isEmpty); - - container.read(provider); - - expect(container.exists(provider), true); - }); - - test('handles autoDispose', () async { - final provider = Provider.autoDispose((ref) => 0); - final container = createContainer( - overrides: [ - provider.overrideWith((ref) => 42), - ], - ); - - expect(container.exists(provider), false); - expect(container.getAllProviderElements(), isEmpty); - - container.read(provider); - - expect(container.exists(provider), true); - - await container.pump(); - - expect(container.getAllProviderElements(), isEmpty); - expect(container.exists(provider), false); - expect(container.getAllProviderElements(), isEmpty); - }); - - test('Handles uninitialized overrideWith', () { - final provider = Provider((ref) => 0); - final container = createContainer( - overrides: [ - provider.overrideWith((ref) => 42), - ], - ); - - expect(container.exists(provider), false); - expect(container.getAllProviderElements(), isEmpty); - - container.read(provider); - - expect(container.exists(provider), true); - }); - - test('handles nested providers', () { - final provider = Provider((ref) => 0); - final provider2 = Provider((ref) => 0); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider2]); - - expect(container.exists(provider), false); - expect(container.exists(provider2), false); - expect(container.getAllProviderElements(), isEmpty); - expect(root.getAllProviderElements(), isEmpty); - - container.read(provider); - - expect(container.exists(provider), true); - expect(container.exists(provider2), false); - expect(container.getAllProviderElements(), isEmpty); - expect(root.getAllProviderElements().map((e) => e.origin), [provider]); - - container.read(provider2); - - expect(container.exists(provider2), true); - expect( - container.getAllProviderElements().map((e) => e.origin), - [provider2], - ); - expect(root.getAllProviderElements().map((e) => e.origin), [provider]); - }); - }); - - group('debugReassemble', () { - test( - 'reload providers if the debugGetCreateSourceHash of a provider returns a different value', - () { - final noDebugGetCreateSourceHashBuild = OnBuildMock(); - final noDebugGetCreateSourceHash = Provider((ref) { - noDebugGetCreateSourceHashBuild(); - return 0; - }); - final constantHashBuild = OnBuildMock(); - final constantHash = Provider.internal( - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: () => 'hash', - (ref) { - constantHashBuild(); - return 0; - }, - ); - var hashResult = '42'; - final changingHashBuild = OnBuildMock(); - final changingHash = Provider.internal( - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: () => hashResult, - (ref) { - changingHashBuild(); - return 0; - }, - ); - final container = ProviderContainer(); - - container.read(noDebugGetCreateSourceHash); - container.read(constantHash); - container.read(changingHash); - - clearInteractions(noDebugGetCreateSourceHashBuild); - clearInteractions(constantHashBuild); - clearInteractions(changingHashBuild); - - hashResult = 'new hash'; - container.debugReassemble(); - container.read(noDebugGetCreateSourceHash); - container.read(constantHash); - container.read(changingHash); - - verifyOnly(changingHashBuild, changingHashBuild()); - verifyNoMoreInteractions(constantHashBuild); - verifyNoMoreInteractions(noDebugGetCreateSourceHashBuild); - - container.debugReassemble(); - container.read(noDebugGetCreateSourceHash); - container.read(constantHash); - container.read(changingHash); - - verifyNoMoreInteractions(changingHashBuild); - verifyNoMoreInteractions(constantHashBuild); - verifyNoMoreInteractions(noDebugGetCreateSourceHashBuild); - }); - }); - - test('invalidate triggers a rebuild on next frame', () async { - final container = createContainer(); - final listener = Listener(); - var result = 0; - final provider = Provider((r) => result); - - container.listen(provider, listener.call); - verifyZeroInteractions(listener); - - container.invalidate(provider); - container.invalidate(provider); - result = 1; - - verifyZeroInteractions(listener); - - await container.pump(); - - verifyOnly(listener, listener(0, 1)); - }); - - test( - 'when using overrideWithProvider, handles overriding with a more specific provider type', - () { - final fooProvider = Provider((ref) => Foo()); - - final container = createContainer( - overrides: [ - // ignore: deprecated_member_use_from_same_package - fooProvider.overrideWithProvider( - Provider((ref) => Bar()), - ), - ], - ); - - expect(container.read(fooProvider), isA()); - }); - - test( - 'when the same provider is overridden multiple times at once, uses the latest override', - () { - final provider = Provider((ref) => 0); - final container = createContainer( - overrides: [ - provider.overrideWithValue(21), - provider.overrideWithValue(42), - ], - ); - - expect(container.read(provider), 42); - expect(container.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', provider), - ]); - }); - - test( - 'when the same family is overridden multiple times at once, uses the latest override', - () { - final provider = Provider.family((ref, value) => 0); - final container = createContainer( - overrides: [ - provider.overrideWithProvider((value) => Provider((ref) => 21)), - provider.overrideWithProvider((value) => Provider((ref) => 42)), - ], - ); - - expect(container.read(provider(0)), 42); - expect(container.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', provider(0)), - ]); - }); - - group('validate that properties respect `dependencies`', () { - test('on reading an element, asserts that dependencies are respected', - () { - final dep = Provider((ref) => 0); - final provider = Provider((ref) => ref.watch(dep)); - - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [dep.overrideWithValue(42)], - ); - - expect( - () => container.readProviderElement(provider), - throwsA(isA()), - ); - }); - - test( - 'on reading an element, asserts that transitive dependencies are also respected', - () { - final transitiveDep = Provider((ref) => 0); - final dep = Provider((ref) => ref.watch(transitiveDep)); - final provider = Provider((ref) => ref.watch(dep)); - - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [transitiveDep.overrideWithValue(42)], - ); - - expect( - () => container.readProviderElement(provider), - throwsA(isA()), - ); - }); - }); - - group('updateOverrides', () { - test('is not allowed to remove overrides ', () { - final provider = Provider((_) => 0); - - final container = - createContainer(overrides: [provider.overrideWithValue(42)]); - - expect(container.read(provider), 42); - - expect( - () => container.updateOverrides([]), - throwsA(isAssertionError), - ); - }); - }); - - test( - 'after a child container is disposed, ref.watch keeps working on providers associated with the ancestor container', - () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final provider = Provider((ref) => ref.watch(dep)); - final listener = Listener(); - final child = createContainer(parent: container); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 0)); - - child.dispose(); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyOnly(listener, listener(0, 1)); - }); - - test( - 'flushes listened-to providers even if they have no external listeners', - () async { - final dep = StateProvider((ref) => 0); - final provider = Provider((ref) => ref.watch(dep)); - final another = StateProvider((ref) { - ref.listen(provider, (prev, value) => ref.controller.state++); - return 0; - }); - final container = createContainer(); - - expect(container.read(another), 0); - - container.read(dep.notifier).state = 42; - - expect(container.read(another), 1); - }); - - test( - 'flushes listened-to providers even if they have no external listeners (with ProviderListenable)', - () async { - final dep = StateProvider((ref) => 0); - final provider = Provider((ref) => ref.watch(dep)); - final another = StateProvider((ref) { - ref.listen(provider, (prev, value) => ref.controller.state++); - return 0; - }); - final container = createContainer(); - - expect(container.read(another), 0); - - container.read(dep.notifier).state = 42; - - expect(container.read(another), 1); - }); - - group('.pump', () { - test( - '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)); - final b = Provider((ref) => ref.watch(dep)); - final aListener = Listener(); - final bListener = Listener(); - - final root = createContainer(); - final scoped = createContainer(parent: root, overrides: [b]); - - scoped.listen(a, aListener.call, fireImmediately: true); - scoped.listen(b, bListener.call, fireImmediately: true); - - verifyOnly(aListener, aListener(null, 0)); - verifyOnly(bListener, bListener(null, 0)); - - root.read(dep.notifier).state++; - await scoped.pump(); - - verifyOnly(aListener, aListener(0, 1)); - verifyOnly(bListener, bListener(0, 1)); - - scoped.read(dep.notifier).state++; - await scoped.pump(); - - verifyOnly(aListener, aListener(1, 2)); - verifyOnly(bListener, bListener(1, 2)); - }); - }); - - test('depth', () { - final root = createContainer(); - final a = createContainer(parent: root); - final b = createContainer(parent: a); - final c = createContainer(parent: a); - - final root2 = createContainer(); - - expect(root.depth, 0); - expect(root2.depth, 0); - expect(a.depth, 1); - expect(b.depth, 2); - expect(c.depth, 2); - }); - - group('getAllProviderElements', () { - test('list scoped providers that depends on nothing', () { - final scopedProvider = Provider((ref) => 0); - final parent = createContainer(); - final child = createContainer( - parent: parent, - overrides: [scopedProvider], - ); - - child.read(scopedProvider); - - expect( - child.getAllProviderElements().single, - isA>() - .having((e) => e.origin, 'origin', scopedProvider), - ); - }); - - test( - 'list scoped providers that depends on providers from another container', - () { - final dependency = Provider((ref) => 0); - final scopedProvider = Provider((ref) => ref.watch(dependency)); - final parent = createContainer(); - final child = createContainer( - parent: parent, - overrides: [scopedProvider], - ); - - child.read(scopedProvider); - - expect( - child.getAllProviderElements().single, - isA>() - .having((e) => e.origin, 'origin', scopedProvider), - ); - }); - - test( - 'list only elements associated with the container (ignoring inherited and descendent elements)', - () { - final provider = Provider((ref) => 0); - final provider2 = Provider((ref) => 0); - final provider3 = Provider((ref) => 0); - final root = createContainer(); - final mid = createContainer(parent: root, overrides: [provider2]); - final leaf = createContainer(parent: mid, overrides: [provider3]); - - leaf.read(provider); - leaf.read(provider2); - leaf.read(provider3); - - expect( - root.getAllProviderElements().single, - isA>() - .having((e) => e.provider, 'provider', provider), - ); - expect( - mid.getAllProviderElements().single, - isA>() - .having((e) => e.provider, 'provider', provider2), - ); - expect( - leaf.getAllProviderElements().single, - isA>() - .having((e) => e.provider, 'provider', provider3), - ); - }); - - test('list the currently mounted providers', () async { - final container = ProviderContainer(); - final unrelated = Provider((_) => 42); - final provider = Provider.autoDispose((ref) => 0); - - expect(container.read(unrelated), 42); - var sub = container.listen(provider, (_, __) {}); - - expect( - container.getAllProviderElements(), - unorderedMatches([ - isA>(), - isA>(), - ]), - ); - - sub.close(); - await container.pump(); - - expect( - container.getAllProviderElements(), - [isA>()], - ); - - sub = container.listen(provider, (_, __) {}); - - expect( - container.getAllProviderElements(), - unorderedMatches([ - isA>(), - isA>(), - ]), - ); - }); - }); - - group('getAllProviderElementsInOrder', () { - test('list scoped providers that depends on nothing', () { - final scopedProvider = Provider((ref) => 0); - final parent = createContainer(); - final child = createContainer( - parent: parent, - overrides: [scopedProvider], - ); - - child.read(scopedProvider); - - expect( - child.getAllProviderElementsInOrder().single, - isA>() - .having((e) => e.origin, 'origin', scopedProvider), - ); - }); - - test( - 'list scoped providers that depends on providers from another container', - () { - final dependency = Provider((ref) => 0); - final scopedProvider = Provider((ref) => ref.watch(dependency)); - final parent = createContainer(); - final child = createContainer( - parent: parent, - overrides: [scopedProvider], - ); - - child.read(scopedProvider); - - expect( - child.getAllProviderElementsInOrder().single, - isA>() - .having((e) => e.origin, 'origin', scopedProvider), - ); - }); - }); - - test( - 'does not re-initialize a provider if read by a child container after the provider was initialized', - () { - final root = createContainer(); - // the child must be created before the provider is initialized - final child = createContainer(parent: root); - - var buildCount = 0; - final provider = Provider((ref) { - buildCount++; - return 0; - }); - - expect(root.read(provider), 0); - - expect(buildCount, 1); - - expect(child.read(provider), 0); - - expect(buildCount, 1); - }); - - test('can downcast the listener value', () { - final container = createContainer(); - final provider = StateProvider((ref) => 0); - final listener = Listener(); - - container.listen(provider, listener.call); - - verifyZeroInteractions(listener); - - container.read(provider.notifier).state++; - - verifyOnly(listener, listener(any, any)); - }); - - test( - 'can close a ProviderSubscription multiple times with no effect', - () { - final container = createContainer(); - final provider = - StateNotifierProvider, int>((ref) { - return StateController(0); - }); - final listener = Listener(); - - final controller = container.read(provider.notifier); - - final sub = container.listen(provider, listener.call); - - sub.close(); - sub.close(); - - controller.state++; - - verifyZeroInteractions(listener); - }, - ); - - test( - 'closing an already closed ProviderSubscription does not remove subscriptions with the same listener', - () { - final container = createContainer(); - final provider = - StateNotifierProvider, int>((ref) { - return StateController(0); - }); - final listener = Listener(); - - final controller = container.read(provider.notifier); - - final sub = container.listen(provider, listener.call); - container.listen(provider, listener.call); - - controller.state++; - - verify(listener(0, 1)).called(2); - verifyNoMoreInteractions(listener); - - sub.close(); - sub.close(); - - controller.state++; - - verifyOnly(listener, listener(1, 2)); - }, - ); - - test('builds providers at most once per container', () { - var result = 42; - final container = createContainer(); - var callCount = 0; - final provider = Provider((_) { - callCount++; - return result; - }); - - expect(callCount, 0); - expect(container.read(provider), 42); - expect(callCount, 1); - expect(container.read(provider), 42); - expect(callCount, 1); - - final container2 = createContainer(); - - result = 21; - expect(container2.read(provider), 21); - expect(callCount, 2); - expect(container2.read(provider), 21); - expect(callCount, 2); - expect(container.read(provider), 42); - expect(callCount, 2); - }); - test( - 'does not refresh providers if their dependencies changes but they have no active listeners', - () async { - final container = createContainer(); - - var buildCount = 0; - final dep = StateProvider((ref) => 0); - final provider = Provider((ref) { - buildCount++; - return ref.watch(dep); - }); - - container.read(provider); - - expect(buildCount, 1); - - container.read(dep.notifier).state++; - await container.pump(); - - expect(buildCount, 1); - }, - ); - }); -} - -class Foo {} - -class Bar extends Foo {} diff --git a/packages/riverpod/test/framework/provider_element_test.dart b/packages/riverpod/test/framework/provider_element_test.dart deleted file mode 100644 index b5ced8dd5..000000000 --- a/packages/riverpod/test/framework/provider_element_test.dart +++ /dev/null @@ -1,1331 +0,0 @@ -import 'dart:async'; - -import 'package:mockito/mockito.dart'; -import 'package:riverpod/src/internals.dart'; -import 'package:test/test.dart'; - -import '../utils.dart'; - -void main() { - group('Ref.exists', () { - test('Returns true if available on ancestor container', () { - final root = createContainer(); - final container = createContainer(parent: root); - final provider = Provider((ref) => 0); - - root.read(provider); - - expect(container.exists(provider), true); - expect(root.exists(provider), true); - }); - - test('simple use-case', () { - final container = createContainer(); - final provider = Provider((ref) => 0); - final refProvider = Provider((ref) => ref); - - final ref = container.read(refProvider); - - expect( - container.getAllProviderElements().map((e) => e.origin), - [refProvider], - ); - expect(container.exists(refProvider), true); - expect(ref.exists(provider), false); - - ref.read(provider); - - expect(ref.exists(refProvider), true); - expect(ref.exists(provider), true); - }); - }); - - group('ref.notifyListeners', () { - test('If called after initialization, notify listeners', () { - final observer = ProviderObserverMock(); - final listener = Listener(); - final selfListener = Listener(); - final container = createContainer(observers: [observer]); - late Ref ref; - final provider = Provider((r) { - ref = r; - ref.listenSelf(selfListener.call); - return 0; - }); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(observer, observer.didAddProvider(provider, 0, container)); - verifyOnly(listener, listener(null, 0)); - verifyOnly(selfListener, selfListener(null, 0)); - - ref.notifyListeners(); - - verifyOnly(listener, listener(0, 0)); - verifyOnly(selfListener, selfListener(0, 0)); - verifyOnly( - observer, - observer.didUpdateProvider(provider, 0, 0, container), - ); - }); - - test( - 'can be invoked during first initialization, and does not notify listeners', - () { - final observer = ProviderObserverMock(); - final selfListener = Listener(); - final listener = Listener(); - final container = createContainer(observers: [observer]); - final provider = Provider((ref) { - ref.listenSelf(selfListener.call); - ref.notifyListeners(); - return 0; - }); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(observer, observer.didAddProvider(provider, 0, container)); - verifyOnly(listener, listener(null, 0)); - verifyOnly(selfListener, selfListener(null, 0)); - }); - - test( - 'can be invoked during a re-initialization, and does not notify listeners', - () { - final observer = ProviderObserverMock(); - final listener = Listener(); - final selfListener = Listener(); - final container = createContainer(observers: [observer]); - var callNotifyListeners = false; - const firstValue = 'first'; - const secondValue = 'second'; - var result = firstValue; - final provider = Provider((ref) { - ref.listenSelf(selfListener.call); - if (callNotifyListeners) { - ref.notifyListeners(); - } - return result; - }); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly( - observer, - observer.didAddProvider(provider, firstValue, container), - ); - verifyOnly(selfListener, selfListener(null, firstValue)); - verifyOnly(listener, listener(null, firstValue)); - - result = secondValue; - callNotifyListeners = true; - container.refresh(provider); - - verifyOnly(selfListener, selfListener(firstValue, secondValue)); - verifyOnly(listener, listener(firstValue, secondValue)); - verify(observer.didDisposeProvider(provider, container)); - verify( - observer.didUpdateProvider( - provider, - firstValue, - secondValue, - container, - ), - ).called(1); - verifyNoMoreInteractions(observer); - }); - }); - - group('ref.refresh', () { - test('Throws if a circular dependency is detected', () { - // Regression test for https://github.com/rrousselGit/riverpod/issues/2336 - late Ref ref; - final a = Provider(name: 'a', (r) { - ref = r; - return 0; - }); - final b = Provider(name: 'b', (r) => r.watch(a)); - final container = createContainer(); - - container.read(b); - - expect( - () => ref.refresh(b), - throwsA(isA()), - ); - }); - }); - - group('ref.invalidate', () { - test('Throws if a circular dependency is detected', () { - // Regression test for https://github.com/rrousselGit/riverpod/issues/2336 - late Ref ref; - final a = Provider((r) { - ref = r; - return 0; - }); - final b = Provider((r) => r.watch(a)); - final container = createContainer(); - - container.read(b); - - expect( - () => ref.invalidate(b), - throwsA(isA()), - ); - }); - - test('Circular dependency ignores families', () { - late Ref ref; - final a = Provider((r) { - ref = r; - return 0; - }); - final b = Provider.family((r, id) => r.watch(a)); - final container = createContainer(); - - container.read(b(0)); - - expect( - () => ref.invalidate(b), - returnsNormally, - ); - }); - - test('triggers a rebuild on next frame', () async { - final container = createContainer(); - final listener = Listener(); - var result = 0; - final provider = Provider((r) => result); - late Ref ref; - final another = Provider((r) { - ref = r; - }); - - container.listen(provider, listener.call); - container.read(another); - verifyZeroInteractions(listener); - - ref.invalidate(provider); - ref.invalidate(provider); - result = 1; - - verifyZeroInteractions(listener); - - await container.pump(); - - verifyOnly(listener, listener(0, 1)); - }); - - group('on families', () { - test('recomputes providers associated with the family', () async { - final container = createContainer(); - final listener = Listener(); - final listener2 = Listener(); - final listener3 = Listener(); - var result = 0; - final unrelated = Provider((ref) => result); - final provider = Provider.family((r, i) => '$result-$i'); - late Ref ref; - final another = Provider((r) { - ref = r; - }); - - container.read(another); - - container.listen(provider(0), listener.call, fireImmediately: true); - container.listen(provider(1), listener2.call, fireImmediately: true); - container.listen(unrelated, listener3.call, fireImmediately: true); - - verifyOnly(listener, listener(null, '0-0')); - verifyOnly(listener2, listener2(null, '0-1')); - verifyOnly(listener3, listener3(null, 0)); - - ref.invalidate(provider); - ref.invalidate(provider); - result = 1; - - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - verifyNoMoreInteractions(listener3); - - await container.pump(); - - verifyOnly(listener, listener('0-0', '1-0')); - verifyOnly(listener2, listener2('0-1', '1-1')); - verifyNoMoreInteractions(listener3); - }); - - test('clears only on the closest family override', () async { - late Ref ref; - final another = Provider((r) { - ref = r; - }); - var result = 0; - final provider = Provider.family((r, i) => result); - final listener = Listener(); - final listener2 = Listener(); - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [provider, another], - ); - - container.read(another); - root.listen(provider(0), listener.call, fireImmediately: true); - container.listen(provider(1), listener2.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 0)); - verifyOnly(listener2, listener2(null, 0)); - - ref.invalidate(provider); - result = 1; - - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - await container.pump(); - - verifyOnly(listener2, listener2(0, 1)); - verifyNoMoreInteractions(listener); - }); - }); - }); - - group('ref.invalidateSelf', () { - test('calls dispose immediately', () { - final container = createContainer(); - final listener = OnDisposeMock(); - late Ref ref; - final provider = Provider((r) { - ref = r; - ref.onDispose(listener.call); - }); - - container.read(provider); - verifyZeroInteractions(listener); - - ref.invalidateSelf(); - - verifyOnly(listener, listener()); - - ref.invalidateSelf(); - - verifyNoMoreInteractions(listener); - }); - - test('triggers a rebuild on next frame', () async { - final container = createContainer(); - final listener = Listener(); - var result = 0; - late Ref ref; - final provider = Provider((r) { - ref = r; - return result; - }); - - container.listen(provider, listener.call); - verifyZeroInteractions(listener); - - ref.invalidateSelf(); - ref.invalidateSelf(); - result = 1; - - verifyZeroInteractions(listener); - - await container.pump(); - - verifyOnly(listener, listener(0, 1)); - }); - - test('merges the rebuild with dependency change rebuild', () async { - final container = createContainer(); - final listener = Listener(); - final dep = StateProvider((ref) => 0); - late Ref ref; - final provider = Provider((r) { - ref = r; - return ref.watch(dep); - }); - - container.listen(provider, listener.call); - verifyZeroInteractions(listener); - - ref.invalidateSelf(); - container.read(dep.notifier).state++; - - verifyZeroInteractions(listener); - - await container.pump(); - - verifyOnly(listener, listener(0, 1)); - }); - }); - - group('ref.onRemoveListener', () { - test('is not called on read', () { - final container = createContainer(); - final listener = OnRemoveListener(); - final provider = Provider((ref) { - ref.onRemoveListener(listener.call); - }); - - container.read(provider); - - verifyZeroInteractions(listener); - }); - - test('calls listeners when container.listen subscriptions are closed', () { - final container = createContainer(); - final listener = OnRemoveListener(); - final listener2 = OnRemoveListener(); - final provider = Provider((ref) { - ref.onRemoveListener(listener.call); - ref.onRemoveListener(listener2.call); - }); - - final sub = container.listen(provider, (previous, next) {}); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - sub.close(); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - final sub2 = container.listen(provider, (previous, next) {}); - - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - sub2.close(); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('calls listeners when ref.listen subscriptions are closed', () { - final container = createContainer(); - final listener = OnRemoveListener(); - final listener2 = OnRemoveListener(); - final dep = Provider( - name: 'dep', - (ref) { - ref.onRemoveListener(listener.call); - ref.onRemoveListener(listener2.call); - }, - ); - late Ref ref; - final provider = Provider( - name: 'provider', - (r) { - ref = r; - }, - ); - - // initialize ref - container.read(provider); - - final sub = ref.listen(dep, (previous, next) {}); - - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - sub.close(); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - final sub2 = ref.listen(dep, (previous, next) {}); - - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - sub2.close(); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('calls listeners when ref.watch subscriptions are removed', () { - final container = createContainer(); - final listener = OnRemoveListener(); - final listener2 = OnRemoveListener(); - final dep = Provider( - name: 'dep', - (ref) { - ref.onRemoveListener(listener.call); - ref.onRemoveListener(listener2.call); - }, - ); - late Ref ref; - final provider = Provider( - name: 'provider', - (r) => ref = r, - ); - - // initialize refs - container.read(provider); - - ref.watch(dep); - - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - container.refresh(provider); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('listeners are cleared on rebuild', () { - final container = createContainer(); - final listener = OnRemoveListener(); - final listener2 = OnRemoveListener(); - var isSecondBuild = false; - final provider = Provider((ref) { - if (isSecondBuild) { - ref.onRemoveListener(listener2.call); - } else { - ref.onRemoveListener(listener.call); - } - }); - - container.read(provider); - isSecondBuild = true; - container.refresh(provider); - - final sub = container.listen(provider, (previous, next) {}); - sub.close(); - - verify(listener2()).called(1); - verifyNoMoreInteractions(listener2); - verifyZeroInteractions(listener); - }); - - test('if a listener throws, still calls all listeners', () { - final errors = []; - final container = createContainer(); - final listener = OnRemoveListener(); - final listener2 = OnRemoveListener(); - when(listener()).thenThrow(42); - final provider = Provider((ref) { - ref.onRemoveListener(listener.call); - ref.onRemoveListener(listener2.call); - }); - - final sub = container.listen(provider, (prev, next) {}); - - runZonedGuarded( - sub.close, - (err, stack) => errors.add(err), - ); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - expect(errors, [42]); - }); - }); - - group('ref.onAddListener', () { - test('is not called on read', () { - final container = createContainer(); - final listener = OnAddListener(); - final provider = Provider((ref) { - ref.onAddListener(listener.call); - }); - - container.read(provider); - - verifyZeroInteractions(listener); - }); - - test('calls listeners when container.listen is invoked', () { - final container = createContainer(); - final listener = OnAddListener(); - final listener2 = OnAddListener(); - final provider = Provider((ref) { - ref.onAddListener(listener.call); - ref.onAddListener(listener2.call); - }); - - container.listen(provider, (previous, next) {}); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - container.listen(provider, (previous, next) {}); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('calls listeners when new ref.listen is invoked', () { - final container = createContainer(); - final listener = OnAddListener(); - final listener2 = OnAddListener(); - final dep = Provider(name: 'dep', (ref) { - ref.onAddListener(listener.call); - ref.onAddListener(listener2.call); - }); - late Ref ref; - final provider = Provider( - name: 'provider', - (r) => ref = r, - ); - - // initialize ref - container.read(provider); - - ref.listen(dep, (previous, next) {}); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - ref.listen(dep, (previous, next) {}); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('calls listeners when new ref.watch is invoked', () { - final container = createContainer(); - final listener = OnAddListener(); - final listener2 = OnAddListener(); - final dep = Provider( - name: 'dep', - (ref) { - ref.onAddListener(listener.call); - ref.onAddListener(listener2.call); - }, - ); - late Ref ref; - final provider = Provider( - name: 'provider', - (r) => ref = r, - ); - late Ref ref2; - final provider2 = Provider( - name: 'provider', - (r) => ref2 = r, - ); - - // initialize refs - container.read(provider); - container.read(provider2); - - ref.watch(dep); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - ref.watch(dep); - - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - ref2.watch(dep); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('listeners are cleared on rebuild', () { - final container = createContainer(); - final listener = OnAddListener(); - final listener2 = OnAddListener(); - var isSecondBuild = false; - final provider = Provider((ref) { - if (isSecondBuild) { - ref.onAddListener(listener2.call); - } else { - ref.onAddListener(listener.call); - } - }); - - container.read(provider); - isSecondBuild = true; - container.refresh(provider); - - container.listen(provider, (previous, next) {}); - - verify(listener2()).called(1); - verifyNoMoreInteractions(listener2); - verifyZeroInteractions(listener); - }); - - test('if a listener throws, still calls all listeners', () { - final errors = []; - final container = createContainer(); - final listener = OnAddListener(); - final listener2 = OnAddListener(); - when(listener()).thenThrow(42); - final provider = Provider((ref) { - ref.onAddListener(listener.call); - ref.onAddListener(listener2.call); - }); - - runZonedGuarded( - () => container.listen(provider, (prev, next) {}), - (err, stack) => errors.add(err), - ); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - expect(errors, [42]); - }); - }); - - group('ref.onResume', () { - test('is not called on initial subscription', () { - final container = createContainer(); - final listener = OnResume(); - final provider = Provider((ref) { - ref.onResume(listener.call); - }); - - container.read(provider); - container.listen(provider, (previous, next) {}); - - verifyZeroInteractions(listener); - }); - - test('calls listeners on the first new container.listen after a cancel', - () { - final container = createContainer(); - final listener = OnResume(); - final listener2 = OnResume(); - final provider = Provider((ref) { - ref.onResume(listener.call); - ref.onResume(listener2.call); - }); - - final sub = container.listen(provider, (previous, next) {}); - sub.close(); - - verifyZeroInteractions(listener); - verifyZeroInteractions(listener2); - - container.listen(provider, (previous, next) {}); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - container.listen(provider, (previous, next) {}); - - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('calls listeners on the first new ref.listen after a cancel', () { - final container = createContainer(); - final listener = OnResume(); - final listener2 = OnResume(); - final dep = Provider( - name: 'dep', - (ref) { - ref.onResume(listener.call); - ref.onResume(listener2.call); - }, - ); - late Ref ref; - final provider = Provider( - name: 'provider', - (r) => ref = r, - ); - - // initialize ref - container.read(provider); - - final sub = ref.listen(dep, (previous, next) {}); - sub.close(); - - verifyZeroInteractions(listener); - - ref.listen(dep, (previous, next) {}); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - - ref.listen(dep, (previous, next) {}); - - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('does not call listeners on read after a cancel', () { - final container = createContainer(); - final listener = OnResume(); - final provider = Provider((ref) { - ref.onResume(listener.call); - }); - - final sub = container.listen(provider, (previous, next) {}); - sub.close(); - - verifyZeroInteractions(listener); - - container.read(provider); - - verifyZeroInteractions(listener); - }); - - test('calls listeners when ref.watch is invoked after a cancel', () { - final container = createContainer(); - final listener = OnResume(); - final listener2 = OnResume(); - final dep = Provider( - name: 'dep', - (ref) { - ref.onAddListener(listener.call); - ref.onAddListener(listener2.call); - }, - ); - late Ref ref; - final provider = Provider( - name: 'provider', - (r) => ref = r, - ); - - // initialize refs - container.read(provider); - - final sub = container.listen(provider, (previous, next) {}); - sub.close(); - - verifyZeroInteractions(listener); - - ref.watch(dep); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('listeners are cleared on rebuild', () { - final container = createContainer(); - final listener = OnResume(); - final listener2 = OnResume(); - var isSecondBuild = false; - final provider = Provider((ref) { - if (isSecondBuild) { - ref.onResume(listener2.call); - } else { - ref.onResume(listener.call); - } - }); - - container.read(provider); - isSecondBuild = true; - container.refresh(provider); - - final sub = container.listen(provider, (previous, next) {}); - sub.close(); - - verifyZeroInteractions(listener); - verifyZeroInteractions(listener2); - - container.listen(provider, (previous, next) {}); - - verify(listener2()).called(1); - verifyNoMoreInteractions(listener2); - verifyZeroInteractions(listener); - }); - - test('internal resume status is cleared on rebuild', () { - final container = createContainer(); - final listener = OnResume(); - final provider = Provider((ref) { - ref.onResume(listener.call); - }); - - final sub = container.listen(provider, (previous, next) {}); - sub.close(); - - container.refresh(provider); - - final sub2 = container.listen(provider, (previous, next) {}); - sub2.close(); - - verifyZeroInteractions(listener); - - container.listen(provider, (previous, next) {}); - - verifyOnly(listener, listener()); - }); - - test('if a listener throws, still calls all listeners', () { - final errors = []; - final container = createContainer(); - final listener = OnResume(); - final listener2 = OnResume(); - when(listener()).thenThrow(42); - final provider = Provider((ref) { - ref.onResume(listener.call); - ref.onResume(listener2.call); - }); - - final sub = container.listen(provider, (previous, next) {}); - sub.close(); - - verifyZeroInteractions(listener); - verifyZeroInteractions(listener2); - - runZonedGuarded( - () => container.listen(provider, (prev, next) {}), - (err, stack) => errors.add(err), - ); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - expect(errors, [42]); - }); - }); - - group('ref.onCancel', () { - test( - 'is called when dependent is invalidated and was the only listener', - skip: 'Waiting for "clear dependencies after futureprovider rebuilds"', - () async { - // - final container = createContainer(); - final onCancel = OnCancelMock(); - final dep = StateProvider((ref) { - ref.onCancel(onCancel.call); - return 0; - }); - final provider = Provider.autoDispose((ref) => ref.watch(dep)); - - container.read(provider); - - verifyZeroInteractions(onCancel); - - container.read(dep.notifier).state++; - - verify(onCancel()).called(1); - - await container.pump(); - - verifyNoMoreInteractions(onCancel); - }, - ); - - test('is called when all container listeners are removed', () { - final container = createContainer(); - final listener = OnCancelMock(); - final listener2 = OnCancelMock(); - final provider = Provider((ref) { - ref.onCancel(listener.call); - ref.onCancel(listener2.call); - }); - - final sub = container.listen(provider, (previous, next) {}); - final sub2 = container.listen(provider, (previous, next) {}); - - verifyZeroInteractions(listener); - verifyZeroInteractions(listener2); - - sub.close(); - - verifyZeroInteractions(listener2); - - sub2.close(); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('is called when all provider listeners are removed', () { - final container = createContainer(); - final listener = OnCancelMock(); - final listener2 = OnCancelMock(); - final dep = Provider((ref) { - ref.onCancel(listener.call); - ref.onCancel(listener2.call); - }); - late Ref ref; - final provider = Provider((r) { - ref = r; - }); - - container.read(provider); - final sub = ref.listen(dep, (previous, next) {}); - final sub2 = ref.listen(dep, (previous, next) {}); - - verifyZeroInteractions(listener); - verifyZeroInteractions(listener2); - - sub.close(); - - verifyZeroInteractions(listener2); - - sub2.close(); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('is called when all provider dependencies are removed', () { - final container = createContainer(); - final listener = OnCancelMock(); - final listener2 = OnCancelMock(); - final dep = Provider((ref) { - ref.onCancel(listener.call); - ref.onCancel(listener2.call); - }); - var watching = true; - final provider = Provider((ref) { - if (watching) ref.watch(dep); - }); - final provider2 = Provider((ref) { - if (watching) ref.watch(dep); - }); - - container.read(provider); - container.read(provider2); - - verifyZeroInteractions(listener); - verifyZeroInteractions(listener2); - - watching = false; - // remove the dependency provider<>dep - container.refresh(provider); - - verifyZeroInteractions(listener2); - - // remove the dependency provider2<>dep - container.refresh(provider2); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('is not called when using container.read', () async { - final container = createContainer(); - final listener = OnCancelMock(); - final provider = Provider((ref) { - ref.onCancel(listener.call); - }); - - container.read(provider); - await container.pump(); - - verifyZeroInteractions(listener); - }); - - test( - 'is not called when using container.read (autoDispose)', - skip: true, - () async { - final container = createContainer(); - final listener = OnCancelMock(); - final dispose = OnDisposeMock(); - final provider = StateProvider.autoDispose((ref) { - ref.keepAlive(); - ref.onCancel(listener.call); - ref.onDispose(dispose.call); - }); - - container.read(provider); - await container.pump(); - - verifyZeroInteractions(listener); - verifyZeroInteractions(dispose); - }, - ); - - test('listeners are cleared on rebuild', () { - final container = createContainer(); - final listener = OnCancelMock(); - final listener2 = OnCancelMock(); - var isSecondBuild = false; - final provider = Provider((ref) { - if (isSecondBuild) { - ref.onCancel(listener2.call); - } else { - ref.onCancel(listener.call); - } - }); - - container.read(provider); - isSecondBuild = true; - container.refresh(provider); - - verifyZeroInteractions(listener); - verifyZeroInteractions(listener2); - - final sub = container.listen(provider, (previous, next) {}); - - verifyZeroInteractions(listener); - verifyZeroInteractions(listener2); - - sub.close(); - - verify(listener2()).called(1); - verifyNoMoreInteractions(listener2); - verifyZeroInteractions(listener); - }); - - test('if a listener throws, still calls all listeners', () { - final errors = []; - final container = createContainer(); - final listener = OnCancelMock(); - final listener2 = OnCancelMock(); - when(listener()).thenThrow(42); - final provider = Provider((ref) { - ref.onCancel(listener.call); - ref.onCancel(listener2.call); - }); - - final sub = container.listen(provider, (previous, next) {}); - - verifyZeroInteractions(listener); - verifyZeroInteractions(listener2); - - runZonedGuarded( - sub.close, - (err, stack) => errors.add(err), - ); - - verifyInOrder([listener(), listener2()]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - expect(errors, [42]); - }); - }); - - test( - 'onDispose is triggered only once if within autoDispose unmount, a dependency changed', - () async { - // regression test for https://github.com/rrousselGit/riverpod/issues/1064 - final container = createContainer(); - final onDispose = OnDisposeMock(); - final dep = StateProvider((ref) => 0); - final provider = Provider.autoDispose((ref) { - ref.watch(dep); - ref.onDispose(onDispose.call); - }); - - when(onDispose()).thenAnswer((realInvocation) { - container.read(dep.notifier).state++; - }); - - container.read(provider); - verifyZeroInteractions(onDispose); - - // cause provider to be disposed - await container.pump(); - - verify(onDispose()).called(1); - verifyNoMoreInteractions(onDispose); - }); - - test( - 'does not throw outdated error when a dependency is flushed while the dependent is building', - () async { - final container = createContainer(); - final a = StateProvider((ref) => 0); - - final dep = Provider((ref) { - return ref.watch(a) + 10; - }); - final dependent = Provider((ref) { - if (ref.watch(a) > 0) { - ref.watch(dep); - // Voluntarily using "watch" twice. - // When `dep` is flushed, it could cause subsequent "watch" calls to throw - // because `dependent` is considered as outdated - return ref.watch(dep); - } - return null; - }); - final listener = Listener(); - - expect(container.read(dep), 10); - container.listen(dependent, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, null)); - - // schedules `dep` and `dependent` to rebuild - // Will build `dependent` before `dep` because `dependent` doesn't depend on `dep` yet - // And since nothing is watchin `dep` at the moment, then `dependent` will - // rebuild before `dep` even though `dep` is its ancestor. - // This is fine since nothing is listening to `dep` yet, but it should - // not cause certain assertions to trigger - container.read(a.notifier).state++; - await container.pump(); - - verifyOnly(listener, listener(null, 11)); - }); - - group('getState', () { - test('throws on providers that threw', () { - final container = createContainer(); - final provider = Provider((ref) => throw UnimplementedError()); - - final element = container.readProviderElement(provider); - - expect( - element.getState(), - isA>() - .having((e) => e.error, 'error', isUnimplementedError), - ); - }); - }); - - group('readSelf', () { - test('throws on providers that threw', () { - final container = createContainer(); - final provider = Provider((ref) => throw UnimplementedError()); - - final element = container.readProviderElement(provider); - - expect( - element.readSelf, - throwsUnimplementedError, - ); - }); - }); - - group('visitChildren', () { - test('includes ref.watch dependents', () { - final container = createContainer(); - final provider = Provider((ref) => 0); - final dependent = Provider((ref) { - ref.watch(provider); - }); - final dependent2 = Provider((ref) { - ref.watch(provider); - }); - - container.read(dependent); - container.read(dependent2); - - final children = >[]; - - container - .readProviderElement(provider) - .visitChildren(elementVisitor: children.add, notifierVisitor: (_) {}); - expect( - children, - unorderedMatches([ - isA>() - .having((e) => e.provider, 'provider', dependent), - isA>() - .having((e) => e.provider, 'provider', dependent2), - ]), - ); - }); - - test('includes ref.listen dependents', () { - final container = createContainer(); - final provider = Provider((ref) => 0); - final dependent = Provider((ref) { - ref.listen(provider, (_, __) {}); - }); - final dependent2 = Provider((ref) { - ref.listen(provider, (_, __) {}); - }); - - container.read(dependent); - container.read(dependent2); - - final children = >[]; - - container - .readProviderElement(provider) - .visitChildren(elementVisitor: children.add, notifierVisitor: (_) {}); - expect( - children, - unorderedMatches([ - isA>() - .having((e) => e.provider, 'provider', dependent), - isA>() - .having((e) => e.provider, 'provider', dependent2), - ]), - ); - }); - - test('include ref.read dependents', () {}, skip: true); - }); - - group('hasListeners', () { - test('includes provider listeners', () async { - final provider = Provider((ref) => 0); - final dep = Provider((ref) { - ref.listen(provider, (prev, value) {}); - }); - final container = createContainer(); - - expect(container.readProviderElement(provider).hasListeners, false); - - container.read(dep); - - expect(container.readProviderElement(provider).hasListeners, true); - }); - - test('includes provider dependents', () async { - final provider = Provider((ref) => 0); - final dep = Provider((ref) { - ref.watch(provider); - }); - final container = createContainer(); - - expect(container.readProviderElement(provider).hasListeners, false); - - container.read(dep); - - expect(container.readProviderElement(provider).hasListeners, true); - }); - - test('includes container listeners', () async { - final provider = Provider((ref) => 0); - final container = createContainer(); - - expect(container.readProviderElement(provider).hasListeners, false); - - container.listen(provider, (_, __) {}); - - expect(container.readProviderElement(provider).hasListeners, true); - }); - }); - - test('does not notify listeners when rebuilding the state', () async { - final container = createContainer(); - final listener = Listener(); - - final dep = StateProvider((ref) => 0); - final provider = Provider((ref) { - ref.watch(dep); - return ref.state = 0; - }); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 0)); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyNoMoreInteractions(listener); - }); -} diff --git a/packages/riverpod/test/framework/ref_test.dart b/packages/riverpod/test/framework/ref_test.dart deleted file mode 100644 index 1ec8938b6..000000000 --- a/packages/riverpod/test/framework/ref_test.dart +++ /dev/null @@ -1,428 +0,0 @@ -import 'package:mockito/mockito.dart'; -import 'package:riverpod/riverpod.dart'; -import 'package:test/test.dart'; - -import '../utils.dart'; - -void main() { - group('Ref', () { - group('invalidateSelf', () { - test('can disposes of the element if not used anymore', () async { - late Ref ref; - final provider = Provider.autoDispose((r) { - ref = r; - r.keepAlive(); - return 0; - }); - final container = createContainer(); - - container.read(provider); - ref.invalidateSelf(); - - await container.pump(); - - expect(container.getAllProviderElements(), isEmpty); - }); - }); - - group('invalidate', () { - test('can disposes of the element if not used anymore', () async { - late Ref ref; - final dep = Provider((r) { - ref = r; - return 0; - }); - final provider = Provider.autoDispose((r) { - r.keepAlive(); - return 0; - }); - final container = createContainer(); - - container.read(dep); - container.read(provider); - ref.invalidate(provider); - - await container.pump(); - - expect( - container.getAllProviderElements().map((e) => e.origin), - [dep], - ); - }); - }); - - test( - 'cannot call ref.watch/ref.read/ref.listen/ref.onDispose after a dependency changed', - () { - late Ref ref; - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final provider = Provider((r) { - r.watch(dep); - ref = r; - }); - - container.read(provider); - - container.read(dep.notifier).state++; - - final another = Provider((ref) => 0); - - expect( - () => ref.watch(another), - throwsA(isA()), - ); - expect( - () => ref.refresh(another), - throwsA(isA()), - ); - expect( - () => ref.read(another), - throwsA(isA()), - ); - expect( - () => ref.onDispose(() {}), - throwsA(isA()), - ); - expect( - () => ref.listen(another, (_, __) {}), - throwsA(isA()), - ); - }, - ); - - group('refresh', () { - test('refreshes a provider and return the new state', () { - var value = 0; - final state = Provider((ref) => value); - late Ref ref; - final provider = Provider((r) { - ref = r; - }); - final container = createContainer(); - - container.read(provider); - - expect(container.read(state), 0); - - value = 42; - expect(ref.refresh(state), 42); - expect(container.read(state), 42); - }); - }); - - group('listen', () { - test('ref.listen on outdated provider causes it to rebuild', () { - final dep = StateProvider((ref) => 0); - var buildCount = 0; - final provider = Provider((ref) { - buildCount++; - return ref.watch(dep); - }); - final listener = Listener(); - final another = Provider((ref) { - ref.listen(provider, listener.call, fireImmediately: true); - }); - final container = createContainer(); - - expect(container.read(provider), 0); - expect(buildCount, 1); - - container.read(dep.notifier).state = 42; - - expect(buildCount, 1); - - container.read(another); - - expect(buildCount, 2); - verifyOnly(listener, listener(null, 42)); - }); - - test('can downcast the value', () async { - final listener = Listener(); - final dep = StateProvider((ref) => 0); - final provider = Provider((ref) { - ref.listen(dep, listener.call); - }); - - final container = createContainer(); - container.read(provider); - - verifyZeroInteractions(listener); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyOnly(listener, listener(0, 1)); - }); - - test('can listen selectors', () async { - final container = createContainer(); - final provider = - StateNotifierProvider, int>((ref) { - return StateController(0); - }); - final isEvenSelector = Selector(false, (c) => c.isEven); - final isEvenListener = Listener(); - var buildCount = 0; - - final another = Provider((ref) { - buildCount++; - ref.listen( - provider.select(isEvenSelector.call), - isEvenListener.call, - ); - return 0; - }); - - container.read(another); - - expect(buildCount, 1); - verifyZeroInteractions(isEvenListener); - verifyOnly(isEvenSelector, isEvenSelector(0)); - - container.read(provider.notifier).state = 2; - - verifyOnly(isEvenSelector, isEvenSelector(2)); - verifyZeroInteractions(isEvenListener); - - container.read(provider.notifier).state = 3; - - verifyOnly(isEvenSelector, isEvenSelector(3)); - verifyOnly(isEvenListener, isEvenListener(true, false)); - - container.read(provider.notifier).state = 4; - - verifyOnly(isEvenSelector, isEvenSelector(4)); - verifyOnly(isEvenListener, isEvenListener(false, true)); - - await container.pump(); - - expect(buildCount, 1); - }); - - test('listen on selectors supports fireImmediately', () async { - final container = createContainer(); - final provider = - StateNotifierProvider, int>((ref) { - return StateController(0); - }); - final isEvenSelector = Selector(false, (c) => c.isEven); - final isEvenListener = Listener(); - var buildCount = 0; - - final another = Provider((ref) { - buildCount++; - ref.listen( - provider.select(isEvenSelector.call), - isEvenListener.call, - fireImmediately: true, - ); - return 0; - }); - - container.read(another); - - expect(buildCount, 1); - verifyOnly(isEvenListener, isEvenListener(null, true)); - verifyOnly(isEvenSelector, isEvenSelector(0)); - - container.read(provider.notifier).state = 2; - - verifyOnly(isEvenSelector, isEvenSelector(2)); - verifyNoMoreInteractions(isEvenListener); - - container.read(provider.notifier).state = 3; - - verifyOnly(isEvenSelector, isEvenSelector(3)); - verifyOnly(isEvenListener, isEvenListener(true, false)); - - await container.pump(); - - expect(buildCount, 1); - }); - }); - - group('.onDispose', () { - test( - 'calls all the listeners in order when the ProviderContainer is disposed', - () { - final onDispose = OnDisposeMock(); - final onDispose2 = OnDisposeMock(); - final provider = Provider((ref) { - ref.onDispose(onDispose.call); - ref.onDispose(onDispose2.call); - }); - - final container = ProviderContainer(); - addTearDown(container.dispose); - - container.read(provider); // register the onDispose hooks - - verifyZeroInteractions(onDispose); - verifyZeroInteractions(onDispose2); - - container.dispose(); - - verifyInOrder([ - onDispose(), - onDispose2(), - ]); - verifyNoMoreInteractions(onDispose); - verifyNoMoreInteractions(onDispose2); - }); - - test('calls all listeners in order when one of its dependency changed', - () async { - final onDispose = OnDisposeMock(); - final onDispose2 = OnDisposeMock(); - - final count = StateProvider((ref) => 0); - final provider = Provider((ref) { - ref.watch(count); - ref.onDispose(onDispose.call); - ref.onDispose(onDispose2.call); - }); - - final container = ProviderContainer(); - addTearDown(container.dispose); - - container.read(provider); // register the onDispose hooks - - verifyZeroInteractions(onDispose); - verifyZeroInteractions(onDispose2); - - container.read(count.notifier).state++; - await container.pump(); - - verifyInOrder([ - onDispose(), - onDispose2(), - ]); - verifyNoMoreInteractions(onDispose); - verifyNoMoreInteractions(onDispose2); - }); - - test('does not call listeners again if more than one dependency changed', - () { - final onDispose = OnDisposeMock(); - - final count = StateProvider((ref) => 0); - final count2 = StateProvider((ref) => 0); - final provider = Provider((ref) { - ref.watch(count); - ref.watch(count2); - ref.onDispose(onDispose.call); - }); - - final container = ProviderContainer(); - addTearDown(container.dispose); - - container.read(provider); // register the onDispose hooks - - verifyZeroInteractions(onDispose); - - container.read(count.notifier).state++; - container.read(count2.notifier).state++; - - verifyOnly(onDispose, onDispose()); - }); - - test( - 'does not call listeners again if a dependency changed then ProviderContainer was disposed', - () async { - final onDispose = OnDisposeMock(); - var buildCount = 0; - - final count = StateProvider((ref) => 0); - final provider = Provider((ref) { - buildCount++; - ref.watch(count); - ref.onDispose(onDispose.call); - }); - - final container = ProviderContainer(); - addTearDown(container.dispose); - - container.read(provider); // register the onDispose hooks - expect(buildCount, 1); - - verifyZeroInteractions(onDispose); - - container.read(count.notifier).state++; - // no pump() because that would rebuild the provider, which means it would - // need to be disposed once again. - - verifyOnly(onDispose, onDispose()); - - container.dispose(); - - expect(buildCount, 1); - verifyNoMoreInteractions(onDispose); - }); - - test( - 'once a provider was disposed, cannot add more listeners until it is rebuilt', - () {}, - skip: 'TODO', - ); - }); - - group('mounted', () { - test('is false during onDispose caused by ref.watch', () { - final container = createContainer(); - bool? mounted; - late ProviderElementBase element; - final dep = StateProvider((ref) => 0); - final provider = Provider((ref) { - ref.watch(dep); - element = ref as ProviderElementBase; - ref.onDispose(() => mounted = element.mounted); - }); - - container.read(provider); - expect(mounted, null); - - container.read(dep.notifier).state++; - - expect(mounted, false); - }); - - test('is false during onDispose caused by container dispose', () { - final container = createContainer(); - bool? mounted; - late ProviderElementBase element; - final dep = StateProvider((ref) => 0); - final provider = Provider((ref) { - ref.watch(dep); - element = ref as ProviderElementBase; - ref.onDispose(() => mounted = element.mounted); - }); - - container.read(provider); - expect(mounted, null); - - container.dispose(); - - expect(mounted, false); - }); - - test('is false in between rebuilds', () { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - late ProviderElementBase element; - final provider = Provider((ref) { - ref.watch(dep); - element = ref as ProviderElementBase; - }); - - container.read(provider); - expect(element.mounted, true); - - container.read(dep.notifier).state++; - - expect(element.mounted, false); - }); - }); - }); -} diff --git a/packages/riverpod/test/framework/result_test.dart b/packages/riverpod/test/framework/result_test.dart deleted file mode 100644 index a3b9d357f..000000000 --- a/packages/riverpod/test/framework/result_test.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:riverpod/src/result.dart'; -import 'package:test/test.dart'; - -void main() { - group('Result.data', () { - test('implements hashCode/==', () { - expect(Result.data(42), Result.data(42)); - expect(Result.data(42), isNot(Result.data(21))); - - expect(Result.data(42).hashCode, Result.data(42).hashCode); - expect(Result.data(42).hashCode, isNot(Result.data(21).hashCode)); - }); - }); - - group('Result.error', () { - test('implements hashCode/==', () { - expect( - Result.error(42, StackTrace.empty), - Result.error(42, StackTrace.empty), - ); - expect( - Result.error(42, StackTrace.empty), - isNot(Result.error(21, StackTrace.empty)), - ); - - expect( - Result.error(42, StackTrace.empty).hashCode, - Result.error(42, StackTrace.empty).hashCode, - ); - expect( - Result.error(42, StackTrace.empty).hashCode, - isNot(Result.error(21, StackTrace.empty).hashCode), - ); - }); - }); -} diff --git a/packages/riverpod/test/legacy/devtool_test.dart b/packages/riverpod/test/legacy/devtool_test.dart deleted file mode 100644 index 771ab9549..000000000 --- a/packages/riverpod/test/legacy/devtool_test.dart +++ /dev/null @@ -1,175 +0,0 @@ -// import 'package:riverpod/riverpod.dart'; -// import 'package:riverpod/src/provider.dart'; -// import 'package:test/test.dart'; - -// import '../matchers.dart'; - -void main() { - // late PostEventSpy spy; - - // setUp(() { - // spy = spyPostEvent(); - // }); - - // tearDown(() => spy.dispose()); - - // test('calls postEvent whenever a provider is updated', () { - // expect(RiverpodBinding.debugInstance.containers, isEmpty); - // expect(spy.logs, isEmpty); - - // final container = ProviderContainer(); - // addTearDown(container.dispose); - - // final provider = StateProvider((ref) => 42); - - // final state = container.read(provider); - // spy.logs.clear(); - - // expect(spy.logs, isEmpty); - - // state.state++; - - // expect( - // spy.logs, - // [ - // isPostEventCall('riverpod:provider_changed', { - // 'container_id': container.debugId, - // 'provider_id': provider.debugId, - // }), - // ], - // ); - // spy.logs.clear(); - // }); - - // test('RiverpodBinding contains the list of ProviderContainers', () { - // expect(RiverpodBinding.debugInstance.containers, isEmpty); - // expect(spy.logs, isEmpty); - - // final first = ProviderContainer(); - - // expect( - // spy.logs, - // [isPostEventCall('riverpod:container_list_changed', isEmpty)], - // ); - // spy.logs.clear(); - // expect( - // RiverpodBinding.debugInstance.containers, - // {first.debugId: first}, - // ); - - // final second = ProviderContainer(); - - // expect( - // spy.logs, - // [isPostEventCall('riverpod:container_list_changed', isEmpty)], - // ); - // spy.logs.clear(); - // expect( - // RiverpodBinding.debugInstance.containers, - // {first.debugId: first, second.debugId: second}, - // ); - - // first.dispose(); - - // expect( - // spy.logs, - // [isPostEventCall('riverpod:container_list_changed', isEmpty)], - // ); - // spy.logs.clear(); - // expect( - // RiverpodBinding.debugInstance.containers, - // {second.debugId: second}, - // ); - - // second.dispose(); - - // expect( - // spy.logs, - // [isPostEventCall('riverpod:container_list_changed', isEmpty)], - // ); - // spy.logs.clear(); - // expect(RiverpodBinding.debugInstance.containers, isEmpty); - // }); - - // test( - // 'ProviderContainer calls postEvent whenever it mounts/unmount a provider', - // () async { - // final container = ProviderContainer(); - // addTearDown(container.dispose); - // spy.logs.clear(); - - // final provider = Provider.autoDispose((ref) => 0); - // final provider2 = Provider((ref) => 0); - - // expect(spy.logs, isEmpty); - - // var sub = container.listen(provider, (_) {}); - - // expect( - // spy.logs, - // [ - // isPostEventCall( - // 'riverpod:provider_list_changed', - // {'container_id': container.debugId}, - // ) - // ], - // ); - // spy.logs.clear(); - - // var sub2 = container.listen(provider2, (_) {}); - - // expect( - // spy.logs, - // [ - // isPostEventCall( - // 'riverpod:provider_list_changed', - // {'container_id': container.debugId}, - // ) - // ], - // ); - // spy.logs.clear(); - - // sub.close(); - - // expect(spy.logs, isEmpty); - // await Future.value(null); - - // expect( - // spy.logs, - // [ - // isPostEventCall( - // 'riverpod:provider_list_changed', - // {'container_id': container.debugId}, - // ) - // ], - // ); - // spy.logs.clear(); - - // sub2.close(); - // await Future.value(null); - - // expect(spy.logs, isEmpty); - // await Future.value(null); - - // expect(spy.logs, isEmpty, reason: 'provider2 is not autoDispose'); - - // // re-subscribe to the provider that was unmounted - // sub = container.listen(provider, (_) {}); - - // expect( - // spy.logs, - // [ - // isPostEventCall( - // 'riverpod:provider_list_changed', - // {'container_id': container.debugId}, - // ) - // ], - // ); - // spy.logs.clear(); - - // // re-subscribe to the provider that was no longer listened to but still mounted - // sub2 = container.listen(provider2, (_) {}); - - // expect(spy.logs, isEmpty); - // }); -} diff --git a/packages/riverpod/test/meta_test.dart b/packages/riverpod/test/meta_test.dart new file mode 100644 index 000000000..ccc67210c --- /dev/null +++ b/packages/riverpod/test/meta_test.dart @@ -0,0 +1,272 @@ +import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/dart/analysis/utilities.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:analyzer/dart/element/visitor.dart'; +import 'package:path/path.dart' as path; +import 'package:test/test.dart'; + +void main() { + // TODO assert all constructor parameters point to a field with dartdoc. + + // This verifies that: + // - All public APIs are documented + // - public APIs have no unexported types + // - {@template} are not duplicated + // - all {@template} are used + // - all {@macro} have an associated {@template} + + late final LibraryElement riverpod; + late final _PublicAPIVisitor visitor; + + setUpAll(() async { + const file = './example/lib/main.dart'; + final absolute = path.normalize(path.absolute(file)); + + final result = await resolveFile2(path: absolute); + result as ResolvedUnitResult; + + riverpod = result.libraryElement.importedLibraries.firstWhere( + (e) => e.source.uri.toString() == 'package:riverpod/riverpod.dart', + ); + visitor = _PublicAPIVisitor(riverpod); + + riverpod.accept(visitor); + for (final publicApi in riverpod.exportNamespace.definedNames.values) { + publicApi.accept(visitor); + } + }); + + test('overrides re-apply annotations', () async { + expect(visitor.missingInheritedAnnotations, isEmpty); + }); + + test('public API snapshot', skip: 'Disabled', () async { + // TODO consider other imports + expect(riverpod.exportNamespace.definedNames.keys, [ + 'AsyncValue', + 'AsyncData', + 'AsyncLoading', + 'AsyncError', + 'ProviderBase', + 'NotifierBase', + 'Refreshable', + 'ProviderContainer', + 'ProviderObserver', + 'Family', + 'ProviderSubscription', + 'ProviderListenableOrFamily', + 'ProviderOrFamily', + 'ProviderListenable', + 'Ref', + 'KeepAliveLink', + 'Override', + 'AsyncNotifier', + 'AsyncNotifierProvider', + 'FamilyAsyncNotifier', + 'FutureProvider', + 'Notifier', + 'NotifierProvider', + 'FamilyNotifier', + 'Provider', + 'FamilyStreamNotifier', + 'StreamNotifier', + 'StreamNotifierProvider', + 'StreamProvider', + ]); + + expect(visitor.undocumentedElements, isEmpty); + + expect(visitor.duplicateTemplates, isEmpty, reason: 'Duplicate templates'); + for (final template in visitor.templates) { + expect(visitor.macros, contains(template), reason: 'Unused template'); + } + for (final macro in visitor.macros) { + expect(visitor.templates, contains(macro), reason: 'Missing template'); + } + }); + + test('public API does not contain unexported elements', skip: 'Disabled', () { + expect(visitor.unexportedElements, isEmpty); + }); + + test('all public APIs are documented', skip: 'Disabled', () { + expect(visitor.undocumentedElements, isEmpty); + }); + + test('all templates are used', skip: 'Disabled', () { + expect(visitor.duplicateTemplates, isEmpty, reason: 'Duplicate templates'); + for (final template in visitor.templates) { + expect(visitor.macros, contains(template), reason: 'Unused template'); + } + for (final macro in visitor.macros) { + expect(visitor.templates, contains(macro), reason: 'Missing template'); + } + }); +} + +class _PublicAPIVisitor extends GeneralizingElementVisitor { + _PublicAPIVisitor(this.riverpod); + + final LibraryElement riverpod; + final missingInheritedAnnotations = {}; + final unexportedElements = {}; + final undocumentedElements = {}; + + final templates = {}; + final duplicateTemplates = {}; + final macros = {}; + + void _verifyTypeIsExported(DartType type, VariableElement element) { + if (type is TypeParameterType) { + _verifyTypeIsExported(type.bound, element); + return; + } + + if (type is FunctionType) { + _verifyTypeIsExported(type.returnType, element); + for (final parameter in type.parameters) { + _verifyTypeIsExported(parameter.type, element); + } + return; + } + + if (type.isCore) return; + + final key = type.element?.name ?? ''; + if (!riverpod.exportNamespace.definedNames.containsKey(key)) { + unexportedElements.add('${element.location}#$key'); + } + } + + void _verifyHasDocs(Element element) { + if (element.documentationComment?.isNotEmpty != true) { + undocumentedElements.add('${element.location}'); + } + } + + bool _isPublicApi(Element element) { + if (element.isPrivate) return false; + // Is part of an @internal element + if (element.thisOrAncestorMatching((e) => e.hasInternal) != null) { + return false; + } + + return true; + } + + @override + void visitElement(Element element) { + super.visitElement(element); + + if (_isPublicApi(element)) { + _verifyInheritsAnnotations(element); + _verifyHasDocs(element); + _parseTemplatesAndMacros(element); + } + } + + @override + void visitClassElement(ClassElement element) { + super.visitClassElement(element); + + // Verify that inherited members also respect public API constraints + for (final superType in element.allSupertypes) { + visitElement(element); + } + } + + @override + void visitVariableElement(VariableElement element) { + super.visitVariableElement(element); + + _verifyTypeIsExported(element.type, element); + } + + void _verifyInheritsAnnotations(Element element) { + final parent = element.enclosingElement3; + + if (parent is! ClassElement) return; + + final overrides = parent.allSupertypes + .map((e) { + if (element is MethodElement) { + return e.getMethod(element.name); + } else if (element is PropertyAccessorElement && element.isGetter) { + return e.getGetter(element.name); + } else if (element is PropertyAccessorElement && element.isSetter) { + return e.getSetter(element.name); + } + }) + .nonNulls + .toList(); + + if (overrides.isEmpty) return; + + for (final override in overrides) { + if ((!element.hasInternal && override.hasInternal) || + (!element.hasProtected && override.hasProtected) || + (!element.hasVisibleForOverriding && + override.hasVisibleForOverriding) || + (!element.hasVisibleForTesting && override.hasVisibleForTesting)) { + missingInheritedAnnotations.add( + '${element.location} vs ${override.location}\n' + '${element.metadata} vs ${override.metadata}', + ); + } + } + } + + void _parseTemplatesAndMacros(Element element) { + final docs = element.documentationComment; + if (docs == null) return; + + final regExp = RegExp(r'{@(\w+) (\S+)}', multiLine: true); + for (final match in regExp.allMatches(docs)) { + final type = match.group(1)!; + final name = match.group(2)!; + + if (type == 'template') { + if (!templates.add(name)) { + duplicateTemplates.add(name); + } + } else if (type == 'macro') { + macros.add(name); + } + } + } +} + +extension on DartType { + /// If it is from the Dart SDK + bool get isCore { + if (this is DynamicType || + this is VoidType || + isDartAsyncFuture || + isDartAsyncFutureOr || + isDartAsyncStream || + isDartCoreBool || + isDartCoreDouble || + isDartCoreEnum || + isDartCoreFunction || + isDartCoreInt || + isDartCoreIterable || + isDartCoreList || + isDartCoreMap || + isDartCoreNull || + isDartCoreNum || + isDartCoreObject || + isDartCoreRecord || + isDartCoreSet || + isDartCoreString || + isDartCoreSymbol || + isDartCoreType) { + return true; + } + + final element = this.element; + if (element == null) return false; + + return element.librarySource?.uri.toString().startsWith('dart:') ?? false; + } +} diff --git a/packages/riverpod/test/old/framework/auto_dispose_test.dart b/packages/riverpod/test/old/framework/auto_dispose_test.dart new file mode 100644 index 000000000..c8f70ae79 --- /dev/null +++ b/packages/riverpod/test/old/framework/auto_dispose_test.dart @@ -0,0 +1,340 @@ +import 'package:mockito/mockito.dart'; +import 'package:riverpod/src/internals.dart'; +import 'package:test/test.dart'; + +import '../utils.dart'; + +Future main() async { + test( + 'when a provider conditionally depends on another provider, rebuilding without the dependency can dispose the dependency', + () async { + final container = ProviderContainer.test(); + var dependencyDisposeCount = 0; + final dependency = Provider.autoDispose( + name: 'dependency', + (ref) { + ref.onDispose(() => dependencyDisposeCount++); + return 0; + }, + ); + final isDependingOnDependency = StateProvider( + name: 'isDependingOnDependency', + (ref) => true, + ); + final provider = Provider.autoDispose( + name: 'provider', + (ref) { + if (ref.watch(isDependingOnDependency)) { + ref.watch(dependency); + } + }, + ); + + container.listen(provider, (_, __) {}); + + expect(dependencyDisposeCount, 0); + expect( + container.getAllProviderElements().map((e) => e.provider), + unorderedEquals([ + dependency, + provider, + isDependingOnDependency, + ]), + ); + + container.read(isDependingOnDependency.notifier).state = false; + await container.pump(); + + expect(dependencyDisposeCount, 1); + expect( + container.getAllProviderElements().map((e) => e.provider), + unorderedEquals([ + provider, + isDependingOnDependency, + ]), + ); + }); + + test('works if used across a ProviderContainer', () async { + var value = 0; + var buildCount = 0; + var disposeCount = 0; + final listener = Listener(); + final provider = Provider.autoDispose((ref) { + buildCount++; + ref.onDispose(() => disposeCount++); + return value; + }); + + final root = ProviderContainer.test(); + final container = ProviderContainer.test(parent: root); + + final sub = container.listen( + provider, + listener.call, + fireImmediately: true, + ); + + verifyOnly(listener, listener(null, 0)); + expect(buildCount, 1); + expect(disposeCount, 0); + + sub.close(); + await container.pump(); + + expect(buildCount, 1); + expect(disposeCount, 1); + verifyNoMoreInteractions(listener); + expect(root.getAllProviderElements(), isEmpty); + expect(container.getAllProviderElements(), isEmpty); + + value = 42; + container.listen(provider, listener.call, fireImmediately: true); + + expect(buildCount, 2); + expect(disposeCount, 1); + verifyOnly(listener, listener(null, 42)); + }); + + test('unsub to A then make B sub to A then unsub to B disposes B before A', + () async { + final container = ProviderContainer.test(); + final aDispose = OnDisposeMock(); + final a = Provider.autoDispose((ref) { + ref.onDispose(aDispose.call); + return 42; + }); + final bDispose = OnDisposeMock(); + final b = Provider.autoDispose((ref) { + ref.onDispose(bDispose.call); + ref.watch(a); + return '42'; + }); + + final subA = container.listen(a, (prev, value) {}); + subA.close(); + + final subB = container.listen(b, (prev, value) {}); + subB.close(); + + verifyNoMoreInteractions(aDispose); + verifyNoMoreInteractions(bDispose); + + await container.pump(); + + verifyInOrder([ + bDispose(), + aDispose(), + ]); + verifyNoMoreInteractions(aDispose); + verifyNoMoreInteractions(bDispose); + }); + + test('chain', () async { + final container = ProviderContainer.test(); + final onDispose = OnDisposeMock(); + var value = 42; + final provider = Provider.autoDispose((ref) { + ref.onDispose(onDispose.call); + return value; + }); + final onDispose2 = OnDisposeMock(); + final provider2 = Provider.autoDispose((ref) { + ref.onDispose(onDispose2.call); + return ref.watch(provider); + }); + final listener = Listener(); + + var sub = container.listen(provider2, listener.call, fireImmediately: true); + + verify(listener(null, 42)).called(1); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(onDispose); + verifyNoMoreInteractions(onDispose2); + + sub.close(); + + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(onDispose); + verifyNoMoreInteractions(onDispose2); + + await container.pump(); + + verifyNoMoreInteractions(listener); + verifyInOrder([ + onDispose2(), + onDispose(), + ]); + verifyNoMoreInteractions(onDispose); + verifyNoMoreInteractions(onDispose2); + + value = 21; + sub = container.listen(provider2, listener.call, fireImmediately: true); + + verify(listener(null, 21)).called(1); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(onDispose); + verifyNoMoreInteractions(onDispose2); + }); + + test("auto dispose A then auto dispose B doesn't dispose A again", () async { + final container = ProviderContainer.test(); + final aDispose = OnDisposeMock(); + final a = Provider.autoDispose((ref) { + ref.onDispose(aDispose.call); + return 42; + }); + final bDispose = OnDisposeMock(); + final b = Provider.autoDispose((ref) { + ref.onDispose(bDispose.call); + return 42; + }); + + var subA = container.listen(a, (prev, value) {}); + verifyNoMoreInteractions(aDispose); + verifyNoMoreInteractions(bDispose); + subA.close(); + + await container.pump(); + + verify(aDispose()).called(1); + verifyNoMoreInteractions(aDispose); + verifyNoMoreInteractions(bDispose); + + subA = container.listen(a, (prev, value) {}); + final subB = container.listen(b, (prev, value) {}); + + subB.close(); + + await container.pump(); + + verify(bDispose()).called(1); + verifyNoMoreInteractions(aDispose); + verifyNoMoreInteractions(bDispose); + }); + + test('ProviderContainer was disposed before Scheduler handled the dispose', + () async { + final container = ProviderContainer.test(); + final onDispose = OnDisposeMock(); + final provider = Provider.autoDispose((ref) { + ref.onDispose(onDispose.call); + return 42; + }); + + final sub = container.listen(provider, (prev, value) {}); + + verifyNoMoreInteractions(onDispose); + + sub.close(); + verifyNoMoreInteractions(onDispose); + + container.dispose(); + + verify(onDispose()).called(1); + verifyNoMoreInteractions(onDispose); + + await container.pump(); + + verifyNoMoreInteractions(onDispose); + }); + + test('unsub no-op if another sub is added before event-loop', () async { + final container = ProviderContainer.test(); + final onDispose = OnDisposeMock(); + final provider = Provider.autoDispose((ref) { + ref.onDispose(onDispose.call); + return 42; + }); + + final sub = container.listen(provider, (prev, value) {}); + + verifyNoMoreInteractions(onDispose); + + sub.close(); + verifyNoMoreInteractions(onDispose); + + final sub2 = container.listen(provider, (prev, value) {}); + + await container.pump(); + + verifyNoMoreInteractions(onDispose); + + sub2.close(); + await container.pump(); + + verify(onDispose()).called(1); + verifyNoMoreInteractions(onDispose); + }); + + test('no-op if when removing listener if there is still a listener', + () async { + final container = ProviderContainer.test(); + final onDispose = OnDisposeMock(); + final provider = Provider.autoDispose((ref) { + ref.onDispose(onDispose.call); + return 42; + }); + + final sub = container.listen(provider, (prev, value) {}); + final sub2 = container.listen(provider, (prev, value) {}); + + verifyNoMoreInteractions(onDispose); + + sub.close(); + await container.pump(); + + verifyNoMoreInteractions(onDispose); + + sub2.close(); + await container.pump(); + + verify(onDispose()).called(1); + verifyNoMoreInteractions(onDispose); + }); + + test('Do not dispose twice when ProviderContainer is disposed first', + () async { + final onDispose = OnDisposeMock(); + final provider = Provider.autoDispose((ref) { + ref.onDispose(onDispose.call); + return 42; + }); + final container = ProviderContainer.test(); + + final sub = container.listen(provider, (_, __) {}); + sub.close(); + + container.dispose(); + + verify(onDispose()).called(1); + verifyNoMoreInteractions(onDispose); + + await container.pump(); + + verifyNoMoreInteractions(onDispose); + }); + + test('providers with only a "listen" as subscribers are kept alive', + () async { + final container = ProviderContainer.test(); + var mounted = true; + final listened = Provider.autoDispose((ref) { + ref.onDispose(() => mounted = false); + return 0; + }); + final provider = Provider.autoDispose((ref) { + ref.listen(listened, (prev, value) {}); + return 0; + }); + + container.listen(provider, (prev, value) {}); + final sub = container.listen(listened, (prev, value) {}); + + sub.close(); + + await container.pump(); + + expect(mounted, true); + }); +} diff --git a/packages/riverpod/test/framework/family_test.dart b/packages/riverpod/test/old/framework/family_test.dart similarity index 79% rename from packages/riverpod/test/framework/family_test.dart rename to packages/riverpod/test/old/framework/family_test.dart index 6f771e3c7..6d56330b6 100644 --- a/packages/riverpod/test/framework/family_test.dart +++ b/packages/riverpod/test/old/framework/family_test.dart @@ -1,37 +1,19 @@ import 'dart:async'; import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; import 'package:test/test.dart'; import '../utils.dart'; void main() { - test('throw if overrideWithProvider returns a provider with dependencies', - () { - final family = Provider.family((ref, _) => 0); - final a = Provider((ref) => 0); - - final container = createContainer( - overrides: [ - family.overrideWithProvider( - (argument) => Provider((ref) => 0, dependencies: [a]), - ), - ], - ); - - expect( - () => container.read(family(42)), - throwsA(isA()), - ); - }); - test( 'does not re-initialize a family if read by a child container after the provider was initialized', () { - final root = createContainer(); + final root = ProviderContainer.test(); // the child must be created before the provider is initialized - final child = createContainer(parent: root); + final child = ProviderContainer.test(parent: root); var buildCount = 0; final provider = Provider.family((ref, param) { @@ -52,15 +34,18 @@ void main() { 'does not re-initialize a scoped family if read by a child container after the provider was initialized', () { var buildCount = 0; - final provider = Provider.family((ref, param) { - buildCount++; - return 42; - }); + final provider = Provider.family( + (ref, param) { + buildCount++; + return 42; + }, + dependencies: const [], + ); - final root = createContainer(); - final scope = createContainer(parent: root, overrides: [provider]); + final root = ProviderContainer.test(); + final scope = ProviderContainer.test(parent: root, overrides: [provider]); // the child must be created before the provider is initialized - final child = createContainer(parent: scope); + final child = ProviderContainer.test(parent: scope); expect(scope.read(provider(0)), 42); @@ -75,7 +60,7 @@ void main() { test('caches the provider per value', () { final family = Provider.family((ref, a) => '$a'); - final container = createContainer(); + final container = ProviderContainer.test(); expect(family(42), family(42)); expect(container.read(family(42)), '42'); @@ -92,7 +77,7 @@ void main() { final family = StreamProvider.family((ref, a) { return controllers[a]!.stream; }); - final container = createContainer(); + final container = ProviderContainer.test(); final listener = Listener>(); final listener2 = Listener>(); @@ -147,9 +132,9 @@ void main() { ); }); - test('family override', () { + test('Can override both the family as a provider of that family', () { final family = Provider.family((ref, a) => 'Hello $a'); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ // Provider overrides always takes over family overrides family(84).overrideWithValue('Bonjour 84'), diff --git a/packages/riverpod/test/framework/modifiers_test.dart b/packages/riverpod/test/old/framework/modifiers_test.dart similarity index 76% rename from packages/riverpod/test/framework/modifiers_test.dart rename to packages/riverpod/test/old/framework/modifiers_test.dart index c8df4cc1a..ac28709a1 100644 --- a/packages/riverpod/test/framework/modifiers_test.dart +++ b/packages/riverpod/test/old/framework/modifiers_test.dart @@ -1,7 +1,8 @@ // ignore_for_file: prefer_const_constructors import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; -import 'package:riverpod/src/builders.dart'; +import 'package:riverpod/src/builder.dart'; import 'package:test/test.dart'; import '../utils.dart'; @@ -10,7 +11,7 @@ void main() { group('_ProxySubscription', () { group('read', () { test('throws if used after close', () { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = FutureProvider((ref) async => 0); final sub = container.listen(provider.future, (prev, value) {}); @@ -25,7 +26,7 @@ void main() { }); test('Listening to a modifier correctly fires ref life-cycles', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final onDispose = OnDisposeMock(); final onAddListener = OnAddListener(); final onRemoveListener = OnRemoveListener(); @@ -90,7 +91,6 @@ void main() { }); test('FutureProvider', () { - final futureProviderBuilder = FutureProviderBuilder(); FutureProviderFamilyBuilder(); AutoDisposeFutureProviderBuilder(); AutoDisposeFutureProviderFamilyBuilder(); @@ -108,21 +108,12 @@ void main() { FutureProvider.family.autoDispose, ); expect( - futureProviderBuilder.autoDispose, - FutureProvider.autoDispose, - ); - expect( - futureProviderBuilder.family, - FutureProvider.family, - ); - expect( - futureProviderBuilder((ref) async => 42, name: 'foo'), + FutureProvider((ref) async => 42, name: 'foo'), isA>().having((s) => s.name, 'name', 'foo'), ); }); test('StreamProvider', () { - final streamProviderBuilder = StreamProviderBuilder(); StreamProviderFamilyBuilder(); AutoDisposeStreamProviderBuilder(); AutoDisposeStreamProviderFamilyBuilder(); @@ -140,21 +131,12 @@ void main() { StreamProvider.family.autoDispose, ); expect( - streamProviderBuilder.autoDispose, - StreamProvider.autoDispose, - ); - expect( - streamProviderBuilder.family, - StreamProvider.family, - ); - expect( - streamProviderBuilder((ref) => Stream.value(42), name: 'foo'), + StreamProvider((ref) => Stream.value(42), name: 'foo'), isA>().having((s) => s.name, 'name', 'foo'), ); }); test('StateNotifierProvider', () { - final stateNotifierProviderBuilder = StateNotifierProviderBuilder(); StateNotifierProviderFamilyBuilder(); AutoDisposeStateNotifierProviderBuilder(); AutoDisposeStateNotifierProviderFamilyBuilder(); @@ -172,15 +154,7 @@ void main() { StateNotifierProvider.family.autoDispose, ); expect( - stateNotifierProviderBuilder.autoDispose, - StateNotifierProvider.autoDispose, - ); - expect( - stateNotifierProviderBuilder.family, - StateNotifierProvider.family, - ); - expect( - stateNotifierProviderBuilder, int>( + StateNotifierProvider, int>( (ref) => StateController(42), name: 'foo', ), @@ -190,7 +164,6 @@ void main() { }); test('Provider', () { - final providerBuilder = ProviderBuilder(); ProviderFamilyBuilder(); AutoDisposeProviderBuilder(); AutoDisposeProviderFamilyBuilder(); @@ -208,22 +181,13 @@ void main() { Provider.family.autoDispose, ); expect( - providerBuilder.autoDispose, - Provider.autoDispose, - ); - expect( - providerBuilder.family, - Provider.family, - ); - expect( - providerBuilder((ref) => StateController(42), name: 'foo'), + Provider((ref) => StateController(42), name: 'foo'), isA>>() .having((s) => s.name, 'name', 'foo'), ); }); test('StateProvider', () { - final stateProviderBuilder = StateProviderBuilder(); StateProviderFamilyBuilder(); expect( @@ -231,11 +195,7 @@ void main() { const StateProviderFamilyBuilder(), ); expect( - stateProviderBuilder.family, - StateProvider.family, - ); - expect( - stateProviderBuilder((ref) => StateController(42), name: 'foo'), + StateProvider((ref) => StateController(42), name: 'foo'), isA>>() .having((s) => s.name, 'name', 'foo'), ); diff --git a/packages/riverpod/test/old/framework/provider_container_test.dart b/packages/riverpod/test/old/framework/provider_container_test.dart new file mode 100644 index 000000000..36c54c899 --- /dev/null +++ b/packages/riverpod/test/old/framework/provider_container_test.dart @@ -0,0 +1,415 @@ +import 'package:mockito/mockito.dart'; +import 'package:riverpod/src/internals.dart'; +import 'package:test/test.dart'; + +import '../../src/matrix.dart'; +import '../utils.dart'; + +void main() { + group('ProviderContainer', () { + group('debugReassemble', () { + test( + 'reload providers if the debugGetCreateSourceHash of a provider returns a different value', + skip: 'Needs overloading the method', () { + final noDebugGetCreateSourceHashBuild = OnBuildMock(); + final noDebugGetCreateSourceHash = Provider((ref) { + noDebugGetCreateSourceHashBuild(); + return 0; + }); + final constantHashBuild = OnBuildMock(); + final constantHash = Provider.internal( + isAutoDispose: false, + from: null, + argument: null, + name: null, + dependencies: null, + allTransitiveDependencies: null, + retry: null, + (ref) { + constantHashBuild(); + return 0; + }, + ); + var hashResult = '42'; + final changingHashBuild = OnBuildMock(); + final changingHash = Provider.internal( + isAutoDispose: false, + from: null, + argument: null, + name: null, + dependencies: null, + retry: null, + allTransitiveDependencies: null, + (ref) { + changingHashBuild(); + return 0; + }, + ); + final container = ProviderContainer(); + + container.read(noDebugGetCreateSourceHash); + container.read(constantHash); + container.read(changingHash); + + clearInteractions(noDebugGetCreateSourceHashBuild); + clearInteractions(constantHashBuild); + clearInteractions(changingHashBuild); + + hashResult = 'new hash'; + container.debugReassemble(); + container.read(noDebugGetCreateSourceHash); + container.read(constantHash); + container.read(changingHash); + + verifyOnly(changingHashBuild, changingHashBuild()); + verifyNoMoreInteractions(constantHashBuild); + verifyNoMoreInteractions(noDebugGetCreateSourceHashBuild); + + container.debugReassemble(); + container.read(noDebugGetCreateSourceHash); + container.read(constantHash); + container.read(changingHash); + + verifyNoMoreInteractions(changingHashBuild); + verifyNoMoreInteractions(constantHashBuild); + verifyNoMoreInteractions(noDebugGetCreateSourceHashBuild); + }); + }); + + test('invalidate triggers a rebuild on next frame', () async { + final container = ProviderContainer.test(); + final listener = Listener(); + var result = 0; + final provider = Provider((r) => result); + + container.listen(provider, listener.call); + verifyZeroInteractions(listener); + + container.invalidate(provider); + container.invalidate(provider); + result = 1; + + verifyZeroInteractions(listener); + + await container.pump(); + + verifyOnly(listener, listener(0, 1)); + }); + + group('validate that properties respect `dependencies`', () { + test('on reading an element, asserts that dependencies are respected', + () { + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); + final provider = Provider((ref) => ref.watch(dep)); + + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [dep.overrideWithValue(42)], + ); + + expect( + () => container.read(provider), + throwsA(isA()), + ); + }); + + test( + 'on reading an element, asserts that transitive dependencies are also respected', + () { + final transitiveDep = Provider( + (ref) => 0, + dependencies: const [], + ); + final dep = Provider((ref) => ref.watch(transitiveDep)); + final provider = Provider((ref) => ref.watch(dep)); + + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [transitiveDep.overrideWithValue(42)], + ); + + expect( + () => container.read(provider), + throwsA(isA()), + ); + }); + }); + + test( + 'flushes listened-to providers even if they have no external listeners', + () async { + final dep = StateProvider((ref) => 0); + final provider = Provider((ref) => ref.watch(dep)); + final another = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + ref.listen(provider, (prev, value) => self.state++); + return 0; + }), + ); + final container = ProviderContainer.test(); + + expect(container.read(another), 0); + + container.read(dep.notifier).state = 42; + + expect(container.read(another), 1); + }); + + test( + 'flushes listened-to providers even if they have no external listeners (with ProviderListenable)', + () async { + final dep = StateProvider((ref) => 0); + final provider = Provider((ref) => ref.watch(dep)); + final another = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + ref.listen(provider, (prev, value) => self.state++); + return 0; + }), + ); + final container = ProviderContainer.test(); + + expect(container.read(another), 0); + + container.read(dep.notifier).state = 42; + + expect(container.read(another), 1); + }); + + group('getAllProviderElements', () { + test('list scoped providers that depends on nothing', () { + final scopedProvider = Provider( + (ref) => 0, + dependencies: const [], + ); + final parent = ProviderContainer.test(); + final child = ProviderContainer.test( + parent: parent, + overrides: [scopedProvider], + ); + + child.read(scopedProvider); + + expect( + child.getAllProviderElements().single, + isA<$ProviderElement>() + .having((e) => e.origin, 'origin', scopedProvider), + ); + }); + + test( + 'list scoped providers that depends on providers from another container', + () { + final dependency = Provider((ref) => 0); + final scopedProvider = Provider( + (ref) => ref.watch(dependency), + dependencies: const [], + ); + final parent = ProviderContainer.test(); + final child = ProviderContainer.test( + parent: parent, + overrides: [scopedProvider], + ); + + child.read(scopedProvider); + + expect( + child.getAllProviderElements().single, + isA<$ProviderElement>() + .having((e) => e.origin, 'origin', scopedProvider), + ); + }); + + test( + 'list only elements associated with the container (ignoring inherited and descendent elements)', + () { + final provider = Provider((ref) => 0); + final provider2 = Provider( + (ref) => 0, + dependencies: const [], + ); + final provider3 = Provider( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( + parent: root, + overrides: [provider2], + ); + final leaf = ProviderContainer.test( + parent: mid, + overrides: [provider3], + ); + + leaf.read(provider); + leaf.read(provider2); + leaf.read(provider3); + + expect( + root.getAllProviderElements().single, + isA<$ProviderElement>() + .having((e) => e.provider, 'provider', provider), + ); + expect( + mid.getAllProviderElements().single, + isA<$ProviderElement>() + .having((e) => e.provider, 'provider', provider2), + ); + expect( + leaf.getAllProviderElements().single, + isA<$ProviderElement>() + .having((e) => e.provider, 'provider', provider3), + ); + }); + + test('list the currently mounted providers', () async { + final container = ProviderContainer(); + final unrelated = Provider((_) => 42); + final provider = Provider.autoDispose((ref) => 0); + + expect(container.read(unrelated), 42); + var sub = container.listen(provider, (_, __) {}); + + expect( + container.getAllProviderElements().map((e) => e.origin), + unorderedEquals([provider, unrelated]), + ); + + sub.close(); + await container.pump(); + + expect( + container.getAllProviderElements(), + [isA>()], + ); + + sub = container.listen(provider, (_, __) {}); + + expect( + container.getAllProviderElements().map((e) => e.origin), + unorderedEquals([provider, unrelated]), + ); + }); + }); + + group('getAllProviderElementsInOrder', () { + test('list scoped providers that depends on nothing', () { + final scopedProvider = Provider( + (ref) => 0, + dependencies: const [], + ); + final parent = ProviderContainer.test(); + final child = ProviderContainer.test( + parent: parent, + overrides: [scopedProvider], + ); + + child.read(scopedProvider); + + expect( + child.getAllProviderElementsInOrder().single, + isA<$ProviderElement>() + .having((e) => e.origin, 'origin', scopedProvider), + ); + }); + + test( + 'list scoped providers that depends on providers from another container', + () { + final dependency = Provider((ref) => 0); + final scopedProvider = Provider( + (ref) => ref.watch(dependency), + dependencies: const [], + ); + final parent = ProviderContainer.test(); + final child = ProviderContainer.test( + parent: parent, + overrides: [scopedProvider], + ); + + child.read(scopedProvider); + + expect( + child.getAllProviderElementsInOrder().single, + isA<$ProviderElement>() + .having((e) => e.origin, 'origin', scopedProvider), + ); + }); + }); + + test( + 'does not re-initialize a provider if read by a child container after the provider was initialized', + () { + final root = ProviderContainer.test(); + // the child must be created before the provider is initialized + final child = ProviderContainer.test(parent: root); + + var buildCount = 0; + final provider = Provider((ref) { + buildCount++; + return 0; + }); + + expect(root.read(provider), 0); + + expect(buildCount, 1); + + expect(child.read(provider), 0); + + expect(buildCount, 1); + }); + + test('builds providers at most once per container', () { + var result = 42; + final container = ProviderContainer.test(); + var callCount = 0; + final provider = Provider((_) { + callCount++; + return result; + }); + + expect(callCount, 0); + expect(container.read(provider), 42); + expect(callCount, 1); + expect(container.read(provider), 42); + expect(callCount, 1); + + final container2 = ProviderContainer.test(); + + result = 21; + expect(container2.read(provider), 21); + expect(callCount, 2); + expect(container2.read(provider), 21); + expect(callCount, 2); + expect(container.read(provider), 42); + expect(callCount, 2); + }); + test( + 'does not refresh providers if their dependencies changes but they have no active listeners', + () async { + final container = ProviderContainer.test(); + + var buildCount = 0; + final dep = StateProvider((ref) => 0); + final provider = Provider((ref) { + buildCount++; + return ref.watch(dep); + }); + + container.read(provider); + + expect(buildCount, 1); + + container.read(dep.notifier).state++; + await container.pump(); + + expect(buildCount, 1); + }, + ); + }); +} diff --git a/packages/riverpod/test/framework/proxy_provider_listenable_test.dart b/packages/riverpod/test/old/framework/proxy_provider_listenable_test.dart similarity index 100% rename from packages/riverpod/test/framework/proxy_provider_listenable_test.dart rename to packages/riverpod/test/old/framework/proxy_provider_listenable_test.dart diff --git a/packages/riverpod/test/framework/ref_watch_test.dart b/packages/riverpod/test/old/framework/ref_watch_test.dart similarity index 89% rename from packages/riverpod/test/framework/ref_watch_test.dart rename to packages/riverpod/test/old/framework/ref_watch_test.dart index e9e00ed92..89845bab3 100644 --- a/packages/riverpod/test/framework/ref_watch_test.dart +++ b/packages/riverpod/test/old/framework/ref_watch_test.dart @@ -1,5 +1,7 @@ import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; import '../utils.dart'; @@ -17,7 +19,7 @@ class Counter extends StateNotifier { void main() { test('can chain select', () { - final container = createContainer(); + final container = ProviderContainer.test(); var buildCount = 0; final dep = StateProvider((ref) => 0); @@ -54,7 +56,7 @@ void main() { }); test('can listen multiple providers at once', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final count = StateProvider((ref) => 0); final count2 = StateProvider((ref) => 0); @@ -105,10 +107,8 @@ void main() { expect(buildCount, 2); }); - test('when selector throws, rebuild providers', () {}, skip: true); - test('on provider that threw, exceptions bypass the selector', () { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = Provider((ref) { throw UnimplementedError(); }); @@ -125,7 +125,7 @@ void main() { test( 'when rebuilding a provider after an uncaught exception, correctly updates dependents', () { - final container = createContainer(); + final container = ProviderContainer.test(); final throws = StateProvider((ref) => true); final provider = Provider((ref) { if (ref.watch(throws)) { @@ -151,7 +151,7 @@ void main() { test( 'when rebuilding a provider after an uncaught selected exception, correctly updates dependents', () { - final container = createContainer(); + final container = ProviderContainer.test(); final throws = StateProvider((ref) => true); final provider = Provider((ref) { if (ref.watch(throws)) { @@ -179,7 +179,7 @@ void main() { final onDispose = OnDisposeMock(); final dep = StateProvider((ref) => 0); final dep2 = StateProvider((ref) => 0); - final container = createContainer(); + final container = ProviderContainer.test(); final provider = Provider((ref) { ref.onDispose(onDispose.call); ref.watch(dep); @@ -207,7 +207,7 @@ void main() { ref.watch(dep.select((value) => ref.read(dep))); }, ); - final container = createContainer(); + final container = ProviderContainer.test(); expect( () => container.read(provider), @@ -222,7 +222,7 @@ void main() { final provider = Provider((ref) { ref.watch(dep.select((value) => ref.watch(dep))); }); - final container = createContainer(); + final container = ProviderContainer.test(); expect( () => container.read(provider), @@ -242,7 +242,7 @@ void main() { }), ); }); - final container = createContainer(); + final container = ProviderContainer.test(); expect( () => container.read(provider), @@ -251,9 +251,9 @@ void main() { }); test( - 'when selecting a provider, element.visitChildren visits the selected provider', + 'when selecting a provider, element.visitAncestors visits the selected provider', () { - final container = createContainer(); + final container = ProviderContainer.test(); final selected = StateNotifierProvider, int>((ref) { return StateController(0); }); @@ -261,17 +261,20 @@ void main() { ref.watch(selected.select((value) => null)); }); + container.read(provider); + container.read(selected); + final element = container.readProviderElement(provider); final selectedElement = container.readProviderElement(selected); - final ancestors = >[]; + final ancestors = []; element.visitAncestors(ancestors.add); expect(ancestors, [selectedElement]); }); test('can watch selectors', () { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = StateNotifierProvider, int>( name: 'provider', (ref) => StateController(0), @@ -314,16 +317,10 @@ void main() { () async { final stateProvider = StateProvider((ref) => 0, name: 'state'); final notifier0 = Counter(); - final provider0 = StateNotifierProvider( - (_) => notifier0, - name: '0', - ); + final provider0 = StateNotifierProvider((ref) => notifier0); final notifier1 = Counter(42); - final provider1 = StateNotifierProvider( - (_) => notifier1, - name: '1', - ); + final provider1 = StateNotifierProvider((ref) => notifier1); var computedBuildCount = 0; final computed = Provider((ref) { @@ -334,7 +331,7 @@ void main() { }); final computedListener = Listener(); - final container = createContainer(); + final container = ProviderContainer.test(); container.read(provider0); container.read(provider1); @@ -345,8 +342,8 @@ void main() { verifyOnly(computedListener, computedListener(null, '0 0')); expect(computedBuildCount, 1); - expect(provider0Element.hasListeners, true); - expect(provider1Element.hasListeners, false); + expect(provider0Element.hasNonWeakListeners, true); + expect(provider1Element.hasNonWeakListeners, false); notifier0.increment(); await container.pump(); @@ -366,8 +363,8 @@ void main() { verifyOnly(computedListener, computedListener('1 1', '1 43')); expect(computedBuildCount, 3); - expect(provider1Element.hasListeners, true); - expect(provider0Element.hasListeners, true); + expect(provider1Element.hasNonWeakListeners, true); + expect(provider0Element.hasNonWeakListeners, true); notifier1.increment(); await container.pump(); @@ -404,7 +401,7 @@ void main() { }); final computedListener = Listener(); - final container = createContainer(); + final container = ProviderContainer.test(); final provider0Element = container.readProviderElement(provider0); final provider1Element = container.readProviderElement(provider1); @@ -413,8 +410,8 @@ void main() { verifyOnly(computedListener, computedListener(null, 0)); expect(computedBuildCount, 1); - expect(provider0Element.hasListeners, true); - expect(provider1Element.hasListeners, false); + expect(provider0Element.hasNonWeakListeners, true); + expect(provider1Element.hasNonWeakListeners, false); notifier0.increment(); await container.pump(); @@ -434,8 +431,8 @@ void main() { expect(computedBuildCount, 3); verifyOnly(computedListener, computedListener(1, 43)); - expect(provider1Element.hasListeners, true); - expect(provider0Element.hasListeners, false); + expect(provider1Element.hasNonWeakListeners, true); + expect(provider0Element.hasNonWeakListeners, false); notifier1.increment(); await container.pump(); @@ -452,14 +449,14 @@ void main() { test('Provider.family', () async { final computed = - Provider.family>((ref, provider) { + Provider.family>((ref, provider) { return ref.watch(provider).toString(); }); final notifier = Counter(); final provider = StateNotifierProvider((_) { return notifier; }); - final container = createContainer(); + final container = ProviderContainer.test(); final listener = Listener(); container.listen(computed(provider), listener.call, fireImmediately: true); @@ -475,7 +472,7 @@ void main() { test( 'multiple ref.watch, when one of them forces re-evaluate, all dependencies are still flushed', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final notifier = Notifier(0); final provider = StateNotifierProvider, int>((_) { return notifier; @@ -520,7 +517,7 @@ void main() { secondCallCount++; return ref.watch(first).toString(); }); - final container = createContainer(); + final container = ProviderContainer.test(); final controller = container.read(state.notifier); @@ -537,7 +534,7 @@ void main() { }); test('can call ref.watch asynchronously', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final notifier = Notifier(0); final provider = StateNotifierProvider, int>( name: 'provider', @@ -579,7 +576,7 @@ void main() { }); test('the value is cached between multiple listeners', () { - final container = createContainer(); + final container = ProviderContainer.test(); final notifier = Notifier(0); final provider = StateNotifierProvider, int>((_) { return notifier; @@ -624,7 +621,7 @@ void main() { }); test('Simple Provider flow', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final notifier = Notifier(0); final provider = StateNotifierProvider, int>((_) { return notifier; diff --git a/packages/riverpod/test/legacy/framework2/framework_test.dart b/packages/riverpod/test/old/legacy/framework2/framework_test.dart similarity index 81% rename from packages/riverpod/test/legacy/framework2/framework_test.dart rename to packages/riverpod/test/old/legacy/framework2/framework_test.dart index 268376a5a..ff85b6977 100644 --- a/packages/riverpod/test/legacy/framework2/framework_test.dart +++ b/packages/riverpod/test/old/legacy/framework2/framework_test.dart @@ -1,7 +1,10 @@ import 'dart:async'; import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' + show $ProviderElement, ProviderElement; import 'package:test/test.dart'; import '../../utils.dart'; @@ -11,7 +14,7 @@ void main() { final didChange = Listener(); setUp(() { - container = createContainer(); + container = ProviderContainer.test(); }); tearDown(() { @@ -20,22 +23,22 @@ void main() { }); test('ProviderContainer.children', () { - final root = createContainer(); + final root = ProviderContainer.test(); expect(root.debugChildren, isEmpty); - final mid = createContainer(parent: root); + final mid = ProviderContainer.test(parent: root); expect(root.debugChildren, containsAll([mid])); expect(mid.debugChildren, isEmpty); - final mid2 = createContainer(parent: root); + final mid2 = ProviderContainer.test(parent: root); expect(root.debugChildren, containsAll([mid, mid2])); expect(mid.debugChildren, isEmpty); expect(mid2.debugChildren, isEmpty); - final leaf = createContainer(parent: mid); + final leaf = ProviderContainer.test(parent: mid); expect(root.debugChildren, containsAll([mid, mid2])); expect(mid.debugChildren, containsAll([leaf])); @@ -61,8 +64,8 @@ void main() { }); test('Ref.container exposes the root container', () { - final root = createContainer(); - final container = createContainer(parent: root); + final root = ProviderContainer.test(); + final container = ProviderContainer.test(parent: root); final provider = Provider((ref) => ref); expect(container.read(provider).container, root); @@ -79,13 +82,13 @@ void main() { expect(family2(0).name, 'name'); expect( family2(0).toString(), - equalsIgnoringHashCodes('name:Provider#00000(0)'), + equalsIgnoringHashCodes('name(0)'), ); expect(family2(1).name, 'name'); expect( family2(1).toString(), - equalsIgnoringHashCodes('name:Provider#00000(1)'), + equalsIgnoringHashCodes('name(1)'), ); }); }); @@ -130,14 +133,15 @@ void main() { .maybeWhen(data: (d) => d, orElse: () => null); }); - expect(callCount, 0); - expect(container.read(provider), null); + final sub = container.listen(provider, (p, n) {}); + + expect(sub.read(), null); expect(callCount, 1); controller.add(42); expect(callCount, 1); - expect(container.read(provider), 42); + expect(sub.read(), 42); expect(callCount, 2); }); @@ -168,7 +172,7 @@ void main() { buildCount++; ref.onDispose(onDispose.call); }); - final container = createContainer(); + final container = ProviderContainer.test(); container.read(provider); @@ -188,8 +192,8 @@ void main() { }); test('disposing child container does not dispose the providers', () { - final container = createContainer(); - final child = createContainer(parent: container); + final container = ProviderContainer.test(); + final child = ProviderContainer.test(parent: container); var disposed = false; final provider = Provider((ref) { ref.onDispose(() => disposed = true); @@ -209,10 +213,10 @@ void main() { test('child container uses root overrides', () { final provider = Provider((ref) => 0); - final container = createContainer( + final container = ProviderContainer.test( overrides: [provider.overrideWithValue(42)], ); - final child = createContainer(parent: container); + final child = ProviderContainer.test(parent: container); expect(child.read(provider), 42); }); @@ -254,60 +258,48 @@ void main() { final sub = container.listen(computed, (_, __) {}); expect(sub.read(), '0'); - var firstDependents = >[]; - firstElement.visitChildren( - elementVisitor: firstDependents.add, - notifierVisitor: (_) {}, - ); - var secondDependents = >[]; - secondElement.visitChildren( - elementVisitor: secondDependents.add, - notifierVisitor: (_) {}, - ); + var firstDependents = []; + firstElement.visitChildren(firstDependents.add); + var secondDependents = []; + secondElement.visitChildren(secondDependents.add); expect(firstDependents, [computedElement]); - expect(firstElement.hasListeners, true); + expect(firstElement.hasNonWeakListeners, true); expect(secondDependents, [computedElement]); - expect(secondElement.hasListeners, true); + expect(secondElement.hasNonWeakListeners, true); container.read(first.notifier).state++; expect(sub.read(), 'fallback'); - firstDependents = >[]; - firstElement.visitChildren( - elementVisitor: firstDependents.add, - notifierVisitor: (_) {}, - ); - secondDependents = >[]; - secondElement.visitChildren( - elementVisitor: secondDependents.add, - notifierVisitor: (_) {}, - ); + firstDependents = []; + firstElement.visitChildren(firstDependents.add); + secondDependents = []; + secondElement.visitChildren(secondDependents.add); expect(firstDependents, [computedElement]); - expect(firstElement.hasListeners, true); - expect(secondDependents, >[]); - expect(secondElement.hasListeners, false); + expect(firstElement.hasNonWeakListeners, true); + expect(secondDependents, <$ProviderElement>[]); + expect(secondElement.hasNonWeakListeners, false); }); - // group('overrideWithValue', () { - // test('synchronously overrides the value', () { - // var callCount = 0; - // final provider = FutureProvider((ref) async { - // callCount++; - // return 0; - // }); - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); + group('overrideWithValue', () { + test('synchronously overrides the value', () { + var callCount = 0; + final provider = FutureProvider((ref) async { + callCount++; + return 0; + }); + final container = ProviderContainer.test( + overrides: [provider.overrideWithValue(const AsyncValue.data(42))], + ); - // addTearDown(container.dispose); + addTearDown(container.dispose); - // final sub = container.listen(provider, (_, __) {}); + final sub = container.listen(provider, (_, __) {}); - // expect(callCount, 0); - // expect(sub.read(), const AsyncValue.data(42)); - // }); - // }); + expect(callCount, 0); + expect(sub.read(), const AsyncValue.data(42)); + }); + }); test('remove dependencies on dispose', () async { final first = StateProvider((ref) => 0); @@ -319,24 +311,18 @@ void main() { final sub = container.listen(computed, (_, __) {}); expect(sub.read(), 0); - var firstDependents = >[]; - firstElement.visitChildren( - elementVisitor: firstDependents.add, - notifierVisitor: (_) {}, - ); + var firstDependents = []; + firstElement.visitChildren(firstDependents.add); expect(firstDependents, {computedElement}); - expect(firstElement.hasListeners, true); + expect(firstElement.hasNonWeakListeners, true); sub.close(); await container.pump(); - firstDependents = >[]; - firstElement.visitChildren( - elementVisitor: firstDependents.add, - notifierVisitor: (_) {}, - ); - expect(firstDependents, >{}); - expect(firstElement.hasListeners, false); + firstDependents = []; + firstElement.visitChildren(firstDependents.add); + expect(firstDependents, <$ProviderElement>{}); + expect(firstElement.hasNonWeakListeners, false); }); test( @@ -426,16 +412,16 @@ void main() { final provider = Provider((ref) => ref.watch(first)); final element = container.readProviderElement(provider); - expect(element.hasListeners, false); + expect(element.hasNonWeakListeners, false); final sub = container.listen(provider, didChange.call); - expect(element.hasListeners, true); + expect(element.hasNonWeakListeners, true); sub.close(); counter.increment(); - expect(element.hasListeners, false); + expect(element.hasNonWeakListeners, false); verifyZeroInteractions(didChange); }); @@ -497,8 +483,8 @@ void main() { group('container.refresh', () { test('still refresh providers on non-root containers', () { - final root = createContainer(); - final container = createContainer(parent: root); + final root = ProviderContainer.test(); + final container = ProviderContainer.test(parent: root); var callCount = 0; late Ref providerReference; var result = 0; @@ -528,7 +514,7 @@ void main() { callCount++; return future; }); - final container = createContainer(); + final container = ProviderContainer.test(); await expectLater(container.read(provider.future), completion(42)); @@ -562,7 +548,7 @@ void main() { callCount++; return Future.value(42); }); - final container = createContainer(); + final container = ProviderContainer.test(); expect(callCount, 0); expect( @@ -589,7 +575,7 @@ void main() { ref.watch(dep); return future; }); - final container = createContainer(); + final container = ProviderContainer.test(); container.refresh(provider); diff --git a/packages/riverpod/test/legacy/framework_test.dart b/packages/riverpod/test/old/legacy/framework_test.dart similarity index 52% rename from packages/riverpod/test/legacy/framework_test.dart rename to packages/riverpod/test/old/legacy/framework_test.dart index 46680abb8..7b3c81081 100644 --- a/packages/riverpod/test/legacy/framework_test.dart +++ b/packages/riverpod/test/old/legacy/framework_test.dart @@ -1,121 +1,41 @@ -import 'dart:async'; - import 'package:mockito/mockito.dart'; import 'package:riverpod/riverpod.dart'; -import 'package:riverpod/src/internals.dart'; +import 'package:riverpod/src/internals.dart' + show CircularDependencyError, ProviderElement; import 'package:test/test.dart'; import '../utils.dart'; void main() { - test('hasListeners', () { - final container = createContainer(); - final provider = Provider((_) => 42); - - expect(container.read(provider), 42); - - final state = container.getAllProviderElements().single; - - expect(state.hasListeners, false); - - final sub = container.listen(provider, (_, __) {}); - - expect(state.hasListeners, true); - - sub.close(); - - expect(state.hasListeners, false); - }); - test('test two families one overridden the other not', () { final family = Provider.family((ref, value) { return '$value'; }); - final family2 = Provider.family((ref, value) { - return '$value 2'; - }); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [family2]); + final family2 = Provider.family( + (ref, value) { + return '$value 2'; + }, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [family2], + ); expect(container.read(family(0)), '0'); expect(container.read(family2(0)), '0 2'); expect(container.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', family2(0)), + isA().having((e) => e.origin, 'origin', family2(0)), ]); expect(root.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', family(0)), + isA().having((e) => e.origin, 'origin', family(0)), ]); }); - test('changing the override type at a given index throws', () { - final provider = Provider((ref) => 0); - final family = Provider.family((ref, value) => 0); - final container = createContainer(overrides: [family]); - - expect( - () => container.updateOverrides([provider]), - throwsA(isA()), - ); - }); - - test("can't call onDispose inside onDispose", () { - final provider = Provider((ref) { - ref.onDispose(() { - ref.onDispose(() {}); - }); - return ref; - }); - final container = createContainer(); - - container.read(provider); - - final errors = []; - runZonedGuarded(container.dispose, (err, _) => errors.add(err)); - - expect(errors, [isStateError]); - }); - - test("can't call read inside onDispose", () { - final provider2 = Provider((ref) => 0); - final provider = Provider((ref) { - ref.onDispose(() { - ref.read(provider2); - }); - return ref; - }); - final container = createContainer(); - - container.read(provider); - - final errors = []; - runZonedGuarded(container.dispose, (err, _) => errors.add(err)); - - expect(errors, [isStateError]); - }); - - test("can't call watch inside onDispose", () { - final provider2 = Provider((ref) => 0); - final provider = Provider((ref) { - ref.onDispose(() { - ref.watch(provider2); - }); - return ref; - }); - final container = createContainer(); - - container.read(provider); - - final errors = []; - runZonedGuarded(container.dispose, (err, _) => errors.add(err)); - - expect(errors, [isStateError]); - }); - test('disposing an already disposed container is no-op', () { - final container = createContainer(); + final container = ProviderContainer.test(); container.dispose(); container.dispose(); @@ -124,7 +44,7 @@ void main() { test('Owner.read', () { final provider = Provider((ref) => 0); final provider2 = Provider((ref) => 1); - final container = createContainer(); + final container = ProviderContainer.test(); final value1 = container.read(provider); final value2 = container.read(provider); @@ -138,27 +58,6 @@ void main() { expect(value21, 1); }); - test( - "updating overrides / dispose don't compute provider states if not loaded yet", - () { - var callCount = 0; - final provider = Provider((_) => callCount++); - - final container = createContainer( - overrides: [provider], - ); - - expect(callCount, 0); - - container.updateOverrides([provider]); - - expect(callCount, 0); - - container.dispose(); - - expect(callCount, 0); - }); - test('circular dependencies (sync)', () { late Provider provider; @@ -172,7 +71,7 @@ void main() { return ref.watch(provider2) + 1; }); - final container = createContainer(); + final container = ProviderContainer.test(); expect( () => container.read(provider), throwsA(isA()), @@ -183,16 +82,46 @@ void main() { late Provider provider; final provider1 = Provider((ref) { - return ref.watch(provider)() + 1; + return () => ref.watch(provider)() + 1; }); final provider2 = Provider((ref) { - return ref.watch(provider1) + 1; + return () => ref.watch(provider1)() + 1; + }); + provider = Provider((ref) { + return () => ref.watch(provider2)() + 1; + }); + + final container = ProviderContainer.test(); + + container.read(provider); + container.read(provider1); + container.read(provider2); + + expect( + () => container.read(provider)(), + throwsA(isA()), + ); + }); + + test('circular dependencies when dependencies are already mounted', () { + late Provider provider; + + final provider1 = Provider((ref) { + ref.watch(provider); + }); + final provider2 = Provider((ref) { + ref.watch(provider1); }); provider = Provider((ref) { - return () => ref.watch(provider2) + 1; + return () => ref.watch(provider2); }); - final container = createContainer(); + final container = ProviderContainer.test(); + + container.read(provider); + container.read(provider1); + container.read(provider2); + expect( () => container.read(provider)(), throwsA(isA()), @@ -200,7 +129,7 @@ void main() { }); test('circular dependencies #2', () { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = Provider((ref) => ref); final provider1 = Provider((ref) => ref); @@ -217,7 +146,7 @@ void main() { }); test('dispose providers in dependency order (simple)', () { - final container = createContainer(); + final container = ProviderContainer.test(); final onDispose1 = OnDisposeMock(); final onDispose2 = OnDisposeMock(); final onDispose3 = OnDisposeMock(); @@ -253,23 +182,6 @@ void main() { verifyNoMoreInteractions(onDispose3); }); - test('Ref is unusable after dispose (read/onDispose)', () { - final container = createContainer(); - late ProviderElement ref; - final provider = Provider((s) { - ref = s as ProviderElement; - return 42; - }); - final other = Provider((_) => 42); - - expect(container.read(provider), 42); - container.dispose(); - - expect(ref.mounted, isFalse); - expect(() => ref.onDispose(() {}), throwsStateError); - expect(() => ref.read(other), throwsStateError); - }); - test('if a provider threw on creation, onDispose still works', () { var callCount = 0; final onDispose = OnDisposeMock(); @@ -281,7 +193,7 @@ void main() { ref.onDispose(onDispose.call); throw error; }); - final container = createContainer(); + final container = ProviderContainer.test(); expect(() => container.read(provider), throwsA(error)); expect(callCount, 1); diff --git a/packages/riverpod/test/providers/state_notifier_provider/auto_dispose_family_test.dart b/packages/riverpod/test/old/legacy_providers/deprecated/state_notifier_provider/auto_dispose_family_test.dart similarity index 73% rename from packages/riverpod/test/providers/state_notifier_provider/auto_dispose_family_test.dart rename to packages/riverpod/test/old/legacy_providers/deprecated/state_notifier_provider/auto_dispose_family_test.dart index 5bfc79884..a0ed6dda4 100644 --- a/packages/riverpod/test/providers/state_notifier_provider/auto_dispose_family_test.dart +++ b/packages/riverpod/test/old/legacy_providers/deprecated/state_notifier_provider/auto_dispose_family_test.dart @@ -1,7 +1,9 @@ +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; -import '../../utils.dart'; +import '../../../utils.dart'; void main() { group('StateNotifier.family', () { @@ -16,14 +18,17 @@ void main() { }); test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = StateNotifierProvider.autoDispose .family, int, int>( (ref, i) => StateController(ref.watch(dep) + i), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -40,36 +45,39 @@ void main() { final provider = StateNotifierProvider.autoDispose .family, int, int>( (ref, _) => controller, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], ); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); expect(container.read(provider(0).notifier), controller); expect(container.read(provider(0)), 0); expect( container.getAllProviderElementsInOrder(), unorderedEquals([ - isA>() + isA() .having((e) => e.origin, 'origin', provider(0)), ]), ); expect(root.getAllProviderElementsInOrder(), isEmpty); }); - test('when using provider.overrideWithProvider', () async { + test('when using provider.overrideWith', () async { final controller = StateController(0); final provider = StateNotifierProvider.autoDispose - .family, int, int>((ref, _) => controller); - final root = createContainer(); + .family, int, int>( + (ref, _) => controller, + dependencies: const [], + ); + final root = ProviderContainer.test(); final controllerOverride = StateController(42); - final container = createContainer( + final container = ProviderContainer.test( parent: root, overrides: [ - provider.overrideWithProvider( - (value) => StateNotifierProvider.autoDispose( - (ref) => controllerOverride, - ), - ), + provider.overrideWith((ref, value) => controllerOverride), ], ); @@ -79,7 +87,7 @@ void main() { expect( container.getAllProviderElementsInOrder(), unorderedEquals([ - isA>() + isA() .having((e) => e.origin, 'origin', provider(0)), ]), ); diff --git a/packages/riverpod/test/providers/state_notifier_provider/auto_dispose_state_notifier_provider_test.dart b/packages/riverpod/test/old/legacy_providers/deprecated/state_notifier_provider/auto_dispose_state_notifier_provider_test.dart similarity index 59% rename from packages/riverpod/test/providers/state_notifier_provider/auto_dispose_state_notifier_provider_test.dart rename to packages/riverpod/test/old/legacy_providers/deprecated/state_notifier_provider/auto_dispose_state_notifier_provider_test.dart index d309b162f..2915e4c91 100644 --- a/packages/riverpod/test/providers/state_notifier_provider/auto_dispose_state_notifier_provider_test.dart +++ b/packages/riverpod/test/old/legacy_providers/deprecated/state_notifier_provider/auto_dispose_state_notifier_provider_test.dart @@ -1,34 +1,24 @@ import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; -import '../../utils.dart'; +import '../../../utils.dart'; void main() { - test('can read and set current StateNotifier', () async { - final container = createContainer(); - final listener = Listener(); - late AutoDisposeStateNotifierProviderRef ref; - final provider = StateNotifierProvider.autoDispose((r) { - ref = r; - return Counter(); - }); - - container.listen(provider, listener.call); - - verifyZeroInteractions(listener); - expect(ref.notifier.state, 0); - }); - test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = StateNotifierProvider.autoDispose, int>( (ref) => StateController(ref.watch(dep)), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -44,7 +34,7 @@ void main() { final provider = StateNotifierProvider.autoDispose( (ref) => Counter(initialValue), ); - final container = createContainer(); + final container = ProviderContainer.test(); container.listen(provider, (prev, value) {}); @@ -59,7 +49,7 @@ void main() { test('can be refreshed', () async { var result = StateController(0); - final container = createContainer(); + final container = ProviderContainer.test(); final provider = StateNotifierProvider.autoDispose, int>( (ref) => result, @@ -81,9 +71,13 @@ void main() { final provider = StateNotifierProvider.autoDispose, int>( (ref) => controller, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], ); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); expect(container.read(provider.notifier), controller); expect(container.read(provider), 0); @@ -91,82 +85,20 @@ void main() { expect( container.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), ]), ); }); - - // test('when using provider.overrideWithValue', () async { - // final controller = StateController(0); - // final provider = - // StateNotifierProvider.autoDispose, int>( - // (ref) => controller); - // final root = createContainer(); - // final controllerOverride = StateController(42); - // final container = createContainer(parent: root, overrides: [ - // provider.overrideWithValue(controllerOverride), - // ]); - - // expect(container.read(provider.notifier), controllerOverride); - // expect(container.read(provider), 42); - // expect(root.getAllProviderElements(), isEmpty); - // expect( - // container.getAllProviderElements(), - // unorderedEquals([ - // isA>() - // .having((e) => e.origin, 'origin', provider), - // isA>() - // .having((e) => e.origin, 'origin', provider.notifier), - // ]), - // ); - // }); }); - // test('overriding the provider overrides provider.state too', () { - // final provider = StateNotifierProvider.autoDispose((_) { - // return TestNotifier(); - // }); - // final notifier = TestNotifier(42); - // final container = createContainer( - // overrides: [ - // provider.overrideWithValue(TestNotifier(10)), - // ], - // ); - // addTearDown(container.dispose); - // final stateListener = Listener(); - // final notifierListener = Listener(); - - // // does not crash - // container.updateOverrides([ - // provider.overrideWithValue(notifier), - // ]); - - // container.listen( - // provider.notifier, - // notifierListener, - // fireImmediately: true, - // ); - // verify(notifierListener(null, notifier)).called(1); - // verifyNoMoreInteractions(notifierListener); - - // container.listen(provider, stateListener, fireImmediately: true); - // verify(stateListener(null, 42)).called(1); - // verifyNoMoreInteractions(stateListener); - - // notifier.increment(); - - // verify(stateListener(42, 43)).called(1); - // verifyNoMoreInteractions(notifierListener); - // verifyNoMoreInteractions(stateListener); - // }); - test('can specify name', () { - final provider = StateNotifierProvider.autoDispose( + final provider = StateNotifierProvider.autoDispose( (_) => TestNotifier(), name: 'example', ); - final provider2 = StateNotifierProvider.autoDispose((_) => TestNotifier()); + final provider2 = StateNotifierProvider.autoDispose( + (_) => TestNotifier(), + ); expect(provider.name, 'example'); expect(provider2.name, isNull); @@ -177,7 +109,7 @@ void main() { final provider = StateNotifierProvider.autoDispose((_) { return notifier; }); - final container = createContainer(); + final container = ProviderContainer.test(); addTearDown(container.dispose); container.listen(provider, (prev, value) {}); @@ -194,7 +126,7 @@ void main() { return notifier; }); final listener = Listener(); - final container = createContainer(); + final container = ProviderContainer.test(); addTearDown(container.dispose); container.listen(provider.notifier, listener.call, fireImmediately: true); @@ -221,7 +153,7 @@ void main() { return notifier; }); final listener = Listener(); - final container = createContainer(); + final container = ProviderContainer.test(); addTearDown(container.dispose); container.listen(provider, listener.call, fireImmediately: true); @@ -245,7 +177,7 @@ void main() { StateNotifierProvider.autoDispose((ref) { return ref.watch(dep) == 0 ? notifier : notifier2; }); - final container = createContainer(); + final container = ProviderContainer.test(); addTearDown(container.dispose); var callCount = 0; @@ -268,20 +200,15 @@ void main() { expect(callCount, 1); }); - test('overrideWithProvider preserves the state across update', () async { + test('overrideWith preserves the state across update', () async { final provider = StateNotifierProvider.autoDispose((_) { return TestNotifier(); }); final notifier = TestNotifier(42); final notifier2 = TestNotifier(21); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - // ignore: deprecated_member_use_from_same_package - provider.overrideWithProvider( - StateNotifierProvider.autoDispose((_) { - return notifier; - }), - ), + provider.overrideWith((_) => notifier), ], ); addTearDown(container.dispose); @@ -298,12 +225,7 @@ void main() { verifyOnly(listener, listener(42, 43)); container.updateOverrides([ - // ignore: deprecated_member_use_from_same_package - provider.overrideWithProvider( - StateNotifierProvider.autoDispose((_) { - return notifier2; - }), - ), + provider.overrideWith((_) => notifier2), ]); await container.pump(); diff --git a/packages/riverpod/test/providers/state_notifier_provider/family_test.dart b/packages/riverpod/test/old/legacy_providers/deprecated/state_notifier_provider/family_test.dart similarity index 79% rename from packages/riverpod/test/providers/state_notifier_provider/family_test.dart rename to packages/riverpod/test/old/legacy_providers/deprecated/state_notifier_provider/family_test.dart index 72de07218..c60e3edea 100644 --- a/packages/riverpod/test/providers/state_notifier_provider/family_test.dart +++ b/packages/riverpod/test/old/legacy_providers/deprecated/state_notifier_provider/family_test.dart @@ -1,8 +1,10 @@ import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; -import '../../utils.dart'; +import '../../../utils.dart'; import 'state_notifier_provider_test.dart'; void main() { @@ -17,14 +19,17 @@ void main() { }); test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = StateNotifierProvider.family, int, int>( (ref, i) => StateController(ref.watch(dep) + i), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -41,36 +46,39 @@ void main() { final provider = StateNotifierProvider.family, int, int>( (ref, _) => controller, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], ); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); expect(container.read(provider(0).notifier), controller); expect(container.read(provider(0)), 0); expect( container.getAllProviderElementsInOrder(), unorderedEquals([ - isA>() + isA() .having((e) => e.origin, 'origin', provider(0)), ]), ); expect(root.getAllProviderElementsInOrder(), isEmpty); }); - test('when using provider.overrideWithProvider', () async { + test('when using provider.overrideWith', () async { final controller = StateController(0); final provider = StateNotifierProvider.family, int, int>( (ref, _) => controller, + dependencies: const [], ); - final root = createContainer(); + final root = ProviderContainer.test(); final controllerOverride = StateController(42); - final container = createContainer( + final container = ProviderContainer.test( parent: root, overrides: [ - provider.overrideWithProvider( - (value) => StateNotifierProvider((ref) => controllerOverride), - ), + provider.overrideWith((ref, value) => controllerOverride), ], ); @@ -80,7 +88,7 @@ void main() { expect( container.getAllProviderElementsInOrder(), unorderedEquals([ - isA>() + isA() .having((e) => e.origin, 'origin', provider(0)), ]), ); @@ -97,9 +105,7 @@ void main() { expect( family('foo').toString(), - equalsIgnoringHashCodes( - 'Example:StateNotifierProvider#05480(foo)', - ), + equalsIgnoringHashCodes('Example(foo)'), ); }, ); @@ -119,9 +125,13 @@ void main() { () { final family = StateNotifierProvider.family( (ref, id) => Counter(), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [family], ); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [family]); expect(container.read(family('0')), 0); expect(container.read(family('0').notifier), isA()); @@ -129,7 +139,7 @@ void main() { expect( container.getAllProviderElementsInOrder(), [ - isA>() + isA() .having((e) => e.provider, 'provider', family('0')), ], ); @@ -140,13 +150,9 @@ void main() { final notifier2 = TestNotifier(42); final provider = StateNotifierProvider.autoDispose .family((ref, a) => TestNotifier()); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - provider.overrideWithProvider((a) { - return StateNotifierProvider.autoDispose( - (ref) => notifier2, - ); - }), + provider.overrideWith((ref, a) => notifier2), ], ); addTearDown(container.dispose); diff --git a/packages/riverpod/test/providers/state_notifier_provider/state_notifier_provider_test.dart b/packages/riverpod/test/old/legacy_providers/deprecated/state_notifier_provider/state_notifier_provider_test.dart similarity index 65% rename from packages/riverpod/test/providers/state_notifier_provider/state_notifier_provider_test.dart rename to packages/riverpod/test/old/legacy_providers/deprecated/state_notifier_provider/state_notifier_provider_test.dart index b0c82de7b..d2fa7f120 100644 --- a/packages/riverpod/test/providers/state_notifier_provider/state_notifier_provider_test.dart +++ b/packages/riverpod/test/old/legacy_providers/deprecated/state_notifier_provider/state_notifier_provider_test.dart @@ -1,12 +1,37 @@ // ignore_for_file: avoid_types_on_closure_parameters +import 'dart:async'; + import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; -import '../../utils.dart'; +import '../../../utils.dart'; void main() { + test('Guards StateNotifier.dispose', () { + final notifier = DelegateNotifier( + onDispose: () => throw StateError('called'), + ); + final container = ProviderContainer.test(); + final provider = StateNotifierProvider( + (_) => notifier, + ); + + container.read(provider); + + final errors = []; + + runZonedGuarded( + () => container.invalidate(provider), + (error, stack) => errors.add(error), + ); + + expect(errors, [isStateError]); + }); + test('supports overrideWith', () { final provider = StateNotifierProvider( (ref) => TestNotifier(), @@ -14,15 +39,10 @@ void main() { final autoDispose = StateNotifierProvider.autoDispose( (ref) => TestNotifier(), ); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - provider.overrideWith( - (StateNotifierProviderRef ref) => TestNotifier(42), - ), - autoDispose.overrideWith( - (AutoDisposeStateNotifierProviderRef ref) => - TestNotifier(84), - ), + provider.overrideWith((ref) => TestNotifier(42)), + autoDispose.overrideWith((ref) => TestNotifier(84)), ], ); @@ -38,18 +58,11 @@ void main() { StateNotifierProvider.autoDispose.family( (ref, arg) => TestNotifier(0 + arg), ); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - family.overrideWith( - (StateNotifierProviderRef ref, int arg) => - TestNotifier(42 + arg), - ), + family.overrideWith((ref, int arg) => TestNotifier(42 + arg)), autoDisposeFamily.overrideWith( - ( - AutoDisposeStateNotifierProviderRef ref, - int arg, - ) => - TestNotifier(84 + arg), + (ref, int arg) => TestNotifier(84 + arg), ), ], ); @@ -62,7 +75,7 @@ void main() { 'on refresh, does not notify listeners if the new value is identical to the previous one', () { // regression test for https://github.com/rrousselGit/riverpod/issues/1560 - final container = createContainer(); + final container = ProviderContainer.test(); final provider = StateNotifierProvider, int>( (ref) => StateController(0), ); @@ -77,46 +90,17 @@ void main() { verifyNoMoreInteractions(listener); }); - test('ref.listenSelf listens to state changes', () { - final listener = Listener(); - final container = createContainer(); - final provider = StateNotifierProvider, int>((ref) { - ref.listenSelf(listener.call); - return StateController(0); - }); - - final notifier = container.read(provider.notifier); - - verifyOnly(listener, listener(null, 0)); - - notifier.state++; - - verifyOnly(listener, listener(0, 1)); - }); - - test('can read and set current StateNotifier', () async { - final container = createContainer(); - final listener = Listener(); - late StateNotifierProviderRef ref; - final provider = StateNotifierProvider((r) { - ref = r; - return Counter(); - }); - - container.listen(provider, listener.call); - - verifyZeroInteractions(listener); - expect(ref.notifier.state, 0); - }); - test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = StateNotifierProvider, int>( (ref) => StateController(ref.watch(dep)), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -131,7 +115,7 @@ void main() { var initialValue = 1; final provider = StateNotifierProvider((ref) => Counter(initialValue)); - final container = createContainer(); + final container = ProviderContainer.test(); expect(container.read(provider), 1); expect(container.read(provider.notifier).state, 1); @@ -144,7 +128,7 @@ void main() { test('can be refreshed', () async { var result = StateController(0); - final container = createContainer(); + final container = ProviderContainer.test(); final provider = StateNotifierProvider, int>((ref) => result); @@ -164,9 +148,13 @@ void main() { final controller = StateController(0); final provider = StateNotifierProvider, int>( (ref) => controller, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], ); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); expect(container.read(provider.notifier), controller); expect(container.read(provider), 0); @@ -174,53 +162,24 @@ void main() { expect( container.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), ]), ); }); - // test('when using provider.overrideWithValue', () async { - // final controller = StateController(0); - // final provider = StateNotifierProvider, int>( - // (ref) => controller); - // final root = createContainer(); - // final controllerOverride = StateController(42); - // final container = createContainer(parent: root, overrides: [ - // provider.overrideWithValue(controllerOverride), - // ]); - - // expect(container.read(provider.notifier), controllerOverride); - // expect(container.read(provider), 42); - // expect(root.getAllProviderElements(), isEmpty); - // expect( - // container.getAllProviderElements(), - // unorderedEquals([ - // isA>() - // .having((e) => e.origin, 'origin', provider), - // isA>() - // .having((e) => e.origin, 'origin', provider.notifier), - // ]), - // ); - // }); - - test('when using provider.overrideWithProvider', () async { + test('when using provider.overrideWith', () async { final controller = StateController(0); final provider = StateNotifierProvider.autoDispose, int>( (ref) => controller, + dependencies: const [], ); - final root = createContainer(); + final root = ProviderContainer.test(); final controllerOverride = StateController(42); - final container = createContainer( + final container = ProviderContainer.test( parent: root, overrides: [ - // ignore: deprecated_member_use_from_same_package - provider.overrideWithProvider( - StateNotifierProvider.autoDispose, int>( - (ref) => controllerOverride, - ), - ), + provider.overrideWith((ref) => controllerOverride), ], ); @@ -230,38 +189,13 @@ void main() { expect( container.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), ]), ); }); }); }); - // test('overriding the provider overrides provider.state too', () { - // final notifier = TestNotifier(42); - // final provider = - // StateNotifierProvider((_) => TestNotifier()); - // final container = createContainer( - // overrides: [ - // provider.overrideWithValue(TestNotifier(10)), - // ], - // ); - // addTearDown(container.dispose); - - // // does not crash - // container.updateOverrides([ - // provider.overrideWithValue(notifier), - // ]); - - // expect(container.read(provider.notifier), notifier); - // expect(container.read(provider), 42); - - // notifier.increment(); - - // expect(container.read(provider), 43); - // }); - test('can specify name', () { final provider = StateNotifierProvider( (_) { @@ -281,7 +215,7 @@ void main() { final provider = StateNotifierProvider((_) { return notifier; }); - final container = createContainer(); + final container = ProviderContainer.test(); addTearDown(container.dispose); expect(container.read(provider.notifier), notifier); @@ -298,7 +232,7 @@ void main() { return notifier; }); final listener = Listener(); - final container = createContainer(); + final container = ProviderContainer.test(); addTearDown(container.dispose); container.listen(provider.notifier, listener.call, fireImmediately: true); @@ -324,7 +258,7 @@ void main() { return TestNotifier(); }); final listener = Listener(); - final container = createContainer(); + final container = ProviderContainer.test(); addTearDown(container.dispose); container.listen(provider, listener.call, fireImmediately: true); @@ -347,7 +281,7 @@ void main() { final provider = StateNotifierProvider((ref) { return ref.watch(dep) == 0 ? notifier : notifier2; }); - final container = createContainer(); + final container = ProviderContainer.test(); addTearDown(container.dispose); var callCount = 0; @@ -383,7 +317,7 @@ void main() { // }); // final notifier = TestNotifier(42); // final notifier2 = TestNotifier(21); - // final container = createContainer(overrides: [ + // final container = ProviderContainer.test(overrides: [ // provider.overrideWithValue(notifier), // ]); // addTearDown(container.dispose); @@ -420,20 +354,15 @@ void main() { // verifyOnly(listener, listener(21, 22)); // }); - test('overrideWithProvider preserves the state across update', () async { + test('overrideWith preserves the state across update', () async { final provider = StateNotifierProvider((_) { return TestNotifier(); }); final notifier = TestNotifier(42); final notifier2 = TestNotifier(21); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - // ignore: deprecated_member_use_from_same_package - provider.overrideWithProvider( - StateNotifierProvider((_) { - return notifier; - }), - ), + provider.overrideWith((_) => notifier), ], ); addTearDown(container.dispose); @@ -451,12 +380,7 @@ void main() { verifyOnly(listener, listener(42, 43)); container.updateOverrides([ - // ignore: deprecated_member_use_from_same_package - provider.overrideWithProvider( - StateNotifierProvider((_) { - return notifier2; - }), - ), + provider.overrideWith((_) => notifier2), ]); await container.pump(); @@ -487,3 +411,15 @@ class TestNotifier extends StateNotifier { return 'TestNotifier($state)'; } } + +class DelegateNotifier extends StateNotifier { + DelegateNotifier({this.onDispose}) : super(0); + + final void Function()? onDispose; + + @override + void dispose() { + onDispose?.call(); + super.dispose(); + } +} diff --git a/packages/riverpod/test/providers/state_provider/state_controller_test.dart b/packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_controller_test.dart similarity index 90% rename from packages/riverpod/test/providers/state_provider/state_controller_test.dart rename to packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_controller_test.dart index a45896c5f..814ec2a76 100644 --- a/packages/riverpod/test/providers/state_provider/state_controller_test.dart +++ b/packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_controller_test.dart @@ -1,4 +1,4 @@ -import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/legacy.dart'; import 'package:test/test.dart'; void main() { diff --git a/packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_provider_auto_dispose_test.dart b/packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_provider_auto_dispose_test.dart new file mode 100644 index 000000000..182e7ba38 --- /dev/null +++ b/packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_provider_auto_dispose_test.dart @@ -0,0 +1,140 @@ +import 'package:riverpod/legacy.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; +import 'package:test/test.dart'; + +void main() { + test('supports .name', () { + expect( + StateProvider.autoDispose((ref) => 0).name, + null, + ); + expect( + StateProvider.autoDispose((ref) => 0, name: 'foo').name, + 'foo', + ); + }); + + test('can be auto-scoped', () async { + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); + final provider = StateProvider.autoDispose( + (ref) => ref.watch(dep), + dependencies: [dep], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [dep.overrideWithValue(42)], + ); + + expect(container.read(provider), 42); + expect(container.read(provider.notifier).state, 42); + + expect(root.getAllProviderElements(), isEmpty); + }); + + test('can refresh .notifier', () async { + var initialValue = 1; + final provider = StateProvider.autoDispose((ref) => initialValue); + final container = ProviderContainer.test(); + + container.listen(provider.notifier, (prev, value) {}); + + expect(container.read(provider), 1); + expect(container.read(provider.notifier).state, 1); + + initialValue = 42; + + expect(container.refresh(provider.notifier).state, 42); + expect(container.read(provider), 42); + }); + + test('can be refreshed', () async { + var result = 0; + final container = ProviderContainer.test(); + final provider = StateProvider.autoDispose((ref) => result); + + final notifier = container.read(provider.notifier); + expect(container.read(provider), 0); + expect(notifier.state, 0); + + result = 42; + expect(container.refresh(provider), 42); + + expect(container.read(provider), 42); + expect(container.read(provider.notifier), isNot(notifier)); + expect(container.read(provider.notifier).state, 42); + }); + + group('scoping an override overrides all the associated subproviders', () { + test('when passing the provider itself', () async { + final provider = StateProvider.autoDispose( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); + + expect(container.read(provider.notifier).state, 0); + expect(container.read(provider), 0); + expect(root.getAllProviderElements(), isEmpty); + expect( + container.getAllProviderElements(), + unorderedEquals([ + isA().having((e) => e.origin, 'origin', provider), + ]), + ); + }); + + test('when using provider.overrideWith', () async { + final provider = StateProvider.autoDispose( + (ref) => 0, + name: 'true', + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + provider.overrideWith((ref) => 42), + ], + ); + + expect(container.read(provider.notifier).state, 42); + expect(container.read(provider), 42); + expect( + container.getAllProviderElements(), + unorderedEquals([ + isA().having((e) => e.origin, 'origin', provider), + ]), + ); + expect(root.getAllProviderElements(), isEmpty); + }); + }); + + group('overrideWith', () { + test( + 'properly disposes of the StateController when the provider is disposed', + () async { + final container = ProviderContainer.test(); + final provider = StateProvider.autoDispose((ref) => 0); + + final notifier = container.read(provider.notifier); + final sub = container.listen(provider, (prev, value) {}); + + expect(notifier.hasListeners, true); + + sub.close(); + await container.pump(); + + expect(() => notifier.hasListeners, throwsStateError); + }, + ); + }); +} diff --git a/packages/riverpod/test/providers/state_provider/state_provider_family_auto_dispose_test.dart b/packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_provider_family_auto_dispose_test.dart similarity index 68% rename from packages/riverpod/test/providers/state_provider/state_provider_family_auto_dispose_test.dart rename to packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_provider_family_auto_dispose_test.dart index c525d24c5..9f5dd6e34 100644 --- a/packages/riverpod/test/providers/state_provider/state_provider_family_auto_dispose_test.dart +++ b/packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_provider_family_auto_dispose_test.dart @@ -1,8 +1,8 @@ +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; -import '../../utils.dart'; - void main() { test('supports .name', () { expect( @@ -29,33 +29,38 @@ void main() { group('scoping an override overrides all the associated subproviders', () { test('when passing the provider itself', () async { - final provider = - StateProvider.autoDispose.family((ref, _) => 0); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = StateProvider.autoDispose.family( + (ref, _) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); expect(container.read(provider(0).notifier).state, 0); expect(container.read(provider(0)), 0); expect( container.getAllProviderElementsInOrder(), unorderedEquals([ - isA>() + isA() .having((e) => e.origin, 'origin', provider(0)), ]), ); expect(root.getAllProviderElementsInOrder(), isEmpty); }); - test('when using provider.overrideWithProvider', () async { - final provider = - StateProvider.autoDispose.family((ref, _) => 0); - final root = createContainer(); - final container = createContainer( + test('when using provider.overrideWith', () async { + final provider = StateProvider.autoDispose.family( + (ref, _) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ - provider.overrideWithProvider( - (value) => StateProvider.autoDispose((ref) => 42), - ), + provider.overrideWith((ref, value) => 42), ], ); @@ -65,7 +70,7 @@ void main() { expect( container.getAllProviderElementsInOrder(), unorderedEquals([ - isA>() + isA() .having((e) => e.origin, 'origin', provider(0)), ]), ); @@ -73,13 +78,16 @@ void main() { }); test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = StateProvider.autoDispose.family( (ref, i) => ref.watch(dep) + i, dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); diff --git a/packages/riverpod/test/providers/state_provider/state_provider_family_test.dart b/packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_provider_family_test.dart similarity index 69% rename from packages/riverpod/test/providers/state_provider/state_provider_family_test.dart rename to packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_provider_family_test.dart index 65698ac4e..3e3f3e277 100644 --- a/packages/riverpod/test/providers/state_provider/state_provider_family_test.dart +++ b/packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_provider_family_test.dart @@ -1,8 +1,8 @@ +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; -import '../../utils.dart'; - void main() { test('supports .name', () { expect( @@ -27,31 +27,38 @@ void main() { group('scoping an override overrides all the associated subproviders', () { test('when passing the provider itself', () async { - final provider = StateProvider.family((ref, _) => 0); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = StateProvider.family( + (ref, _) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); expect(container.read(provider(0).notifier).state, 0); expect(container.read(provider(0)), 0); expect( container.getAllProviderElementsInOrder(), unorderedEquals([ - isA>() + isA() .having((e) => e.origin, 'origin', provider(0)), ]), ); expect(root.getAllProviderElementsInOrder(), isEmpty); }); - test('when using provider.overrideWithProvider', () async { - final provider = StateProvider.family((ref, _) => 0); - final root = createContainer(); - final container = createContainer( + test('when using provider.overrideWith', () async { + final provider = StateProvider.family( + (ref, _) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ - provider.overrideWithProvider( - (value) => StateProvider((ref) => 42), - ), + provider.overrideWith((ref, value) => 42), ], ); @@ -61,7 +68,7 @@ void main() { expect( container.getAllProviderElementsInOrder(), unorderedEquals([ - isA>() + isA() .having((e) => e.origin, 'origin', provider(0)), ]), ); @@ -69,13 +76,16 @@ void main() { }); test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = StateProvider.family( (ref, i) => ref.watch(dep) + i, dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -90,7 +100,7 @@ void main() { final provider = StateProvider.family((ref, a) { return '$a'; }); - final container = createContainer(); + final container = ProviderContainer.test(); expect(container.read(provider(0)), '0'); expect(container.read(provider(1)), '1'); @@ -100,11 +110,9 @@ void main() { final provider = StateProvider.family((ref, a) { return '$a'; }); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - provider.overrideWithProvider((a) { - return StateProvider((ref) => 'override $a'); - }), + provider.overrideWith((ref, a) => 'override $a'), ], ); diff --git a/packages/riverpod/test/providers/state_provider/state_provider_test.dart b/packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_provider_test.dart similarity index 54% rename from packages/riverpod/test/providers/state_provider/state_provider_test.dart rename to packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_provider_test.dart index 7c23161ad..37f5092d6 100644 --- a/packages/riverpod/test/providers/state_provider/state_provider_test.dart +++ b/packages/riverpod/test/old/legacy_providers/deprecated/state_provider/state_provider_test.dart @@ -1,10 +1,12 @@ // ignore_for_file: avoid_types_on_closure_parameters import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; -import '../../utils.dart'; +import '../../../utils.dart'; void main() { test('supports overrideWith', () { @@ -14,10 +16,10 @@ void main() { final autoDispose = StateProvider.autoDispose( (ref) => 0, ); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - provider.overrideWith((StateProviderRef ref) => 42), - autoDispose.overrideWith((AutoDisposeStateProviderRef ref) => 84), + provider.overrideWith((ref) => 42), + autoDispose.overrideWith((ref) => 84), ], ); @@ -30,14 +32,10 @@ void main() { final autoDisposeFamily = StateProvider.autoDispose.family( (ref, arg) => '0 $arg', ); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - family.overrideWith( - (StateProviderRef ref, int arg) => '42 $arg', - ), - autoDisposeFamily.overrideWith( - (AutoDisposeStateProviderRef ref, int arg) => '84 $arg', - ), + family.overrideWith((ref, int arg) => '42 $arg'), + autoDisposeFamily.overrideWith((ref, int arg) => '84 $arg'), ], ); @@ -49,7 +47,7 @@ void main() { 'on refresh, does not notify listeners if the new value is identical to the previous one', () { // regression test for https://github.com/rrousselGit/riverpod/issues/1560 - final container = createContainer(); + final container = ProviderContainer.test(); final provider = StateProvider((ref) => 0); final listener = Listener(); @@ -62,23 +60,6 @@ void main() { verifyNoMoreInteractions(listener); }); - test('ref.listenSelf listens to state changes', () { - final listener = Listener(); - final container = createContainer(); - final provider = StateProvider((ref) { - ref.listenSelf(listener.call); - return 0; - }); - - final notifier = container.read(provider.notifier); - - verifyOnly(listener, listener(null, 0)); - - notifier.state++; - - verifyOnly(listener, listener(0, 1)); - }); - test('supports .name', () { expect( StateProvider((ref) => 0).name, @@ -90,37 +71,17 @@ void main() { ); }); - test('.state clears listener when autoDisposed', () async { - final observer = ObserverMock(); - final container = createContainer(observers: [observer]); - final provider = StateProvider.autoDispose((ref) => 0); - final listener = Listener>(); - - container.listen(provider.notifier, (previous, next) {}); - - // ignore: deprecated_member_use_from_same_package - container.read(provider.state); - await container.pump(); - - verifyZeroInteractions(listener); - - // ignore: deprecated_member_use_from_same_package - container.listen(provider.state, listener.call); - - container.read(provider.notifier).state++; - - verify(listener(any, any)).called(1); - verify(observer.didUpdateProvider(any, any, any, container)).called(1); - }); - test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = StateProvider( (ref) => ref.watch(dep), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -131,74 +92,10 @@ void main() { expect(root.getAllProviderElements(), isEmpty); }); - group('ref.controller', () { - test('can read and change current value', () { - final container = createContainer(); - final listener = Listener(); - late StateProviderRef ref; - final provider = StateProvider((r) { - ref = r; - return 0; - }); - - container.listen(provider, listener.call); - verifyZeroInteractions(listener); - - expect(ref.controller, container.read(provider.notifier)); - - ref.controller.state = 42; - - verifyOnly(listener, listener(0, 42)); - - expect(ref.controller.state, 42); - }); - - test('fails if trying to read the state before it was set', () { - final container = createContainer(); - Object? err; - final provider = StateProvider((ref) { - try { - ref.controller; - } catch (e) { - err = e; - } - return 0; - }); - - container.read(provider); - expect(err, isStateError); - }); - - test('on rebuild, still fails if trying to read the state before was built', - () { - final dep = StateProvider((ref) => false); - final container = createContainer(); - Object? err; - final provider = StateProvider((ref) { - if (ref.watch(dep)) { - try { - ref.controller; - } catch (e) { - err = e; - } - } - return 0; - }); - - container.read(provider); - expect(err, isNull); - - container.read(dep.notifier).state = true; - container.read(provider); - - expect(err, isStateError); - }); - }); - test('can refresh .notifier', () async { var initialValue = 1; final provider = StateProvider((ref) => initialValue); - final container = createContainer(); + final container = ProviderContainer.test(); expect(container.read(provider), 1); expect(container.read(provider.notifier).state, 1); @@ -209,28 +106,23 @@ void main() { expect(container.read(provider), 42); }); - test( - 'can refresh .state', - skip: 'TODO', - () async { - // TODO fix this test - var initialValue = 1; - final provider = StateProvider((ref) => initialValue); - final container = createContainer(); + test('can refresh .state', () async { + var initialValue = 1; + final provider = StateProvider((ref) => initialValue); + final container = ProviderContainer.test(); - expect(container.read(provider), 1); - expect(container.read(provider.notifier).state, 1); + expect(container.read(provider), 1); + expect(container.read(provider.notifier).state, 1); - initialValue = 42; + initialValue = 42; - expect(container.refresh(provider.notifier).state, 42); - expect(container.read(provider), 42); - }, - ); + expect(container.refresh(provider.notifier).state, 42); + expect(container.read(provider), 42); + }); test('can be refreshed', () async { var result = 0; - final container = createContainer(); + final container = ProviderContainer.test(); final provider = StateProvider((ref) => result); final notifier = container.read(provider.notifier); @@ -247,9 +139,15 @@ void main() { group('scoping an override overrides all the associated subproviders', () { test('when passing the provider itself', () async { - final provider = StateProvider((ref) => 0); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = StateProvider( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); expect(container.read(provider.notifier).state, 0); expect(container.read(provider), 0); @@ -257,43 +155,21 @@ void main() { expect( container.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), ]), ); }); - // test('when using provider.overrideWithValue', () async { - // final provider = StateProvider((ref) => 0); - // final root = createContainer(); - // final container = createContainer(parent: root, overrides: [ - // provider.overrideWithValue(StateController(42)), - // ]); - - // expect(container.read(provider.notifier).state, 42); - // expect(container.read(provider), 42); - // expect(root.getAllProviderElements(), isEmpty); - // expect( - // container.getAllProviderElements(), - // unorderedEquals([ - // isA>() - // .having((e) => e.origin, 'origin', provider.state), - // isA>() - // .having((e) => e.origin, 'origin', provider.notifier), - // ]), - // ); - // }); - - test('when using provider.overrideWithProvider', () async { - final provider = StateProvider((ref) => 0); - final root = createContainer(); - final container = createContainer( + test('when using provider.overrideWith', () async { + final provider = StateProvider( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ - // ignore: deprecated_member_use_from_same_package - provider.overrideWithProvider( - StateProvider((ref) => 42), - ), + provider.overrideWith((ref) => 42), ], ); @@ -303,60 +179,17 @@ void main() { expect( container.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), ]), ); }); }); - // test( - // 'overrideWithValue listens to the new StateController and support controller changes', - // () { - // final provider = StateProvider((ref) => 0); - // final container = createContainer(overrides: [ - // provider.overrideWithValue(StateController(42)), - // ]); - // final listener = Listener(); - - // container.listen>( - // provider.state, - // (prev, controller) => listener(prev?.state, controller.state), - // fireImmediately: true, - // ); - - // verifyOnly(listener, listener(null, 42)); - - // container.read(provider.notifier).state++; - - // verifyOnly(listener, listener(43, 43)); - // }, - // ); - - group('overrideWithProvider', () { - // test('listens to state changes', () { - // final override = StateController(21); - // final provider = StateProvider((ref) => 0); - // final container = createContainer(overrides: [ - // provider.overrideWithValue(override), - // ]); - // addTearDown(container.dispose); - // final container2 = ProviderContainer(overrides: [ - // provider.overrideWithProvider( - // StateProvider((ref) => 42), - // ), - // ]); - // addTearDown(container.dispose); - - // expect(container.read(provider.notifier), override); - // expect(container.read(provider), 21); - // expect(container2.read(provider), 42); - // }); - + group('overrideWith', () { test( 'properly disposes of the StateController when the provider is disposed', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = StateProvider.autoDispose((ref) => 0); final notifier = container.read(provider.notifier); @@ -373,7 +206,7 @@ void main() { }); test('Expose a state and allows modifying it', () { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = StateProvider((ref) => 0); final listener = Listener(); @@ -389,7 +222,7 @@ void main() { }); test('disposes the controller when the container is disposed', () { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = StateProvider((ref) => 0); final controller = container.read(provider.notifier); @@ -402,7 +235,7 @@ void main() { }); test('disposes the controller when the provider is re-evaluated', () { - final container = createContainer(); + final container = ProviderContainer.test(); final other = StateProvider((ref) => 0); final provider = StateProvider((ref) => ref.watch(other) * 2); @@ -425,7 +258,7 @@ void main() { group('StateProvider', () { test('.notifier obtains the controller without listening to it', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateProvider((ref) => 0); final provider = StateProvider((ref) { ref.watch(dep); @@ -461,7 +294,7 @@ void main() { group('StateProvider.autoDispose', () { test('.notifier obtains the controller without listening to it', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateProvider((ref) => 0); final provider = StateProvider.autoDispose((ref) { ref.watch(dep); @@ -495,7 +328,7 @@ void main() { }); test('creates a new controller when no longer listened to', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = StateProvider.autoDispose((ref) => 0); final sub = container.listen(provider.notifier, (_, __) {}); @@ -519,7 +352,7 @@ void main() { group('StateProvider.family.autoDispose', () { test('creates a new controller when no longer listened to', () async { - final container = createContainer(); + final container = ProviderContainer.test(); StateProvider.family.autoDispose((ref, id) { return 42; diff --git a/packages/riverpod/test/providers/future_provider/auto_dispose_family_future_provider_test.dart b/packages/riverpod/test/old/legacy_providers/future_provider/auto_dispose_family_future_provider_test.dart similarity index 54% rename from packages/riverpod/test/providers/future_provider/auto_dispose_family_future_provider_test.dart rename to packages/riverpod/test/old/legacy_providers/future_provider/auto_dispose_family_future_provider_test.dart index bd1a3434b..fa7607dd2 100644 --- a/packages/riverpod/test/providers/future_provider/auto_dispose_family_future_provider_test.dart +++ b/packages/riverpod/test/old/legacy_providers/future_provider/auto_dispose_family_future_provider_test.dart @@ -1,6 +1,8 @@ import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/framework.dart'; import 'package:test/test.dart'; +import '../../../src/core/provider_container_test.dart'; import '../../utils.dart'; void main() { @@ -11,30 +13,48 @@ void main() { expect(provider(0).argument, 0); }); - group('scoping an override overrides all the associated subproviders', () { + group('scoping an override overrides all the associated sub-providers', () { test('when passing the provider itself', () async { - final provider = - FutureProvider.autoDispose.family((ref, _) async => 0); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = FutureProvider.autoDispose.family( + (ref, _) async => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); - expect(await container.read(provider(0).future), 0); - expect(container.read(provider(0)), const AsyncData(0)); - expect(container.getAllProviderElementsInOrder(), [ - isA>() - .having((e) => e.origin, 'origin', provider(0)), - ]); - expect(root.getAllProviderElementsInOrder(), isEmpty); + container.listen(provider(0), (_, __) {}); + + expect( + container.pointerManager.familyPointers[provider], + isProviderDirectory( + pointers: {provider(0): isPointer(element: isNotNull)}, + ), + ); + + expect( + root.pointerManager.orphanPointers.pointers, + isEmpty, + ); + expect( + root.pointerManager.familyPointers, + isEmpty, + ); }); test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = FutureProvider.family.autoDispose( (ref, i) => ref.watch(dep) + i, dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -45,17 +65,16 @@ void main() { expect(root.getAllProviderElements(), isEmpty); }); - test('when using provider.overrideWithProvider', () async { - final provider = FutureProvider.autoDispose.family((ref, _) { - return 0; - }); - final root = createContainer(); - final container = createContainer( + test('when using provider.overrideWith', () async { + final provider = FutureProvider.autoDispose.family( + (ref, _) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ - provider.overrideWithProvider( - (value) => FutureProvider.autoDispose((ref) => 42), - ), + provider.overrideWith((ref, value) => 42), ], ); @@ -63,8 +82,7 @@ void main() { expect(container.read(provider(0)), const AsyncData(42)); expect(root.getAllProviderElementsInOrder(), isEmpty); expect(container.getAllProviderElementsInOrder(), [ - isA>() - .having((e) => e.origin, 'origin', provider(0)), + isA().having((e) => e.origin, 'origin', provider(0)), ]); }); }); @@ -73,7 +91,7 @@ void main() { final provider = FutureProvider.autoDispose.family((ref, a) { return Future.value(a * 2); }); - final container = createContainer(); + final container = ProviderContainer.test(); final listener = Listener>(); container.listen(provider(21), listener.call, fireImmediately: true); diff --git a/packages/riverpod/test/providers/future_provider/auto_dispose_future_provider_test.dart b/packages/riverpod/test/old/legacy_providers/future_provider/auto_dispose_future_provider_test.dart similarity index 69% rename from packages/riverpod/test/providers/future_provider/auto_dispose_future_provider_test.dart rename to packages/riverpod/test/old/legacy_providers/future_provider/auto_dispose_future_provider_test.dart index da0a91f6a..13b38f231 100644 --- a/packages/riverpod/test/providers/future_provider/auto_dispose_future_provider_test.dart +++ b/packages/riverpod/test/old/legacy_providers/future_provider/auto_dispose_future_provider_test.dart @@ -1,51 +1,25 @@ import 'dart:async'; import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; import '../../utils.dart'; void main() { - test('can read and set current AsyncValue', () { - final container = createContainer(); - final listener = Listener>(); - late AutoDisposeFutureProviderRef ref; - final provider = FutureProvider.autoDispose((r) { - ref = r; - return 0; - }); - - container.listen(provider, listener.call); - - expect(ref.state, const AsyncData(0)); - verifyZeroInteractions(listener); - - ref.state = const AsyncLoading(); - - expect( - ref.state, - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ); - verifyOnly( - listener, - listener( - const AsyncData(0), - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ), - ); - }); - test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = FutureProvider.autoDispose( (ref) => ref.watch(dep), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -58,7 +32,7 @@ void main() { test('can return a value synchronously, bypassing AsyncLoading', () async { final provider = FutureProvider.autoDispose((ref) => 0); - final container = createContainer(); + final container = ProviderContainer.test(); container.listen(provider, (_, __) {}); @@ -69,7 +43,7 @@ void main() { test('can return an error synchronously, bypassing AsyncLoading', () async { final provider = FutureProvider.autoDispose((ref) => throw UnimplementedError()); - final container = createContainer(); + final container = ProviderContainer.test(); expect( container.read(provider), @@ -87,7 +61,7 @@ void main() { () async { final dep = StateProvider((ref) => Future.value(42)); final provider = FutureProvider.autoDispose((ref) => ref.watch(dep)); - final container = createContainer(); + final container = ProviderContainer.test(); final listener = Listener>(); container.listen(provider, (prev, value) {}); @@ -132,7 +106,7 @@ void main() { test('can refresh .future', () async { var future = Future.value(1); final provider = FutureProvider.autoDispose((ref) => future); - final container = createContainer(); + final container = ProviderContainer.test(); container.listen(provider.future, (prev, value) {}); @@ -146,7 +120,7 @@ void main() { test('can be refreshed', () async { var result = 0; - final container = createContainer(); + final container = ProviderContainer.test(); final provider = FutureProvider.autoDispose((ref) => Future.value(result)); container.listen(provider, (prev, value) {}); @@ -166,7 +140,7 @@ void main() { test('does not update dependents if the created stream did not change', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateProvider((ref) => 0); final completer = Completer(); final provider = FutureProvider.autoDispose((ref) { @@ -188,7 +162,7 @@ void main() { test( '.stream does not update dependents if the created stream did not change', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateProvider((ref) => 0); final completer = Completer(); final provider = FutureProvider.autoDispose((ref) { @@ -212,47 +186,36 @@ void main() { container.read(provider.future).catchError((Object _) => 0); }); - group('scoping an override overrides all the associated subproviders', () { + group('scoping an override overrides all the associated sub-providers', () { test('when passing the provider itself', () async { - final provider = FutureProvider.autoDispose((ref) async => 0); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = FutureProvider.autoDispose( + (ref) async => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); expect(await container.read(provider.future), 0); expect(container.read(provider), const AsyncValue.data(0)); expect(root.getAllProviderElementsInOrder(), isEmpty); expect(container.getAllProviderElementsInOrder(), [ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), ]); }); - // test('when using provider.overrideWithValue', () async { - // final provider = FutureProvider.autoDispose((ref) async => 0); - // final root = createContainer(); - // final container = createContainer(parent: root, overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // expect(await container.read(provider.future), 42); - // expect(container.read(provider), const AsyncValue.data(42)); - // expect(root.getAllProviderElementsInOrder(), isEmpty); - // expect(container.getAllProviderElementsInOrder(), [ - // isA>().having((e) => e.origin, 'origin', provider), - // isA>() - // .having((e) => e.origin, 'origin', provider.future) - // ]); - // }); - - test('when using provider.overrideWithProvider', () async { - final provider = FutureProvider.autoDispose((ref) async => 0); - final root = createContainer(); - final container = createContainer( + test('when using provider.overrideWithValue', () async { + final provider = FutureProvider.autoDispose( + (ref) async => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ - provider - // ignore: deprecated_member_use_from_same_package - .overrideWithProvider(FutureProvider.autoDispose((ref) => 42)), + provider.overrideWithValue(const AsyncValue.data(42)), ], ); @@ -260,8 +223,26 @@ void main() { expect(container.read(provider), const AsyncValue.data(42)); expect(root.getAllProviderElementsInOrder(), isEmpty); expect(container.getAllProviderElementsInOrder(), [ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), + ]); + }); + + test('when using provider.overrideWith', () async { + final provider = FutureProvider.autoDispose( + (ref) async => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider.overrideWith((ref) => 42)], + ); + + expect(await container.read(provider.future), 42); + expect(container.read(provider), const AsyncValue.data(42)); + expect(root.getAllProviderElementsInOrder(), isEmpty); + expect(container.getAllProviderElementsInOrder(), [ + isA().having((e) => e.origin, 'origin', provider), ]); }); }); @@ -273,7 +254,7 @@ void main() { ref.onDispose(onDispose.call); return future; }); - final container = createContainer(); + final container = ProviderContainer.test(); final listener = Listener>(); final sub = diff --git a/packages/riverpod/test/providers/future_provider/family_future_provider_test.dart b/packages/riverpod/test/old/legacy_providers/future_provider/family_future_provider_test.dart similarity index 60% rename from packages/riverpod/test/providers/future_provider/family_future_provider_test.dart rename to packages/riverpod/test/old/legacy_providers/future_provider/family_future_provider_test.dart index 370ffe53a..2f9d98272 100644 --- a/packages/riverpod/test/providers/future_provider/family_future_provider_test.dart +++ b/packages/riverpod/test/old/legacy_providers/future_provider/family_future_provider_test.dart @@ -1,8 +1,7 @@ import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; -import '../../utils.dart'; - void main() { test('specifies `from` & `argument` for related providers', () { final provider = FutureProvider.family((ref, _) => 0); @@ -11,29 +10,37 @@ void main() { expect(provider(0).argument, 0); }); - group('scoping an override overrides all the associated subproviders', () { + group('scoping an override overrides all the associated sub-providers', () { test('when passing the provider itself', () async { - final provider = FutureProvider.family((ref, _) async => 0); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = FutureProvider.family( + (ref, _) async => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); expect(await container.read(provider(0).future), 0); expect(container.read(provider(0)), const AsyncData(0)); expect(container.getAllProviderElementsInOrder(), [ - isA>() - .having((e) => e.origin, 'origin', provider(0)), + isA().having((e) => e.origin, 'origin', provider(0)), ]); expect(root.getAllProviderElementsInOrder(), isEmpty); }); test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = FutureProvider.family( (ref, i) => ref.watch(dep) + i, dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -44,15 +51,16 @@ void main() { expect(root.getAllProviderElements(), isEmpty); }); - test('when using provider.overrideWithProvider', () async { - final provider = FutureProvider.family((ref, _) async => 0); - final root = createContainer(); - final container = createContainer( + test('when using provider.overrideWith', () async { + final provider = FutureProvider.family( + (ref, _) async => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ - provider.overrideWithProvider( - (value) => FutureProvider((ref) async => 42), - ), + provider.overrideWith((ref, value) async => 42), ], ); @@ -60,8 +68,7 @@ void main() { expect(container.read(provider(0)), const AsyncData(42)); expect(root.getAllProviderElementsInOrder(), isEmpty); expect(container.getAllProviderElementsInOrder(), [ - isA>() - .having((e) => e.origin, 'origin', provider(0)), + isA().having((e) => e.origin, 'origin', provider(0)), ]); }); }); @@ -70,7 +77,7 @@ void main() { final provider = FutureProvider.family((ref, a) { return Future.value('$a'); }); - final container = createContainer(); + final container = ProviderContainer.test(); expect(container.read(provider(0)), const AsyncValue.loading()); diff --git a/packages/riverpod/test/providers/future_provider/future_provider_test.dart b/packages/riverpod/test/old/legacy_providers/future_provider/future_provider_test.dart similarity index 50% rename from packages/riverpod/test/providers/future_provider/future_provider_test.dart rename to packages/riverpod/test/old/legacy_providers/future_provider/future_provider_test.dart index 0857d0b83..2755a5730 100644 --- a/packages/riverpod/test/providers/future_provider/future_provider_test.dart +++ b/packages/riverpod/test/old/legacy_providers/future_provider/future_provider_test.dart @@ -3,134 +3,21 @@ import 'dart:async'; import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; import '../../utils.dart'; void main() { - group('FutureProviderRef.future', () { - test( - '.future does not immediately notify listeners when adding a new listener ' - 'to .future flushes the provider', () async { - // Regression test for https://github.com/rrousselGit/riverpod/issues/2041 - - final container = createContainer(); - final onFuture = Listener>(); - - final dep = FutureProvider((ref) => 0); - final provider = Provider( - (ref) { - ref.listen(dep.future, onFuture.call); - return 0; - }, - ); - - container.read(dep); - container.invalidate(dep); - - container.read(provider); - - verifyZeroInteractions(onFuture); - }); - - test('Regression 2041', () async { - final container = createContainer(); - final onFuture = Listener>(); - - final testNotifierProvider = FutureProvider.autoDispose((ref) => 0); - // ProxyProvider is never rebuild directly, but rather indirectly through - // testNotifierProvider. This means the scheduler does not naturally cover it. - // Then testProvider is the one to trigger the rebuild by listening to it. - final proxyProvider = FutureProvider.autoDispose( - (ref) => ref.watch(testNotifierProvider.future), - ); - - var buildCount = 0; - final testProvider = FutureProvider.autoDispose( - (ref) async { - buildCount++; - return (await ref.watch(proxyProvider.future)) + 2; - }, - ); - - container.listen>( - testProvider, - (previous, next) { - if (!next.isLoading && next is AsyncError) { - Zone.current.handleUncaughtError(next.error, next.stackTrace); - } - }, - fireImmediately: true, - ); - - container.invalidate(testNotifierProvider); - container.invalidate(testProvider); - - verifyZeroInteractions(onFuture); - expect(buildCount, 1); - - await container.pump(); - verifyZeroInteractions(onFuture); - - expect(buildCount, 2); - }); - - test('returns the pending future', () async { - final container = createContainer(); - Future? future; - int? value; - final provider = FutureProvider((ref) { - future = ref.future; - if (value == null) return ref.future; - return value; - }); - - container.read(provider); - - expect( - future, - same(container.read(provider.future)), - ); - expect(future, completion(42)); - - value = 42; - container.refresh(provider); - - expect( - future, - same(container.read(provider.future)), - ); - expect(future, completion(42)); - }); - - test('flushes the provider when reading ref.future', () async { - final container = createContainer(); - var result = Future.value(42); - late FutureProviderRef ref; - final provider = FutureProvider((r) { - ref = r; - return result; - }); - - container.read(provider); - - await expectLater(ref.future, completion(42)); - - result = Future.value(21); - container.invalidate(provider); - - expect(ref.future, completion(21)); - }); - }); - test('Supports void type', () async { // Regression test for https://github.com/rrousselGit/riverpod/issues/2028 final testProvider = FutureProvider((ref) async { return Future.value(); }); - final container = createContainer(); + final container = ProviderContainer.test(); expect(container.read(testProvider), const AsyncLoading()); await container.read(testProvider.future); @@ -141,12 +28,10 @@ void main() { test('supports overrideWith', () { final provider = FutureProvider((ref) => 0); final autoDispose = FutureProvider.autoDispose((ref) => 0); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - provider.overrideWith((FutureProviderRef ref) => 42), - autoDispose.overrideWith( - (AutoDisposeFutureProviderRef ref) => 84, - ), + provider.overrideWith((ref) => 42), + autoDispose.overrideWith((ref) => 84), ], ); @@ -154,57 +39,15 @@ void main() { expect(container.read(autoDispose).value, 84); }); - test('Does not skip return value if ref.state was set', () async { - final provider = FutureProvider((ref) async { - await Future.value(); - ref.state = const AsyncData(1); - await Future.value(); - ref.state = const AsyncData(2); - await Future.value(); - return 3; - }); - final container = createContainer(); - final listener = Listener>(); - // Completer used for the sole purpose of being able to await `provider.future` - // Since `provider` emits `AsyncData` before the future completes, then - // `provider.future` completes early. - // As such, awaiting `provider.future` isn't enough to fully await the FutureProvider - final completer = Completer(); - - container.listen>( - provider, - (prev, next) { - if (next.value == 3) { - completer.complete(); - } - listener(prev, next); - }, - fireImmediately: true, - ); - - await completer.future; - - verifyInOrder([ - listener(null, const AsyncLoading()), - listener(const AsyncLoading(), const AsyncData(1)), - listener(const AsyncData(1), const AsyncData(2)), - listener(const AsyncData(2), const AsyncData(3)), - ]); - }); - test('supports family overrideWith', () { final family = FutureProvider.family((ref, arg) => '0 $arg'); final autoDisposeFamily = FutureProvider.autoDispose.family( (ref, arg) => '0 $arg', ); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - family.overrideWith( - (FutureProviderRef ref, int arg) => '42 $arg', - ), - autoDisposeFamily.overrideWith( - (AutoDisposeFutureProviderRef ref, int arg) => '84 $arg', - ), + family.overrideWith((ref, int arg) => '42 $arg'), + autoDisposeFamily.overrideWith((ref, int arg) => '84 $arg'), ], ); @@ -212,30 +55,9 @@ void main() { expect(container.read(autoDisposeFamily(10)).value, '84 10'); }); - test('Emits AsyncLoading before the create function is executed', () async { - final container = createContainer(); - late AsyncValue state; - final provider = FutureProvider((ref) { - state = ref.state; - return 0; - }); - - container.read(provider); - - expect(state, const AsyncLoading()); - - await container.read(provider.future); - container.refresh(provider); - - expect( - state, - const AsyncLoading().copyWithPrevious(const AsyncData(0)), - ); - }); - test('On dispose, .future resolves with the future returned itself', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final completer1 = Completer.sync(); final completer2 = Completer.sync(); var result = completer1.future; @@ -258,7 +80,7 @@ void main() { test( 'provider.future resolves with the new data instead of the old future result', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final completer1 = Completer.sync(); final completer2 = Completer.sync(); var result = completer1.future; @@ -284,7 +106,7 @@ void main() { test( 'sets isRefreshing to true if triggered by a ref.invalidate/ref.refresh', () async { - final container = createContainer(); + final container = ProviderContainer.test(); var count = 0; final provider = FutureProvider((ref) => Future.value(count++)); @@ -311,7 +133,7 @@ void main() { test('does not set isRefreshing if triggered by a dependency change', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateProvider((ref) => 0); final provider = FutureProvider((ref) => Future.value(ref.watch(dep))); @@ -332,7 +154,7 @@ void main() { test( 'does not set isRefreshing if both triggered by a dependency change and ref.refresh', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateProvider((ref) => 0); final provider = FutureProvider((ref) => Future.value(ref.watch(dep))); @@ -351,46 +173,17 @@ void main() { }); }); - test('can read and set current AsyncValue', () { - final container = createContainer(); - final listener = Listener>(); - late FutureProviderRef ref; - final provider = FutureProvider((r) { - ref = r; - return 0; - }); - - container.listen(provider, listener.call); - - expect(ref.state, const AsyncData(0)); - verifyZeroInteractions(listener); - - ref.state = const AsyncLoading(); - - expect( - ref.state, - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ); - - verifyOnly( - listener, - listener( - const AsyncData(0), - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ), - ); - }); - test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = FutureProvider( (ref) => ref.watch(dep), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -403,7 +196,7 @@ void main() { test('can return a value synchronously, bypassing AsyncLoading', () async { final provider = FutureProvider((ref) => 0); - final container = createContainer(); + final container = ProviderContainer.test(); expect(container.read(provider), const AsyncData(0)); await expectLater(container.read(provider.future), completion(0)); @@ -411,7 +204,7 @@ void main() { test('can return an error synchronously, bypassing AsyncLoading', () async { final provider = FutureProvider((ref) => throw UnimplementedError()); - final container = createContainer(); + final container = ProviderContainer.test(); expect( container.read(provider), @@ -427,7 +220,7 @@ void main() { test('can refresh .future', () async { var future = Future.value(1); final provider = FutureProvider((ref) => future); - final container = createContainer(); + final container = ProviderContainer.test(); expect(await container.read(provider.future), 1); @@ -439,7 +232,7 @@ void main() { test('can be refreshed', () async { var result = 0; - final container = createContainer(); + final container = ProviderContainer.test(); final provider = FutureProvider((ref) => Future.value(result)); expect(await container.read(provider.future), 0); @@ -457,7 +250,7 @@ void main() { test('does not update dependents if the created stream did not change', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateProvider((ref) => 0); final completer = Completer(); final provider = FutureProvider((ref) { @@ -479,7 +272,7 @@ void main() { test( '.future does not update dependents if the created future did not change', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateProvider((ref) => 0); final completer = Completer(); final provider = FutureProvider((ref) { @@ -498,46 +291,57 @@ void main() { verifyNoMoreInteractions(listener); }); - group('scoping an override overrides all the associated subproviders', () { + group('scoping an override overrides all the associated sub-providers', () { test('when passing the provider itself', () async { - final provider = FutureProvider((ref) async => 0); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = FutureProvider( + (ref) async => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); expect(await container.read(provider.future), 0); expect(container.read(provider), const AsyncValue.data(0)); expect(root.getAllProviderElementsInOrder(), isEmpty); expect(container.getAllProviderElementsInOrder(), [ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), + ]); + }); + + test('when using provider.overrideWithValue', () async { + final provider = FutureProvider( + (ref) async => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + provider.overrideWithValue(const AsyncValue.data(42)), + ], + ); + + expect(await container.read(provider.future), 42); + expect(container.read(provider), const AsyncValue.data(42)); + expect(root.getAllProviderElementsInOrder(), isEmpty); + expect(container.getAllProviderElementsInOrder(), [ + isA().having((e) => e.origin, 'origin', provider), ]); }); - // test('when using provider.overrideWithValue', () async { - // final provider = FutureProvider((ref) async => 0); - // final root = createContainer(); - // final container = createContainer(parent: root, overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // expect(await container.read(provider.future), 42); - // expect(container.read(provider), const AsyncValue.data(42)); - // expect(root.getAllProviderElementsInOrder(), isEmpty); - // expect(container.getAllProviderElementsInOrder(), [ - // isA>().having((e) => e.origin, 'origin', provider), - // isA>() - // .having((e) => e.origin, 'origin', provider.future) - // ]); - // }); - - test('when using provider.overrideWithProvider', () async { - final provider = FutureProvider((ref) async => 0); - final root = createContainer(); - final container = createContainer( + test('when using provider.overrideWith', () async { + final provider = FutureProvider( + (ref) async => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ - // ignore: deprecated_member_use_from_same_package - provider.overrideWithProvider(FutureProvider((ref) async => 42)), + provider.overrideWith((ref) async => 42), ], ); @@ -545,35 +349,36 @@ void main() { expect(container.read(provider), const AsyncValue.data(42)); expect(root.getAllProviderElementsInOrder(), isEmpty); expect(container.getAllProviderElementsInOrder(), [ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), ]); }); }); - // test( - // 'when overridden with an error but provider.future is not listened to, it should not emit an error to the zone', - // () async { - // final error = Error(); - // final future = FutureProvider((ref) async => 0); + test( + 'when overridden with an error but provider.future is not listened to, ' + 'it should not emit an error to the zone', () async { + final error = Error(); + final future = FutureProvider((ref) async => 0); - // final container = createContainer(overrides: [ - // future.overrideWithValue(AsyncValue.error(error)), - // ]); - // addTearDown(container.dispose); + final container = ProviderContainer.test( + overrides: [ + future.overrideWithValue(AsyncValue.error(error, StackTrace.empty)), + ], + ); + addTearDown(container.dispose); - // expect( - // container.read(future), - // AsyncValue.error(error), - // ); + expect( + container.read(future), + AsyncValue.error(error, StackTrace.empty), + ); - // // the test will naturally fail if a non-caught future is created - // }); + // the test will naturally fail if a non-caught future is created + }); test('throwing inside "create" result in an AsyncValue.error', () { // ignore: only_throw_errors final provider = FutureProvider((ref) => throw 42); - final container = createContainer(); + final container = ProviderContainer.test(); expect( container.read(provider), @@ -597,7 +402,7 @@ void main() { test('handle errors', () async { // ignore: only_throw_errors final provider = FutureProvider((_) async => throw 42); - final container = createContainer(); + final container = ProviderContainer.test(); expect(container.read(provider), const AsyncValue.loading()); @@ -619,7 +424,7 @@ void main() { test('noop if fails after provider dispose', () async { // ignore: only_throw_errors final provider = FutureProvider((_) async => throw 42); - final container = createContainer(); + final container = ProviderContainer.test(); expect(container.read(provider), const AsyncValue.loading()); @@ -629,17 +434,82 @@ void main() { // No errors are reported to the zone }); - test('is AlwaysAliveProviderBase', () { - final provider = FutureProvider((_) async => 42); + group('FutureProvider().future', () { + test( + '.future does not immediately notify listeners when adding a new listener ' + 'to .future flushes the provider', () async { + // Regression test for https://github.com/rrousselGit/riverpod/issues/2041 - expect(provider, isA>>()); - }); + final container = ProviderContainer.test(); + final onFuture = Listener>(); + + final dep = FutureProvider((ref) => 0); + final provider = Provider( + (ref) { + ref.listen(dep.future, onFuture.call); + return 0; + }, + ); + + container.read(dep); + container.invalidate(dep); + + container.read(provider); + + verifyZeroInteractions(onFuture); + }); + + test('Regression 2041', () async { + final container = ProviderContainer.test(); + final onFuture = Listener>(); + + final testNotifierProvider = + FutureProvider.autoDispose((ref) => 0, name: 'testNotifier'); + // ProxyProvider is never rebuild directly, but rather indirectly through + // testNotifierProvider. This means the scheduler does not naturally cover it. + // Then testProvider is the one to trigger the rebuild by listening to it. + final proxyProvider = FutureProvider.autoDispose( + (ref) => ref.watch(testNotifierProvider.future), + name: 'proxy', + ); + + var buildCount = 0; + final testProvider = FutureProvider.autoDispose( + (ref) async { + buildCount++; + final res = (await ref.watch(proxyProvider.future)) + 2; + + return res; + }, + name: 'test', + ); + + container.listen>( + testProvider, + (previous, next) { + if (!next.isLoading && next is AsyncError) { + Zone.current.handleUncaughtError(next.error, next.stackTrace); + } + }, + fireImmediately: true, + ); + + container.invalidate(testNotifierProvider); + container.invalidate(testProvider); + + verifyZeroInteractions(onFuture); + expect(buildCount, 1); + + await container.pump(); + verifyZeroInteractions(onFuture); + + expect(buildCount, 2); + }); - group('FutureProvider().future', () { test('does not update dependents when the future completes', () async { final completer = Completer.sync(); final provider = FutureProvider((_) => completer.future); - final container = createContainer(); + final container = ProviderContainer.test(); var callCount = 0; final dependent = Provider>((ref) { callCount++; @@ -670,7 +540,7 @@ void main() { callCount++; return ref.watch(provider.future); }); - final container = createContainer(); + final container = ProviderContainer.test(); final futureController = container.read(futureProvider.notifier); await expectLater(container.read(dependent), completion(42)); @@ -699,7 +569,7 @@ void main() { callCount++; return ref.watch(provider.future); }); - final container = createContainer(); + final container = ProviderContainer.test(); final futureController = container.read(futureProvider.notifier); @@ -717,7 +587,7 @@ void main() { test('does not update dependents when the future completes', () async { final completer = Completer.sync(); final provider = FutureProvider.autoDispose((_) => completer.future); - final container = createContainer(); + final container = ProviderContainer.test(); var callCount = 0; final dependent = Provider.autoDispose((ref) { callCount++; @@ -745,7 +615,7 @@ void main() { ref.onDispose(() => didDispose = true); return Future.value(42); }); - final container = createContainer(); + final container = ProviderContainer.test(); final sub = container.listen(provider.future, (_, __) {}); expect(didDispose, false); @@ -761,7 +631,7 @@ void main() { }); test('read', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final completer = Completer.sync(); final other = FutureProvider((_) => completer.future); final simple = Provider((_) => 21); @@ -788,7 +658,7 @@ void main() { }); test('exposes data', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final listener = Listener>(); final completer = Completer.sync(); final provider = FutureProvider((_) => completer.future); @@ -809,285 +679,301 @@ void main() { verifyNoMoreInteractions(listener); }); - // group('mock as value', () { - // test('value immediately then other value', () async { - // final provider = FutureProvider((_) async => 0); - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); + group('mock as value', () { + test('value immediately then other value', () async { + final provider = FutureProvider((_) async => 0); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(const AsyncValue.data(42)), + ], + ); - // await expectLater( - // container.read(provider.future), - // completion(42), - // ); + await expectLater( + container.read(provider.future), + completion(42), + ); - // final sub = container.listen(provider, (_, __) {}); + final sub = container.listen(provider, (_, __) {}); - // expect(sub.read(), const AsyncValue.data(42)); + expect(sub.read(), const AsyncValue.data(42)); - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(21)), - // ]); + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.data(21)), + ]); - // await expectLater( - // container.read(provider.future), - // completion(21), - // ); - // expect(sub.read(), const AsyncValue.data(21)); - // }); + await expectLater( + container.read(provider.future), + completion(21), + ); + expect(sub.read(), const AsyncValue.data(21)); + }); - // test('value immediately then error', () async { - // final provider = FutureProvider((_) async => 0); - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); + test('value immediately then error', () async { + final provider = FutureProvider((_) async => 0); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(const AsyncValue.data(42)), + ], + ); - // await expectLater( - // container.read(provider.future), - // completion(42), - // ); + await expectLater( + container.read(provider.future), + completion(42), + ); - // final sub = container.listen(provider, (_, __) {}); + final sub = container.listen(provider, (_, __) {}); - // expect(sub.read(), const AsyncValue.data(42)); + expect(sub.read(), const AsyncValue.data(42)); - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.error(21)), - // ]); + container.updateOverrides([ + provider + .overrideWithValue(const AsyncValue.error(21, StackTrace.empty)), + ]); - // await expectLater( - // container.read(provider.future), - // throwsA(21), - // ); - // expect( - // sub.read(), - // const AsyncValue.error(21).copyWithPrevious(const AsyncData(42)), - // ); - // }); + await expectLater( + container.read(provider.future), + throwsA(21), + ); + expect( + sub.read(), + const AsyncValue.error(21, StackTrace.empty) + .copyWithPrevious(const AsyncData(42)), + ); + }); - // test('value immediately then loading', () async { - // final provider = FutureProvider((_) async => 0); - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); + test('value immediately then loading', () async { + final provider = FutureProvider((_) async => 0); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(const AsyncValue.data(42)), + ], + ); - // final future = container.read(provider.future); + final future = container.read(provider.future); - // await expectLater( - // future, - // completion(42), - // ); + await expectLater( + future, + completion(42), + ); - // final sub = container.listen(provider, (_, __) {}); + final sub = container.listen(provider, (_, __) {}); - // expect(sub.read(), const AsyncValue.data(42)); + expect(sub.read(), const AsyncValue.data(42)); - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.loading()), + ]); - // expect(container.read(provider.future), isNot(future)); - // expect( - // sub.read(), - // const AsyncLoading() - // .copyWithPrevious(const AsyncValue.data(42)), - // ); - // }); + expect(container.read(provider.future), isNot(future)); + expect( + sub.read(), + const AsyncLoading() + .copyWithPrevious(const AsyncValue.data(42)), + ); + }); - // test('loading immediately then value', () async { - // final provider = FutureProvider((_) async => 0); - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); + test('loading immediately then value', () async { + final provider = FutureProvider((_) async => 0); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(const AsyncValue.loading()), + ], + ); - // final future = container.read(provider.future); + final future = container.read(provider.future); - // final sub = container.listen(provider, (_, __) {}); + final sub = container.listen(provider, (_, __) {}); - // expect(sub.read(), const AsyncValue.loading()); + expect(sub.read(), const AsyncValue.loading()); - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.data(42)), + ]); - // expect(sub.read(), const AsyncValue.data(42)); + expect(sub.read(), const AsyncValue.data(42)); - // await expectLater(future, completion(42)); - // }); + await expectLater(future, completion(42)); + }); - // test('loading immediately then error', () async { - // final provider = FutureProvider((_) async => 0); - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); + test('loading immediately then error', () async { + final provider = FutureProvider((_) async => 0); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(const AsyncValue.loading()), + ], + ); - // final future = container.read(provider.future); + final future = container.read(provider.future); - // final sub = container.listen(provider, (_, __) {}); + final sub = container.listen(provider, (_, __) {}); - // expect(sub.read(), const AsyncValue.loading()); + expect(sub.read(), const AsyncValue.loading()); - // final stackTrace = StackTrace.current; + final stackTrace = StackTrace.current; - // container.updateOverrides([ - // provider - // .overrideWithValue(AsyncValue.error(42, stackTrace: stackTrace)), - // ]); + container.updateOverrides([ + provider.overrideWithValue(AsyncValue.error(42, stackTrace)), + ]); - // expect(sub.read(), AsyncValue.error(42, stackTrace: stackTrace)); + expect(sub.read(), AsyncValue.error(42, stackTrace)); - // await expectLater(future, throwsA(42)); - // }); + await expectLater(future, throwsA(42)); + }); - // test('loading immediately then loading', () async { - // final provider = FutureProvider((_) async => 0); - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - // final listener = Listener>(); + test('loading immediately then loading', () async { + final provider = FutureProvider((_) async => 0); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(const AsyncValue.loading()), + ], + ); + final listener = Listener>(); - // final future = container.read(provider.future); + final future = container.read(provider.future); - // container.listen(provider, listener, fireImmediately: true); + container.listen(provider, listener.call, fireImmediately: true); - // verifyOnly(listener, listener(null, const AsyncValue.loading())); + verifyOnly(listener, listener(null, const AsyncValue.loading())); - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.loading()), + ]); - // verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener); - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.data(42)), + ]); - // verifyOnly( - // listener, - // listener(const AsyncValue.loading(), const AsyncValue.data(42)), - // ); - // await expectLater(future, completion(42)); - // }); + verifyOnly( + listener, + listener(const AsyncValue.loading(), const AsyncValue.data(42)), + ); + await expectLater(future, completion(42)); + }); - // test('error immediately then different error', () async { - // final stackTrace = StackTrace.current; - // final provider = FutureProvider((_) async => 0); - // final container = createContainer(overrides: [ - // provider - // .overrideWithValue(AsyncValue.error(42, stackTrace: stackTrace)), - // ]); + test('error immediately then different error', () async { + final stackTrace = StackTrace.current; + final provider = FutureProvider((_) async => 0); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(AsyncValue.error(42, stackTrace)), + ], + ); + + await expectLater( + container.read(provider.future), + throwsA(42), + ); + + final sub = container.listen(provider, (_, __) {}); + + expect(sub.read(), AsyncValue.error(42, stackTrace)); + + container.updateOverrides([ + provider.overrideWithValue(AsyncValue.error(21, stackTrace)), + ]); - // await expectLater( - // container.read(provider.future), - // throwsA(42), - // ); + await expectLater( + container.read(provider.future), + throwsA(21), + ); + expect(sub.read(), AsyncValue.error(21, stackTrace)); + }); + + test('error immediately then different stacktrace', () async { + final stackTrace = StackTrace.current; + final provider = FutureProvider((_) async => 0); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(AsyncValue.error(42, stackTrace)), + ], + ); + + final future = container.read(provider.future); - // final sub = container.listen(provider, (_, __) {}); + await expectLater(future, throwsA(42)); - // expect(sub.read(), AsyncValue.error(42, stackTrace: stackTrace)); + final sub = container.listen(provider, (_, __) {}); - // container.updateOverrides([ - // provider - // .overrideWithValue(AsyncValue.error(21, stackTrace: stackTrace)), - // ]); - - // await expectLater( - // container.read(provider.future), - // throwsA(21), - // ); - // expect(sub.read(), AsyncValue.error(21, stackTrace: stackTrace)); - // }); + expect(sub.read(), AsyncValue.error(42, stackTrace)); - // test('error immediately then different stacktrace', () async { - // final stackTrace = StackTrace.current; - // final provider = FutureProvider((_) async => 0); - // final container = createContainer(overrides: [ - // provider - // .overrideWithValue(AsyncValue.error(42, stackTrace: stackTrace)), - // ]); - - // final future = container.read(provider.future); - - // await expectLater(future, throwsA(42)); - - // final sub = container.listen(provider, (_, __) {}); - - // expect(sub.read(), AsyncValue.error(42, stackTrace: stackTrace)); - - // final stack2 = StackTrace.current; - - // container.updateOverrides([ - // provider.overrideWithValue(AsyncValue.error(42, stackTrace: stack2)), - // ]); - - // expect( - // container.read(provider.future), - // isNot(future), - // ); - // await expectLater( - // container.read(provider.future), - // throwsA(42), - // ); - // expect(sub.read(), AsyncValue.error(42, stackTrace: stack2)); - // }); - - // test('error immediately then data', () async { - // final stackTrace = StackTrace.current; - // final provider = FutureProvider((_) async => 0); - // final container = createContainer(overrides: [ - // provider - // .overrideWithValue(AsyncValue.error(42, stackTrace: stackTrace)), - // ]); - - // await expectLater( - // container.read(provider.future), - // throwsA(42), - // ); - - // final sub = container.listen(provider, (_, __) {}); - - // expect(sub.read(), AsyncValue.error(42, stackTrace: stackTrace)); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // await expectLater( - // container.read(provider.future), - // completion(42), - // ); - // expect( - // sub.read(), - // const AsyncValue.data(42) - // .copyWithPrevious(AsyncError(42, stackTrace: stackTrace)), - // ); - // }); - - // test('error immediately then loading', () async { - // final stackTrace = StackTrace.current; - // final provider = FutureProvider((_) async => 0); - // final container = createContainer(overrides: [ - // provider - // .overrideWithValue(AsyncValue.error(42, stackTrace: stackTrace)), - // ]); - - // final future = container.read(provider.future); - // await expectLater(future, throwsA(42)); - - // final sub = container.listen(provider, (_, __) {}); - - // expect(sub.read(), AsyncValue.error(42, stackTrace: stackTrace)); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // expect(container.read(provider.future), isNot(future)); - // expect( - // sub.read(), - // const AsyncLoading() - // .copyWithPrevious(AsyncError(42, stackTrace: stackTrace)), - // ); - // }); - // }); + final stack2 = StackTrace.current; + + container.updateOverrides([ + provider.overrideWithValue(AsyncValue.error(42, stack2)), + ]); + + expect( + container.read(provider.future), + isNot(future), + ); + await expectLater( + container.read(provider.future), + throwsA(42), + ); + expect(sub.read(), AsyncValue.error(42, stack2)); + }); + + test('error immediately then data', () async { + final stackTrace = StackTrace.current; + final provider = FutureProvider((_) async => 0); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(AsyncValue.error(42, stackTrace)), + ], + ); + + await expectLater( + container.read(provider.future), + throwsA(42), + ); + + final sub = container.listen(provider, (_, __) {}); + + expect(sub.read(), AsyncValue.error(42, stackTrace)); + + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.data(42)), + ]); + + await expectLater( + container.read(provider.future), + completion(42), + ); + expect( + sub.read(), + const AsyncValue.data(42) + .copyWithPrevious(AsyncError(42, stackTrace)), + ); + }); + + test('error immediately then loading', () async { + final stackTrace = StackTrace.current; + final provider = FutureProvider((_) async => 0); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(AsyncValue.error(42, stackTrace)), + ], + ); + + final future = container.read(provider.future); + await expectLater(future, throwsA(42)); + + final sub = container.listen(provider, (_, __) {}); + + expect(sub.read(), AsyncValue.error(42, stackTrace)); + + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.loading()), + ]); + + expect(container.read(provider.future), isNot(future)); + expect( + sub.read(), + const AsyncLoading() + .copyWithPrevious(AsyncError(42, stackTrace)), + ); + }); + }); } diff --git a/packages/riverpod/test/providers/provider/auto_dispose_provider_family_test.dart b/packages/riverpod/test/old/legacy_providers/provider/auto_dispose_provider_family_test.dart similarity index 74% rename from packages/riverpod/test/providers/provider/auto_dispose_provider_family_test.dart rename to packages/riverpod/test/old/legacy_providers/provider/auto_dispose_provider_family_test.dart index 3db83acc5..32b7f67b1 100644 --- a/packages/riverpod/test/providers/provider/auto_dispose_provider_family_test.dart +++ b/packages/riverpod/test/old/legacy_providers/provider/auto_dispose_provider_family_test.dart @@ -1,5 +1,6 @@ import 'package:mockito/mockito.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; import '../../utils.dart'; @@ -15,27 +16,35 @@ void main() { group('scoping an override overrides all the associated subproviders', () { test('when passing the provider itself', () { - final provider = Provider.autoDispose.family((ref, _) => 0); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = Provider.autoDispose.family( + (ref, _) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); expect(container.read(provider(0)), 0); expect(container.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', provider(0)), + isA().having((e) => e.origin, 'origin', provider(0)), ]); expect(root.getAllProviderElements(), isEmpty); }); }); test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = Provider.family.autoDispose( (ref, i) => ref.watch(dep) + i, dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -52,7 +61,7 @@ void main() { return '$value'; }); final listener = Listener(); - final container = createContainer(); + final container = ProviderContainer.test(); final sub = container.listen(provider(0), listener.call, fireImmediately: true); @@ -76,11 +85,9 @@ void main() { final listener = Listener(); final container = ProviderContainer( overrides: [ - provider.overrideWithProvider((value) { - return Provider.autoDispose((ref) { - ref.onDispose(onDispose.call); - return '$value override'; - }); + provider.overrideWith((ref, value) { + ref.onDispose(onDispose.call); + return '$value override'; }), ], ); diff --git a/packages/riverpod/test/old/legacy_providers/provider/auto_dispose_provider_test.dart b/packages/riverpod/test/old/legacy_providers/provider/auto_dispose_provider_test.dart new file mode 100644 index 000000000..643c0b4a1 --- /dev/null +++ b/packages/riverpod/test/old/legacy_providers/provider/auto_dispose_provider_test.dart @@ -0,0 +1,100 @@ +import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; +import 'package:test/test.dart'; + +void main() { + group('Provider.autoDispose', () { + test('can be refreshed', () async { + var result = 0; + final container = ProviderContainer.test(); + final provider = Provider.autoDispose((ref) => result); + + expect(container.read(provider), 0); + + result = 1; + expect(container.refresh(provider), 1); + + expect(container.read(provider), 1); + }); + + group('scoping an override overrides all the associated subproviders', () { + test('when passing the provider itself', () { + final provider = Provider.autoDispose( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); + + expect(container.read(provider), 0); + expect(container.getAllProviderElements(), [ + isA().having((e) => e.origin, 'origin', provider), + ]); + expect(root.getAllProviderElements(), isEmpty); + }); + + test('when using provider.overrideWithValue', () { + final provider = Provider.autoDispose( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + provider.overrideWithValue(42), + ], + ); + + expect(container.read(provider), 42); + expect(container.getAllProviderElements(), [ + isA().having((e) => e.origin, 'origin', provider), + ]); + expect(root.getAllProviderElements(), isEmpty); + }); + + test('when using provider.overrideWith', () { + final provider = Provider.autoDispose( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + provider.overrideWith((ref) => 42), + ], + ); + + expect(container.read(provider), 42); + expect(container.getAllProviderElements(), [ + isA().having((e) => e.origin, 'origin', provider), + ]); + expect(root.getAllProviderElements(), isEmpty); + }); + }); + + test('can be auto-scoped', () async { + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); + final provider = Provider.autoDispose( + (ref) => ref.watch(dep), + dependencies: [dep], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [dep.overrideWithValue(42)], + ); + + expect(container.read(provider), 42); + + expect(root.getAllProviderElements(), isEmpty); + }); + }); +} diff --git a/packages/riverpod/test/providers/provider/provider_family_test.dart b/packages/riverpod/test/old/legacy_providers/provider/provider_family_test.dart similarity index 54% rename from packages/riverpod/test/providers/provider/provider_family_test.dart rename to packages/riverpod/test/old/legacy_providers/provider/provider_family_test.dart index 17c435671..b0e13e1c9 100644 --- a/packages/riverpod/test/providers/provider/provider_family_test.dart +++ b/packages/riverpod/test/old/legacy_providers/provider/provider_family_test.dart @@ -1,8 +1,7 @@ import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; -import '../../utils.dart'; - void main() { group('Provider.family', () { test('specifies `from` & `argument` for related providers', () { @@ -14,45 +13,53 @@ void main() { group('scoping an override overrides all the associated subproviders', () { test('when passing the provider itself', () { - final provider = Provider.family((ref, _) => 0); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = Provider.family( + (ref, _) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); expect(container.read(provider(0)), 0); expect(container.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', provider(0)), + isA().having((e) => e.origin, 'origin', provider(0)), ]); expect(root.getAllProviderElements(), isEmpty); }); - test('when using provider.overrideWithProvider', () { - final provider = Provider.family((ref, _) => 0); - final root = createContainer(); - final container = createContainer( + test('when using provider.overrideWith', () { + final provider = Provider.family( + (ref, _) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, - overrides: [ - provider.overrideWithProvider((value) => Provider((ref) => 42)), - ], + overrides: [provider.overrideWith((ref, value) => 42)], ); expect(root.getAllProviderElements(), isEmpty); expect(container.read(provider(0)), 42); expect(container.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', provider(0)), + isA().having((e) => e.origin, 'origin', provider(0)), ]); }); }); test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = Provider.family( (ref, i) => ref.watch(dep) + i, dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); diff --git a/packages/riverpod/test/old/legacy_providers/provider/provider_test.dart b/packages/riverpod/test/old/legacy_providers/provider/provider_test.dart new file mode 100644 index 000000000..917d9c82b --- /dev/null +++ b/packages/riverpod/test/old/legacy_providers/provider/provider_test.dart @@ -0,0 +1,229 @@ +// ignore_for_file: avoid_types_on_closure_parameters + +import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; +import 'package:test/test.dart'; + +import '../../utils.dart'; + +void main() { + group('Provider', () { + test('supports overrideWith', () { + final provider = Provider((ref) => 0); + final autoDispose = Provider.autoDispose((ref) => 0); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWith((ref) => 42), + autoDispose.overrideWith((ref) => 84), + ], + ); + + expect(container.read(provider), 42); + expect(container.read(autoDispose), 84); + }); + + test('supports family overrideWith', () { + final family = Provider.family((ref, arg) => '0 $arg'); + final autoDisposeFamily = Provider.autoDispose.family( + (ref, arg) => '0 $arg', + ); + final container = ProviderContainer.test( + overrides: [ + family.overrideWith((ref, int arg) => '42 $arg'), + autoDisposeFamily.overrideWith((ref, int arg) => '84 $arg'), + ], + ); + + expect(container.read(family(10)), '42 10'); + expect(container.read(autoDisposeFamily(10)), '84 10'); + }); + + test('can be refreshed', () async { + var result = 0; + final container = ProviderContainer.test(); + final provider = Provider((ref) => result); + + expect(container.read(provider), 0); + + result = 1; + expect(container.refresh(provider), 1); + + expect(container.read(provider), 1); + }); + + group('scoping an override overrides all the associated sub-providers', () { + test('when passing the provider itself', () { + final provider = Provider( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); + + expect(container.read(provider), 0); + expect(container.getAllProviderElements(), [ + isA().having((e) => e.origin, 'origin', provider), + ]); + expect(root.getAllProviderElements(), isEmpty); + }); + + test('when using provider.overrideWithValue', () { + final provider = Provider( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider.overrideWithValue(42)], + ); + + expect(container.read(provider), 42); + expect(container.getAllProviderElements(), [ + isA().having((e) => e.origin, 'origin', provider), + ]); + expect(root.getAllProviderElements(), isEmpty); + }); + + test('when using provider.overrideWith', () { + final provider = Provider( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + provider.overrideWith((ref) => 42), + ], + ); + + expect(container.read(provider), 42); + expect(container.getAllProviderElements(), [ + isA().having((e) => e.origin, 'origin', provider), + ]); + expect(root.getAllProviderElements(), isEmpty); + }); + }); + + test('can specify name', () { + final provider = Provider( + (_) => 0, + name: 'example', + ); + + expect(provider.name, 'example'); + + final provider2 = Provider((_) => 0); + + expect(provider2.name, isNull); + }); + }); + + test('dispose', () { + final container = ProviderContainer.test(); + final onDispose = OnDisposeMock(); + final provider = Provider((ref) { + ref.onDispose(onDispose.call); + return 42; + }); + + expect(container.read(provider), 42); + + verifyZeroInteractions(onDispose); + + container.dispose(); + + verify(onDispose()).called(1); + }); + + test('Read creates the value only once', () { + final container = ProviderContainer.test(); + var callCount = 0; + final provider = Provider((ref) { + callCount++; + return 42; + }); + + expect(callCount, 0); + expect(container.read(provider), 42); + expect(callCount, 1); + + expect(container.read(provider), 42); + expect(callCount, 1); + }); + + group('updateShouldNotify', () { + test("rebuild don't notify clients if == doesn't change", () { + final container = ProviderContainer.test(); + final counter = Counter(); + final other = StateNotifierProvider((ref) => counter); + var buildCount = 0; + final provider = Provider((ref) { + buildCount++; + return ref.watch(other).isEven; + }); + final listener = Listener(); + + final sub = + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, true)); + expect(sub.read(), true); + expect(buildCount, 1); + + counter.increment(); + counter.increment(); + + expect(sub.read(), true); + expect(buildCount, 2); + verifyNoMoreInteractions(listener); + }); + + test('rebuild notify clients if == did change', () { + final container = ProviderContainer.test(); + final counter = Counter(); + final other = StateNotifierProvider((ref) => counter); + final provider = Provider((ref) { + return ref.watch(other).isEven; + }); + final listener = Listener(); + + final sub = + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, true)); + expect(sub.read(), true); + + counter.increment(); + + expect(sub.read(), false); + verifyOnly(listener, listener(true, false)); + }); + }); + + test('can be auto-scoped', () async { + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); + final provider = Provider( + (ref) => ref.watch(dep), + dependencies: [dep], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [dep.overrideWithValue(42)], + ); + + expect(container.read(provider), 42); + + expect(root.getAllProviderElements(), isEmpty); + }); +} diff --git a/packages/riverpod/test/old/legacy_providers/scoped_provider/scoped_provider_test.dart b/packages/riverpod/test/old/legacy_providers/scoped_provider/scoped_provider_test.dart new file mode 100644 index 000000000..f2c9528ac --- /dev/null +++ b/packages/riverpod/test/old/legacy_providers/scoped_provider/scoped_provider_test.dart @@ -0,0 +1,140 @@ +import 'package:riverpod/legacy.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; +import 'package:test/test.dart'; + +import '../../utils.dart'; + +void main() { + test( + 'It is possible to read provider sub-values by specifying the provider in `dependencies`', + () { + final dep = StateProvider((ref) => 0); + final provider = Provider( + (ref) => ref.watch(dep.notifier), + dependencies: [dep], + ); + final container = ProviderContainer.test(); + + expect(container.read(provider).state, 0); + }); + + group('scoping mechanism', () { + test('use the deepest override', () { + final provider = Provider( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test( + overrides: [provider.overrideWithValue(1)], + ); + final mid = ProviderContainer.test( + parent: root, + overrides: [ + provider.overrideWithValue(42), + ], + ); + final container = ProviderContainer.test(parent: mid); + + expect(container.read(provider), 42); + + expect(container.getAllProviderElements(), isEmpty); + expect(mid.getAllProviderElements(), [ + isA>() + .having((e) => e.origin, 'origin', provider) + .having((e) => e.readSelf(), 'readSelf()', 42), + ]); + expect(root.getAllProviderElements(), isEmpty); + }); + + test('can read both parent and child simultaneously', () async { + final provider = Provider( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test( + overrides: [provider.overrideWithValue(21)], + ); + final container = ProviderContainer.test( + parent: root, + overrides: [provider.overrideWithValue(42)], + ); + + expect(container.read(provider), 42); + expect(root.read(provider), 21); + expect(container.read(provider), 42); + expect(root.read(provider), 21); + }); + + test('can be overridden on non-root container', () { + final provider = Provider( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider.overrideWithValue(42)], + ); + + expect(container.read(provider), 42); + }); + + test('can listen to other scoped providers', () async { + final listener = Listener(); + final provider = Provider( + (ref) => 0, + dependencies: const [], + ); + final provider2 = Provider( + (ref) => ref.watch(provider) * 2, + dependencies: [provider], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + provider.overrideWithValue(1), + provider2, + ], + ); + + container.listen(provider2, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 2)); + + container.updateOverrides([ + provider.overrideWithValue(2), + provider2, + ]); + + await container.pump(); + + verifyOnly(listener, listener(2, 4)); + }); + + test('can listen to other normal providers', () async { + final listener = Listener(); + final provider = StateProvider((ref) => 1); + final provider2 = Provider( + (ref) => ref.watch(provider) * 2, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider2], + ); + + container.listen(provider2, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 2)); + + root.read(provider.notifier).state++; + + await container.pump(); + + verifyOnly(listener, listener(2, 4)); + }); + }); +} diff --git a/packages/riverpod/test/providers/stream_provider/auto_dispose_family_stream_provider_test.dart b/packages/riverpod/test/old/legacy_providers/stream_provider/auto_dispose_family_stream_provider_test.dart similarity index 74% rename from packages/riverpod/test/providers/stream_provider/auto_dispose_family_stream_provider_test.dart rename to packages/riverpod/test/old/legacy_providers/stream_provider/auto_dispose_family_stream_provider_test.dart index c9ae50a84..b417d6e83 100644 --- a/packages/riverpod/test/providers/stream_provider/auto_dispose_family_stream_provider_test.dart +++ b/packages/riverpod/test/old/legacy_providers/stream_provider/auto_dispose_family_stream_provider_test.dart @@ -1,4 +1,5 @@ import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; import '../../utils.dart'; @@ -16,20 +17,25 @@ void main() { group('scoping an override overrides all the associated subproviders', () { test('when passing the provider itself', () async { - final provider = StreamProvider.autoDispose - .family((ref, _) => Stream.value(0)); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = StreamProvider.autoDispose.family( + (ref, _) => Stream.value(0), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); + + container.listen(provider(0), (p, n) {}); - // ignore: deprecated_member_use_from_same_package - expect(await container.read(provider(0).stream).first, 0); expect(await container.read(provider(0).future), 0); expect(container.read(provider(0)), const AsyncData(0)); expect(root.getAllProviderElements(), isEmpty); expect( container.getAllProviderElements(), unorderedEquals([ - isA>() + isA() .having((e) => e.origin, 'origin', provider(0)), ]), ); @@ -40,7 +46,7 @@ void main() { final provider = StreamProvider.autoDispose.family((ref, a) { return Stream.value(a * 2); }); - final container = createContainer(); + final container = ProviderContainer.test(); final listener = Listener>(); container.listen(provider(21), listener.call, fireImmediately: true); @@ -55,15 +61,13 @@ void main() { ); }); - test('overrideWithProvider', () async { + test('overrideWith', () async { final provider = StreamProvider.autoDispose.family((ref, a) { return Stream.value(a * 2); }); final container = ProviderContainer( overrides: [ - provider.overrideWithProvider((a) { - return StreamProvider.autoDispose((ref) => Stream.value(a * 4)); - }), + provider.overrideWith((ref, a) => Stream.value(a * 4)), ], ); final listener = Listener>(); @@ -84,19 +88,22 @@ void main() { }); test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = StreamProvider.autoDispose.family( (ref, i) => Stream.value(ref.watch(dep) + i), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); - // ignore: deprecated_member_use_from_same_package - await expectLater(container.read(provider(10).stream), emits(52)); + container.listen(provider(10), (p, n) {}); + await expectLater(container.read(provider(10).future), completion(52)); expect(container.read(provider(10)), const AsyncData(52)); diff --git a/packages/riverpod/test/providers/stream_provider/auto_dispose_stream_provider_test.dart b/packages/riverpod/test/old/legacy_providers/stream_provider/auto_dispose_stream_provider_test.dart similarity index 53% rename from packages/riverpod/test/providers/stream_provider/auto_dispose_stream_provider_test.dart rename to packages/riverpod/test/old/legacy_providers/stream_provider/auto_dispose_stream_provider_test.dart index 9965473be..8bf9a8af6 100644 --- a/packages/riverpod/test/providers/stream_provider/auto_dispose_stream_provider_test.dart +++ b/packages/riverpod/test/old/legacy_providers/stream_provider/auto_dispose_stream_provider_test.dart @@ -1,66 +1,34 @@ +// ignore_for_file: avoid_types_on_closure_parameters + import 'dart:async'; import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; import '../../utils.dart'; void main() { group('StreamProvider.autoDispose', () { - test('can read and set current AsyncValue', () async { - final container = createContainer(); - final listener = Listener>(); - late AutoDisposeStreamProviderRef ref; - final provider = StreamProvider.autoDispose((r) { - ref = r; - return Stream.value(0); - }); - - container.listen(provider, listener.call); - - await container.read(provider.future); - expect(ref.state, const AsyncData(0)); - verifyOnly( - listener, - listener( - const AsyncLoading(), - const AsyncData(0), - ), - ); - - ref.state = const AsyncLoading(); - - expect( - ref.state, - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ); - - verifyOnly( - listener, - listener( - const AsyncData(0), - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ), - ); - }); - test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = StreamProvider.autoDispose( (ref) => Stream.value(ref.watch(dep)), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); - // ignore: deprecated_member_use_from_same_package - await expectLater(container.read(provider.stream), emits(42)); + container.listen(provider, (p, n) {}); + await expectLater(container.read(provider.future), completion(42)); expect(container.read(provider), const AsyncData(42)); @@ -72,7 +40,7 @@ void main() { () async { final dep = StateProvider((ref) => Stream.value(42)); final provider = StreamProvider.autoDispose((ref) => ref.watch(dep)); - final container = createContainer(); + final container = ProviderContainer.test(); final listener = Listener>(); final controller = StreamController(); addTearDown(controller.close); @@ -80,9 +48,8 @@ void main() { container.listen(provider, (prev, value) {}); await expectLater( - // ignore: deprecated_member_use_from_same_package - container.read(provider.stream), - emits(42), + container.read(provider.future), + completion(42), ); expect( container.read(provider), @@ -106,9 +73,8 @@ void main() { verifyNoMoreInteractions(listener); await expectLater( - // ignore: deprecated_member_use_from_same_package - container.read(provider.stream), - emits(21), + container.read(provider.future), + completion(21), ); expect( container.read(provider), @@ -118,14 +84,12 @@ void main() { test('can be refreshed', () async { var result = 0; - final container = createContainer(); + final container = ProviderContainer.test(); final provider = StreamProvider.autoDispose((ref) => Stream.value(result)); container.listen(provider, (_, __) {}); - // ignore: deprecated_member_use_from_same_package - expect(container.read(provider.stream), emits(0)); expect(await container.read(provider.future), 0); expect(container.read(provider), const AsyncValue.data(0)); @@ -136,15 +100,13 @@ void main() { .copyWithPrevious(const AsyncValue.data(0)), ); - // ignore: deprecated_member_use_from_same_package - expect(container.read(provider.stream), emits(1)); expect(await container.read(provider.future), 1); expect(container.read(provider), const AsyncValue.data(1)); }); test('does not update dependents if the created stream did not change', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateProvider((ref) => 0); final provider = StreamProvider.autoDispose((ref) { ref.watch(dep); @@ -162,32 +124,10 @@ void main() { verifyNoMoreInteractions(listener); }); - test( - '.stream does not update dependents if the created stream did not change', - () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final provider = StreamProvider.autoDispose((ref) { - ref.watch(dep); - return const Stream.empty(); - }); - final listener = Listener>(); - - // ignore: deprecated_member_use_from_same_package - container.listen(provider.stream, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(any, any)); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyNoMoreInteractions(listener); - }); - test( '.future does not update dependents if the created future did not change', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateProvider((ref) => 0); final provider = StreamProvider.autoDispose((ref) { ref.watch(dep); @@ -206,77 +146,82 @@ void main() { // No value were emitted, so the future will fail. Catching the error to // avoid false positive. - // ignore: unawaited_futures, avoid_types_on_closure_parameters - container.read(provider.future).catchError((Object _) => 0); + unawaited( + container.read(provider.future).catchError((Object _) => 0), + ); }); group('scoping an override overrides all the associated subproviders', () { test('when passing the provider itself', () async { - final provider = StreamProvider.autoDispose((ref) => Stream.value(0)); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = StreamProvider.autoDispose( + (ref) => Stream.value(0), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); + + container.listen(provider, (p, n) {}); - // ignore: deprecated_member_use_from_same_package - expect(await container.read(provider.stream).first, 0); expect(await container.read(provider.future), 0); expect(container.read(provider), const AsyncValue.data(0)); expect(root.getAllProviderElements(), isEmpty); expect( container.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), ]), ); }); - // test('when using provider.overrideWithValue', () async { - // final provider = StreamProvider.autoDispose((ref) => Stream.value(0)); - // final root = createContainer(); - // final container = createContainer(parent: root, overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // expect(await container.read(provider.stream).first, 42); - // expect(await container.read(provider.future), 42); - // expect(container.read(provider), const AsyncValue.data(42)); - // expect(root.getAllProviderElements(), isEmpty); - // expect( - // container.getAllProviderElements(), - // unorderedEquals([ - // isA>() - // .having((e) => e.origin, 'origin', provider), - // isA>() - // .having((e) => e.origin, 'origin', provider.future), - // isA>() - // .having((e) => e.origin, 'origin', provider.stream), - // ]), - // ); - // }); - - test('when using provider.overrideWithProvider', () async { - final provider = StreamProvider.autoDispose((ref) => Stream.value(0)); - final root = createContainer(); - final container = createContainer( + test('when using provider.overrideWithValue', () async { + final provider = StreamProvider.autoDispose( + (ref) => Stream.value(0), + dependencies: [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ - // ignore: deprecated_member_use_from_same_package - provider.overrideWithProvider( - StreamProvider.autoDispose((ref) => Stream.value(42)), - ), + provider.overrideWithValue(const AsyncValue.data(42)), ], ); - // ignore: deprecated_member_use_from_same_package - expect(await container.read(provider.stream).first, 42); expect(await container.read(provider.future), 42); expect(container.read(provider), const AsyncValue.data(42)); expect(root.getAllProviderElements(), isEmpty); expect( container.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), + ]), + ); + }); + + test('when using provider.overrideWith', () async { + final provider = StreamProvider.autoDispose( + (ref) => Stream.value(0), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + provider.overrideWith((ref) => Stream.value(42)), + ], + ); + + container.listen(provider, (p, n) {}); + + expect(await container.read(provider.future), 42); + expect(container.read(provider), const AsyncValue.data(42)); + expect(root.getAllProviderElements(), isEmpty); + expect( + container.getAllProviderElements(), + unorderedEquals([ + isA().having((e) => e.origin, 'origin', provider), ]), ); }); @@ -289,7 +234,7 @@ void main() { ref.onDispose(onDispose.call); return stream; }); - final container = createContainer(); + final container = ProviderContainer.test(); final listener = Listener>(); final sub = diff --git a/packages/riverpod/test/providers/stream_provider/stream_provider_family_test.dart b/packages/riverpod/test/old/legacy_providers/stream_provider/stream_provider_family_test.dart similarity index 63% rename from packages/riverpod/test/providers/stream_provider/stream_provider_family_test.dart rename to packages/riverpod/test/old/legacy_providers/stream_provider/stream_provider_family_test.dart index 6f48ec3e6..b7bd0f7cf 100644 --- a/packages/riverpod/test/providers/stream_provider/stream_provider_family_test.dart +++ b/packages/riverpod/test/old/legacy_providers/stream_provider/stream_provider_family_test.dart @@ -1,8 +1,7 @@ import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; import 'package:test/test.dart'; -import '../../utils.dart'; - void main() { group('StreamProvider.family', () { test('specifies `from` & `argument` for related providers', () { @@ -15,47 +14,52 @@ void main() { group('scoping an override overrides all the associated subproviders', () { test('when passing the provider itself', () async { - final provider = - StreamProvider.family((ref, _) => Stream.value(0)); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); + final provider = StreamProvider.family( + (ref, _) => Stream.value(0), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); + + container.listen(provider(0), (p, n) {}); - // ignore: deprecated_member_use_from_same_package - expect(await container.read(provider(0).stream).first, 0); expect(await container.read(provider(0).future), 0); expect(container.read(provider(0)), const AsyncData(0)); expect(root.getAllProviderElements(), isEmpty); expect( container.getAllProviderElements(), unorderedEquals([ - isA>() + isA() .having((e) => e.origin, 'origin', provider(0)), ]), ); }); - test('when using provider.overrideWithProvider', () async { - final provider = - StreamProvider.family((ref, _) => Stream.value(0)); - final root = createContainer(); - final container = createContainer( + test('when using provider.overrideWith', () async { + final provider = StreamProvider.family( + (ref, _) => Stream.value(0), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ - provider.overrideWithProvider( - (value) => StreamProvider((ref) => Stream.value(42)), - ), + provider.overrideWith((ref, value) => Stream.value(42)), ], ); - // ignore: deprecated_member_use_from_same_package - expect(await container.read(provider(0).stream).first, 42); + container.listen(provider(0), (p, n) {}); + expect(await container.read(provider(0).future), 42); expect(container.read(provider(0)), const AsyncData(42)); expect(root.getAllProviderElements(), isEmpty); expect( container.getAllProviderElements(), unorderedEquals([ - isA>() + isA() .having((e) => e.origin, 'origin', provider(0)), ]), ); @@ -63,19 +67,22 @@ void main() { }); test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = StreamProvider.family( (ref, i) => Stream.value(ref.watch(dep) + i), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); - // ignore: deprecated_member_use_from_same_package - await expectLater(container.read(provider(10).stream), emits(52)); + container.listen(provider(10), (p, n) {}); + await expectLater(container.read(provider(10).future), completion(52)); expect(container.read(provider(10)), const AsyncData(52)); @@ -88,12 +95,12 @@ void main() { }); final container = ProviderContainer( overrides: [ - provider.overrideWithProvider( - (a) => StreamProvider((ref) => Stream.value('override $a')), - ), + provider.overrideWith((ref, a) => Stream.value('override $a')), ], ); + container.listen(provider(0), (p, n) {}); + expect(container.read(provider(0)), const AsyncValue.loading()); await container.pump(); diff --git a/packages/riverpod/test/old/legacy_providers/stream_provider/stream_provider_test.dart b/packages/riverpod/test/old/legacy_providers/stream_provider/stream_provider_test.dart new file mode 100644 index 000000000..6ea01c5ae --- /dev/null +++ b/packages/riverpod/test/old/legacy_providers/stream_provider/stream_provider_test.dart @@ -0,0 +1,894 @@ +// ignore_for_file: avoid_types_on_closure_parameters + +import 'dart:async'; + +import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/internals.dart' show ProviderElement; +import 'package:test/test.dart'; + +import '../../utils.dart'; + +void main() { + test('supports overrideWith', () { + final provider = StreamProvider((ref) => Stream.value(1)); + final autoDispose = StreamProvider.autoDispose( + (ref) => Stream.value(1), + ); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWith((ref) => Stream.value(42)), + autoDispose.overrideWith((ref) => Stream.value(84)), + ], + ); + + expect( + container.listen(provider.future, (a, b) {}).read(), + completion(42), + ); + expect( + container.listen(autoDispose.future, (a, b) {}).read(), + completion(84), + ); + }); + + test('supports family overrideWith', () { + final family = StreamProvider.family((ref, arg) { + return Stream.value('0 $arg'); + }); + final autoDisposeFamily = StreamProvider.autoDispose.family( + (ref, arg) => Stream.value('0 $arg'), + ); + final container = ProviderContainer.test( + overrides: [ + family.overrideWith((ref, int arg) => Stream.value('42 $arg')), + autoDisposeFamily.overrideWith( + (ref, int arg) => Stream.value('84 $arg'), + ), + ], + ); + + expect( + container.listen(family(10).future, (a, b) {}).read(), + completion('42 10'), + ); + expect( + container.listen(autoDisposeFamily(10).future, (a, b) {}).read(), + completion('84 10'), + ); + }); + + group('When going back to AsyncLoading', () { + test( + 'sets isRefreshing to true if triggered by a ref.invalidate/ref.refresh', + () async { + final container = ProviderContainer.test(); + var count = 0; + final provider = StreamProvider((ref) => Stream.value(count++)); + + container.listen(provider, (p, n) {}); + + await expectLater(container.read(provider.future), completion(0)); + expect(container.read(provider), const AsyncData(0)); + + expect( + container.refresh(provider), + const AsyncLoading().copyWithPrevious(const AsyncData(0)), + ); + + await expectLater(container.read(provider.future), completion(1)); + expect(container.read(provider), const AsyncData(1)); + + container.invalidate(provider); + + expect( + container.read(provider), + const AsyncLoading().copyWithPrevious(const AsyncData(1)), + ); + await expectLater(container.read(provider.future), completion(2)); + expect(container.read(provider), const AsyncData(2)); + }); + + test('does not set isRefreshing if triggered by a dependency change', + () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final provider = StreamProvider((ref) => Stream.value(ref.watch(dep))); + + container.listen(provider, (p, n) {}); + + await expectLater(container.read(provider.future), completion(0)); + expect(container.read(provider), const AsyncData(0)); + + container.read(dep.notifier).state++; + expect( + container.read(provider), + const AsyncLoading() + .copyWithPrevious(const AsyncData(0), isRefresh: false), + ); + + await expectLater(container.read(provider.future), completion(1)); + expect(container.read(provider), const AsyncData(1)); + }); + + test( + 'does not set isRefreshing if both triggered by a dependency change and ref.refresh', + () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final provider = StreamProvider((ref) => Stream.value(ref.watch(dep))); + + container.listen(provider, (p, n) {}); + + await expectLater(container.read(provider.future), completion(0)); + expect(container.read(provider), const AsyncData(0)); + + container.read(dep.notifier).state++; + expect( + container.refresh(provider), + const AsyncLoading() + .copyWithPrevious(const AsyncData(0), isRefresh: false), + ); + + await expectLater(container.read(provider.future), completion(1)); + expect(container.read(provider), const AsyncData(1)); + }); + }); + + test('can be auto-scoped', () async { + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); + final provider = StreamProvider( + (ref) => Stream.value(ref.watch(dep)), + dependencies: [dep], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [dep.overrideWithValue(42)], + ); + + container.listen(provider, (p, n) {}); + await expectLater(container.read(provider.future), completion(42)); + expect(container.read(provider), const AsyncData(42)); + + expect(root.getAllProviderElements(), isEmpty); + }); + + test( + 'when going from AsyncLoading to AsyncLoading, does not notify listeners', + () async { + final dep = StateProvider((ref) => Stream.value(42)); + final provider = StreamProvider((ref) => ref.watch(dep)); + final container = ProviderContainer.test(); + final listener = Listener>(); + final controller = StreamController(); + addTearDown(controller.close); + + container.listen(provider, (p, n) {}); + await expectLater( + container.read(provider.future), + completion(42), + ); + expect( + container.read(provider), + const AsyncData(42), + ); + + container.read(dep.notifier).state = controller.stream; + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly( + listener, + listener( + null, + const AsyncLoading() + .copyWithPrevious(const AsyncData(42), isRefresh: false), + ), + ); + + container.read(dep.notifier).state = Stream.value(21); + + verifyNoMoreInteractions(listener); + + await expectLater( + container.read(provider.future), + completion(21), + ); + expect( + container.read(provider), + const AsyncData(21), + ); + }); + + test('can be refreshed', () async { + var result = 0; + final container = ProviderContainer.test(); + final provider = StreamProvider((ref) => Stream.value(result)); + + container.listen(provider, (p, n) {}); + expect(await container.read(provider.future), 0); + expect(container.read(provider), const AsyncValue.data(0)); + + result = 1; + expect( + container.refresh(provider), + const AsyncLoading().copyWithPrevious(const AsyncValue.data(0)), + ); + + expect(await container.read(provider.future), 1); + expect(container.read(provider), const AsyncValue.data(1)); + }); + + group('scoping an override overrides all the associated sub-providers', () { + test('when passing the provider itself', () async { + final provider = StreamProvider( + (ref) => Stream.value(0), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); + + container.listen(provider, (p, n) {}); + expect(await container.read(provider.future), 0); + expect(container.read(provider), const AsyncValue.data(0)); + expect(root.getAllProviderElements(), isEmpty); + expect( + container.getAllProviderElements(), + unorderedEquals([ + isA().having((e) => e.origin, 'origin', provider), + ]), + ); + }); + + test('when using provider.overrideWithValue', () async { + final provider = StreamProvider( + (ref) => Stream.value(0), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + provider.overrideWithValue(const AsyncValue.data(42)), + ], + ); + + container.listen(provider, (p, n) {}); + expect(await container.read(provider.future), 42); + expect(container.read(provider), const AsyncValue.data(42)); + expect(root.getAllProviderElements(), isEmpty); + expect( + container.getAllProviderElements(), + unorderedEquals([ + isA().having((e) => e.origin, 'origin', provider), + ]), + ); + }); + + test('when using provider.overrideWith', () async { + final provider = StreamProvider( + (ref) => Stream.value(0), + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + provider.overrideWith((ref) => Stream.value(42)), + ], + ); + + container.listen(provider, (p, n) {}); + expect(await container.read(provider.future), 42); + expect(container.read(provider), const AsyncValue.data(42)); + expect(root.getAllProviderElements(), isEmpty); + expect( + container.getAllProviderElements(), + unorderedEquals([ + isA().having((e) => e.origin, 'origin', provider), + ]), + ); + }); + }); + + test('Loading to data', () { + final container = ProviderContainer.test(); + final controller = StreamController(sync: true); + addTearDown(() => controller.close); + final provider = StreamProvider((ref) => controller.stream); + + container.listen(provider, (p, n) {}); + + expect(container.read(provider), const AsyncValue.loading()); + + controller.add(42); + + expect(container.read(provider), const AsyncValue.data(42)); + }); + + test('Loading to error', () { + final container = ProviderContainer.test(); + final controller = StreamController(sync: true); + addTearDown(() => controller.close); + final provider = StreamProvider((ref) => controller.stream); + + container.listen(provider, (p, n) {}); + + expect(container.read(provider), const AsyncValue.loading()); + + final stack = StackTrace.current; + controller.addError(42, stack); + + expect( + container.read(provider), + AsyncValue.error(42, stack), + ); + }); + + group('.future', () { + final provider = StreamProvider((ref) async* {}); + + test( + 'throws StateError if the provider is disposed before a value was emitted', + () async { + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(const AsyncLoading()), + ], + ); + + final future = container.read(provider.future); + + container.dispose(); + + await expectLater( + future, + throwsA( + isA().having( + (e) => e.message, + 'message', + equalsIgnoringHashCodes( + 'The provider StreamProvider#00000 was disposed during loading state, ' + 'yet no value could be emitted.', + ), + ), + ), + ); + }); + + test('supports loading then error then loading', () async { + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(const AsyncLoading()), + ], + ); + + var future = container.read(provider.future); + + final error = Error(); + + container.updateOverrides([ + provider.overrideWithValue(AsyncValue.error(error, StackTrace.empty)), + ]); + + container.listen(provider, (p, n) {}); + expect(container.read(provider.future), future); + + await expectLater(future, throwsA(error)); + expect(await future.stackTrace, StackTrace.empty); + + final error2 = Error(); + + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.loading()), + ]); + + future = container.read(provider.future); + + container.updateOverrides([ + provider.overrideWithValue(AsyncValue.error(error2, StackTrace.empty)), + ]); + + expect(container.read(provider.future), future); + + await expectLater(future, throwsA(error2)); + expect(await future.stackTrace, StackTrace.empty); + }); + + test('supports loading then error then another error', () async { + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(const AsyncLoading()), + ], + ); + + container.listen(provider, (p, n) {}); + var future = container.read(provider.future); + + final error = Error(); + + container.updateOverrides([ + provider.overrideWithValue(AsyncValue.error(error, StackTrace.empty)), + ]); + + expect(container.read(provider.future), future); + + await expectLater(future, throwsA(error)); + expect(await future.stackTrace, StackTrace.empty); + + final error2 = Error(); + + // error without passing by an intermediary loading state + container.updateOverrides([ + provider.overrideWithValue(AsyncValue.error(error2, StackTrace.empty)), + ]); + + future = container.read(provider.future); + + await expectLater(future, throwsA(error2)); + expect(await future.stackTrace, StackTrace.empty); + }); + + test('supports loading then data then loading', () async { + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(const AsyncLoading()), + ], + ); + + container.listen(provider, (p, n) {}); + var future = container.read(provider.future); + + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.data(42)), + ]); + + expect(container.read(provider.future), future); + await expectLater(future, completion(42)); + + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.loading()), + ]); + + future = container.read(provider.future); + + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.data(21)), + ]); + + expect(container.read(provider.future), future); + await expectLater(future, completion(21)); + }); + + test('supports loading then data then another data', () async { + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(const AsyncLoading()), + ], + ); + + container.listen(provider, (p, n) {}); + var future = container.read(provider.future); + + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.data(42)), + ]); + + expect(container.read(provider.future), future); + await expectLater(future, completion(42)); + + // data without passing by an intermediary loading state + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.data(21)), + ]); + + future = container.read(provider.future); + + await expectLater(future, completion(21)); + }); + }); + + test('does not filter identical values', () async { + final container = ProviderContainer.test(); + final controller = StreamController(sync: true); + addTearDown(() => controller.close); + final provider = StreamProvider((ref) => controller.stream); + + final sub = container.listen(provider, (_, __) {}); + + expect(sub.read(), const AsyncValue.loading()); + + controller.add(42); + await container.pump(); + + expect(sub.read(), const AsyncValue.data(42)); + + controller.add(42); + await container.pump(); + + expect(sub.read(), const AsyncValue.data(42)); + }); + + test('throwing inside "create" result in an AsyncValue.error', () { + final container = ProviderContainer.test(); + + // ignore: only_throw_errors + final provider = StreamProvider((ref) => throw 42); + + expect( + container.read(provider), + isA>().having((s) => s.error, 'error', 42), + ); + }); + + test('does not update dependents if the created stream did not change', + () async { + final container = ProviderContainer.test(); + + final dep = StateProvider((ref) => 0); + final provider = StreamProvider((ref) { + ref.watch(dep); + return const Stream.empty(); + }); + final listener = Listener>(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, const AsyncValue.loading())); + + container.read(dep.notifier).state++; + await container.pump(); + + verifyNoMoreInteractions(listener); + }); + + test( + '.future does not update dependents if the created future did not change', + () async { + final container = ProviderContainer.test(); + + final dep = StateProvider((ref) => 0); + + final provider = StreamProvider((ref) { + ref.watch(dep); + return const Stream.empty(); + }); + final listener = Listener>(); + + container.listen(provider.future, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(any, any)); + + container.read(dep.notifier).state++; + await container.pump(); + + verifyNoMoreInteractions(listener); + + // No value were emitted, so the future will fail. Catching the error to + // avoid false positive. + unawaited(container.read(provider.future).catchError((Object _) => 0)); + }); + + group('overrideWithValue(T)', () { + test( + 'when overridden with an error but provider.stream is not listened to, it should not emit an error to the zone', + () async { + final error = Error(); + final stream = StreamProvider((ref) => const Stream.empty()); + + final container = ProviderContainer( + overrides: [ + stream.overrideWithValue(AsyncValue.error(error, StackTrace.empty)), + ], + ); + addTearDown(container.dispose); + + expect( + container.read(stream), + AsyncValue.error(error, StackTrace.empty), + ); + }); + }); + + test('StreamProvider.family', () async { + final provider = StreamProvider.family((ref, a) { + return Stream.value('$a'); + }); + final container = ProviderContainer.test(); + + container.listen(provider(0), (p, n) {}); + + expect(container.read(provider(0)), const AsyncValue.loading()); + + await container.pump(); + + expect( + container.read(provider(0)), + const AsyncValue.data('0'), + ); + }); + + test('can specify name', () { + final provider = StreamProvider( + (_) => const Stream.empty(), + name: 'example', + ); + + expect(provider.name, 'example'); + + final provider2 = StreamProvider((_) => const Stream.empty()); + + expect(provider2.name, isNull); + }); + + test('subscribe exposes loading synchronously then value on change', + () async { + final container = ProviderContainer.test(); + final controller = StreamController(sync: true); + final provider = StreamProvider((_) => controller.stream); + final listener = Listener>(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, const AsyncValue.loading())); + + controller.add(42); + + verifyOnly( + listener, + listener(const AsyncValue.loading(), const AsyncValue.data(42)), + ); + + controller.add(21); + + verifyOnly( + listener, + listener(const AsyncValue.data(42), const AsyncValue.data(21)), + ); + + await controller.close(); + }); + + test('errors', () async { + final container = ProviderContainer.test(); + final controller = StreamController(sync: true); + final provider = StreamProvider((_) => controller.stream); + final listener = Listener>(); + final error = Error(); + final stack = StackTrace.current; + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, const AsyncValue.loading())); + + controller.addError(error, stack); + + verifyOnly( + listener, + listener( + const AsyncValue.loading(), + AsyncValue.error(error, stack), + ), + ); + + controller.add(21); + + verifyOnly( + listener, + listener( + AsyncError(error, stack), + const AsyncValue.data(21).copyWithPrevious( + AsyncError(error, stack), + ), + ), + ); + + await controller.close(); + }); + + test('stops subscription', () async { + final container = ProviderContainer.test(); + final controller = StreamController(sync: true); + final dispose = OnDisposeMock(); + final provider = StreamProvider((ref) { + ref.onDispose(dispose.call); + return controller.stream; + }); + final listener = Listener>(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, const AsyncValue.loading())); + + controller.add(42); + + verifyOnly( + listener, + listener(const AsyncValue.loading(), const AsyncValue.data(42)), + ); + verifyNoMoreInteractions(dispose); + + container.dispose(); + + verify(dispose()).called(1); + verifyNoMoreInteractions(dispose); + + // if the listener wasn't removed, this would throw because markNeedsNotifyListeners + // cannot be called once the provider was disposed. + controller.add(21); + + await controller.close(); + }); + + group('StreamProvider.future', () { + group('from StreamProvider', () { + test('read currentValue before first value', () async { + final container = ProviderContainer.test(); + final controller = StreamController(); + final provider = StreamProvider((_) => controller.stream); + + container.listen(provider, (p, n) {}); + final future = container.read(provider.future); + + controller.add(42); + + await expectLater(future, completion(42)); + + await controller.close(); + }); + + test('read currentValue before after value', () async { + final container = ProviderContainer.test(); + final controller = StreamController(); + final provider = StreamProvider((_) => controller.stream); + + controller.add(42); + + container.listen(provider, (p, n) {}); + final future = container.read(provider.future); + + await expectLater(future, completion(42)); + + await controller.close(); + }); + + test('read currentValue before first error', () async { + final container = ProviderContainer.test(); + final controller = StreamController(); + final provider = StreamProvider((_) => controller.stream); + + container.listen(provider, (p, n) {}); + final future = container.read(provider.future); + + controller.addError(42); + + await expectLater(future, throwsA(42)); + + await controller.close(); + }); + + test('read currentValue before after error', () async { + final container = ProviderContainer.test(); + final controller = StreamController(); + final provider = StreamProvider((_) => controller.stream); + + controller.addError(42); + + container.listen(provider, (p, n) {}); + final future = container.read(provider.future); + + await expectLater(future, throwsA(42)); + + await controller.close(); + }); + }); + + group('from StreamProvider.overrideWithValue', () { + test('read currentValue before first value', () async { + final provider = StreamProvider((_) async* {}); + final container = ProviderContainer( + overrides: [ + provider.overrideWithValue(const AsyncValue.loading()), + ], + ); + + container.listen(provider, (p, n) {}); + final future = container.read(provider.future); + + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.data(42)), + ]); + + await expectLater(future, completion(42)); + }); + + test('read currentValue before after value', () async { + final provider = StreamProvider((_) async* {}); + final container = ProviderContainer( + overrides: [ + provider.overrideWithValue(const AsyncValue.loading()), + ], + ); + + container.updateOverrides([ + provider.overrideWithValue(const AsyncValue.data(42)), + ]); + + container.listen(provider, (p, n) {}); + final future = container.read(provider.future); + + await expectLater(future, completion(42)); + }); + + test('read currentValue before first error', () async { + final provider = StreamProvider((_) async* {}); + final container = ProviderContainer( + overrides: [ + provider.overrideWithValue(const AsyncValue.loading()), + ], + ); + + container.listen(provider, (p, n) {}); + final future = container.read(provider.future); + + container.updateOverrides([ + provider + .overrideWithValue(const AsyncValue.error(42, StackTrace.empty)), + ]); + + await expectLater(future, throwsA(42)); + }); + + test('read currentValue before after error', () async { + final provider = StreamProvider((_) async* {}); + final container = ProviderContainer( + overrides: [ + provider.overrideWithValue(const AsyncValue.loading()), + ], + ); + + container.updateOverrides([ + provider + .overrideWithValue(const AsyncValue.error(42, StackTrace.empty)), + ]); + + container.listen(provider, (p, n) {}); + final future = container.read(provider.future); + + await expectLater(future, throwsA(42)); + }); + + test('synchronous first event', () async { + final provider = StreamProvider((_) async* {}); + final container = ProviderContainer( + overrides: [ + provider.overrideWithValue(const AsyncValue.data(42)), + ], + ); + + container.listen(provider, (p, n) {}); + final future = container.read(provider.future); + + await expectLater(future, completion(42)); + }); + }); + }); +} + +extension on Future { + Future get stackTrace async { + try { + await this; + return null; + } catch (e, s) { + return s; + } + } +} diff --git a/packages/riverpod/test/utils.dart b/packages/riverpod/test/old/utils.dart similarity index 73% rename from packages/riverpod/test/utils.dart rename to packages/riverpod/test/old/utils.dart index 745864173..6f8965cf0 100644 --- a/packages/riverpod/test/utils.dart +++ b/packages/riverpod/test/old/utils.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; import 'package:test/test.dart'; @@ -22,20 +23,6 @@ class Counter extends StateNotifier { set state(int value) => super.state = value; } -ProviderContainer createContainer({ - ProviderContainer? parent, - List overrides = const [], - List? observers, -}) { - final container = ProviderContainer( - parent: parent, - overrides: overrides, - observers: observers, - ); - addTearDown(container.dispose); - return container; -} - List errorsOf(void Function() cb) { final errors = []; runZonedGuarded(cb, (err, _) => errors.add(err)); @@ -49,7 +36,16 @@ class OnBuildMock extends Mock { } class OnDisposeMock extends Mock { + OnDisposeMock([this.label]); + + final String? label; + void call(); + + @override + String toString() { + return 'OnDisposeMock($label)'; + } } class OnCancelMock extends Mock { @@ -186,56 +182,82 @@ class ObserverMock extends Mock implements ProviderObserver { } @override - void didDisposeProvider( - ProviderBase? provider, - ProviderContainer? container, - ) { - super.noSuchMethod( - Invocation.method(#didDisposeProvider, [provider, container]), - ); - } + void didAddProvider( + ProviderObserverContext? context, + Object? value, + ); @override void providerDidFail( - ProviderBase? provider, + ProviderObserverContext? context, Object? error, - Object? stackTrace, - Object? container, - ) { - super.noSuchMethod( - Invocation.method( - #providerDidFail, - [provider, error, stackTrace, container], - ), - ); - } - - @override - void didAddProvider( - ProviderBase? provider, - Object? value, - ProviderContainer? container, - ) { - super.noSuchMethod( - Invocation.method(#didAddProvider, [provider, value, container]), - ); - } + StackTrace? stackTrace, + ); @override void didUpdateProvider( - ProviderBase? provider, + ProviderObserverContext? context, Object? previousValue, Object? newValue, - ProviderContainer? container, - ) { - super.noSuchMethod( - Invocation.method( - #didUpdateProvider, - [provider, previousValue, newValue, container], - ), - ); - } + ); + + @override + void didDisposeProvider(ProviderObserverContext? context); + + @override + void mutationReset(ProviderObserverContext? context); + + @override + void mutationStart( + ProviderObserverContext? context, + MutationContext? mutation, + ); + + @override + void mutationError( + ProviderObserverContext? context, + MutationContext? mutation, + Object? error, + StackTrace? stackTrace, + ); + + @override + void mutationSuccess( + ProviderObserverContext? context, + MutationContext? mutation, + Object? result, + ); } // can subclass ProviderObserver without implementing all life-cycles class CustomObserver extends ProviderObserver {} + +TypeMatcher isProviderObserverContext( + Object? provider, + Object? container, { + Object? mutation, +}) { + var matcher = isA(); + + matcher = matcher.having((e) => e.provider, 'provider', provider); + matcher = matcher.having((e) => e.container, 'container', container); + if (mutation != null) { + matcher = matcher.having((e) => e.mutation, 'mutation', mutation); + } + + return matcher; +} + +TypeMatcher isMutationContext( + Object? invocation, { + Object? notifier, +}) { + var matcher = isA(); + + matcher = matcher.having((e) => e.invocation, 'invocation', invocation); + if (notifier != null) { + matcher = matcher.having((e) => e.notifier, 'notifier', notifier); + } + + return matcher; +} diff --git a/packages/riverpod/test/providers/async_notifier/async_notifier_test.dart b/packages/riverpod/test/providers/async_notifier/async_notifier_test.dart deleted file mode 100644 index c32b7b3ab..000000000 --- a/packages/riverpod/test/providers/async_notifier/async_notifier_test.dart +++ /dev/null @@ -1,1184 +0,0 @@ -// ignore_for_file: avoid_types_on_closure_parameters - -import 'dart:async'; - -import 'package:meta/meta.dart'; -import 'package:mockito/mockito.dart'; -import 'package:riverpod/riverpod.dart' hide ErrorListener; -import 'package:test/test.dart'; - -import '../../utils.dart'; -import 'factory.dart'; - -void main() { - for (final factory in matrix()) { - group(factory.label, () { - test('Can assign `AsyncLoading` to `AsyncValue`', () { - // Regression test for https://github.com/rrousselGit/riverpod/issues/2120 - final provider = factory.simpleTestProvider((ref) => 42); - final container = createContainer(); - - final sub = container.listen(provider.notifier, (prev, next) {}); - - // ignore: void_checks - expect(sub.read().state, const AsyncData(42)); - - sub.read().state = const AsyncLoading(); - - expect( - sub.read().state, - isA>() - .having((e) => e.hasValue, 'hasValue', true) - .having((e) => e.value, 'value', 42), - ); - }); - - test('Can assign `AsyncData` to `AsyncValue`', () { - // Regression test for https://github.com/rrousselGit/riverpod/issues/2120 - final provider = factory.simpleTestProvider((ref) => 42); - final container = createContainer(); - - final sub = container.listen(provider.notifier, (prev, next) {}); - - // ignore: void_checks - expect(sub.read().state, const AsyncData(42)); - - sub.read().state = const AsyncData(42); - - expect( - sub.read().state, - isA>() - .having((e) => e.hasValue, 'hasValue', true) - .having((e) => e.value, 'value', 42), - ); - }); - - test('Can assign `AsyncError` to `AsyncValue`', () { - // Regression test for https://github.com/rrousselGit/riverpod/issues/2120 - final provider = factory.simpleTestProvider((ref) => 42); - final container = createContainer(); - - final sub = container.listen(provider.notifier, (prev, next) {}); - - // ignore: void_checks - expect(sub.read().state, const AsyncData(42)); - - sub.read().state = AsyncError(21, StackTrace.current); - - expect( - sub.read().state, - isA>() - .having((e) => e.hasValue, 'hasValue', true) - .having((e) => e.value, 'value', 42) - .having((e) => e.hasError, 'hasError', true) - .having((e) => e.error, 'error', 21), - ); - }); - - group('supports AsyncValue transition', () { - test( - 'performs seamless copyWithPrevious if triggered by ref.invalidate/ref.refresh', - () async { - final container = createContainer(); - var count = 0; - final provider = factory.simpleTestProvider( - (ref) => Future.value(count++), - ); - - container.listen(provider, (previous, next) {}); - - await expectLater(container.read(provider.future), completion(0)); - expect(container.read(provider), const AsyncData(0)); - - expect( - container.refresh(provider), - const AsyncLoading().copyWithPrevious(const AsyncData(0)), - ); - - await expectLater(container.read(provider.future), completion(1)); - expect(container.read(provider), const AsyncData(1)); - - container.invalidate(provider); - - expect( - container.read(provider), - const AsyncLoading().copyWithPrevious(const AsyncData(1)), - ); - await expectLater(container.read(provider.future), completion(2)); - expect(container.read(provider), const AsyncData(2)); - }); - - test( - 'performs seamless:false copyWithPrevious on `state = AsyncLoading()`', - () async { - final container = createContainer(); - final provider = factory.simpleTestProvider((ref) => Future.value(0)); - - final sub = container.listen(provider.notifier, (previous, next) {}); - - await expectLater(container.read(provider.future), completion(0)); - expect(container.read(provider), const AsyncData(0)); - - sub.read().state = const AsyncLoading(); - - expect( - sub.read().state, - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ); - }); - - test( - 'performs seamless:false copyWithPrevious if triggered by a dependency change', - () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final provider = factory.simpleTestProvider( - (ref) => Future.value(ref.watch(dep)), - ); - - container.listen(provider, (previous, next) {}); - - await expectLater(container.read(provider.future), completion(0)); - expect(container.read(provider), const AsyncData(0)); - - container.read(dep.notifier).state++; - expect( - container.read(provider), - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ); - - await expectLater(container.read(provider.future), completion(1)); - expect(container.read(provider), const AsyncData(1)); - }); - - test('performs seamless data > loading > error transition', () async { - final container = createContainer(); - var result = Future.value(42); - final provider = FutureProvider((ref) => result); - - final sub = container.listen(provider.future, (_, __) {}); - - expect(container.read(provider), const AsyncLoading()); - expect(await sub.read(), 42); - expect(container.read(provider), const AsyncData(42)); - - result = Future.error('err', StackTrace.empty); - container.invalidate(provider); - - expect( - container.read(provider), - const AsyncLoading().copyWithPrevious(const AsyncData(42)), - ); - await expectLater(sub.read(), throwsA('err')); - expect( - container.read(provider), - const AsyncError('err', StackTrace.empty) - .copyWithPrevious(const AsyncData(42)), - ); - }); - - test( - 'performs seamless:false copyWithPrevious if both triggered by a dependency change and ref.refresh', - () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final provider = factory.simpleTestProvider( - (ref) => Future.value(ref.watch(dep)), - ); - - container.listen(provider, (previous, next) {}); - - await expectLater(container.read(provider.future), completion(0)); - expect(container.read(provider), const AsyncData(0)); - - container.read(dep.notifier).state++; - expect( - container.refresh(provider), - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ); - - await expectLater(container.read(provider.future), completion(1)); - expect(container.read(provider), const AsyncData(1)); - }); - }); - - test('does not notify listeners when refreshed during loading', () async { - final provider = factory.simpleTestProvider((ref) => Future.value(0)); - final container = createContainer(); - final listener = Listener>(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, const AsyncLoading())); - - container.refresh(provider); - - await container.read(provider.future); - - verifyOnly( - listener, - listener(const AsyncLoading(), const AsyncData(0)), - ); - }); - - test('supports listenSelf', () { - final listener = Listener>(); - final onError = ErrorListener(); - final provider = factory.simpleTestProvider((ref) { - ref.listenSelf(listener.call, onError: onError.call); - Error.throwWithStackTrace(42, StackTrace.empty); - }); - final container = createContainer(); - - container.listen(provider, (previous, next) {}); - - verifyOnly( - listener, - listener(null, const AsyncError(42, StackTrace.empty)), - ); - verifyZeroInteractions(onError); - - container.read(provider.notifier).state = const AsyncData(42); - - verifyNoMoreInteractions(onError); - verifyOnly( - listener, - listener( - const AsyncError(42, StackTrace.empty), - const AsyncData(42), - ), - ); - }); - - test( - 'converts AsyncNotifier.build into an AsyncData if the future completes', - () async { - final provider = factory.simpleTestProvider((ref) => Future.value(0)); - final container = createContainer(); - final listener = Listener>(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, const AsyncLoading())); - expect( - container.read(provider.notifier).state, - const AsyncLoading(), - ); - - expect(await container.read(provider.future), 0); - - verifyOnly( - listener, - listener(const AsyncLoading(), const AsyncData(0)), - ); - expect( - container.read(provider.notifier).state, - const AsyncData(0), - ); - }); - - test( - 'converts AsyncNotifier.build into an AsyncError if the future fails', - () async { - final provider = factory.simpleTestProvider( - (ref) => Future.error(0, StackTrace.empty), - ); - final container = createContainer(); - final listener = Listener>(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, const AsyncLoading())); - expect( - container.read(provider.notifier).state, - const AsyncLoading(), - ); - - await expectLater(container.read(provider.future), throwsA(0)); - - verifyOnly( - listener, - listener(const AsyncLoading(), const AsyncError(0, StackTrace.empty)), - ); - expect( - container.read(provider.notifier).state, - const AsyncError(0, StackTrace.empty), - ); - }); - - test('supports cases where the AsyncNotifier constructor throws', - () async { - final provider = factory.testProvider( - () => Error.throwWithStackTrace(0, StackTrace.empty), - ); - final container = createContainer(); - final listener = Listener>(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly( - listener, - listener(null, const AsyncError(0, StackTrace.empty)), - ); - expect( - () => container.read(provider.notifier), - throwsA(0), - ); - - await expectLater(container.read(provider.future), throwsA(0)); - }); - - test( - 'synchronously emits AsyncData if AsyncNotifier.build emits synchronously', - () async { - final provider = factory.simpleTestProvider((ref) => 0); - final container = createContainer(); - final listener = Listener>(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, const AsyncData(0))); - expect(container.read(provider.notifier).state, const AsyncData(0)); - await expectLater(container.read(provider.future), completion(0)); - }); - - test( - 'synchronously emits AsyncError if AsyncNotifier.build throws synchronously', - () async { - final provider = factory.simpleTestProvider( - (ref) => Error.throwWithStackTrace(42, StackTrace.empty), - ); - final container = createContainer(); - final listener = Listener>(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly( - listener, - listener(null, const AsyncError(42, StackTrace.empty)), - ); - expect( - container.read(provider.notifier).state, - const AsyncError(42, StackTrace.empty), - ); - await expectLater(container.read(provider.future), throwsA(42)); - }); - - test( - 'stops listening to the previous future data when the provider rebuilds', - () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final completers = { - 0: Completer.sync(), - 1: Completer.sync(), - }; - final provider = factory.simpleTestProvider( - (ref) => completers[ref.watch(dep)]!.future, - ); - final listener = Listener>(); - - container.listen(provider, listener.call); - - expect( - container.read(provider.future), - completion(21), - reason: 'The provider rebuilt while the future was still pending, ' - 'so .future should resolve with the next value', - ); - verifyZeroInteractions(listener); - expect(container.read(provider), const AsyncLoading()); - - container.read(dep.notifier).state++; - completers[0]!.complete(42); - - verifyZeroInteractions(listener); - - expect(container.read(provider.future), completion(21)); - expect(container.read(provider), const AsyncLoading()); - - completers[1]!.complete(21); - - expect(await container.read(provider.future), 21); - expect(container.read(provider), const AsyncData(21)); - }); - - test( - 'stops listening to the previous future error when the provider rebuilds', - () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final completers = { - 0: Completer.sync(), - 1: Completer.sync(), - }; - final provider = factory.simpleTestProvider( - (ref) => completers[ref.watch(dep)]!.future, - ); - final listener = Listener>(); - - container.listen(provider, listener.call); - - expect( - container.read(provider.future), - throwsA(21), - reason: 'The provider rebuilt while the future was still pending, ' - 'so .future should resolve with the next value', - ); - verifyZeroInteractions(listener); - expect(container.read(provider), const AsyncLoading()); - - container.read(dep.notifier).state++; - completers[0]!.completeError(42, StackTrace.empty); - - verifyZeroInteractions(listener); - - expect(container.read(provider.future), throwsA(21)); - expect(container.read(provider), const AsyncLoading()); - - completers[1]!.completeError(21, StackTrace.empty); - - await expectLater(container.read(provider.future), throwsA(21)); - expect( - container.read(provider), - const AsyncError(21, StackTrace.empty), - ); - }); - - group('AsyncNotifier.state', () { - test( - 'when manually modifying the state, the new exposed value contains the previous state when possible', - () async { - final provider = factory.simpleTestProvider((ref) => 0); - final container = createContainer(); - - final sub = container.listen(provider.notifier, (previous, next) {}); - - // ignore: prefer_const_constructors, not using `const` as we voluntarility break identity to test `identical` - final newState = AsyncData(84); - // ignore: prefer_const_constructors, not using `const` as we voluntarility break identity to test `identical` - final newLoading = AsyncLoading(); - // ignore: prefer_const_constructors, not using `const` as we voluntarility break identity to test `identical` - final newError = AsyncError(84, StackTrace.empty); - - sub.read().state = newState; - - expect(sub.read().state, same(newState)); - - sub.read().state = newLoading; - - expect( - sub.read().state, - const AsyncLoading() - .copyWithPrevious(newState, isRefresh: false), - ); - - sub.read().state = newError; - - expect( - sub.read().state, - newError.copyWithPrevious( - const AsyncLoading() - .copyWithPrevious(newState, isRefresh: false), - ), - ); - }); - - test( - 'when read on outdated provider, refreshes the provider and return the up-to-date state', - () async { - final listener = OnBuildMock(); - final dep = StateProvider((ref) => 0); - final provider = factory.simpleTestProvider( - (ref) { - listener(); - return Future.value(ref.watch(dep)); - }, - ); - final container = createContainer(); - - container.listen(provider, (previous, next) {}); - final notifier = container.read(provider.notifier); - - expect(notifier.state, const AsyncLoading()); - expect(await container.read(provider.future), 0); - expect(notifier.state, const AsyncData(0)); - verify(listener()).called(1); - - container.read(dep.notifier).state++; - - expect( - notifier.state, - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ); - expect(await container.read(provider.future), 1); - expect(notifier.state, const AsyncData(1)); - verify(listener()).called(1); - }); - - test('can be read inside build', () { - final dep = StateProvider((ref) => 0); - late AsyncValue state; - final provider = factory.testProvider( - () { - late AsyncTestNotifierBase notifier; - return notifier = factory.notifier( - (ref) { - state = notifier.state; - return Future.value(ref.watch(dep)); - }, - ); - }, - ); - final container = createContainer(); - - container.listen(provider, (previous, next) {}); - - expect(state, const AsyncLoading()); - - container.read(provider.notifier).state = const AsyncData(42); - container.refresh(provider); - - expect( - state, - const AsyncLoading().copyWithPrevious(const AsyncData(42)), - ); - }); - - test('notifies listeners when the setter is called', () { - final provider = factory.simpleTestProvider((ref) => 0); - final container = createContainer(); - final listener = Listener>(); - - container.listen(provider, listener.call); - - verifyZeroInteractions(listener); - - container.read(provider.notifier).state = const AsyncData(42); - - verifyOnly( - listener, - listener(const AsyncData(0), const AsyncData(42)), - ); - }); - }); - - group('AsyncNotifier.future', () { - test( - 'when disposed during loading, resolves with the content of AsyncNotifier.build', - () async { - final container = createContainer(); - final completer = Completer.sync(); - final provider = factory.simpleTestProvider( - (ref) => completer.future, - ); - - final future = container.read(provider.future); - container.dispose(); - - completer.complete(42); - - await expectLater(future, completion(42)); - }); - - test( - 'when disposed during loading, resolves with the error of AsyncNotifier.build', - () async { - final container = createContainer(); - final completer = Completer.sync(); - final provider = factory.simpleTestProvider( - (ref) => completer.future, - ); - - final future = container.read(provider.future); - - container.dispose(); - - completer.completeError(42); - - await expectLater(future, throwsA(42)); - }); - - test( - 'going data > loading while the future is still pending. ' - 'Resolves with last future result', - () async { - final container = createContainer(); - final completer = Completer.sync(); - final provider = factory.simpleTestProvider( - (ref) => completer.future, - ); - - container.read(provider); - container.read(provider.notifier).state = const AsyncData(42); - container.read(provider.notifier).state = const AsyncLoading(); - - final future = container.read(provider.future); - - container.dispose(); - - completer.complete(42); - - await expectLater(future, completion(42)); - }, - ); - - test( - 'if going back to loading after future resolved, throws StateError', - () async { - final container = createContainer(); - final completer = Completer.sync(); - final provider = factory.simpleTestProvider( - (ref) => completer.future, - ); - - container.read(provider); - - completer.complete(42); - - container.read(provider.notifier).state = const AsyncData(42); - container.read(provider.notifier).state = const AsyncLoading(); - - final future = container.read(provider.future); - - container.dispose(); - - await expectLater(future, throwsStateError); - }, - ); - - test( - 'resolves with the new state if AsyncNotifier.state is modified during loading', - () async { - final container = createContainer(); - final completer = Completer.sync(); - final provider = factory.simpleTestProvider( - (ref) => completer.future, - ); - final listener = Listener>(); - - final sub = container.listen(provider.notifier, (previous, next) {}); - container.listen(provider.future, listener.call); - - expect(sub.read().future, completion(21)); - - sub.read().state = const AsyncData(21); - - completer.complete(42); - - expect(sub.read().future, completion(42)); - final capture = - verifyOnly(listener, listener(captureAny, captureAny)).captured; - - expect(capture.length, 2); - expect(capture.first, completion(21)); - expect(capture.last, completion(42)); - }); - - test('resolves with the new state when notifier.state is changed', - () async { - final container = createContainer(); - final provider = factory.simpleTestProvider((ref) => 0); - final listener = Listener>(); - - final sub = container.listen(provider.notifier, (previous, next) {}); - container.listen( - provider.future, - listener.call, - fireImmediately: true, - ); - - await expectLater(sub.read().future, completion(0)); - verifyOnly( - listener, - listener(argThat(equals(null)), argThat(completion(0))), - ); - - sub.read().state = const AsyncData(1); - - await expectLater(sub.read().future, completion(1)); - }); - - test('retuns a Future identical to that of .future', () { - final listener = OnBuildMock(); - final dep = StateProvider((ref) => 0); - final provider = factory.simpleTestProvider( - (ref) { - listener(); - return Future.value(ref.watch(dep)); - }, - ); - final container = createContainer(); - - container.listen(provider.notifier, (previous, next) {}); - final notifier = container.read(provider.notifier); - - expect(notifier.future, same(container.read(provider.future))); - }); - - test( - 'when read on outdated provider, refreshes the provider and return the up-to-date state', - () async { - final listener = OnBuildMock(); - final dep = StateProvider((ref) => 0); - final provider = factory.simpleTestProvider( - (ref) { - listener(); - return Future.value(ref.watch(dep)); - }, - ); - final container = createContainer(); - - container.listen(provider, (previous, next) {}); - final notifier = container.read(provider.notifier); - - expect(await container.read(provider.future), 0); - verify(listener()).called(1); - - container.read(dep.notifier).state++; - - expect(notifier.future, notifier.future); - expect(notifier.future, same(container.read(provider.future))); - expect(await notifier.future, 1); - verify(listener()).called(1); - }); - }); - - group('AsyncNotifierProvider.notifier', () { - test( - 'never emits an update. The Notifier is never recreated once it is instantiated', - () async { - final listener = OnBuildMock(); - final dep = StateProvider((ref) => 0); - final provider = factory.testProvider(() { - listener(); - return factory.notifier((ref) => ref.watch(dep)); - }); - final container = createContainer(); - - container.listen(provider, (previous, next) {}); - final notifier = container.read(provider.notifier); - - verify(listener()).called(1); - expect(container.read(provider), const AsyncData(0)); - - container.read(dep.notifier).state++; - - expect(container.read(provider), const AsyncData(1)); - expect(container.read(provider.notifier), same(notifier)); - verifyNoMoreInteractions(listener); - }); - }); - - test( - 'Can override AsyncNotifier.updateShouldNotify to change the default filter logic', - () { - final provider = factory.simpleTestProvider>( - (ref) => Equal(42), - updateShouldNotify: (a, b) => a != b, - ); - final container = createContainer(); - final listener = Listener>>(); - - container.listen(provider, listener.call); - final notifier = container.read(provider.notifier); - - // voluntarily assigning the same value - final self = notifier.state; - notifier.state = self; - - verifyZeroInteractions(listener); - - notifier.state = AsyncData(Equal(42)); - - verifyZeroInteractions(listener); - - notifier.state = AsyncData(Equal(21)); - - verifyOnly( - listener, - listener(AsyncData(Equal(42)), AsyncData(Equal(21))), - ); - }); - - group('AsyncNotifer.update', () { - test('passes in the latest state', () async { - final container = createContainer(); - final provider = factory.simpleTestProvider( - (ref) => 0, - ); - - final sub = container.listen(provider.notifier, (prev, next) {}); - - expect( - container.read(provider), - const AsyncData(0), - ); - - await expectLater( - sub.read().update((prev) => prev + 1), - completion(1), - ); - await expectLater( - sub.read().future, - completion(1), - ); - await expectLater( - sub.read().update((prev) => prev + 1), - completion(2), - ); - }); - - test('can specify onError to handle error scenario', () async { - final container = createContainer(); - final provider = factory.simpleTestProvider( - (ref) => Error.throwWithStackTrace(42, StackTrace.empty), - ); - var callCount = 0; - Object? actualErr; - Object? actualStack; - - final sub = container.listen(provider.notifier, (prev, next) {}); - - expect( - container.read(provider), - const AsyncError(42, StackTrace.empty), - ); - - await expectLater( - sub.read().update( - (prev) { - callCount++; - return prev; - }, - onError: (err, stack) { - actualErr = err; - actualStack = stack; - return 21; - }, - ), - completion(21), - ); - expect(callCount, 0); - expect(actualErr, 42); - expect(actualStack, StackTrace.empty); - expect(container.read(provider), const AsyncData(21)); - }); - - test( - 'executes immediately with current state if a state is avalailable', - () async { - final container = createContainer(); - final provider = factory.simpleTestProvider((ref) => 1); - - final sub = container.listen(provider.notifier, (prev, next) {}); - - expect(container.read(provider), const AsyncData(1)); - - await expectLater( - sub.read().update((prev) => prev + 1), - completion(2), - ); - expect(container.read(provider), const AsyncData(2)); - }); - - test( - 'executes immediately with current state if an error is avalailable', - () async { - final container = createContainer(); - final provider = factory.simpleTestProvider( - (ref) => Error.throwWithStackTrace(42, StackTrace.empty), - ); - var callCount = 0; - - final sub = container.listen(provider.notifier, (prev, next) {}); - - expect( - container.read(provider), - const AsyncError(42, StackTrace.empty), - ); - - await expectLater( - sub.read().update((prev) { - callCount++; - return prev + 1; - }), - throwsA(42), - ); - - expect(callCount, 0); - expect( - container.read(provider), - const AsyncError(42, StackTrace.empty), - ); - }); - - test('awaits the future resolution if in loading state', () async { - final container = createContainer(); - final provider = factory.simpleTestProvider( - (ref) => Future.value(42), - ); - - final sub = container.listen(provider.notifier, (prev, next) {}); - - expect(container.read(provider), const AsyncLoading()); - - await expectLater( - sub.read().update((prev) => prev + 1), - completion(43), - ); - expect(container.read(provider), const AsyncData(43)); - }); - }); - }); - } - - test('supports overrideWith', () { - final provider = AsyncNotifierProvider, int>( - () => AsyncTestNotifier((ref) => 0), - ); - final autoDispose = AsyncNotifierProvider.autoDispose< - AutoDisposeAsyncTestNotifier, int>( - () => AutoDisposeAsyncTestNotifier((ref) => 0), - ); - final container = createContainer( - overrides: [ - provider.overrideWith(() => AsyncTestNotifier((ref) => 42)), - autoDispose.overrideWith( - () => AutoDisposeAsyncTestNotifier((ref) => 84), - ), - ], - ); - - expect(container.read(provider).value, 42); - expect(container.read(autoDispose).value, 84); - }); - - test('supports family overrideWith', () { - final family = - AsyncNotifierProvider.family, int, int>( - () => AsyncTestNotifierFamily((ref) => 0), - ); - final autoDisposeFamily = AsyncNotifierProvider.autoDispose - .family, int, int>( - () => AutoDisposeAsyncTestNotifierFamily((ref) => 0), - ); - final container = createContainer( - overrides: [ - family.overrideWith( - () => AsyncTestNotifierFamily((ref) => 42), - ), - autoDisposeFamily.overrideWith( - () => AutoDisposeAsyncTestNotifierFamily((ref) => 84), - ), - ], - ); - - expect(container.read(family(10)).value, 42); - expect(container.read(autoDisposeFamily(10)).value, 84); - }); - - group('AutoDispose variant', () { - test('can watch autoDispose providers', () { - final dep = Provider.autoDispose((ref) => 0); - final provider = AutoDisposeAsyncNotifierProvider< - AutoDisposeAsyncTestNotifier, int>( - () => AutoDisposeAsyncTestNotifier((ref) { - return ref.watch(dep); - }), - ); - final container = createContainer(); - - expect(container.read(provider), const AsyncData(0)); - }); - }); - - group('modifiers', () { - void canBeAssignedToAlwaysAliveRefreshable( - AlwaysAliveRefreshable provider, - ) {} - - void canBeAssignedToRefreshable( - Refreshable provider, - ) {} - - void canBeAssignedToAlwaysAliveListenable( - AlwaysAliveProviderListenable provider, - ) {} - - void canBeAssignedToProviderListenable( - ProviderListenable provider, - ) {} - - // TODO use package:expect_error to test that commented lined are not compiling - - test('provider', () { - final provider = AsyncNotifierProvider, int>( - () => AsyncTestNotifier((ref) => 0), - ); - - provider.select((AsyncValue value) => 0); - provider.selectAsync((int value) => 0); - - canBeAssignedToProviderListenable>(provider); - canBeAssignedToAlwaysAliveListenable>(provider); - canBeAssignedToRefreshable>(provider); - canBeAssignedToAlwaysAliveRefreshable>(provider); - - canBeAssignedToProviderListenable>(provider.future); - canBeAssignedToAlwaysAliveListenable>(provider.future); - canBeAssignedToRefreshable>(provider.future); - canBeAssignedToAlwaysAliveRefreshable>(provider.future); - - canBeAssignedToProviderListenable>(provider.notifier); - canBeAssignedToAlwaysAliveListenable>( - provider.notifier, - ); - canBeAssignedToRefreshable>(provider.notifier); - canBeAssignedToAlwaysAliveRefreshable>( - provider.notifier, - ); - }); - - test('autoDispose', () { - final autoDispose = AsyncNotifierProvider.autoDispose< - AutoDisposeAsyncTestNotifier, int>( - () => AutoDisposeAsyncTestNotifier((ref) => 0), - ); - - autoDispose.select((AsyncValue value) => 0); - autoDispose.selectAsync((int value) => 0); - - canBeAssignedToProviderListenable>(autoDispose); - // canBeAssignedToAlwaysAliveListenable>(autoDispose); - canBeAssignedToRefreshable>(autoDispose); - // canBeAssignedToAlwaysAliveRefreshable>(autoDispose); - - canBeAssignedToProviderListenable>(autoDispose.future); - // canBeAssignedToAlwaysAliveListenable>(autoDispose.future); - canBeAssignedToRefreshable>(autoDispose.future); - // canBeAssignedToAlwaysAliveRefreshable>(autoDispose.future); - - canBeAssignedToProviderListenable>( - autoDispose.notifier, - ); - // canBeAssignedToAlwaysAliveListenable>( - // autoDispose.notifier, - // ); - canBeAssignedToRefreshable>( - autoDispose.notifier, - ); - // canBeAssignedToAlwaysAliveRefreshable>( - // autoDispose.notifier, - // ); - }); - - test('family', () { - final family = AsyncNotifierProvider.family< - AsyncTestNotifierFamily, String, int>( - () => AsyncTestNotifierFamily((ref) => '0'), - ); - - family(0).select((AsyncValue value) => 0); - family(0).selectAsync((String value) => 0); - - canBeAssignedToProviderListenable>(family(0)); - canBeAssignedToAlwaysAliveListenable>(family(0)); - canBeAssignedToRefreshable>(family(0)); - canBeAssignedToAlwaysAliveRefreshable>(family(0)); - - canBeAssignedToProviderListenable>(family(0).future); - canBeAssignedToAlwaysAliveListenable>(family(0).future); - canBeAssignedToRefreshable>(family(0).future); - canBeAssignedToAlwaysAliveRefreshable>(family(0).future); - - canBeAssignedToProviderListenable>( - family(0).notifier, - ); - canBeAssignedToAlwaysAliveListenable>( - family(0).notifier, - ); - canBeAssignedToRefreshable>( - family(0).notifier, - ); - canBeAssignedToAlwaysAliveRefreshable>( - family(0).notifier, - ); - }); - - test('autoDisposeFamily', () { - expect( - AsyncNotifierProvider.autoDispose.family, - same(AsyncNotifierProvider.family.autoDispose), - ); - - final autoDisposeFamily = AsyncNotifierProvider.autoDispose - .family, String, int>( - () => AutoDisposeAsyncTestNotifierFamily((ref) => '0'), - ); - - autoDisposeFamily(0).select((AsyncValue value) => 0); - autoDisposeFamily(0).selectAsync((String value) => 0); - - canBeAssignedToProviderListenable>( - autoDisposeFamily(0), - ); - // canBeAssignedToAlwaysAliveListenable>( - // autoDisposeFamily(0), - // ); - canBeAssignedToRefreshable>( - autoDisposeFamily(0), - ); - // canBeAssignedToAlwaysAliveRefreshable>( - // autoDisposeFamily(0), - // ); - - canBeAssignedToProviderListenable>( - autoDisposeFamily(0).future, - ); - // canBeAssignedToAlwaysAliveListenable>( - // autoDisposeFamily(0).future, - // ); - canBeAssignedToRefreshable>( - autoDisposeFamily(0).future, - ); - // canBeAssignedToAlwaysAliveRefreshable>( - // autoDisposeFamily(0).future, - // ); - - canBeAssignedToProviderListenable< - AutoDisposeFamilyAsyncNotifier>( - autoDisposeFamily(0).notifier, - ); - // canBeAssignedToAlwaysAliveListenable< - // AutoDisposeFamilyAsyncNotifier>( - // autoDisposeFamily(0).notifier, - // ); - canBeAssignedToRefreshable>( - autoDisposeFamily(0).notifier, - ); - // canBeAssignedToAlwaysAliveRefreshable< - // AutoDisposeFamilyAsyncNotifier>( - // autoDisposeFamily(0).notifier, - // ); - }); - }); -} - -@immutable -class Equal { - // ignore: prefer_const_constructors_in_immutables - Equal(this.value); - - final T value; - - @override - bool operator ==(Object other) => other is Equal && other.value == value; - - @override - int get hashCode => Object.hash(runtimeType, value); -} diff --git a/packages/riverpod/test/providers/async_notifier/factory.dart b/packages/riverpod/test/providers/async_notifier/factory.dart deleted file mode 100644 index 6349d73d5..000000000 --- a/packages/riverpod/test/providers/async_notifier/factory.dart +++ /dev/null @@ -1,312 +0,0 @@ -import 'dart:async'; - -import 'package:riverpod/src/internals.dart'; - -typedef AsyncNotifierProviderFactoryType - = AsyncNotifierProviderBase - Function, T>( - NotifierT Function() create, { - Object? argument, - Iterable? dependencies, - Family? from, - String? name, -}); - -typedef AsyncNotifierFactoryType = AsyncTestNotifierBase Function( - FutureOr Function(AsyncNotifierProviderRef), { - bool Function(AsyncValue, AsyncValue)? updateShouldNotify, -}); - -typedef SimpleTestProviderFactoryType - = AsyncNotifierProviderBase, T> Function( - FutureOr Function(AsyncNotifierProviderRef ref) init, { - bool Function(AsyncValue prev, AsyncValue next)? updateShouldNotify, -}); - -typedef TestProviderFactoryType - = AsyncNotifierProviderBase, T> Function( - AsyncTestNotifierBase Function() createNotifier, -); - -List matrix({ - bool alwaysAlive = true, - bool autoDispose = true, -}) { - return [ - if (alwaysAlive) - AsyncNotifierFactory( - label: 'AsyncNotifierProvider', - isAutoDispose: false, - provider: AsyncNotifierProviderImpl.new, - notifier: AsyncTestNotifier.new, - testProvider: (createNotifier) { - return AsyncNotifierProviderImpl, T>( - createNotifier, - ); - }, - simpleTestProvider: (init, {updateShouldNotify}) { - return AsyncNotifierProvider, T>( - () => AsyncTestNotifier( - init, - updateShouldNotify: updateShouldNotify, - ), - ); - }, - ), - if (alwaysAlive) - AsyncNotifierFactory( - label: 'AsyncNotifierProviderFamily', - isAutoDispose: false, - provider: , T>( - create, { - argument, - dependencies, - from, - name, - }) { - return FamilyAsyncNotifierProviderImpl.internal( - create, - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - notifier: AsyncTestNotifierFamily.new, - testProvider: (createNotifier) { - return FamilyAsyncNotifierProviderImpl, T, - int>.internal( - () => createNotifier() as AsyncTestNotifierFamily, - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - simpleTestProvider: (init, {updateShouldNotify}) { - return FamilyAsyncNotifierProviderImpl, T, - int>.internal( - () => AsyncTestNotifierFamily( - init, - updateShouldNotify: updateShouldNotify, - ), - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - ), - if (autoDispose) - AsyncNotifierFactory( - label: 'AutoDisposeAsyncNotifierProvider', - isAutoDispose: true, - provider: AutoDisposeAsyncNotifierProviderImpl.new, - notifier: AutoDisposeAsyncTestNotifier.new, - testProvider: (createNotifier) { - return AutoDisposeAsyncNotifierProviderImpl< - AutoDisposeAsyncTestNotifier, T>( - () => createNotifier() as AutoDisposeAsyncTestNotifier, - ); - }, - simpleTestProvider: (init, {updateShouldNotify}) { - return AutoDisposeAsyncNotifierProvider< - AutoDisposeAsyncTestNotifier, T>( - () => AutoDisposeAsyncTestNotifier( - init, - updateShouldNotify: updateShouldNotify, - ), - ); - }, - ), - if (autoDispose) - AsyncNotifierFactory( - label: 'AutoDisposeAsyncNotifierProviderFamily', - isAutoDispose: true, - provider: , T>( - create, { - argument, - dependencies, - from, - name, - }) { - return AutoDisposeFamilyAsyncNotifierProviderImpl.internal( - create, - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - notifier: AutoDisposeAsyncTestNotifierFamily.new, - testProvider: (createNotifier) { - return AutoDisposeFamilyAsyncNotifierProviderImpl< - AutoDisposeAsyncTestNotifierFamily, T, int>.internal( - () => createNotifier() as AutoDisposeAsyncTestNotifierFamily, - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - simpleTestProvider: (init, {updateShouldNotify}) { - return AutoDisposeFamilyAsyncNotifierProviderImpl< - AutoDisposeAsyncTestNotifierFamily, T, int>.internal( - () => AutoDisposeAsyncTestNotifierFamily( - init, - updateShouldNotify: updateShouldNotify, - ), - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - ), - ]; -} - -class AsyncNotifierFactory { - const AsyncNotifierFactory({ - required this.label, - required this.provider, - required this.notifier, - required this.isAutoDispose, - required this.testProvider, - required this.simpleTestProvider, - }); - - final String label; - final bool isAutoDispose; - final AsyncNotifierProviderFactoryType provider; - final AsyncNotifierFactoryType notifier; - final TestProviderFactoryType testProvider; - final SimpleTestProviderFactoryType simpleTestProvider; -} - -abstract class AsyncTestNotifierBase extends AsyncNotifierBase { - // overriding to remove the @protected - @override - Future update( - FutureOr Function(T p1) cb, { - FutureOr Function(Object err, StackTrace stackTrace)? onError, - }) { - return super.update(cb); - } -} - -class AsyncTestNotifier extends AsyncNotifier - implements AsyncTestNotifierBase { - AsyncTestNotifier( - this._init, { - bool Function(AsyncValue prev, AsyncValue next)? updateShouldNotify, - }) : _updateShouldNotify = updateShouldNotify; - - final FutureOr Function(AsyncNotifierProviderRef ref) _init; - - final bool Function(AsyncValue prev, AsyncValue next)? - _updateShouldNotify; - - @override - FutureOr build() => _init(ref); - - @override - bool updateShouldNotify(AsyncValue prev, AsyncValue next) { - return _updateShouldNotify?.call(prev, next) ?? - super.updateShouldNotify(prev, next); - } - - @override - String toString() { - return 'AsyncTestNotifier<$T>#$hashCode'; - } -} - -class AsyncTestNotifierFamily extends FamilyAsyncNotifier - implements AsyncTestNotifierBase { - AsyncTestNotifierFamily( - this._init, { - bool Function(AsyncValue prev, AsyncValue next)? updateShouldNotify, - }) : _updateShouldNotify = updateShouldNotify; - - final FutureOr Function(AsyncNotifierProviderRef ref) _init; - - final bool Function(AsyncValue prev, AsyncValue next)? - _updateShouldNotify; - - @override - FutureOr build(int arg) => _init(ref); - - @override - bool updateShouldNotify(AsyncValue prev, AsyncValue next) { - return _updateShouldNotify?.call(prev, next) ?? - super.updateShouldNotify(prev, next); - } - - @override - String toString() { - return 'AsyncTestNotifierFamily<$T>#$hashCode'; - } -} - -class AutoDisposeAsyncTestNotifier extends AutoDisposeAsyncNotifier - implements AsyncTestNotifierBase { - AutoDisposeAsyncTestNotifier( - this._init2, { - bool Function(AsyncValue prev, AsyncValue next)? updateShouldNotify, - }) : _updateShouldNotify = updateShouldNotify; - - final FutureOr Function(AutoDisposeAsyncNotifierProviderRef ref) _init2; - - final bool Function(AsyncValue prev, AsyncValue next)? - _updateShouldNotify; - - @override - FutureOr build() => _init2(ref); - - @override - bool updateShouldNotify(AsyncValue prev, AsyncValue next) { - return _updateShouldNotify?.call(prev, next) ?? - super.updateShouldNotify(prev, next); - } - - @override - String toString() { - return 'AutoDisposeAsyncTestNotifier<$T>#$hashCode'; - } -} - -class AutoDisposeAsyncTestNotifierFamily - extends AutoDisposeFamilyAsyncNotifier - implements AsyncTestNotifierBase { - AutoDisposeAsyncTestNotifierFamily( - this._init2, { - bool Function(AsyncValue prev, AsyncValue next)? updateShouldNotify, - }) : _updateShouldNotify = updateShouldNotify; - - final FutureOr Function(AutoDisposeAsyncNotifierProviderRef ref) _init2; - - final bool Function(AsyncValue prev, AsyncValue next)? - _updateShouldNotify; - - @override - FutureOr build(int arg) => _init2(ref); - - @override - bool updateShouldNotify(AsyncValue prev, AsyncValue next) { - return _updateShouldNotify?.call(prev, next) ?? - super.updateShouldNotify(prev, next); - } - - @override - String toString() { - return 'AutoDisposeAsyncTestNotifierFamily<$T, int>#$hashCode'; - } -} diff --git a/packages/riverpod/test/providers/notifier/factory.dart b/packages/riverpod/test/providers/notifier/factory.dart deleted file mode 100644 index d59565914..000000000 --- a/packages/riverpod/test/providers/notifier/factory.dart +++ /dev/null @@ -1,290 +0,0 @@ -import 'package:riverpod/src/internals.dart'; - -typedef NotifierProviderFactoryType = NotifierProviderBase - Function, T>( - NotifierT Function() create, { - Object? argument, - Iterable? dependencies, - Family? from, - String? name, -}); - -typedef NotifierFactoryType = TestNotifierBase Function( - T Function(NotifierProviderRef), { - bool Function(T, T)? updateShouldNotify, -}); - -typedef SimpleTestProviderFactoryType - = NotifierProviderBase, T> Function( - T Function(NotifierProviderRef ref) init, { - bool Function(T prev, T next)? updateShouldNotify, -}); - -typedef TestProviderFactoryType = NotifierProviderBase, T> - Function( - TestNotifierBase Function() createNotifier, -); - -List matrix({ - bool alwaysAlive = true, - bool autoDispose = true, -}) { - return [ - if (alwaysAlive) - NotifierFactory( - label: 'NotifierProvider', - isAutoDispose: false, - provider: NotifierProviderImpl.new, - notifier: TestNotifier.new, - testProvider: (createNotifier) { - return NotifierProviderImpl, T>( - createNotifier, - ); - }, - simpleTestProvider: (init, {updateShouldNotify}) { - return NotifierProvider, T>( - () => TestNotifier( - init, - updateShouldNotify: updateShouldNotify, - ), - ); - }, - ), - if (alwaysAlive) - NotifierFactory( - label: 'NotifierProviderFamily', - isAutoDispose: false, - provider: , T>( - create, { - argument, - dependencies, - from, - name, - }) { - return FamilyNotifierProviderImpl.internal( - create, - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - notifier: TestNotifierFamily.new, - testProvider: (createNotifier) { - return FamilyNotifierProviderImpl, T, - int>.internal( - () => createNotifier() as TestNotifierFamily, - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - simpleTestProvider: (init, {updateShouldNotify}) { - return FamilyNotifierProviderImpl, T, - int>.internal( - () => TestNotifierFamily( - init, - updateShouldNotify: updateShouldNotify, - ), - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - ), - if (autoDispose) - NotifierFactory( - label: 'AutoDisposeNotifierProvider', - isAutoDispose: true, - provider: AutoDisposeNotifierProviderImpl.new, - notifier: AutoDisposeTestNotifier.new, - testProvider: (createNotifier) { - return AutoDisposeNotifierProviderImpl, T>( - () => createNotifier() as AutoDisposeTestNotifier, - ); - }, - simpleTestProvider: (init, {updateShouldNotify}) { - return AutoDisposeNotifierProvider, T>( - () => AutoDisposeTestNotifier( - init, - updateShouldNotify: updateShouldNotify, - ), - ); - }, - ), - if (autoDispose) - NotifierFactory( - label: 'AutoDisposeNotifierProviderFamily', - isAutoDispose: true, - provider: , T>( - create, { - argument, - dependencies, - from, - name, - }) { - return AutoDisposeFamilyNotifierProviderImpl.internal( - create, - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - notifier: AutoDisposeTestNotifierFamily.new, - testProvider: (createNotifier) { - return AutoDisposeFamilyNotifierProviderImpl< - AutoDisposeTestNotifierFamily, T, int>.internal( - () => createNotifier() as AutoDisposeTestNotifierFamily, - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - simpleTestProvider: (init, {updateShouldNotify}) { - return AutoDisposeFamilyNotifierProviderImpl< - AutoDisposeTestNotifierFamily, T, int>.internal( - () => AutoDisposeTestNotifierFamily( - init, - updateShouldNotify: updateShouldNotify, - ), - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - ), - ]; -} - -class NotifierFactory { - const NotifierFactory({ - required this.label, - required this.provider, - required this.notifier, - required this.isAutoDispose, - required this.testProvider, - required this.simpleTestProvider, - }); - - final String label; - final bool isAutoDispose; - final NotifierProviderFactoryType provider; - final NotifierFactoryType notifier; - final TestProviderFactoryType testProvider; - final SimpleTestProviderFactoryType simpleTestProvider; -} - -abstract class TestNotifierBase extends NotifierBase {} - -class TestNotifier extends Notifier implements TestNotifierBase { - TestNotifier(this._init, {bool Function(T prev, T next)? updateShouldNotify}) - : _updateShouldNotify = updateShouldNotify; - - final T Function(NotifierProviderRef ref) _init; - - final bool Function(T prev, T next)? _updateShouldNotify; - - @override - T build() => _init(ref); - - @override - bool updateShouldNotify(T previous, T next) { - return _updateShouldNotify?.call(previous, next) ?? - super.updateShouldNotify(previous, next); - } - - @override - String toString() { - return 'TestNotifier<$T>#$hashCode'; - } -} - -class TestNotifierFamily extends FamilyNotifier - implements TestNotifierBase { - TestNotifierFamily( - this._init, { - bool Function(T prev, T next)? updateShouldNotify, - }) : _updateShouldNotify = updateShouldNotify; - - final T Function(NotifierProviderRef ref) _init; - - final bool Function(T prev, T next)? _updateShouldNotify; - - @override - T build(int arg) => _init(ref); - - @override - bool updateShouldNotify(T previous, T next) { - return _updateShouldNotify?.call(previous, next) ?? - super.updateShouldNotify(previous, next); - } - - @override - String toString() { - return 'TestNotifierFamily<$T>#$hashCode'; - } -} - -class AutoDisposeTestNotifier extends AutoDisposeNotifier - implements TestNotifierBase { - AutoDisposeTestNotifier( - this._init2, { - bool Function(T prev, T next)? updateShouldNotify, - }) : _updateShouldNotify = updateShouldNotify; - - final T Function(AutoDisposeNotifierProviderRef ref) _init2; - - final bool Function(T prev, T next)? _updateShouldNotify; - - @override - T build() => _init2(ref); - - @override - bool updateShouldNotify(T previous, T next) { - return _updateShouldNotify?.call(previous, next) ?? - super.updateShouldNotify(previous, next); - } - - @override - String toString() { - return 'AutoDisposeTestNotifier<$T>#$hashCode'; - } -} - -class AutoDisposeTestNotifierFamily extends AutoDisposeFamilyNotifier - implements TestNotifierBase { - AutoDisposeTestNotifierFamily( - this._init2, { - bool Function(T prev, T next)? updateShouldNotify, - }) : _updateShouldNotify = updateShouldNotify; - - final T Function(AutoDisposeNotifierProviderRef ref) _init2; - - final bool Function(T prev, T next)? _updateShouldNotify; - - @override - T build(int arg) => _init2(ref); - - @override - bool updateShouldNotify(T previous, T next) { - return _updateShouldNotify?.call(previous, next) ?? - super.updateShouldNotify(previous, next); - } - - @override - String toString() { - return 'AutoDisposeTestNotifierFamily<$T, int>#$hashCode'; - } -} diff --git a/packages/riverpod/test/providers/notifier/notifier_test.dart b/packages/riverpod/test/providers/notifier/notifier_test.dart deleted file mode 100644 index 8512c63ed..000000000 --- a/packages/riverpod/test/providers/notifier/notifier_test.dart +++ /dev/null @@ -1,689 +0,0 @@ -// ignore_for_file: avoid_types_on_closure_parameters - -import 'package:meta/meta.dart'; -import 'package:mockito/mockito.dart'; -import 'package:riverpod/riverpod.dart' hide ErrorListener; -import 'package:test/test.dart'; - -import '../../utils.dart'; -import 'factory.dart'; - -void main() { - for (final factory in matrix()) { - group(factory.label, () { - test( - 'throws if the same Notifier instance is reused in different providers', - () { - // Regression test for https://github.com/rrousselGit/riverpod/issues/2617 - final container = createContainer(); - - final notifier = factory.notifier((ref) => 0); - - final provider = factory.provider, int>( - () => notifier, - ); - final provider2 = factory.provider, int>( - () => notifier, - ); - - container.read(provider); - - expect( - () => container.read(provider2), - throwsA(isA()), - ); - }); - - group('Notifier.stateOrNull', () { - test('returns null during first build until state= is set', () { - final stateInBuild = []; - - final provider = NotifierProvider, int>(() { - late TestNotifier notifier; - return notifier = TestNotifier((ref) { - stateInBuild.add(notifier.stateOrNull); - return 0; - }); - }); - final container = createContainer(); - - final sub = container.listen( - provider.notifier, - (_, __) {}, - ); - - expect(stateInBuild, [null]); - - expect(sub.read().stateOrNull, 0); - }); - - test('returns null if Notifier.build threw', () { - final provider = factory.simpleTestProvider( - (ref) => throw Exception('42'), - ); - final container = createContainer(); - - final sub = container.listen( - provider.notifier, - (_, __) {}, - ); - - expect(sub.read().stateOrNull, null); - }); - - test( - 'returns the previous state if using inside Notifier.build ' - 'after the state was already initialized', () { - final stateInBuild = []; - - final provider = NotifierProvider, int>(() { - late TestNotifier notifier; - return notifier = TestNotifier((ref) { - stateInBuild.add(notifier.stateOrNull); - return 0; - }); - }); - final container = createContainer(); - - final sub = container.listen( - provider.notifier, - (_, __) {}, - ); - - sub.read().state = 42; - container.refresh(provider); - - expect(stateInBuild, [null, 42]); - }); - - test('Post build, returns the current state', () { - final provider = factory.simpleTestProvider( - (ref) => 0, - ); - final container = createContainer(); - - final sub = container.listen( - provider.notifier, - (_, __) {}, - ); - - expect(sub.read().stateOrNull, 0); - - sub.read().state = 42; - - expect(sub.read().stateOrNull, 42); - }); - - test( - 'On invalidated providers, rebuilds the notifier and return the new state', - () { - final provider = factory.simpleTestProvider( - (ref) => 0, - ); - final container = createContainer(); - - final sub = container.listen( - provider.notifier, - (_, __) {}, - ); - - sub.read().state = 42; - - expect(sub.read().stateOrNull, 42); - - container.invalidate(provider); - - expect(sub.read().stateOrNull, 0); - }); - }); - - test( - 'uses notifier.build as initial state and update listeners when state changes', - () { - final provider = factory.simpleTestProvider((ref) => 0); - final container = createContainer(); - final listener = Listener(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 0)); - - container.read(provider.notifier).state++; - - verifyOnly(listener, listener(0, 1)); - }); - - test('preserves the notifier between watch updates', () async { - final dep = StateProvider((ref) => 0); - final provider = factory.simpleTestProvider((ref) { - return ref.watch(dep); - }); - final container = createContainer(); - final listener = Listener>(); - - container.listen( - provider.notifier, - listener.call, - fireImmediately: true, - ); - - final notifier = container.read(provider.notifier); - - verifyOnly(listener, listener(null, notifier)); - - container.read(dep.notifier).update((state) => state + 1); - await container.pump(); - - verifyNoMoreInteractions(listener); - expect(container.read(provider.notifier), notifier); - }); - - test('calls notifier.build on every watch update', () async { - final dep = StateProvider((ref) => 0); - final provider = factory.simpleTestProvider((ref) { - return ref.watch(dep); - }); - final container = createContainer(); - final listener = Listener(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 0)); - - container.read(dep.notifier).update((state) => state + 1); - - verifyNoMoreInteractions(listener); - - await container.pump(); - - verifyOnly(listener, listener(0, 1)); - }); - - test( - 'After a state initialization error, the notifier is still available', - () { - final provider = factory.simpleTestProvider((ref) { - throw StateError('Hey'); - }); - final container = createContainer(); - - expect( - () => container.read(provider), - throwsStateError, - ); - - container.read(provider.notifier); - }); - - test('handles fail to initialize the notifier', () { - final err = UnimplementedError(); - final stack = StackTrace.current; - final provider = factory.provider, int>( - () => Error.throwWithStackTrace(err, stack), - ); - final container = createContainer(); - final listener = ErrorListener(); - - expect( - () => container.read(provider.notifier), - throwsUnimplementedError, - ); - expect( - () => container.read(provider), - throwsUnimplementedError, - ); - - final stateSub = container.listen( - provider, - (previous, next) {}, - onError: listener.call, - ); - - verifyNoMoreInteractions(listener); - - container.listen( - provider, - (previous, next) {}, - onError: listener.call, - fireImmediately: true, - ); - - verifyOnly(listener, listener(err, stack)); - - final notifierSub = container.listen( - provider.notifier, - (previous, next) {}, - onError: listener.call, - ); - - verifyNoMoreInteractions(listener); - - container.listen( - provider.notifier, - (previous, next) {}, - onError: listener.call, - fireImmediately: true, - ); - - verifyOnly(listener, listener(err, stack)); - - expect(stateSub.read, throwsUnimplementedError); - expect(notifierSub.read, throwsUnimplementedError); - }); - - test('can read/set the current state within the notifier', () { - final provider = factory.simpleTestProvider((ref) => 0); - final container = createContainer(); - final listener = Listener(); - - container.listen(provider, listener.call, fireImmediately: true); - final notifier = container.read(provider.notifier); - - expect(notifier.state, 0); - verifyOnly(listener, listener(null, 0)); - - notifier.state++; - - expect(notifier.state, 1); - verifyOnly(listener, listener(0, 1)); - - notifier.state++; - - expect(notifier.state, 2); - verifyOnly(listener, listener(1, 2)); - }); - - test( - 'Reading the state inside the notifier rethrows initilization error, if any', - () { - final provider = factory - .simpleTestProvider((ref) => throw UnimplementedError()); - final container = createContainer(); - - final notifier = container.read(provider.notifier); - - expect(() => notifier.state, throwsUnimplementedError); - }); - - test( - 'Setting the state after an initialization error allow listening the state again', - () { - final err = UnimplementedError(); - final stack = StackTrace.current; - final provider = factory.simpleTestProvider( - (ref) => Error.throwWithStackTrace(err, stack), - ); - final container = createContainer(); - final listener = Listener(); - final onError = ErrorListener(); - - container.listen( - provider, - listener.call, - onError: onError.call, - fireImmediately: true, - ); - final notifier = container.read(provider.notifier); - - verifyOnly(onError, onError(err, stack)); - verifyZeroInteractions(listener); - - expect(() => notifier.state, throwsUnimplementedError); - - notifier.state = 0; - - verifyOnly(listener, listener(null, 0)); - verifyNoMoreInteractions(onError); - expect(notifier.state, 0); - expect(container.read(provider), 0); - - container.listen( - provider, - listener.call, - onError: onError.call, - fireImmediately: true, - ); - - verifyOnly(listener, listener(null, 0)); - verifyNoMoreInteractions(onError); - }); - - test( - 'reading notifier.state on invalidated provider rebuilds the provider', - () { - final dep = StateProvider((ref) => 0); - final provider = - factory.simpleTestProvider((ref) => ref.watch(dep)); - final container = createContainer(); - final listener = Listener(); - - container.listen(provider, listener.call); - final notifier = container.read(provider.notifier); - - expect(notifier.state, 0); - - notifier.state = -1; - - verifyOnly(listener, listener(0, -1)); - - container.read(dep.notifier).state++; - - expect(notifier.state, 1); - verifyOnly(listener, listener(-1, 1)); - }); - - test('supports ref.refresh(provider)', () { - final provider = factory.simpleTestProvider((ref) => 0); - final container = createContainer(); - - final notifier = container.read(provider.notifier); - - expect(container.read(provider), 0); - - notifier.state = 42; - - expect(container.read(provider), 42); - - expect(container.refresh(provider), 0); - expect(container.read(provider), 0); - expect(notifier.state, 0); - expect(container.read(provider.notifier), notifier); - }); - - test('supports listenSelf((State? prev, State next) {})', () { - final listener = Listener(); - final onError = ErrorListener(); - final provider = factory.simpleTestProvider((ref) { - ref.listenSelf(listener.call, onError: onError.call); - Error.throwWithStackTrace(42, StackTrace.empty); - }); - final container = createContainer(); - - expect(() => container.read(provider), throwsA(42)); - - verifyOnly(onError, onError(42, StackTrace.empty)); - }); - - test('filters state update by identical by default', () { - final provider = - factory.simpleTestProvider>((ref) => Equal(42)); - final container = createContainer(); - final listener = Listener>(); - - container.listen(provider, listener.call); - final notifier = container.read(provider.notifier); - final firstState = notifier.state; - - // voluntarily assigning the same value - final self = notifier.state; - notifier.state = self; - - verifyZeroInteractions(listener); - - final secondState = notifier.state = Equal(42); - - verifyOnly(listener, listener(firstState, secondState)); - }); - - test( - 'Can override Notifier.updateShouldNotify to change the default filter logic', - () { - final provider = factory.simpleTestProvider>( - (ref) => Equal(42), - updateShouldNotify: (a, b) => a != b, - ); - final container = createContainer(); - final listener = Listener>(); - - container.listen(provider, listener.call); - final notifier = container.read(provider.notifier); - - // voluntarily assigning the same value - final self = notifier.state; - notifier.state = self; - - verifyZeroInteractions(listener); - - notifier.state = Equal(42); - - verifyZeroInteractions(listener); - - notifier.state = Equal(21); - - verifyOnly(listener, listener(Equal(42), Equal(21))); - }); - - test( - 'can override the Notifier with a matching custom implementation', - () {}, - skip: 'TODO', - ); - - test('can override Notifier.build', () {}); - }); - - if (factory.isAutoDispose) { - group('autoDispose', () { - test('keeps state alive if notifier is listened', () async { - final container = createContainer(); - final onDispose = OnDisposeMock(); - final provider = factory.simpleTestProvider((ref) { - ref.onDispose(onDispose.call); - return 0; - }); - - final sub = container.listen(provider, (prev, next) {}); - verifyZeroInteractions(onDispose); - expect(container.getAllProviderElements().single.origin, provider); - - await container.pump(); - - verifyZeroInteractions(onDispose); - expect(container.getAllProviderElements().single.origin, provider); - - sub.close(); - await container.pump(); - - verifyOnly(onDispose, onDispose()); - expect(container.getAllProviderElements(), isEmpty); - }); - }); - } - } - - test('supports overrideWith', () { - final provider = NotifierProvider, int>( - () => TestNotifier((ref) => 0), - ); - final autoDispose = - NotifierProvider.autoDispose, int>( - () => AutoDisposeTestNotifier((ref) => 0), - ); - final container = createContainer( - overrides: [ - provider.overrideWith(() => TestNotifier((ref) => 42)), - autoDispose.overrideWith( - () => AutoDisposeTestNotifier((ref) => 84), - ), - ], - ); - - expect(container.read(provider), 42); - expect(container.read(autoDispose), 84); - }); - - test('supports family overrideWith', () { - final family = NotifierProvider.family, int, int>( - () => TestNotifierFamily((ref) => 0), - ); - final autoDisposeFamily = NotifierProvider.autoDispose - .family, int, int>( - () => AutoDisposeTestNotifierFamily((ref) => 0), - ); - final container = createContainer( - overrides: [ - family.overrideWith( - () => TestNotifierFamily((ref) => 42), - ), - autoDisposeFamily.overrideWith( - () => AutoDisposeTestNotifierFamily((ref) => 84), - ), - ], - ); - - expect(container.read(family(10)), 42); - expect(container.read(autoDisposeFamily(10)), 84); - }); - - group('modifiers', () { - void canBeAssignedToAlwaysAliveRefreshable( - AlwaysAliveRefreshable provider, - ) {} - - void canBeAssignedToRefreshable( - Refreshable provider, - ) {} - - void canBeAssignedToAlwaysAliveListenable( - AlwaysAliveProviderListenable provider, - ) {} - - void canBeAssignedToProviderListenable( - ProviderListenable provider, - ) {} - - // TODO use package:expect_error to test that commented lined are not compiling - - test('provider', () { - final provider = NotifierProvider, int>( - () => TestNotifier((ref) => 0), - ); - - provider.select((int value) => 0); - - canBeAssignedToProviderListenable(provider); - canBeAssignedToAlwaysAliveListenable(provider); - canBeAssignedToRefreshable(provider); - canBeAssignedToAlwaysAliveRefreshable(provider); - - canBeAssignedToProviderListenable>(provider.notifier); - canBeAssignedToAlwaysAliveListenable>( - provider.notifier, - ); - canBeAssignedToRefreshable>(provider.notifier); - canBeAssignedToAlwaysAliveRefreshable>( - provider.notifier, - ); - }); - - test('autoDispose', () { - final autoDispose = - NotifierProvider.autoDispose, int>( - () => AutoDisposeTestNotifier((ref) => 0), - ); - - autoDispose.select((int value) => 0); - - canBeAssignedToProviderListenable(autoDispose); - // canBeAssignedToAlwaysAliveListenable(autoDispose); - canBeAssignedToRefreshable(autoDispose); - // canBeAssignedToAlwaysAliveRefreshable(autoDispose); - - canBeAssignedToProviderListenable>( - autoDispose.notifier, - ); - // canBeAssignedToAlwaysAliveListenable>( - // autoDispose.notifier, - // ); - canBeAssignedToRefreshable>( - autoDispose.notifier, - ); - // canBeAssignedToAlwaysAliveRefreshable>( - // autoDispose.notifier, - // ); - }); - - test('family', () { - final family = - NotifierProvider.family, String, int>( - () => TestNotifierFamily((ref) => '0'), - ); - - family(0).select((String value) => 0); - - canBeAssignedToProviderListenable(family(0)); - canBeAssignedToAlwaysAliveListenable(family(0)); - canBeAssignedToRefreshable(family(0)); - canBeAssignedToAlwaysAliveRefreshable(family(0)); - - canBeAssignedToProviderListenable>( - family(0).notifier, - ); - canBeAssignedToAlwaysAliveListenable>( - family(0).notifier, - ); - canBeAssignedToRefreshable>( - family(0).notifier, - ); - canBeAssignedToAlwaysAliveRefreshable>( - family(0).notifier, - ); - }); - - test('autoDisposeFamily', () { - expect( - NotifierProvider.autoDispose.family, - same(NotifierProvider.family.autoDispose), - ); - - final autoDisposeFamily = NotifierProvider.autoDispose - .family, String, int>( - () => AutoDisposeTestNotifierFamily((ref) => '0'), - ); - - autoDisposeFamily(0).select((String value) => 0); - - canBeAssignedToProviderListenable( - autoDisposeFamily(0), - ); - // canBeAssignedToAlwaysAliveListenable( - // autoDisposeFamily(0), - // ); - canBeAssignedToRefreshable( - autoDisposeFamily(0), - ); - // canBeAssignedToAlwaysAliveRefreshable( - // autoDisposeFamily(0), - // ); - - canBeAssignedToProviderListenable>( - autoDisposeFamily(0).notifier, - ); - // canBeAssignedToAlwaysAliveListenable< - // AutoDisposeFamilyNotifier>( - // autoDisposeFamily(0).notifier, - // ); - canBeAssignedToRefreshable>( - autoDisposeFamily(0).notifier, - ); - // canBeAssignedToAlwaysAliveRefreshable< - // AutoDisposeFamilyNotifier>( - // autoDisposeFamily(0).notifier, - // ); - }); - }); -} - -@immutable -class Equal { - // ignore: prefer_const_constructors_in_immutables - Equal(this.value); - - final T value; - - @override - bool operator ==(Object other) => other is Equal && other.value == value; - - @override - int get hashCode => Object.hash(runtimeType, value); -} diff --git a/packages/riverpod/test/providers/provider/auto_dispose_provider_test.dart b/packages/riverpod/test/providers/provider/auto_dispose_provider_test.dart deleted file mode 100644 index 441e10c59..000000000 --- a/packages/riverpod/test/providers/provider/auto_dispose_provider_test.dart +++ /dev/null @@ -1,202 +0,0 @@ -import 'package:mockito/mockito.dart'; -import 'package:riverpod/riverpod.dart'; -import 'package:test/test.dart'; - -import '../../utils.dart'; - -void main() { - group('Provider.autoDispose', () { - group('ref.state', () { - test('can read and change current value', () { - final container = createContainer(); - final listener = Listener(); - late ProviderRef ref; - final provider = Provider.autoDispose((r) { - ref = r; - return 0; - }); - - container.listen(provider, listener.call); - verifyZeroInteractions(listener); - - expect(ref.state, 0); - - ref.state = 42; - - verifyOnly(listener, listener(0, 42)); - expect(ref.state, 42); - }); - - test('fails if trying to read the state before it was set', () { - final container = createContainer(); - Object? err; - final provider = Provider.autoDispose((ref) { - try { - ref.state; - } catch (e) { - err = e; - } - return 0; - }); - - container.read(provider); - expect(err, isStateError); - }); - - test( - 'on rebuild, still fails if trying to read the state before was built', - () { - final dep = StateProvider((ref) => false); - final container = createContainer(); - Object? err; - final provider = Provider.autoDispose((ref) { - if (ref.watch(dep)) { - try { - ref.state; - } catch (e) { - err = e; - } - } - return 0; - }); - - container.read(provider); - expect(err, isNull); - - container.read(dep.notifier).state = true; - container.read(provider); - - expect(err, isStateError); - }); - - test('can read the state if the setter was called before', () { - final container = createContainer(); - final provider = Provider.autoDispose((ref) { - // ignore: join_return_with_assignment - ref.state = 42; - - return ref.state; - }); - - expect(container.read(provider), 42); - }); - }); - - test('can be refreshed', () async { - var result = 0; - final container = createContainer(); - final provider = Provider.autoDispose((ref) => result); - - expect(container.read(provider), 0); - - result = 1; - expect(container.refresh(provider), 1); - - expect(container.read(provider), 1); - }); - - test('does not notify listeners when called ref.state= with == new value', - () async { - final container = createContainer(); - final listener = Listener(); - late AutoDisposeProviderRef ref; - final provider = AutoDisposeProvider((r) { - ref = r; - return 0; - }); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 0)); - - ref.state = 0; - await container.pump(); - - verifyNoMoreInteractions(listener); - }); - - group('scoping an override overrides all the associated subproviders', () { - test('when passing the provider itself', () { - final provider = Provider.autoDispose((ref) => 0); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); - - expect(container.read(provider), 0); - expect(container.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', provider), - ]); - expect(root.getAllProviderElements(), isEmpty); - }); - - test('when using provider.overrideWithValue', () { - final provider = Provider.autoDispose((ref) => 0); - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [ - provider.overrideWithValue(42), - ], - ); - - expect(container.read(provider), 42); - expect(container.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', provider), - ]); - expect(root.getAllProviderElements(), isEmpty); - }); - - test('when using provider.overrideWithProvider', () { - final provider = Provider.autoDispose((ref) => 0); - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [ - // ignore: deprecated_member_use_from_same_package - provider.overrideWithProvider(Provider.autoDispose((ref) => 42)), - ], - ); - - expect(container.read(provider), 42); - expect(container.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', provider), - ]); - expect(root.getAllProviderElements(), isEmpty); - }); - }); - - test('can be overridden by anything', () { - final provider = Provider.autoDispose((_) => 42); - final override = Provider.autoDispose((_) { - return 21; - }); - final container = createContainer( - overrides: [ - // ignore: deprecated_member_use_from_same_package - provider.overrideWithProvider(override), - ], - ); - - expect(container.read(provider), 21); - }); - - test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); - final provider = Provider.autoDispose( - (ref) => ref.watch(dep), - dependencies: [dep], - ); - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [dep.overrideWithValue(42)], - ); - - expect(container.read(provider), 42); - - expect(root.getAllProviderElements(), isEmpty); - }); - }); -} diff --git a/packages/riverpod/test/providers/provider/provider_test.dart b/packages/riverpod/test/providers/provider/provider_test.dart deleted file mode 100644 index f2b8316a0..000000000 --- a/packages/riverpod/test/providers/provider/provider_test.dart +++ /dev/null @@ -1,380 +0,0 @@ -// ignore_for_file: avoid_types_on_closure_parameters - -import 'package:mockito/mockito.dart'; -import 'package:riverpod/riverpod.dart'; -import 'package:test/test.dart'; - -import '../../utils.dart'; - -void main() { - group('Provider', () { - test('supports overrideWith', () { - final provider = Provider((ref) => 0); - final autoDispose = Provider.autoDispose((ref) => 0); - final container = createContainer( - overrides: [ - provider.overrideWith((ProviderRef ref) => 42), - autoDispose.overrideWith( - (AutoDisposeProviderRef ref) => 84, - ), - ], - ); - - expect(container.read(provider), 42); - expect(container.read(autoDispose), 84); - }); - - test('supports family overrideWith', () { - final family = Provider.family((ref, arg) => '0 $arg'); - final autoDisposeFamily = Provider.autoDispose.family( - (ref, arg) => '0 $arg', - ); - final container = createContainer( - overrides: [ - family.overrideWith((ProviderRef ref, int arg) => '42 $arg'), - autoDisposeFamily.overrideWith( - (AutoDisposeProviderRef ref, int arg) => '84 $arg', - ), - ], - ); - - expect(container.read(family(10)), '42 10'); - expect(container.read(autoDisposeFamily(10)), '84 10'); - }); - - test('can be refreshed', () async { - var result = 0; - final container = createContainer(); - final provider = Provider((ref) => result); - - expect(container.read(provider), 0); - - result = 1; - expect(container.refresh(provider), 1); - - expect(container.read(provider), 1); - }); - - group('ref.state', () { - test('throws on providers that threw', () { - final container = createContainer(); - final provider = Provider((ref) => throw UnimplementedError()); - - expect( - () => container.read(provider), - throwsUnimplementedError, - ); - - final element = - container.readProviderElement(provider) as ProviderElement; - - expect( - () => element.state, - throwsUnimplementedError, - ); - }); - - test('can read and change current value', () { - final container = createContainer(); - final listener = Listener(); - late ProviderRef ref; - final provider = Provider((r) { - ref = r; - return 0; - }); - - container.listen(provider, listener.call); - verifyZeroInteractions(listener); - - expect(ref.state, 0); - - ref.state = 42; - - verifyOnly(listener, listener(0, 42)); - expect(ref.state, 42); - }); - - test('fails if trying to read the state before it was set', () { - final container = createContainer(); - Object? err; - final provider = Provider((ref) { - try { - ref.state; - } catch (e) { - err = e; - } - return 0; - }); - - container.read(provider); - expect(err, isStateError); - }); - - test( - 'on rebuild, still fails if trying to read the state before was built', - () { - final dep = StateProvider((ref) => false); - final container = createContainer(); - Object? err; - final provider = Provider((ref) { - if (ref.watch(dep)) { - try { - ref.state; - } catch (e) { - err = e; - } - } - return 0; - }); - - container.read(provider); - expect(err, isNull); - - container.read(dep.notifier).state = true; - container.read(provider); - - expect(err, isStateError); - }); - - test('can read the state if the setter was called before', () { - final container = createContainer(); - final provider = Provider((ref) { - // ignore: join_return_with_assignment - ref.state = 42; - - return ref.state; - }); - - expect(container.read(provider), 42); - }); - }); - - test('does not notify listeners when called ref.state= with == new value', - () async { - final container = createContainer(); - final listener = Listener(); - late ProviderRef ref; - final provider = Provider((r) { - ref = r; - return 0; - }); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 0)); - - ref.state = 0; - await container.pump(); - - verifyNoMoreInteractions(listener); - }); - - group('scoping an override overrides all the associated subproviders', () { - test('when passing the provider itself', () { - final provider = Provider((ref) => 0); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); - - expect(container.read(provider), 0); - expect(container.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', provider), - ]); - expect(root.getAllProviderElements(), isEmpty); - }); - - test('when using provider.overrideWithValue', () { - final provider = Provider((ref) => 0); - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [provider.overrideWithValue(42)], - ); - - expect(container.read(provider), 42); - expect(container.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', provider), - ]); - expect(root.getAllProviderElements(), isEmpty); - }); - - test('when using provider.overrideWithProvider', () { - final provider = Provider((ref) => 0); - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [ - // ignore: deprecated_member_use_from_same_package - provider.overrideWithProvider(Provider((ref) => 42)), - ], - ); - - expect(container.read(provider), 42); - expect(container.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', provider), - ]); - expect(root.getAllProviderElements(), isEmpty); - }); - }); - - group('override', () { - test('does not notify listeners if updated with the same value', () { - final provider = Provider((ref) => 0); - final container = createContainer( - overrides: [provider.overrideWithValue(42)], - ); - final listener = Listener(); - - addTearDown(container.dispose); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 42)); - - container.updateOverrides([ - provider.overrideWithValue(42), - ]); - - expect(container.read(provider), 42); - verifyNoMoreInteractions(listener); - }); - - test('notify listeners when value changes', () { - final provider = Provider((ref) => 0); - final container = createContainer( - overrides: [provider.overrideWithValue(42)], - ); - final listener = Listener(); - - addTearDown(container.dispose); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 42)); - - container.updateOverrides([ - provider.overrideWithValue(21), - ]); - - verifyOnly(listener, listener(42, 21)); - }); - }); - - test('can specify name', () { - final provider = Provider( - (_) => 0, - name: 'example', - ); - - expect(provider.name, 'example'); - - final provider2 = Provider((_) => 0); - - expect(provider2.name, isNull); - }); - - test('is AlwaysAliveProviderBase', () { - // ignore: unused_local_variable, testing that Provider can be assigned to AlwaysAliveProviderBase - final AlwaysAliveProviderBase provider = Provider((_) => 42); - }); - }); - - test('dispose', () { - final container = createContainer(); - final onDispose = OnDisposeMock(); - final provider = Provider((ref) { - ref.onDispose(onDispose.call); - return 42; - }); - - expect(container.read(provider), 42); - - verifyZeroInteractions(onDispose); - - container.dispose(); - - verify(onDispose()).called(1); - }); - - test('Read creates the value only once', () { - final container = createContainer(); - var callCount = 0; - final provider = Provider((ref) { - callCount++; - return 42; - }); - - expect(callCount, 0); - expect(container.read(provider), 42); - expect(callCount, 1); - - expect(container.read(provider), 42); - expect(callCount, 1); - }); - - test("rebuild don't notify clients if == doesn't change", () { - final container = createContainer(); - final counter = Counter(); - final other = StateNotifierProvider((ref) => counter); - var buildCount = 0; - final provider = Provider((ref) { - buildCount++; - return ref.watch(other).isEven; - }); - final listener = Listener(); - - final sub = - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, true)); - expect(sub.read(), true); - expect(buildCount, 1); - - counter.increment(); - counter.increment(); - - expect(sub.read(), true); - expect(buildCount, 2); - verifyNoMoreInteractions(listener); - }); - - test('rebuild notify clients if == did change', () { - final container = createContainer(); - final counter = Counter(); - final other = StateNotifierProvider((ref) => counter); - final provider = Provider((ref) { - return ref.watch(other).isEven; - }); - final listener = Listener(); - - final sub = - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, true)); - expect(sub.read(), true); - - counter.increment(); - - expect(sub.read(), false); - verifyOnly(listener, listener(true, false)); - }); - - test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); - final provider = Provider( - (ref) => ref.watch(dep), - dependencies: [dep], - ); - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [dep.overrideWithValue(42)], - ); - - expect(container.read(provider), 42); - - expect(root.getAllProviderElements(), isEmpty); - }); -} diff --git a/packages/riverpod/test/providers/scoped_provider/scoped_provider_test.dart b/packages/riverpod/test/providers/scoped_provider/scoped_provider_test.dart deleted file mode 100644 index 4307aad04..000000000 --- a/packages/riverpod/test/providers/scoped_provider/scoped_provider_test.dart +++ /dev/null @@ -1,246 +0,0 @@ -import 'package:mockito/mockito.dart'; -import 'package:riverpod/riverpod.dart'; -import 'package:test/test.dart'; - -import '../../utils.dart'; - -void main() { - test( - 'It is possible to read provider sub-values by specifying the provider in `dependencies`', - () { - final dep = StateProvider((ref) => 0); - final provider = Provider( - (ref) => ref.watch(dep.notifier), - dependencies: [dep], - ); - final container = createContainer(); - - expect(container.read(provider).state, 0); - }); - - group('scoping mechanism', () { - test('use the deepest override', () { - final provider = Provider((ref) => 0); - final root = createContainer( - overrides: [provider.overrideWithValue(1)], - ); - final mid = createContainer( - parent: root, - overrides: [ - provider.overrideWithValue(42), - ], - ); - final container = createContainer(parent: mid); - - expect(container.read(provider), 42); - - expect(container.getAllProviderElements(), isEmpty); - expect(mid.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', provider) - .having((e) => e.readSelf(), 'readSelf()', 42), - ]); - expect(root.getAllProviderElements(), isEmpty); - }); - - test('can read both parent and child simultaneously', () async { - final provider = Provider((ref) => 0); - final root = createContainer( - overrides: [provider.overrideWithValue(21)], - ); - final container = createContainer( - parent: root, - overrides: [provider.overrideWithValue(42)], - ); - - expect(container.read(provider), 42); - expect(root.read(provider), 21); - expect(container.read(provider), 42); - expect(root.read(provider), 21); - }); - - test('updating parent override when there is a child override is no-op', - () async { - final provider = Provider((ref) => 0); - final root = createContainer( - overrides: [provider.overrideWithValue(21)], - ); - final container = createContainer( - parent: root, - overrides: [provider.overrideWithValue(42)], - ); - final listener = Listener(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 42)); - - root.updateOverrides([ - provider.overrideWithValue(22), - ]); - - await container.pump(); - - verifyNoMoreInteractions(listener); - }); - - test('supports auto-dispose', () async { - final provider = Provider.autoDispose((ref) => 0); - final container = createContainer(); - - final sub = container.listen(provider, (_, __) {}); - final element = container.readProviderElement(provider); - - expect(element.mounted, true); - expect(sub.read(), 0); - - sub.close(); - await container.pump(); - - expect(element.mounted, false); - - container.dispose(); - - expect(element.mounted, false); - }); - - test('are disposed on nested containers', () { - final provider = Provider((ref) => 0); - final root = createContainer( - overrides: [provider.overrideWithValue(1)], - ); - final container = createContainer( - parent: root, - overrides: [ - provider.overrideWithValue(42), - ], - ); - - final element = container.readProviderElement(provider); - - expect(element.mounted, true); - - container.dispose(); - - expect(element.mounted, false); - }); - - test('can update multiple ScopeProviders at once', () { - final provider = Provider((ref) => -1); - final provider2 = Provider((ref) => -1); - - final container = createContainer( - overrides: [ - provider.overrideWithValue(21), - provider2.overrideWithValue(42), - ], - ); - - final listener = Listener(); - final listener2 = Listener(); - - container.listen(provider, listener.call, fireImmediately: true); - container.listen(provider2, listener2.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 21)); - verifyOnly(listener2, listener2(null, 42)); - - container.updateOverrides([ - provider.overrideWithValue(22), - provider2.overrideWithValue(43), - ]); - - verifyInOrder([ - listener(21, 22), - listener2(42, 43), - ]); - verifyNoMoreInteractions(listener); - verifyNoMoreInteractions(listener2); - }); - - test('handles parent override update', () { - final provider = Provider((ref) => 0); - final root = createContainer( - overrides: [provider.overrideWithValue(1)], - ); - final mid = createContainer( - parent: root, - overrides: [ - provider.overrideWithValue(42), - ], - ); - final container = createContainer(parent: mid); - final listener = Listener(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 42)); - - mid.updateOverrides([ - provider.overrideWithValue(21), - ]); - - verifyOnly(listener, listener(42, 21)); - }); - - test('can be overridden on non-root container', () { - final provider = Provider((ref) => 0); - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [provider.overrideWithValue(42)], - ); - - expect(container.read(provider), 42); - }); - - test('can listen to other scoped providers', () async { - final listener = Listener(); - final provider = Provider((ref) => 0); - final provider2 = Provider((ref) { - return ref.watch(provider) * 2; - }); - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [ - provider.overrideWithValue(1), - provider2, - ], - ); - - container.listen(provider2, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 2)); - - container.updateOverrides([ - provider.overrideWithValue(2), - provider2, - ]); - - await container.pump(); - - verifyOnly(listener, listener(2, 4)); - }); - - test('can listen to other normal providers', () async { - final listener = Listener(); - final provider = StateProvider((ref) => 1); - final provider2 = Provider((ref) { - return ref.watch(provider) * 2; - }); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider2]); - - container.listen(provider2, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, 2)); - - root.read(provider.notifier).state++; - - await container.pump(); - - verifyOnly(listener, listener(2, 4)); - }); - }); -} diff --git a/packages/riverpod/test/providers/state_provider/state_provider_auto_dispose_test.dart b/packages/riverpod/test/providers/state_provider/state_provider_auto_dispose_test.dart deleted file mode 100644 index bde7425d9..000000000 --- a/packages/riverpod/test/providers/state_provider/state_provider_auto_dispose_test.dart +++ /dev/null @@ -1,238 +0,0 @@ -import 'package:mockito/mockito.dart'; -import 'package:riverpod/riverpod.dart'; -import 'package:test/test.dart'; - -import '../../utils.dart'; - -void main() { - test('supports .name', () { - expect( - StateProvider.autoDispose((ref) => 0).name, - null, - ); - expect( - StateProvider.autoDispose((ref) => 0, name: 'foo').name, - 'foo', - ); - }); - - test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); - final provider = StateProvider.autoDispose( - (ref) => ref.watch(dep), - dependencies: [dep], - ); - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [dep.overrideWithValue(42)], - ); - - expect(container.read(provider), 42); - expect(container.read(provider.notifier).state, 42); - - expect(root.getAllProviderElements(), isEmpty); - }); - - group('ref.state', () { - test('can read and change current value', () { - final container = createContainer(); - final listener = Listener(); - late StateProviderRef ref; - final provider = StateProvider.autoDispose((r) { - ref = r; - return 0; - }); - - container.listen(provider, listener.call); - verifyZeroInteractions(listener); - - expect(ref.controller, container.read(provider.notifier)); - - ref.controller.state = 42; - - verifyOnly(listener, listener(0, 42)); - - expect(ref.controller.state, 42); - }); - - test('fails if trying to read the state before it was set', () { - final container = createContainer(); - Object? err; - final provider = StateProvider.autoDispose((ref) { - try { - ref.controller; - } catch (e) { - err = e; - } - return 0; - }); - - container.read(provider); - expect(err, isStateError); - }); - - test('on rebuild, still fails if trying to read the state before was built', - () { - final dep = StateProvider((ref) => false); - final container = createContainer(); - Object? err; - final provider = StateProvider.autoDispose((ref) { - if (ref.watch(dep)) { - try { - ref.controller; - } catch (e) { - err = e; - } - } - return 0; - }); - - container.read(provider); - expect(err, isNull); - - container.read(dep.notifier).state = true; - container.read(provider); - - expect(err, isStateError); - }); - }); - - test('can refresh .notifier', () async { - var initialValue = 1; - final provider = StateProvider.autoDispose((ref) => initialValue); - final container = createContainer(); - - container.listen(provider.notifier, (prev, value) {}); - - expect(container.read(provider), 1); - expect(container.read(provider.notifier).state, 1); - - initialValue = 42; - - expect(container.refresh(provider.notifier).state, 42); - expect(container.read(provider), 42); - }); - - test('can be refreshed', () async { - var result = 0; - final container = createContainer(); - final provider = StateProvider.autoDispose((ref) => result); - - final notifier = container.read(provider.notifier); - expect(container.read(provider), 0); - expect(notifier.state, 0); - - result = 42; - expect(container.refresh(provider), 42); - - expect(container.read(provider), 42); - expect(container.read(provider.notifier), isNot(notifier)); - expect(container.read(provider.notifier).state, 42); - }); - - group('scoping an override overrides all the associated subproviders', () { - test('when passing the provider itself', () async { - final provider = StateProvider.autoDispose((ref) => 0); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); - - expect(container.read(provider.notifier).state, 0); - expect(container.read(provider), 0); - expect(root.getAllProviderElements(), isEmpty); - expect( - container.getAllProviderElements(), - unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), - ]), - ); - }); - - // test('when using provider.overrideWithValue', () async { - // final provider = StateProvider.autoDispose((ref) => 0); - // final root = createContainer(); - // final container = createContainer(parent: root, overrides: [ - // provider.overrideWithValue(StateController(42)), - // ]); - - // expect(container.read(provider.notifier).state, 42); - // expect(container.read(provider), 42); - // expect(root.getAllProviderElements(), isEmpty); - // expect( - // container.getAllProviderElements(), - // unorderedEquals([ - // isA>() - // .having((e) => e.origin, 'origin', provider), - // isA>() - // .having((e) => e.origin, 'origin', provider.state), - // isA>() - // .having((e) => e.origin, 'origin', provider.notifier), - // ]), - // ); - // }); - - test('when using provider.overrideWithProvider', () async { - final provider = StateProvider.autoDispose((ref) => 0, name: 'true'); - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [ - // ignore: deprecated_member_use_from_same_package - provider.overrideWithProvider( - StateProvider.autoDispose((ref) => 42, name: 'meh'), - ), - ], - ); - - expect(container.read(provider.notifier).state, 42); - expect(container.read(provider), 42); - expect( - container.getAllProviderElements(), - unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), - ]), - ); - expect(root.getAllProviderElements(), isEmpty); - }); - }); - - group('overrideWithProvider', () { - // test('listens to state changes', () { - // final override = StateController(42); - // final provider = StateProvider.autoDispose((ref) => 0); - // final container = createContainer(overrides: [ - // provider.overrideWithValue(override), - // ]); - // addTearDown(container.dispose); - // final container2 = ProviderContainer(overrides: [ - // provider.overrideWithProvider( - // StateProvider.autoDispose((ref) => 42), - // ), - // ]); - // addTearDown(container.dispose); - - // expect(container.read(provider), 42); - // expect(container.read(provider.notifier), override); - // }); - - test( - 'properly disposes of the StateController when the provider is disposed', - () async { - final container = createContainer(); - final provider = StateProvider.autoDispose((ref) => 0); - - final notifier = container.read(provider.notifier); - final sub = container.listen(provider, (prev, value) {}); - - expect(notifier.hasListeners, true); - - sub.close(); - await container.pump(); - - expect(() => notifier.hasListeners, throwsStateError); - }, - ); - }); -} diff --git a/packages/riverpod/test/providers/stream_notifier/async_notifier_test.dart b/packages/riverpod/test/providers/stream_notifier/async_notifier_test.dart deleted file mode 100644 index 8e8660ef3..000000000 --- a/packages/riverpod/test/providers/stream_notifier/async_notifier_test.dart +++ /dev/null @@ -1,1124 +0,0 @@ -// ignore_for_file: avoid_types_on_closure_parameters - -import 'dart:async'; - -import 'package:meta/meta.dart'; -import 'package:mockito/mockito.dart'; -import 'package:riverpod/riverpod.dart' hide ErrorListener; -import 'package:test/test.dart'; - -import '../../utils.dart'; -import 'factory.dart'; - -void main() { - for (final factory in matrix()) { - group(factory.label, () { - group('supports AsyncValue transition', () { - test( - 'performs seamless copyWithPrevious if triggered by ref.invalidate/ref.refresh', - () async { - final container = createContainer(); - var count = 0; - final provider = factory.simpleTestProvider( - (ref) => Stream.value(count++), - ); - - container.listen(provider, (previous, next) {}); - - await expectLater(container.read(provider.future), completion(0)); - expect(container.read(provider), const AsyncData(0)); - - expect( - container.refresh(provider), - const AsyncLoading().copyWithPrevious(const AsyncData(0)), - ); - - await expectLater(container.read(provider.future), completion(1)); - expect(container.read(provider), const AsyncData(1)); - - container.invalidate(provider); - - expect( - container.read(provider), - const AsyncLoading().copyWithPrevious(const AsyncData(1)), - ); - await expectLater(container.read(provider.future), completion(2)); - expect(container.read(provider), const AsyncData(2)); - }); - - test( - 'performs seamless:false copyWithPrevious on `state = AsyncLoading()`', - () async { - final container = createContainer(); - final provider = factory.simpleTestProvider((ref) => Stream.value(0)); - - final sub = container.listen(provider.notifier, (previous, next) {}); - - await expectLater(container.read(provider.future), completion(0)); - expect(container.read(provider), const AsyncData(0)); - - sub.read().state = const AsyncLoading(); - - expect( - sub.read().state, - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ); - }); - - test( - 'performs seamless:false copyWithPrevious if triggered by a dependency change', - () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final provider = factory.simpleTestProvider( - (ref) => Stream.value(ref.watch(dep)), - ); - - container.listen(provider, (previous, next) {}); - - await expectLater(container.read(provider.future), completion(0)); - expect(container.read(provider), const AsyncData(0)); - - container.read(dep.notifier).state++; - expect( - container.read(provider), - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ); - - await expectLater(container.read(provider.future), completion(1)); - expect(container.read(provider), const AsyncData(1)); - }); - - test( - 'performs seamless:false copyWithPrevious if both triggered by a dependency change and ref.refresh', - () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final provider = factory.simpleTestProvider( - (ref) => Stream.value(ref.watch(dep)), - ); - - container.listen(provider, (previous, next) {}); - - await expectLater(container.read(provider.future), completion(0)); - expect(container.read(provider), const AsyncData(0)); - - container.read(dep.notifier).state++; - expect( - container.refresh(provider), - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ); - - await expectLater(container.read(provider.future), completion(1)); - expect(container.read(provider), const AsyncData(1)); - }); - }); - - test('does not notify listeners when refreshed during loading', () async { - final provider = factory.simpleTestProvider((ref) => Stream.value(0)); - final container = createContainer(); - final listener = Listener>(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, const AsyncLoading())); - - container.refresh(provider); - - await container.read(provider.future); - - verifyOnly( - listener, - listener(const AsyncLoading(), const AsyncData(0)), - ); - }); - - test('supports listenSelf', () { - final listener = Listener>(); - final onError = ErrorListener(); - final provider = factory.simpleTestProvider((ref) { - ref.listenSelf(listener.call, onError: onError.call); - Error.throwWithStackTrace(42, StackTrace.empty); - }); - final container = createContainer(); - - container.listen(provider, (previous, next) {}); - - verifyOnly( - listener, - listener(null, const AsyncError(42, StackTrace.empty)), - ); - verifyZeroInteractions(onError); - - container.read(provider.notifier).state = const AsyncData(42); - - verifyNoMoreInteractions(onError); - verifyOnly( - listener, - listener( - const AsyncError(42, StackTrace.empty), - const AsyncData(42), - ), - ); - }); - - test( - 'converts StreamNotifier.build into an AsyncData if the future completes', - () async { - final provider = factory.simpleTestProvider((ref) => Stream.value(0)); - final container = createContainer(); - final listener = Listener>(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, const AsyncLoading())); - expect( - container.read(provider.notifier).state, - const AsyncLoading(), - ); - - expect(await container.read(provider.future), 0); - - verifyOnly( - listener, - listener(const AsyncLoading(), const AsyncData(0)), - ); - expect( - container.read(provider.notifier).state, - const AsyncData(0), - ); - }); - - test( - 'converts StreamNotifier.build into an AsyncError if the future fails', - () async { - final provider = factory.simpleTestProvider( - (ref) => Stream.error(0, StackTrace.empty), - ); - final container = createContainer(); - final listener = Listener>(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, const AsyncLoading())); - expect( - container.read(provider.notifier).state, - const AsyncLoading(), - ); - - await expectLater(container.read(provider.future), throwsA(0)); - - verifyOnly( - listener, - listener(const AsyncLoading(), const AsyncError(0, StackTrace.empty)), - ); - expect( - container.read(provider.notifier).state, - const AsyncError(0, StackTrace.empty), - ); - }); - - test('supports cases where the StreamNotifier constructor throws', - () async { - final provider = factory.testProvider( - () => Error.throwWithStackTrace(0, StackTrace.empty), - ); - final container = createContainer(); - final listener = Listener>(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly( - listener, - listener(null, const AsyncError(0, StackTrace.empty)), - ); - expect( - () => container.read(provider.notifier), - throwsA(0), - ); - - await expectLater(container.read(provider.future), throwsA(0)); - }); - - test( - 'synchronously emits AsyncError if StreamNotifier.build throws synchronously', - () async { - final provider = factory.simpleTestProvider( - (ref) => Error.throwWithStackTrace(42, StackTrace.empty), - ); - final container = createContainer(); - final listener = Listener>(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly( - listener, - listener(null, const AsyncError(42, StackTrace.empty)), - ); - expect( - container.read(provider.notifier).state, - const AsyncError(42, StackTrace.empty), - ); - await expectLater(container.read(provider.future), throwsA(42)); - }); - - test( - 'stops listening to the previous future data when the provider rebuilds', - () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final completers = { - 0: Completer.sync(), - 1: Completer.sync(), - }; - final provider = factory.simpleTestProvider( - (ref) => Stream.fromFuture(completers[ref.watch(dep)]!.future), - ); - final listener = Listener>(); - - container.listen(provider, listener.call); - - expect( - container.read(provider.future), - completion(21), - reason: 'The provider rebuilt while the future was still pending, ' - 'so .future should resolve with the next value', - ); - verifyZeroInteractions(listener); - expect(container.read(provider), const AsyncLoading()); - - container.read(dep.notifier).state++; - completers[0]!.complete(42); - - verifyZeroInteractions(listener); - - expect(container.read(provider.future), completion(21)); - expect(container.read(provider), const AsyncLoading()); - - completers[1]!.complete(21); - - expect(await container.read(provider.future), 21); - expect(container.read(provider), const AsyncData(21)); - }); - - test( - 'stops listening to the previous future error when the provider rebuilds', - () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final completers = { - 0: Completer.sync(), - 1: Completer.sync(), - }; - final provider = factory.simpleTestProvider( - (ref) => Stream.fromFuture(completers[ref.watch(dep)]!.future), - ); - final listener = Listener>(); - - container.listen(provider, listener.call); - - expect( - container.read(provider.future), - throwsA(21), - reason: 'The provider rebuilt while the future was still pending, ' - 'so .future should resolve with the next value', - ); - verifyZeroInteractions(listener); - expect(container.read(provider), const AsyncLoading()); - - container.read(dep.notifier).state++; - completers[0]!.completeError(42, StackTrace.empty); - - verifyZeroInteractions(listener); - - expect(container.read(provider.future), throwsA(21)); - expect(container.read(provider), const AsyncLoading()); - - completers[1]!.completeError(21, StackTrace.empty); - - await expectLater(container.read(provider.future), throwsA(21)); - expect( - container.read(provider), - const AsyncError(21, StackTrace.empty), - ); - }); - - group('StreamNotifier.state', () { - test( - 'when manually modifying the state, the new exposed value contains the previous state when possible', - () async { - final provider = factory.simpleTestProvider( - (ref) => Stream.value(0), - ); - final container = createContainer(); - - final sub = container.listen(provider.notifier, (previous, next) {}); - await container.read(provider.future); - - // ignore: prefer_const_constructors, not using `const` as we voluntarility break identity to test `identical` - final newState = AsyncData(84); - // ignore: prefer_const_constructors, not using `const` as we voluntarility break identity to test `identical` - final newLoading = AsyncLoading(); - // ignore: prefer_const_constructors, not using `const` as we voluntarility break identity to test `identical` - final newError = AsyncError(84, StackTrace.empty); - - sub.read().state = newState; - - expect(sub.read().state, same(newState)); - - sub.read().state = newLoading; - - expect( - sub.read().state, - const AsyncLoading() - .copyWithPrevious(newState, isRefresh: false), - ); - - sub.read().state = newError; - - expect( - sub.read().state, - newError.copyWithPrevious( - const AsyncLoading() - .copyWithPrevious(newState, isRefresh: false), - ), - ); - }); - - test( - 'when read on outdated provider, refreshes the provider and return the up-to-date state', - () async { - final listener = OnBuildMock(); - final dep = StateProvider((ref) => 0); - final provider = factory.simpleTestProvider( - (ref) { - listener(); - return Stream.value(ref.watch(dep)); - }, - ); - final container = createContainer(); - - container.listen(provider, (previous, next) {}); - final notifier = container.read(provider.notifier); - - expect(notifier.state, const AsyncLoading()); - expect(await container.read(provider.future), 0); - expect(notifier.state, const AsyncData(0)); - verify(listener()).called(1); - - container.read(dep.notifier).state++; - - expect( - notifier.state, - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ); - expect(await container.read(provider.future), 1); - expect(notifier.state, const AsyncData(1)); - verify(listener()).called(1); - }); - - test('can be read inside build', () { - final dep = StateProvider((ref) => 0); - late AsyncValue state; - final provider = factory.testProvider( - () { - late StreamTestNotifierBase notifier; - return notifier = factory.notifier( - (ref) { - state = notifier.state; - return Stream.value(ref.watch(dep)); - }, - ); - }, - ); - final container = createContainer(); - - container.listen(provider, (previous, next) {}); - - expect(state, const AsyncLoading()); - - container.read(provider.notifier).state = const AsyncData(42); - container.refresh(provider); - - expect( - state, - const AsyncLoading().copyWithPrevious(const AsyncData(42)), - ); - }); - - test('notifies listeners when the setter is called', () async { - final provider = factory.simpleTestProvider((ref) => Stream.value(0)); - final container = createContainer(); - final listener = Listener>(); - - // Skip the loading - await container.listen(provider.future, (previous, next) {}).read(); - - container.listen(provider, listener.call); - - verifyZeroInteractions(listener); - - container.read(provider.notifier).state = const AsyncData(42); - - verifyOnly( - listener, - listener(const AsyncData(0), const AsyncData(42)), - ); - }); - }); - - group('StreamNotifier.future', () { - test( - 'when disposed during loading, resolves with the content of StreamNotifier.build', - () async { - final container = createContainer(); - final completer = Completer.sync(); - final provider = factory.simpleTestProvider( - (ref) => Stream.fromFuture(completer.future), - ); - - final future = container.read(provider.future); - container.dispose(); - - completer.complete(42); - - await expectLater(future, completion(42)); - }); - - test( - 'when disposed during loading, resolves with the error of StreamNotifier.build', - () async { - final container = createContainer(); - final completer = Completer.sync(); - final provider = factory.simpleTestProvider( - (ref) => Stream.fromFuture(completer.future), - ); - - final future = container.read(provider.future); - - container.dispose(); - - completer.completeError(42); - - await expectLater(future, throwsA(42)); - }); - - test( - 'going data > loading while the future is still pending. ' - 'Resolves with last future result', - () async { - final container = createContainer(); - final completer = Completer.sync(); - final provider = factory.simpleTestProvider( - (ref) => Stream.fromFuture(completer.future), - ); - - container.read(provider); - container.read(provider.notifier).state = const AsyncData(42); - container.read(provider.notifier).state = const AsyncLoading(); - - final future = container.read(provider.future); - - container.dispose(); - - completer.complete(42); - - await expectLater(future, completion(42)); - }, - ); - - test( - 'if going back to loading after future resolved, throws StateError', - () async { - final container = createContainer(); - final completer = Completer.sync(); - final provider = factory.simpleTestProvider( - (ref) => Stream.fromFuture(completer.future), - ); - - container.read(provider); - - completer.complete(42); - - container.read(provider.notifier).state = const AsyncData(42); - container.read(provider.notifier).state = const AsyncLoading(); - - final future = container.read(provider.future); - - container.dispose(); - - await expectLater(future, throwsStateError); - }, - ); - - test( - 'resolves with the new state if StreamNotifier.state is modified during loading', - () async { - final container = createContainer(); - final completer = Completer.sync(); - final provider = factory.simpleTestProvider( - (ref) => Stream.fromFuture(completer.future), - ); - final listener = Listener>(); - - final sub = container.listen(provider.notifier, (previous, next) {}); - container.listen(provider.future, listener.call); - - expect(sub.read().future, completion(21)); - - sub.read().state = const AsyncData(21); - - completer.complete(42); - - expect(sub.read().future, completion(42)); - final capture = - verifyOnly(listener, listener(captureAny, captureAny)).captured; - - expect(capture.length, 2); - expect(capture.first, completion(21)); - expect(capture.last, completion(42)); - }); - - test('resolves with the new state when notifier.state is changed', - () async { - final container = createContainer(); - final provider = factory.simpleTestProvider( - (ref) => Stream.value(0), - ); - final listener = Listener>(); - - final sub = container.listen(provider.notifier, (previous, next) {}); - container.listen( - provider.future, - listener.call, - fireImmediately: true, - ); - - await expectLater(sub.read().future, completion(0)); - verifyOnly( - listener, - listener(argThat(equals(null)), argThat(completion(0))), - ); - - sub.read().state = const AsyncData(1); - - await expectLater(sub.read().future, completion(1)); - }); - - test('retuns a Future identical to that of .future', () { - final listener = OnBuildMock(); - final dep = StateProvider((ref) => 0); - final provider = factory.simpleTestProvider( - (ref) { - listener(); - return Stream.value(ref.watch(dep)); - }, - ); - final container = createContainer(); - - container.listen(provider.notifier, (previous, next) {}); - final notifier = container.read(provider.notifier); - - expect(notifier.future, same(container.read(provider.future))); - }); - - test( - 'when read on outdated provider, refreshes the provider and return the up-to-date state', - () async { - final listener = OnBuildMock(); - final dep = StateProvider((ref) => 0); - final provider = factory.simpleTestProvider( - (ref) { - listener(); - return Stream.value(ref.watch(dep)); - }, - ); - final container = createContainer(); - - container.listen(provider, (previous, next) {}); - final notifier = container.read(provider.notifier); - - expect(await container.read(provider.future), 0); - verify(listener()).called(1); - - container.read(dep.notifier).state++; - - expect(notifier.future, notifier.future); - expect(notifier.future, same(container.read(provider.future))); - expect(await notifier.future, 1); - verify(listener()).called(1); - }); - }); - - group('StreamNotifierProvider.notifier', () { - test( - 'never emits an update. The Notifier is never recreated once it is instantiated', - () async { - final listener = OnBuildMock(); - final dep = StateProvider((ref) => 0); - final provider = factory.testProvider(() { - listener(); - return factory.notifier( - (ref) => Stream.value(ref.watch(dep)), - ); - }); - final container = createContainer(); - - // Skip the loading - await container.listen(provider.future, (previous, next) {}).read(); - - container.listen(provider, (previous, next) {}); - final notifier = container.read(provider.notifier); - - verify(listener()).called(1); - expect(container.read(provider), const AsyncData(0)); - - container.read(dep.notifier).state++; - await container.read(provider.future); - - expect(container.read(provider), const AsyncData(1)); - expect(container.read(provider.notifier), same(notifier)); - verifyNoMoreInteractions(listener); - }); - }); - - test( - 'Can override StreamNotifier.updateShouldNotify to change the default filter logic', - () async { - final provider = factory.simpleTestProvider>( - (ref) => Stream.value(Equal(42)), - updateShouldNotify: (a, b) => a != b, - ); - final container = createContainer(); - final listener = Listener>>(); - - // Skip the loading - await container.listen(provider.future, (previous, next) {}).read(); - - container.listen(provider, listener.call); - final notifier = container.read(provider.notifier); - - // voluntarily assigning the same value - final self = notifier.state; - notifier.state = self; - - verifyZeroInteractions(listener); - - notifier.state = AsyncData(Equal(42)); - - verifyZeroInteractions(listener); - - notifier.state = AsyncData(Equal(21)); - - verifyOnly( - listener, - listener(AsyncData(Equal(42)), AsyncData(Equal(21))), - ); - }); - - group('AsyncNotifer.update', () { - test('passes in the latest state', () async { - final container = createContainer(); - final provider = factory.simpleTestProvider( - (ref) => Stream.value(0), - ); - - // Skip the loading - await container.listen(provider.future, (previous, next) {}).read(); - - final sub = container.listen(provider.notifier, (prev, next) {}); - - expect( - container.read(provider), - const AsyncData(0), - ); - - await expectLater( - sub.read().update((prev) => prev + 1), - completion(1), - ); - await expectLater( - sub.read().future, - completion(1), - ); - await expectLater( - sub.read().update((prev) => prev + 1), - completion(2), - ); - }); - - test('can specify onError to handle error scenario', () async { - final container = createContainer(); - final provider = factory.simpleTestProvider( - (ref) => Error.throwWithStackTrace(42, StackTrace.empty), - ); - var callCount = 0; - Object? actualErr; - Object? actualStack; - - final sub = container.listen(provider.notifier, (prev, next) {}); - - expect( - container.read(provider), - const AsyncError(42, StackTrace.empty), - ); - - await expectLater( - sub.read().update( - (prev) { - callCount++; - return prev; - }, - onError: (err, stack) { - actualErr = err; - actualStack = stack; - return 21; - }, - ), - completion(21), - ); - expect(callCount, 0); - expect(actualErr, 42); - expect(actualStack, StackTrace.empty); - expect(container.read(provider), const AsyncData(21)); - }); - - test( - 'executes immediately with current state if a state is avalailable', - () async { - final container = createContainer(); - final provider = factory.simpleTestProvider( - (ref) => Stream.value(1), - ); - - // Skip the loading - await container.listen(provider.future, (previous, next) {}).read(); - - final sub = container.listen(provider.notifier, (prev, next) {}); - - expect(container.read(provider), const AsyncData(1)); - - await expectLater( - sub.read().update((prev) => prev + 1), - completion(2), - ); - expect(container.read(provider), const AsyncData(2)); - }); - - test( - 'executes immediately with current state if an error is avalailable', - () async { - final container = createContainer(); - final provider = factory.simpleTestProvider( - (ref) => Error.throwWithStackTrace(42, StackTrace.empty), - ); - var callCount = 0; - - final sub = container.listen(provider.notifier, (prev, next) {}); - - expect( - container.read(provider), - const AsyncError(42, StackTrace.empty), - ); - - await expectLater( - sub.read().update((prev) { - callCount++; - return prev + 1; - }), - throwsA(42), - ); - - expect(callCount, 0); - expect( - container.read(provider), - const AsyncError(42, StackTrace.empty), - ); - }); - - test('awaits the future resolution if in loading state', () async { - final container = createContainer(); - final provider = factory.simpleTestProvider( - (ref) => Stream.value(42), - ); - - final sub = container.listen(provider.notifier, (prev, next) {}); - - expect(container.read(provider), const AsyncLoading()); - - await expectLater( - sub.read().update((prev) => prev + 1), - completion(43), - ); - expect(container.read(provider), const AsyncData(43)); - }); - }); - }); - } - - test('supports overrideWith', () async { - final provider = StreamNotifierProvider, int>( - () => StreamTestNotifier((ref) => Stream.value(0)), - ); - final autoDispose = StreamNotifierProvider.autoDispose< - AutoDisposeStreamTestNotifier, int>( - () => AutoDisposeStreamTestNotifier((ref) => Stream.value(0)), - ); - final container = createContainer( - overrides: [ - provider.overrideWith( - () => StreamTestNotifier((ref) => Stream.value(42)), - ), - autoDispose.overrideWith( - () => AutoDisposeStreamTestNotifier((ref) => Stream.value(84)), - ), - ], - ); - - // Skip the loading - await container.listen(provider.future, (previous, next) {}).read(); - await container.listen(autoDispose.future, (previous, next) {}).read(); - - expect(container.read(provider).value, 42); - expect(container.read(autoDispose).value, 84); - }); - - test('supports family overrideWith', () async { - final family = - StreamNotifierProvider.family, int, int>( - () => StreamTestNotifierFamily((ref) => Stream.value(0)), - ); - final autoDisposeFamily = StreamNotifierProvider.autoDispose - .family, int, int>( - () => AutoDisposeStreamTestNotifierFamily((ref) => Stream.value(0)), - ); - final container = createContainer( - overrides: [ - family.overrideWith( - () => StreamTestNotifierFamily((ref) => Stream.value(42)), - ), - autoDisposeFamily.overrideWith( - () => AutoDisposeStreamTestNotifierFamily( - (ref) => Stream.value(84), - ), - ), - ], - ); - - // Skip the loading - await container.listen(family(10).future, (previous, next) {}).read(); - await container - .listen(autoDisposeFamily(10).future, (previous, next) {}) - .read(); - - expect(container.read(family(10)).value, 42); - expect(container.read(autoDisposeFamily(10)).value, 84); - }); - - group('AutoDispose variant', () { - test('can watch autoDispose providers', () async { - final dep = Provider.autoDispose((ref) => 0); - final provider = AutoDisposeStreamNotifierProvider< - AutoDisposeStreamTestNotifier, int>( - () => AutoDisposeStreamTestNotifier((ref) { - return Stream.value(ref.watch(dep)); - }), - ); - final container = createContainer(); - - // Skip the loading - await container.listen(provider.future, (previous, next) {}).read(); - - expect(container.read(provider), const AsyncData(0)); - }); - }); - - group('modifiers', () { - void canBeAssignedToAlwaysAliveRefreshable( - AlwaysAliveRefreshable provider, - ) {} - - void canBeAssignedToRefreshable( - Refreshable provider, - ) {} - - void canBeAssignedToAlwaysAliveListenable( - AlwaysAliveProviderListenable provider, - ) {} - - void canBeAssignedToProviderListenable( - ProviderListenable provider, - ) {} - - // TODO use package:expect_error to test that commented lined are not compiling - - test('provider', () { - final provider = StreamNotifierProvider, int>( - () => StreamTestNotifier((ref) => Stream.value(0)), - ); - - provider.select((AsyncValue value) => 0); - provider.selectAsync((int value) => 0); - - canBeAssignedToProviderListenable>(provider); - canBeAssignedToAlwaysAliveListenable>(provider); - canBeAssignedToRefreshable>(provider); - canBeAssignedToAlwaysAliveRefreshable>(provider); - - canBeAssignedToProviderListenable>(provider.future); - canBeAssignedToAlwaysAliveListenable>(provider.future); - canBeAssignedToRefreshable>(provider.future); - canBeAssignedToAlwaysAliveRefreshable>(provider.future); - - canBeAssignedToProviderListenable>(provider.notifier); - canBeAssignedToAlwaysAliveListenable>( - provider.notifier, - ); - canBeAssignedToRefreshable>(provider.notifier); - canBeAssignedToAlwaysAliveRefreshable>( - provider.notifier, - ); - }); - - test('autoDispose', () { - final autoDispose = StreamNotifierProvider.autoDispose< - AutoDisposeStreamTestNotifier, int>( - () => AutoDisposeStreamTestNotifier((ref) => Stream.value(0)), - ); - - autoDispose.select((AsyncValue value) => 0); - autoDispose.selectAsync((int value) => 0); - - canBeAssignedToProviderListenable>(autoDispose); - // canBeAssignedToAlwaysAliveListenable>(autoDispose); - canBeAssignedToRefreshable>(autoDispose); - // canBeAssignedToAlwaysAliveRefreshable>(autoDispose); - - canBeAssignedToProviderListenable>(autoDispose.future); - // canBeAssignedToAlwaysAliveListenable>(autoDispose.future); - canBeAssignedToRefreshable>(autoDispose.future); - // canBeAssignedToAlwaysAliveRefreshable>(autoDispose.future); - - canBeAssignedToProviderListenable>( - autoDispose.notifier, - ); - // canBeAssignedToAlwaysAliveListenable>( - // autoDispose.notifier, - // ); - canBeAssignedToRefreshable>( - autoDispose.notifier, - ); - // canBeAssignedToAlwaysAliveRefreshable>( - // autoDispose.notifier, - // ); - }); - - test('family', () { - final family = StreamNotifierProvider.family< - StreamTestNotifierFamily, String, int>( - () => StreamTestNotifierFamily((ref) => Stream.value('0')), - ); - - family(0).select((AsyncValue value) => 0); - family(0).selectAsync((String value) => 0); - - canBeAssignedToProviderListenable>(family(0)); - canBeAssignedToAlwaysAliveListenable>(family(0)); - canBeAssignedToRefreshable>(family(0)); - canBeAssignedToAlwaysAliveRefreshable>(family(0)); - - canBeAssignedToProviderListenable>(family(0).future); - canBeAssignedToAlwaysAliveListenable>(family(0).future); - canBeAssignedToRefreshable>(family(0).future); - canBeAssignedToAlwaysAliveRefreshable>(family(0).future); - - canBeAssignedToProviderListenable>( - family(0).notifier, - ); - canBeAssignedToAlwaysAliveListenable>( - family(0).notifier, - ); - canBeAssignedToRefreshable>( - family(0).notifier, - ); - canBeAssignedToAlwaysAliveRefreshable>( - family(0).notifier, - ); - }); - - test('autoDisposeFamily', () { - expect( - StreamNotifierProvider.autoDispose.family, - same(StreamNotifierProvider.family.autoDispose), - ); - - final autoDisposeFamily = StreamNotifierProvider.autoDispose - .family, String, int>( - () => AutoDisposeStreamTestNotifierFamily((ref) => Stream.value('0')), - ); - - autoDisposeFamily(0).select((AsyncValue value) => 0); - autoDisposeFamily(0).selectAsync((String value) => 0); - - canBeAssignedToProviderListenable>( - autoDisposeFamily(0), - ); - // canBeAssignedToAlwaysAliveListenable>( - // autoDisposeFamily(0), - // ); - canBeAssignedToRefreshable>( - autoDisposeFamily(0), - ); - // canBeAssignedToAlwaysAliveRefreshable>( - // autoDisposeFamily(0), - // ); - - canBeAssignedToProviderListenable>( - autoDisposeFamily(0).future, - ); - // canBeAssignedToAlwaysAliveListenable>( - // autoDisposeFamily(0).future, - // ); - canBeAssignedToRefreshable>( - autoDisposeFamily(0).future, - ); - // canBeAssignedToAlwaysAliveRefreshable>( - // autoDisposeFamily(0).future, - // ); - - canBeAssignedToProviderListenable< - AutoDisposeFamilyStreamNotifier>( - autoDisposeFamily(0).notifier, - ); - // canBeAssignedToAlwaysAliveListenable< - // AutoDisposeFamilyStreamNotifier>( - // autoDisposeFamily(0).notifier, - // ); - canBeAssignedToRefreshable>( - autoDisposeFamily(0).notifier, - ); - // canBeAssignedToAlwaysAliveRefreshable< - // AutoDisposeFamilyStreamNotifier>( - // autoDisposeFamily(0).notifier, - // ); - }); - }); -} - -@immutable -class Equal { - // ignore: prefer_const_constructors_in_immutables - Equal(this.value); - - final T value; - - @override - bool operator ==(Object other) => other is Equal && other.value == value; - - @override - int get hashCode => Object.hash(runtimeType, value); -} diff --git a/packages/riverpod/test/providers/stream_notifier/factory.dart b/packages/riverpod/test/providers/stream_notifier/factory.dart deleted file mode 100644 index a2ce5ea0b..000000000 --- a/packages/riverpod/test/providers/stream_notifier/factory.dart +++ /dev/null @@ -1,314 +0,0 @@ -import 'dart:async'; - -import 'package:riverpod/src/internals.dart'; - -typedef StreamNotifierProviderFactoryType - = StreamNotifierProviderBase - Function, T>( - NotifierT Function() create, { - String? name, -}); - -typedef StreamNotifierFactoryType = StreamTestNotifierBase Function( - Stream Function(StreamNotifierProviderRef), { - bool Function(AsyncValue, AsyncValue)? updateShouldNotify, -}); - -typedef SimpleTestProviderFactoryType - = StreamNotifierProviderBase, T> Function( - Stream Function(StreamNotifierProviderRef ref) init, { - bool Function(AsyncValue prev, AsyncValue next)? updateShouldNotify, -}); - -typedef TestProviderFactoryType - = StreamNotifierProviderBase, T> Function( - StreamTestNotifierBase Function() createNotifier, -); - -List matrix({ - bool alwaysAlive = true, - bool autoDispose = true, -}) { - return [ - if (alwaysAlive) - StreamNotifierFactory( - label: 'StreamNotifierProvider', - isAutoDispose: false, - provider: StreamNotifierProviderImpl.new, - notifier: StreamTestNotifier.new, - testProvider: (createNotifier) { - return StreamNotifierProviderImpl, T>( - createNotifier, - ); - }, - simpleTestProvider: (init, {updateShouldNotify}) { - return StreamNotifierProvider, T>( - () => StreamTestNotifier( - init, - updateShouldNotify: updateShouldNotify, - ), - ); - }, - ), - if (alwaysAlive) - StreamNotifierFactory( - label: 'StreamNotifierProviderFamily', - isAutoDispose: false, - provider: , T>( - create, { - argument, - dependencies, - from, - name, - }) { - return FamilyStreamNotifierProviderImpl.internal( - create, - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - notifier: StreamTestNotifierFamily.new, - testProvider: (createNotifier) { - return FamilyStreamNotifierProviderImpl, - T, int>.internal( - () => createNotifier() as StreamTestNotifierFamily, - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - simpleTestProvider: (init, {updateShouldNotify}) { - return FamilyStreamNotifierProviderImpl, - T, int>.internal( - () => StreamTestNotifierFamily( - init, - updateShouldNotify: updateShouldNotify, - ), - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - ), - if (autoDispose) - StreamNotifierFactory( - label: 'AutoDisposeStreamNotifierProvider', - isAutoDispose: true, - provider: AutoDisposeStreamNotifierProviderImpl.new, - notifier: AutoDisposeStreamTestNotifier.new, - testProvider: (createNotifier) { - return AutoDisposeStreamNotifierProviderImpl< - AutoDisposeStreamTestNotifier, T>( - () => createNotifier() as AutoDisposeStreamTestNotifier, - ); - }, - simpleTestProvider: (init, {updateShouldNotify}) { - return AutoDisposeStreamNotifierProvider< - AutoDisposeStreamTestNotifier, T>( - () => AutoDisposeStreamTestNotifier( - init, - updateShouldNotify: updateShouldNotify, - ), - ); - }, - ), - if (autoDispose) - StreamNotifierFactory( - label: 'AutoDisposeStreamNotifierProviderFamily', - isAutoDispose: true, - provider: , T>( - create, { - argument, - dependencies, - from, - name, - }) { - return AutoDisposeFamilyStreamNotifierProviderImpl.internal( - create, - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - notifier: AutoDisposeStreamTestNotifierFamily.new, - testProvider: (createNotifier) { - return AutoDisposeFamilyStreamNotifierProviderImpl< - AutoDisposeStreamTestNotifierFamily, T, int>.internal( - () => createNotifier() as AutoDisposeStreamTestNotifierFamily, - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - simpleTestProvider: (init, {updateShouldNotify}) { - return AutoDisposeFamilyStreamNotifierProviderImpl< - AutoDisposeStreamTestNotifierFamily, T, int>.internal( - () => AutoDisposeStreamTestNotifierFamily( - init, - updateShouldNotify: updateShouldNotify, - ), - argument: 0, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - ); - }, - ), - ]; -} - -class StreamNotifierFactory { - const StreamNotifierFactory({ - required this.label, - required this.provider, - required this.notifier, - required this.isAutoDispose, - required this.testProvider, - required this.simpleTestProvider, - }); - - final String label; - final bool isAutoDispose; - final StreamNotifierProviderFactoryType provider; - final StreamNotifierFactoryType notifier; - final TestProviderFactoryType testProvider; - final SimpleTestProviderFactoryType simpleTestProvider; -} - -abstract class StreamTestNotifierBase extends AsyncNotifierBase { - // overriding to remove the @protected - @override - AsyncValue get state; - - @override - set state(AsyncValue value); - - // overriding to remove the @protected - @override - Future update( - FutureOr Function(T p1) cb, { - FutureOr Function(Object err, StackTrace stackTrace)? onError, - }); -} - -class StreamTestNotifier extends StreamNotifier - implements StreamTestNotifierBase { - StreamTestNotifier( - this._init, { - bool Function(AsyncValue prev, AsyncValue next)? updateShouldNotify, - }) : _updateShouldNotify = updateShouldNotify; - - final Stream Function(StreamNotifierProviderRef ref) _init; - - final bool Function(AsyncValue prev, AsyncValue next)? - _updateShouldNotify; - - @override - Stream build() => _init(ref); - - @override - bool updateShouldNotify(AsyncValue prev, AsyncValue next) { - return _updateShouldNotify?.call(prev, next) ?? - super.updateShouldNotify(prev, next); - } - - @override - String toString() { - return 'StreamTestNotifier<$T>#$hashCode'; - } -} - -class StreamTestNotifierFamily extends FamilyStreamNotifier - implements StreamTestNotifierBase { - StreamTestNotifierFamily( - this._init, { - bool Function(AsyncValue prev, AsyncValue next)? updateShouldNotify, - }) : _updateShouldNotify = updateShouldNotify; - - final Stream Function(StreamNotifierProviderRef ref) _init; - - final bool Function(AsyncValue prev, AsyncValue next)? - _updateShouldNotify; - - @override - Stream build(int arg) => _init(ref); - - @override - bool updateShouldNotify(AsyncValue prev, AsyncValue next) { - return _updateShouldNotify?.call(prev, next) ?? - super.updateShouldNotify(prev, next); - } - - @override - String toString() { - return 'StreamTestNotifierFamily<$T>#$hashCode'; - } -} - -class AutoDisposeStreamTestNotifier extends AutoDisposeStreamNotifier - implements StreamTestNotifierBase { - AutoDisposeStreamTestNotifier( - this._init2, { - bool Function(AsyncValue prev, AsyncValue next)? updateShouldNotify, - }) : _updateShouldNotify = updateShouldNotify; - - final Stream Function(AutoDisposeStreamNotifierProviderRef ref) _init2; - - final bool Function(AsyncValue prev, AsyncValue next)? - _updateShouldNotify; - - @override - Stream build() => _init2(ref); - - @override - bool updateShouldNotify(AsyncValue prev, AsyncValue next) { - return _updateShouldNotify?.call(prev, next) ?? - super.updateShouldNotify(prev, next); - } - - @override - String toString() { - return 'AutoDisposeStreamTestNotifier<$T>#$hashCode'; - } -} - -class AutoDisposeStreamTestNotifierFamily - extends AutoDisposeFamilyStreamNotifier - implements StreamTestNotifierBase { - AutoDisposeStreamTestNotifierFamily( - this._init2, { - bool Function(AsyncValue prev, AsyncValue next)? updateShouldNotify, - }) : _updateShouldNotify = updateShouldNotify; - - final Stream Function(AutoDisposeStreamNotifierProviderRef ref) _init2; - - final bool Function(AsyncValue prev, AsyncValue next)? - _updateShouldNotify; - - @override - Stream build(int arg) => _init2(ref); - - @override - bool updateShouldNotify(AsyncValue prev, AsyncValue next) { - return _updateShouldNotify?.call(prev, next) ?? - super.updateShouldNotify(prev, next); - } - - @override - String toString() { - return 'AutoDisposeStreamTestNotifierFamily<$T, int>#$hashCode'; - } -} diff --git a/packages/riverpod/test/providers/stream_provider/stream_provider_test.dart b/packages/riverpod/test/providers/stream_provider/stream_provider_test.dart deleted file mode 100644 index 2dbad82f6..000000000 --- a/packages/riverpod/test/providers/stream_provider/stream_provider_test.dart +++ /dev/null @@ -1,1695 +0,0 @@ -// ignore_for_file: avoid_types_on_closure_parameters - -import 'dart:async'; - -import 'package:mockito/mockito.dart'; -import 'package:riverpod/riverpod.dart'; -import 'package:test/test.dart'; - -import '../../utils.dart'; - -void main() { - late StreamController controller; - final provider = StreamProvider((ref) => controller.stream); - late ProviderContainer container; - - // TODO remove this setup/teardown - setUp(() { - container = createContainer(); - controller = StreamController(sync: true); - }); - tearDown(() { - container.dispose(); - controller.close(); - }); - - test('supports overrideWith', () { - final provider = StreamProvider( - (ref) { - ref.state = const AsyncData(0); - return Stream.value(1); - }, - ); - final autoDispose = StreamProvider.autoDispose( - (ref) { - ref.state = const AsyncData(0); - return Stream.value(1); - }, - ); - final container = createContainer( - overrides: [ - provider.overrideWith((StreamProviderRef ref) { - ref.state = const AsyncData(42); - return Stream.value(43); - }), - autoDispose.overrideWith((AutoDisposeStreamProviderRef ref) { - ref.state = const AsyncData(84); - return Stream.value(85); - }), - ], - ); - - expect(container.read(provider).value, 42); - expect(container.read(autoDispose).value, 84); - }); - - test('supports family overrideWith', () { - final family = StreamProvider.family((ref, arg) { - ref.state = AsyncData('0 $arg'); - return Stream.value('1 $arg'); - }); - final autoDisposeFamily = StreamProvider.autoDispose.family( - (ref, arg) { - ref.state = AsyncData('0 $arg'); - return Stream.value('1 $arg'); - }, - ); - final container = createContainer( - overrides: [ - family.overrideWith( - (StreamProviderRef ref, int arg) { - ref.state = AsyncData('42 $arg'); - return Stream.value('43 $arg'); - }, - ), - autoDisposeFamily.overrideWith( - (AutoDisposeStreamProviderRef ref, int arg) { - ref.state = AsyncData('84 $arg'); - return Stream.value('85 $arg'); - }, - ), - ], - ); - - expect(container.read(family(10)).value, '42 10'); - expect(container.read(autoDisposeFamily(10)).value, '84 10'); - }); - - test('Emits AsyncLoading before the create function is executed', () async { - final container = createContainer(); - late AsyncValue state; - final provider = StreamProvider((ref) { - state = ref.state; - return Stream.value(0); - }); - - container.read(provider); - - expect(state, const AsyncLoading()); - - await container.read(provider.future); - container.refresh(provider); - - expect( - state, - const AsyncLoading().copyWithPrevious(const AsyncData(0)), - ); - }); - - group('When going back to AsyncLoading', () { - test( - 'sets isRefreshing to true if triggered by a ref.invalidate/ref.refresh', - () async { - final container = createContainer(); - var count = 0; - final provider = StreamProvider((ref) => Stream.value(count++)); - - await expectLater(container.read(provider.future), completion(0)); - expect(container.read(provider), const AsyncData(0)); - - expect( - container.refresh(provider), - const AsyncLoading().copyWithPrevious(const AsyncData(0)), - ); - - await expectLater(container.read(provider.future), completion(1)); - expect(container.read(provider), const AsyncData(1)); - - container.invalidate(provider); - - expect( - container.read(provider), - const AsyncLoading().copyWithPrevious(const AsyncData(1)), - ); - await expectLater(container.read(provider.future), completion(2)); - expect(container.read(provider), const AsyncData(2)); - }); - - test('does not set isRefreshing if triggered by a dependency change', - () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final provider = StreamProvider((ref) => Stream.value(ref.watch(dep))); - - await expectLater(container.read(provider.future), completion(0)); - expect(container.read(provider), const AsyncData(0)); - - container.read(dep.notifier).state++; - expect( - container.read(provider), - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ); - - await expectLater(container.read(provider.future), completion(1)); - expect(container.read(provider), const AsyncData(1)); - }); - - test( - 'does not set isRefreshing if both triggered by a dependency change and ref.refresh', - () async { - final container = createContainer(); - final dep = StateProvider((ref) => 0); - final provider = StreamProvider((ref) => Stream.value(ref.watch(dep))); - - await expectLater(container.read(provider.future), completion(0)); - expect(container.read(provider), const AsyncData(0)); - - container.read(dep.notifier).state++; - expect( - container.refresh(provider), - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ); - - await expectLater(container.read(provider.future), completion(1)); - expect(container.read(provider), const AsyncData(1)); - }); - }); - - test('can read and set current AsyncValue', () async { - final container = createContainer(); - final listener = Listener>(); - late StreamProviderRef ref; - final provider = StreamProvider((r) { - ref = r; - return Stream.value(0); - }); - - container.listen(provider, listener.call); - await container.read(provider.future); - - expect(ref.state, const AsyncData(0)); - verifyOnly( - listener, - listener( - const AsyncLoading(), - const AsyncData(0), - ), - ); - - ref.state = const AsyncLoading(); - - expect( - ref.state, - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ); - - verifyOnly( - listener, - listener( - const AsyncData(0), - const AsyncLoading() - .copyWithPrevious(const AsyncData(0), isRefresh: false), - ), - ); - }); - - test('can be auto-scoped', () async { - final dep = Provider((ref) => 0); - final provider = StreamProvider( - (ref) => Stream.value(ref.watch(dep)), - dependencies: [dep], - ); - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [dep.overrideWithValue(42)], - ); - - // ignore: deprecated_member_use_from_same_package - await expectLater(container.read(provider.stream), emits(42)); - await expectLater(container.read(provider.future), completion(42)); - expect(container.read(provider), const AsyncData(42)); - - expect(root.getAllProviderElements(), isEmpty); - }); - - test( - 'when going from AsyncLoading to AsyncLoading, does not notify listeners', - () async { - final dep = StateProvider((ref) => Stream.value(42)); - final provider = StreamProvider((ref) => ref.watch(dep)); - final container = createContainer(); - final listener = Listener>(); - final controller = StreamController(); - addTearDown(controller.close); - - await expectLater( - // ignore: deprecated_member_use_from_same_package - container.read(provider.stream), - emits(42), - ); - expect( - container.read(provider), - const AsyncData(42), - ); - - container.read(dep.notifier).state = controller.stream; - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly( - listener, - listener( - null, - const AsyncLoading() - .copyWithPrevious(const AsyncData(42), isRefresh: false), - ), - ); - - container.read(dep.notifier).state = Stream.value(21); - - verifyNoMoreInteractions(listener); - - await expectLater( - // ignore: deprecated_member_use_from_same_package - container.read(provider.stream), - emits(21), - ); - expect( - container.read(provider), - const AsyncData(21), - ); - }); - - test('can be refreshed', () async { - var result = 0; - final container = createContainer(); - final provider = StreamProvider((ref) => Stream.value(result)); - - // ignore: deprecated_member_use_from_same_package - expect(container.read(provider.stream), emits(0)); - expect(await container.read(provider.future), 0); - expect(container.read(provider), const AsyncValue.data(0)); - - result = 1; - expect( - container.refresh(provider), - const AsyncLoading().copyWithPrevious(const AsyncValue.data(0)), - ); - - // ignore: deprecated_member_use_from_same_package - expect(container.read(provider.stream), emits(1)); - expect(await container.read(provider.future), 1); - expect(container.read(provider), const AsyncValue.data(1)); - }); - - group('scoping an override overrides all the associated subproviders', () { - test('when passing the provider itself', () async { - final provider = StreamProvider((ref) => Stream.value(0)); - final root = createContainer(); - final container = createContainer(parent: root, overrides: [provider]); - - // ignore: deprecated_member_use_from_same_package - expect(await container.read(provider.stream).first, 0); - expect(await container.read(provider.future), 0); - expect(container.read(provider), const AsyncValue.data(0)); - expect(root.getAllProviderElements(), isEmpty); - expect( - container.getAllProviderElements(), - unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), - ]), - ); - }); - - // test('when using provider.overrideWithValue', () async { - // final provider = StreamProvider((ref) => Stream.value(0)); - // final root = createContainer(); - // final container = createContainer(parent: root, overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // expect(await container.read(provider.stream).first, 42); - // expect(await container.read(provider.future), 42); - // expect(container.read(provider), const AsyncValue.data(42)); - // expect(root.getAllProviderElements(), isEmpty); - // expect( - // container.getAllProviderElements(), - // unorderedEquals([ - // isA>() - // .having((e) => e.origin, 'origin', provider), - // isA>() - // .having((e) => e.origin, 'origin', provider.future), - // isA>() - // .having((e) => e.origin, 'origin', provider.stream), - // ]), - // ); - // }); - - test('when using provider.overrideWithProvider', () async { - final provider = StreamProvider((ref) => Stream.value(0)); - final root = createContainer(); - final container = createContainer( - parent: root, - overrides: [ - provider - // ignore: deprecated_member_use_from_same_package - .overrideWithProvider(StreamProvider((ref) => Stream.value(42))), - ], - ); - - // ignore: deprecated_member_use_from_same_package - expect(await container.read(provider.stream).first, 42); - expect(await container.read(provider.future), 42); - expect(container.read(provider), const AsyncValue.data(42)); - expect(root.getAllProviderElements(), isEmpty); - expect( - container.getAllProviderElements(), - unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), - ]), - ); - }); - }); - - test('Loading to data', () { - expect(container.read(provider), const AsyncValue.loading()); - - controller.add(42); - - expect(container.read(provider), const AsyncValue.data(42)); - }); - - test('Loading to error', () { - expect(container.read(provider), const AsyncValue.loading()); - - final stack = StackTrace.current; - controller.addError(42, stack); - - expect( - container.read(provider), - AsyncValue.error(42, stack), - ); - }); - - group('.future', () { - // test( - // 'throws StateError if the provider is disposed before a value was emitted', - // () async { - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncLoading()), - // ]); - - // final future = container.read(provider.future); - - // container.dispose(); - - // await expectLater( - // future, - // throwsA( - // isA().having( - // (e) => e.message, - // 'message', - // equalsIgnoringHashCodes( - // 'The provider StreamProvider#00000 was disposed before a value was emitted.', - // ), - // ), - // ), - // ); - // }); - - // test('supports loading then error then loading', () async { - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncLoading()), - // ]); - - // var future = container.read(provider.future); - - // final error = Error(); - - // container.updateOverrides([ - // provider.overrideWithValue(AsyncValue.error(error)), - // ]); - - // expect(container.read(provider.future), future); - - // // TODO(rrousselGit) test that the stacktrace is preserved - // await expectLater(future, throwsA(error)); - - // final error2 = Error(); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // future = container.read(provider.future); - - // container.updateOverrides([ - // provider.overrideWithValue(AsyncValue.error(error2)), - // ]); - - // expect(container.read(provider.future), future); - - // // TODO(rrousselGit) test that the stacktrace is preserved - // await expectLater(future, throwsA(error2)); - // }); - - // test('supports loading then error then another error', () async { - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncLoading()), - // ]); - - // var future = container.read(provider.future); - - // final error = Error(); - - // container.updateOverrides([ - // provider.overrideWithValue(AsyncValue.error(error)), - // ]); - - // expect(container.read(provider.future), future); - - // // TODO(rrousselGit) test that the stacktrace is preserved - // await expectLater(future, throwsA(error)); - - // final error2 = Error(); - - // // error without passing by an intermediary loading state - // container.updateOverrides([ - // provider.overrideWithValue(AsyncValue.error(error2)), - // ]); - - // future = container.read(provider.future); - - // // TODO(rrousselGit) test that the stacktrace is preserved - // await expectLater(future, throwsA(error2)); - // }); - - // test('supports loading then data then loading', () async { - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncLoading()), - // ]); - - // var future = container.read(provider.future); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // expect(container.read(provider.future), future); - // await expectLater(future, completion(42)); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // future = container.read(provider.future); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(21)), - // ]); - - // expect(container.read(provider.future), future); - // await expectLater(future, completion(21)); - // }); - - // test('supports loading then data then another data', () async { - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncLoading()), - // ]); - - // var future = container.read(provider.future); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // expect(container.read(provider.future), future); - // await expectLater(future, completion(42)); - - // // data without passing by an intermediary loading state - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(21)), - // ]); - - // future = container.read(provider.future); - - // await expectLater(future, completion(21)); - // }); - }); - - // test('myProvider.stream emits done on dispose', () async { - // final stream = container.read(provider.stream); - - // container.dispose(); - - // await expectLater(stream, emitsDone); - // }); - - // test('myProvider.stream re-create a new stream when re-entering loading', - // () async { - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // final stream = container.read(provider.stream); - - // await expectLater(stream, emits(42)); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // final stream2 = container.read(provider.stream); - - // expect(stream2, isNot(stream)); - // await expectLater(stream, emitsDone); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(21)), - // ]); - - // await expectLater(stream2, emits(21)); - - // container.dispose(); - - // await expectLater(stream2, emitsDone); - // }); - - // test('myProvider.stream works across provider rebuild', () async { - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // final stream = container.read(provider.stream); - - // await expectLater(stream, emits(42)); - - // container.updateOverrides( - // [provider.overrideWithValue(const AsyncValue.data(21))], - // ); - - // await expectLater(stream, emits(21)); - // }); - - test('does not filter identical values', () async { - final sub = container.listen(provider, (_, __) {}); - - expect(sub.read(), const AsyncValue.loading()); - - controller.add(42); - await container.pump(); - - expect(sub.read(), const AsyncValue.data(42)); - - controller.add(42); - await container.pump(); - - expect(sub.read(), const AsyncValue.data(42)); - }); - - test('provider.stream is a broadcast stream', () async { - controller = StreamController(); - - // ignore: deprecated_member_use_from_same_package - final sub = container.listen(provider.stream, (_, __) {}); - - controller.add(42); - - await expectLater(sub.read(), emits(42)); - }); - - test('throwing inside "create" result in an AsyncValue.error', () { - // ignore: only_throw_errors - final provider = StreamProvider((ref) => throw 42); - - expect( - container.read(provider), - isA>().having((s) => s.error, 'error', 42), - ); - }); - - test('does not update dependents if the created stream did not change', - () async { - final dep = StateProvider((ref) => 0); - final provider = StreamProvider((ref) { - ref.watch(dep); - return const Stream.empty(); - }); - final listener = Listener>(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, const AsyncValue.loading())); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyNoMoreInteractions(listener); - }); - - test( - '.stream does not update dependents if the created stream did not change', - () async { - final dep = StateProvider((ref) => 0); - final provider = StreamProvider((ref) { - ref.watch(dep); - return const Stream.empty(); - }); - final listener = Listener>(); - - // ignore: deprecated_member_use_from_same_package - container.listen(provider.stream, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(any, any)); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyNoMoreInteractions(listener); - }); - - test( - '.future does not update dependents if the created future did not change', - () async { - final dep = StateProvider((ref) => 0); - - final provider = StreamProvider((ref) { - ref.watch(dep); - return const Stream.empty(); - }); - final listener = Listener>(); - - container.listen(provider.future, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(any, any)); - - container.read(dep.notifier).state++; - await container.pump(); - - verifyNoMoreInteractions(listener); - - // No value were emitted, so the future will fail. Catching the error to - // avoid false positive. - // ignore: unawaited_futures - container.read(provider.future).catchError((Object _) => 0); - }); - - group('overrideWithValue(T)', () { - // test('.stream is a broadcast stream', () async { - // final provider = StreamProvider((ref) => controller.stream); - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // final sub = container.listen(provider.stream, (_, __) {}); - - // await expectLater(sub.read(), emits(42)); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(21)), - // ]); - - // await expectLater(sub.read(), emits(21)); - // }); - - // test('.stream queues events when there are no listeners', () async { - // final provider = StreamProvider((ref) => controller.stream); - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // final sub = container.listen(provider.stream, (_, __) {}); - - // await expectLater(sub.read(), emits(42)); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(21)), - // ]); - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(22)), - // ]); - - // // This await would normally prevent the test from reading the "21" event - // // unless the event is queued - // await container.pump(); - - // await expectLater( - // sub.read(), - // emitsInOrder([21, 22]), - // ); - // }); - - // test('.stream emits done when the container is disposed', () async { - // final provider = StreamProvider.autoDispose((ref) => controller.stream); - // final container = createContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // final sub = container.listen(provider.stream, (_, __) {}); - - // final stream = sub.read(); - - // container.dispose(); - - // await expectLater(stream, emits(42)); - // await expectLater(stream, emitsDone); - // }); - // }); - - // test( - // 'when overridden with an error but provider.stream is not listened to, it should not emit an error to the zone', - // () async { - // final error = Error(); - // final stream = StreamProvider((ref) => const Stream.empty()); - - // final container = ProviderContainer(overrides: [ - // stream.overrideWithValue(AsyncValue.error(error)), - // ]); - // addTearDown(container.dispose); - - // expect( - // container.read(stream), - // AsyncValue.error(error), - // ); - }); - - test('StreamProvider.family', () async { - final provider = StreamProvider.family((ref, a) { - return Stream.value('$a'); - }); - final container = createContainer(); - - expect(container.read(provider(0)), const AsyncValue.loading()); - - await container.pump(); - - expect( - container.read(provider(0)), - const AsyncValue.data('0'), - ); - }); - - test('can specify name', () { - final provider = StreamProvider( - (_) => const Stream.empty(), - name: 'example', - ); - - expect(provider.name, 'example'); - - final provider2 = StreamProvider((_) => const Stream.empty()); - - expect(provider2.name, isNull); - }); - - test('is AlwaysAliveProviderBase', () { - final provider = StreamProvider((_) async* {}); - - expect(provider, isA>>()); - }); - - test('subscribe exposes loading synchronously then value on change', - () async { - final container = createContainer(); - final controller = StreamController(sync: true); - final provider = StreamProvider((_) => controller.stream); - final listener = Listener>(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, const AsyncValue.loading())); - - controller.add(42); - - verifyOnly( - listener, - listener(const AsyncValue.loading(), const AsyncValue.data(42)), - ); - - controller.add(21); - - verifyOnly( - listener, - listener(const AsyncValue.data(42), const AsyncValue.data(21)), - ); - - await controller.close(); - }); - - test('errors', () async { - final container = createContainer(); - final controller = StreamController(sync: true); - final provider = StreamProvider((_) => controller.stream); - final listener = Listener>(); - final error = Error(); - final stack = StackTrace.current; - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, const AsyncValue.loading())); - - controller.addError(error, stack); - - verifyOnly( - listener, - listener( - const AsyncValue.loading(), - AsyncValue.error(error, stack), - ), - ); - - controller.add(21); - - verifyOnly( - listener, - listener( - AsyncError(error, stack), - const AsyncValue.data(21).copyWithPrevious( - AsyncError(error, stack), - ), - ), - ); - - await controller.close(); - }); - - test('stops subscription', () async { - final container = createContainer(); - final controller = StreamController(sync: true); - final dispose = OnDisposeMock(); - final provider = StreamProvider((ref) { - ref.onDispose(dispose.call); - return controller.stream; - }); - final listener = Listener>(); - - container.listen(provider, listener.call, fireImmediately: true); - - verifyOnly(listener, listener(null, const AsyncValue.loading())); - - controller.add(42); - - verifyOnly( - listener, - listener(const AsyncValue.loading(), const AsyncValue.data(42)), - ); - verifyNoMoreInteractions(dispose); - - container.dispose(); - - verify(dispose()).called(1); - verifyNoMoreInteractions(dispose); - - // if the listener wasn't removed, this would throw because markNeedsNotifyListeners - // cannot be called once the provider was disposed. - controller.add(21); - - await controller.close(); - }); - - group('StreamProvider().future', () { - test('does not update dependents when the future completes', () async { - final controller = StreamController(sync: true); - addTearDown(controller.close); - final provider = StreamProvider((_) => controller.stream); - final container = createContainer(); - var callCount = 0; - final dependent = Provider((ref) { - callCount++; - // ignore: deprecated_member_use_from_same_package - return ref.watch(provider.stream); - }); - - container.listen(dependent, (_, __) {}); - - expect(callCount, 1); - - controller.add(42); - // just making sure the dependent isn't updated asynchronously - await container.pump(); - - expect(callCount, 1); - }); - - test('.stream creates the stream once and it contains all events', - () async { - final currentStream = StateProvider((ref) => Stream.value(42)); - // a StreamProvider that can rebuild with a new future - final streamProvider = StreamProvider((ref) => ref.watch(currentStream)); - final container = createContainer(); - final listener = Listener>(); - - final sub = container.listen( - // ignore: deprecated_member_use_from_same_package - streamProvider.stream, - listener.call, - fireImmediately: true, - ); - - final stream = sub.read(); - - verifyOnly(listener, listener(null, sub.read())); - await expectLater(stream, emits(42)); - - container.read(currentStream.notifier).state = Stream.value(21); - await expectLater(stream, emits(21)); - - // Making sure providers are disposed, sending done events to ".stream". - container.dispose(); - - await expectLater(stream, emitsDone); - verifyNoMoreInteractions(listener); - }); - }); - - group('StreamProvider.autoDispose().stream', () { - test('does not update dependents when the future completes', () async { - final controller = StreamController(sync: true); - addTearDown(controller.close); - final provider = StreamProvider.autoDispose((_) => controller.stream); - final container = createContainer(); - var callCount = 0; - final dependent = Provider.autoDispose((ref) { - callCount++; - // ignore: deprecated_member_use_from_same_package - return ref.watch(provider.stream); - }); - - container.listen(dependent, (_, __) {}); - - expect(callCount, 1); - - controller.add(42); - - // just making sure the dependent isn't updated asynchronously - await container.pump(); - - expect(callCount, 1); - }); - - test('disposes the main provider when no longer used', () async { - final controller = StreamController(sync: true); - addTearDown(controller.close); - var didDispose = false; - final provider = StreamProvider.autoDispose((ref) { - ref.onDispose(() => didDispose = true); - return controller.stream; - }); - final container = createContainer(); - // ignore: deprecated_member_use_from_same_package - final sub = container.listen(provider.stream, (_, __) {}); - - expect(didDispose, false); - - await container.pump(); - expect(didDispose, false); - - sub.close(); - - await container.pump(); - expect(didDispose, true); - }); - }); - - group('StreamProvider.future', () { - group('from StreamProvider', () { - test('read currentValue before first value', () async { - final container = createContainer(); - final controller = StreamController(); - final provider = StreamProvider((_) => controller.stream); - - final future = container.read(provider.future); - - controller.add(42); - - await expectLater(future, completion(42)); - - await controller.close(); - }); - - test('read currentValue before after value', () async { - final container = createContainer(); - final controller = StreamController(); - final provider = StreamProvider((_) => controller.stream); - - controller.add(42); - - final future = container.read(provider.future); - - await expectLater(future, completion(42)); - - await controller.close(); - }); - - test('read currentValue before first error', () async { - final container = createContainer(); - final controller = StreamController(); - final provider = StreamProvider((_) => controller.stream); - - final future = container.read(provider.future); - - controller.addError(42); - - await expectLater(future, throwsA(42)); - - await controller.close(); - }); - - test('read currentValue before after error', () async { - final container = createContainer(); - final controller = StreamController(); - final provider = StreamProvider((_) => controller.stream); - - controller.addError(42); - - final future = container.read(provider.future); - - await expectLater(future, throwsA(42)); - - await controller.close(); - }); - }); - - group('from StreamProvider.overrideWithValue', () { - // test('read currentValue before first value', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // final future = container.read(provider.future); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // await expectLater(future, completion(42)); - // }); - - // test('read currentValue before after value', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // final future = container.read(provider.future); - - // await expectLater(future, completion(42)); - // }); - - // test('read currentValue before first error', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // final future = container.read(provider.future); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.error(42)), - // ]); - - // await expectLater(future, throwsA(42)); - // }); - - // test('read currentValue before after error', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.error(42)), - // ]); - - // final future = container.read(provider.future); - - // await expectLater(future, throwsA(42)); - // }); - - // test('synchronous first event', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // final future = container.read(provider.future); - - // await expectLater(future, completion(42)); - // }); - }); - }); - - group('StreamProvider.stream', () { - group('from StreamProvider', () { - test('read currentValue before first value', () async { - final container = createContainer(); - final controller = StreamController(); - final provider = StreamProvider((_) => controller.stream); - - // ignore: deprecated_member_use_from_same_package - final stream = container.read(provider.stream); - - controller.add(42); - - await expectLater(stream, emits(42)); - - await controller.close(); - }); - - test('read currentValue before after value', () async { - final container = createContainer(); - final controller = StreamController(); - final provider = StreamProvider((_) => controller.stream); - - controller.add(42); - - // ignore: deprecated_member_use_from_same_package - final stream = container.read(provider.stream); - - await expectLater(stream, emits(42)); - - await controller.close(); - }); - - test('read currentValue before first error', () async { - final container = createContainer(); - final controller = StreamController(); - final provider = StreamProvider((_) => controller.stream); - - // ignore: deprecated_member_use_from_same_package - final stream = container.read(provider.stream); - - controller.addError(42); - - await expectLater(stream, emitsError(42)); - - await controller.close(); - }); - - test('read currentValue before after error', () async { - final container = createContainer(); - final controller = StreamController(); - final provider = StreamProvider((_) => controller.stream); - - controller.addError(42); - - // ignore: deprecated_member_use_from_same_package - final stream = container.read(provider.stream); - - await expectLater(stream, emitsError(42)); - - await controller.close(); - }); - }); - - group('from StreamProvider.overrideWithValue', () { - // test('loading to data to loading creates a new stream too', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // final stream0 = container.read(provider.stream); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // final stream1 = container.read(provider.stream); - - // expect(stream0, stream1); - // await expectLater(stream1, emits(42)); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // final stream2 = container.read(provider.stream); - - // expect(stream2, isNot(stream1)); - // await expectLater(stream1, emitsDone); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(21)), - // ]); - - // await expectLater(stream2, emits(21)); - // }); - - // test('data to loading creates a new stream', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // final stream1 = container.read(provider.stream); - - // await expectLater(stream1, emits(42)); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // final stream2 = container.read(provider.stream); - - // expect(stream2, isNot(stream1)); - // await expectLater(stream1, emitsDone); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(21)), - // ]); - - // await expectLater(stream2, emits(21)); - // }); - - // test('error to loading creates a new stream', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.error(42)), - // ]); - - // final stream1 = container.read(provider.stream); - - // await expectLater(stream1, emitsError(42)); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // final stream2 = container.read(provider.stream); - - // expect(stream2, isNot(stream1)); - // await expectLater(stream1, emitsDone); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(21)), - // ]); - - // await expectLater(stream2, emits(21)); - // }); - - // test('read currentValue before first value', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // final stream = container.read(provider.stream); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // await expectLater(stream, emits(42)); - // }); - - // test('read currentValue before after value', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // final stream = container.read(provider.stream); - - // await expectLater(stream, emits(42)); - // }); - - // test('read currentValue before first error', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // final stream = container.read(provider.stream); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.error(42)), - // ]); - - // await expectLater(stream, emitsError(42)); - // }); - - // test('read currentValue before after error', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.error(42)), - // ]); - - // final stream = container.read(provider.stream); - - // await expectLater(stream, emitsError(42)); - // }); - - // test('synchronous first event', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // final stream = container.read(provider.stream); - - // await expectLater(stream, emits(42)); - // }); - }); - }); - - group('mock as value', () { - // test('value immediately then other value', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - // final stream = container.read(provider.stream); - - // final sub = container.listen(provider, (_, __) {}); - - // expect(sub.read(), const AsyncValue.data(42)); - // await expectLater(stream, emits(42)); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(21)), - // ]); - - // expect(sub.read(), const AsyncValue.data(21)); - // await expectLater(stream, emits(21)); - - // container.dispose(); - - // await expectLater(stream, emitsDone); - // }); - - // test('value immediately then error', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - // final stream = container.read(provider.stream); - - // final sub = container.listen(provider, (_, __) {}); - - // expect(sub.read(), const AsyncValue.data(42)); - // await expectLater(stream, emits(42)); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.error(21)), - // ]); - - // expect( - // sub.read(), - // const AsyncValue.error(21).copyWithPrevious(const AsyncData(42)), - // ); - // // TODO why call "read" twice? - // expect( - // sub.read(), - // const AsyncValue.error(21).copyWithPrevious(const AsyncData(42)), - // ); - // await expectLater(stream, emitsError(21)); - - // container.dispose(); - - // await expectLater(stream, emitsDone); - // }); - - // test('value immediately then loading', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - // final stream = container.read(provider.stream); - - // final sub = container.listen(provider, (_, __) {}); - - // expect(sub.read(), const AsyncValue.data(42)); - // await expectLater(stream, emits(42)); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // expect( - // sub.read(), - // const AsyncLoading() - // .copyWithPrevious(const AsyncValue.data(42)), - // ); - - // container.dispose(); - - // await expectLater(stream, emitsDone); - // }); - - // test('loading immediately then value', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - // final stream = container.read(provider.stream); - - // final sub = container.listen(provider, (_, __) {}); - - // expect(sub.read(), const AsyncValue.loading()); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // expect(sub.read(), const AsyncValue.data(42)); - // await expectLater(stream, emits(42)); - - // container.dispose(); - - // await expectLater(stream, emitsDone); - // }); - - // test('loading immediately then error', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - // final stream = container.read(provider.stream); - - // final sub = container.listen(provider, (_, __) {}); - - // expect(sub.read(), const AsyncValue.loading()); - - // final stackTrace = StackTrace.current; - - // container.updateOverrides([ - // provider.overrideWithValue( - // AsyncValue.error(42, stackTrace: stackTrace)), - // ]); - - // expect(sub.read(), AsyncValue.error(42, stackTrace: stackTrace)); - - // await expectLater(stream, emitsError(42)); - - // container.dispose(); - - // await expectLater(stream, emitsDone); - // }); - - // test('loading immediately then loading', () async { - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - // final stream = container.read(provider.stream); - // final listener = Listener>(); - - // container.listen(provider, listener, fireImmediately: true); - - // verifyOnly(listener, listener(null, const AsyncValue.loading())); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // verifyNoMoreInteractions(listener); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(42)), - // ]); - - // verifyOnly( - // listener, - // listener(const AsyncValue.loading(), const AsyncValue.data(42)), - // ); - - // await expectLater(stream, emits(42)); - - // container.dispose(); - - // await expectLater(stream, emitsDone); - // }); - - // test('error immediately then different error', () async { - // final stackTrace = StackTrace.current; - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue( - // AsyncValue.error(42, stackTrace: stackTrace)), - // ]); - // final stream = container.read(provider.stream); - - // final sub = container.listen(provider, (_, __) {}); - - // expect(sub.read(), AsyncValue.error(42, stackTrace: stackTrace)); - // await expectLater(stream, emitsError(42)); - - // container.updateOverrides([ - // provider.overrideWithValue( - // AsyncValue.error(21, stackTrace: stackTrace)), - // ]); - - // expect(sub.read(), AsyncValue.error(21, stackTrace: stackTrace)); - // await expectLater(stream, emitsError(21)); - - // container.dispose(); - - // await expectLater(stream, emitsDone); - // }); - - // test('error immediately then different stacktrace', () async { - // final stackTrace = StackTrace.current; - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue( - // AsyncValue.error(42, stackTrace: stackTrace)), - // ]); - // final stream = container.read(provider.stream); - - // final sub = container.listen(provider, (_, __) {}); - - // expect(sub.read(), AsyncValue.error(42, stackTrace: stackTrace)); - // await expectLater(stream, emitsError(42)); - - // final stackTrace2 = StackTrace.current; - // container.updateOverrides([ - // provider.overrideWithValue( - // AsyncValue.error(42, stackTrace: stackTrace2), - // ), - // ]); - - // expect(sub.read(), AsyncValue.error(42, stackTrace: stackTrace2)); - // await expectLater(stream, emitsError(42)); - - // container.dispose(); - - // await expectLater(stream, emitsDone); - // }); - - // test('error immediately then data', () async { - // const stackTrace = StackTrace.empty; - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue( - // const AsyncValue.error(42, stackTrace: stackTrace)), - // ]); - // final stream = container.read(provider.stream); - - // final sub = container.listen(provider, (_, __) {}); - - // expect( - // sub.read(), - // const AsyncValue.error(42, stackTrace: stackTrace), - // ); - // await expectLater(stream, emitsError(42)); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.data(21)), - // ]); - - // expect( - // sub.read(), - // const AsyncValue.data(21) - // .copyWithPrevious(const AsyncError(42, stackTrace: stackTrace)), - // ); - // await expectLater(stream, emits(21)); - - // container.dispose(); - - // await expectLater(stream, emitsDone); - // }); - - // test('error immediately then loading', () async { - // final stackTrace = StackTrace.current; - // final provider = StreamProvider((_) async* {}); - // final container = ProviderContainer(overrides: [ - // provider.overrideWithValue( - // AsyncValue.error(42, stackTrace: stackTrace)), - // ]); - // final stream = container.read(provider.stream); - - // final sub = container.listen(provider, (_, __) {}); - - // expect(sub.read(), AsyncValue.error(42, stackTrace: stackTrace)); - // await expectLater(stream, emitsError(42)); - - // container.updateOverrides([ - // provider.overrideWithValue(const AsyncValue.loading()), - // ]); - - // expect( - // sub.read(), - // const AsyncLoading() - // .copyWithPrevious(AsyncError(42, stackTrace: stackTrace)), - // ); - - // container.dispose(); - - // await expectLater(stream, emitsDone); - // }); - }); -} - -// class MockStream extends Mock implements Stream { -// @override -// StreamSubscription listen( -// void Function(T event)? onData, { -// Function? onError, -// void Function()? onDone, -// bool? cancelOnError, -// }) { -// return super.noSuchMethod( -// Invocation.method( -// #listen, -// [onData], -// { -// #onError: onError, -// #onDone: onDone, -// #cancelOnError: cancelOnError, -// }, -// ), -// returnValue: MockSubscription(), -// returnValueForMissingStub: MockSubscription(), -// ) as StreamSubscription; -// } -// } - -// class MockSubscription extends Mock implements StreamSubscription { -// @override -// Future cancel() { -// return super.noSuchMethod( -// Invocation.method(#cancel, []), -// returnValue: Future.value(), -// returnValueForMissingStub: Future.value(), -// ) as Future; -// } -// } diff --git a/packages/riverpod/test/src/common/result_test.dart b/packages/riverpod/test/src/common/result_test.dart new file mode 100644 index 000000000..e7e1fec01 --- /dev/null +++ b/packages/riverpod/test/src/common/result_test.dart @@ -0,0 +1,36 @@ +import 'package:riverpod/src/common/result.dart'; +import 'package:test/test.dart'; + +void main() { + group('Result.data', () { + test('implements hashCode/==', () { + expect($Result.data(42), $Result.data(42)); + expect($Result.data(42), isNot($Result.data(21))); + + expect($Result.data(42).hashCode, $Result.data(42).hashCode); + expect($Result.data(42).hashCode, isNot($Result.data(21).hashCode)); + }); + }); + + group('Result.error', () { + test('implements hashCode/==', () { + expect( + $Result.error(42, StackTrace.empty), + $Result.error(42, StackTrace.empty), + ); + expect( + $Result.error(42, StackTrace.empty), + isNot($Result.error(21, StackTrace.empty)), + ); + + expect( + $Result.error(42, StackTrace.empty).hashCode, + $Result.error(42, StackTrace.empty).hashCode, + ); + expect( + $Result.error(42, StackTrace.empty).hashCode, + isNot($Result.error(21, StackTrace.empty).hashCode), + ); + }); + }); +} diff --git a/packages/riverpod/test/framework/async_value_test.dart b/packages/riverpod/test/src/core/async_value_test.dart similarity index 88% rename from packages/riverpod/test/framework/async_value_test.dart rename to packages/riverpod/test/src/core/async_value_test.dart index 7214f2614..7f42d3808 100644 --- a/packages/riverpod/test/framework/async_value_test.dart +++ b/packages/riverpod/test/src/core/async_value_test.dart @@ -1,4 +1,4 @@ -// ignore_for_file: avoid_types_on_closure_parameters +// ignore_for_file: avoid_types_on_closure_parameters, prefer_const_constructors, prefer_const_declarations import 'dart:async'; @@ -7,41 +7,51 @@ import 'package:riverpod/src/internals.dart'; import 'package:test/test.dart'; void main() { - group('custom AsyncValue', () { - test('supports when', () { - expect( - const CustomData(42).whenOrNull(data: (v) => v * 2), - 84, - ); + test('Can do exhaustive pattern matching', () { + expect( + switch (const AsyncValue.loading()) { + AsyncData() => 'data', + AsyncError() => 'error', + AsyncLoading() => 'loading', + }, + 'loading', + ); + }); + group('progress', () { + test('defaults to null', () { + expect(const AsyncLoading().progress, null); + expect(const AsyncData(42).progress, null); + expect(const AsyncError('err', StackTrace.empty).progress, null); + }); + + test('asserts is within bounds', () { expect( - const CustomError(42, stackTrace: StackTrace.empty) - .whenOrNull(data: (v) => v * 2), - null, + () => AsyncLoading(progress: -0.1), + throwsA(isA()), ); - expect( - const CustomLoading().whenOrNull(data: (v) => v * 2), - null, + () => AsyncLoading(progress: 1.1), + throwsA(isA()), ); }); }); test('unwrapPrevious', () { expect( - const AsyncLoading() + const AsyncLoading(progress: .1) .copyWithPrevious(const AsyncData(42)) .unwrapPrevious(), - const AsyncLoading(), + const AsyncLoading(progress: .1), ); expect( - const AsyncLoading() + const AsyncLoading(progress: .1) .copyWithPrevious(const AsyncError(42, StackTrace.empty)) .unwrapPrevious(), - const AsyncLoading(), + const AsyncLoading(progress: .1), ); expect( - const AsyncLoading() + const AsyncLoading(progress: .1) .copyWithPrevious( const AsyncLoading().copyWithPrevious( const AsyncError('err', StackTrace.empty), @@ -49,12 +59,12 @@ void main() { ), ) .unwrapPrevious(), - const AsyncLoading(), + const AsyncLoading(progress: .1), ); expect( const AsyncData(42) - .copyWithPrevious(const AsyncLoading()) + .copyWithPrevious(const AsyncLoading(progress: .1)) .unwrapPrevious(), const AsyncData(42), ); @@ -88,21 +98,22 @@ void main() { group('copyWithPrevious', () { group('with seamless: false', () { test('with AsyncLoading, is identical to the incoming AsyncLoading', () { - final incomingLoading = const AsyncLoading() + final incomingLoading = const AsyncLoading(progress: .1) .copyWithPrevious(const AsyncData(42), isRefresh: false); - final result = const AsyncLoading() + final result = const AsyncLoading(progress: .1) .copyWithPrevious(incomingLoading, isRefresh: false); expect(result, same(incomingLoading)); }); test('with AsyncData, sets value and hasValue', () { - final result = const AsyncLoading() + final result = const AsyncLoading(progress: .1) .copyWithPrevious(const AsyncData(42), isRefresh: false); expect(result, isA>()); expect(result.hasValue, true); expect(result.value, 42); + expect(result.progress, .1); expect(result.hasError, false); expect(result.error, null); @@ -114,12 +125,13 @@ void main() { () { final error = const AsyncError(Object(), StackTrace.empty) .copyWithPrevious(const AsyncData(42)); - final result = - const AsyncLoading().copyWithPrevious(error, isRefresh: false); + final result = const AsyncLoading(progress: .1) + .copyWithPrevious(error, isRefresh: false); expect(result, isA>()); expect(result.hasValue, true); expect(result.value, 42); + expect(result.progress, .1); expect(result.hasError, true); expect(result.error, const Object()); @@ -171,8 +183,10 @@ void main() { group('on AsyncData', () { test('with AsyncLoading', () { expect( - const AsyncData(42) - .copyWithPrevious(const AsyncLoading(), isRefresh: true), + const AsyncData(42).copyWithPrevious( + const AsyncLoading(progress: .1), + isRefresh: true, + ), const AsyncData(42), ); }); @@ -197,33 +211,35 @@ void main() { group('on AsyncLoading', () { test('with AsyncLoading', () { expect( - const AsyncLoading() - .copyWithPrevious(const AsyncLoading()), - const AsyncLoading(), + const AsyncLoading(progress: .1) + .copyWithPrevious(const AsyncLoading(progress: .2)), + const AsyncLoading(progress: .1), ); }); test('with AsyncError', () { - final value = const AsyncLoading() + final value = const AsyncLoading(progress: .1) .copyWithPrevious(const AsyncError('err', StackTrace.empty)); expect(value, isA>()); expect(value.isLoading, true); expect(value.isRefreshing, true); expect(value.hasValue, false); - expect(() => value.value, throwsA('err')); + expect(value.value, null); + expect(value.progress, .1); expect(value.error, 'err'); expect(value.stackTrace, StackTrace.empty); }); test('with AsyncError containing previous data', () { - final value = const AsyncLoading().copyWithPrevious( + final value = const AsyncLoading(progress: .1).copyWithPrevious( const AsyncError('err', StackTrace.empty) .copyWithPrevious(const AsyncData(42)), ); expect(value, isA>()); expect(value.isLoading, true); + expect(value.progress, .1); expect(value.isRefreshing, true); expect(value.hasValue, true); expect(value.value, 42); @@ -233,13 +249,13 @@ void main() { test('with refreshing AsyncError containing previous data', () { expect( - const AsyncLoading().copyWithPrevious( - const AsyncLoading().copyWithPrevious( + const AsyncLoading(progress: .1).copyWithPrevious( + const AsyncLoading(progress: .1).copyWithPrevious( const AsyncError('err', StackTrace.empty) .copyWithPrevious(const AsyncData(42)), ), ), - const AsyncLoading().copyWithPrevious( + const AsyncLoading(progress: .1).copyWithPrevious( const AsyncError('err', StackTrace.empty) .copyWithPrevious(const AsyncData(42)), ), @@ -247,11 +263,12 @@ void main() { }); test('with AsyncData', () { - final value = - const AsyncLoading().copyWithPrevious(const AsyncData(42)); + final value = const AsyncLoading(progress: .1) + .copyWithPrevious(const AsyncData(42)); expect(value, isA>()); expect(value.isLoading, true); + expect(value.progress, .1); expect(value.isRefreshing, true); expect(value.hasValue, true); expect(value.value, 42); @@ -260,12 +277,13 @@ void main() { }); test('with refreshing AsyncData', () { - final value = const AsyncLoading().copyWithPrevious( + final value = const AsyncLoading(progress: .1).copyWithPrevious( const AsyncLoading().copyWithPrevious(const AsyncData(42)), ); expect(value, isA>()); expect(value.isLoading, true); + expect(value.progress, .1); expect(value.isRefreshing, true); expect(value.hasValue, true); expect(value.value, 42); @@ -389,7 +407,6 @@ void main() { test('asError', () { const value = AsyncValue.error(42, StackTrace.empty); - // ignore: omit_local_variable_types, unused_local_variable, testing that assignment works, final AsyncError? error = value.asError; expect(const AsyncData(42).asError, null); @@ -422,12 +439,12 @@ void main() { ); expect( - const AsyncLoading().map( + const AsyncLoading(progress: .1).map( data: (value) => throw Error(), error: (_) => throw Error(), - loading: (AsyncLoading loading) => 'loading', + loading: (AsyncLoading loading) => 'loading ${loading.progress}', ), - 'loading', + 'loading 0.1', ); }); @@ -452,11 +469,11 @@ void main() { ); expect( - const AsyncLoading().maybeMap( - loading: (AsyncLoading loading) => 'loading', + const AsyncLoading(progress: .1).maybeMap( + loading: (AsyncLoading loading) => 'loading ${loading.progress}', orElse: () => throw Error(), ), - 'loading', + 'loading 0.1', ); }); @@ -494,7 +511,6 @@ void main() { group('mapOrNull', () { test('supports returning null when relying on type-inference', () { - // ignore: unused_local_variable, omit_local_variable_types final int? x2 = const AsyncValue.data(1).mapOrNull( data: (value) => null, error: (_) => null, @@ -520,10 +536,10 @@ void main() { ); expect( - const AsyncLoading().mapOrNull( - loading: (AsyncLoading loading) => 'loading', + const AsyncLoading(progress: .1).mapOrNull( + loading: (AsyncLoading loading) => 'loading ${loading.progress}', ), - 'loading', + 'loading 0.1', ); }); @@ -1020,7 +1036,6 @@ void main() { group('whenOrNull', () { test('supports returning null when relying on type-inference', () { - // ignore: unused_local_variable, omit_local_variable_types final int? x2 = const AsyncValue.data(1).whenOrNull( data: (value) => null, error: (err, stack) => null, @@ -1086,9 +1101,7 @@ void main() { }); test('==', () { - // ignore: prefer_const_declarations final value = 42; - // ignore: prefer_const_declarations final value2 = 21; final stack = StackTrace.current; @@ -1153,21 +1166,23 @@ void main() { ); expect( - // ignore: prefer_const_constructors AsyncLoading(), - // ignore: prefer_const_constructors AsyncLoading(), ); expect( - // ignore: prefer_const_constructors + AsyncLoading(progress: .1), + AsyncLoading(progress: .1), + ); + expect( + AsyncLoading(progress: .1), + isNot(AsyncLoading(progress: .2)), + ); + expect( AsyncLoading(), - // ignore: prefer_const_constructors isNot(AsyncValue.loading()), ); expect( - // ignore: prefer_const_constructors AsyncValue.loading(), - // ignore: prefer_const_constructors isNot(AsyncLoading()), ); @@ -1190,9 +1205,7 @@ void main() { }); test('hashCode', () { - // ignore: prefer_const_declarations final value = 42; - // ignore: prefer_const_declarations final value2 = 21; final stack = StackTrace.current; @@ -1253,21 +1266,23 @@ void main() { ); expect( - // ignore: prefer_const_constructors AsyncLoading().hashCode, - // ignore: prefer_const_constructors AsyncLoading().hashCode, ); expect( - // ignore: prefer_const_constructors + AsyncLoading(progress: .1).hashCode, + AsyncLoading(progress: .1).hashCode, + ); + expect( + AsyncLoading(progress: .1).hashCode, + isNot(AsyncLoading(progress: .2).hashCode), + ); + expect( AsyncLoading().hashCode, - // ignore: prefer_const_constructors isNot(AsyncValue.loading().hashCode), ); expect( - // ignore: prefer_const_constructors AsyncValue.loading().hashCode, - // ignore: prefer_const_constructors isNot(AsyncLoading().hashCode), ); @@ -1340,6 +1355,10 @@ void main() { const AsyncLoading().toString(), 'AsyncLoading()', ); + expect( + const AsyncLoading(progress: .1).toString(), + 'AsyncLoading(progress: 0.1)', + ); expect( const AsyncLoading() @@ -1347,6 +1366,12 @@ void main() { .toString(), 'AsyncData(isLoading: true, value: 42)', ); + expect( + const AsyncLoading(progress: .1) + .copyWithPrevious(const AsyncData(42)) + .toString(), + 'AsyncData(isLoading: true, progress: 0.1, value: 42)', + ); expect( const AsyncLoading() .copyWithPrevious(const AsyncError(42, StackTrace.empty)) @@ -1429,30 +1454,31 @@ void main() { group('whenData', () { test('preserves isLoading/isRefreshing', () { expect( - const AsyncLoading() + const AsyncLoading(progress: .1) .copyWithPrevious(const AsyncData(42)) .whenData((value) => value * 2), - const AsyncLoading().copyWithPrevious(const AsyncData(84)), + const AsyncLoading(progress: .1) + .copyWithPrevious(const AsyncData(84)), ); expect( - const AsyncLoading() + const AsyncLoading(progress: .1) .copyWithPrevious(const AsyncData(42)) .whenData( (value) => Error.throwWithStackTrace(84, StackTrace.empty), ), - const AsyncLoading().copyWithPrevious( + const AsyncLoading(progress: .1).copyWithPrevious( const AsyncError(84, StackTrace.empty), ), ); expect( - const AsyncLoading() + const AsyncLoading(progress: .1) .copyWithPrevious( const AsyncError(84, StackTrace.empty), ) .whenData((value) => '$value'), - const AsyncLoading().copyWithPrevious( + const AsyncLoading(progress: .1).copyWithPrevious( const AsyncError(84, StackTrace.empty), ), ); @@ -1464,8 +1490,8 @@ void main() { const AsyncData('42'), ); expect( - const AsyncLoading().whenData((value) => '$value'), - const AsyncLoading(), + const AsyncLoading(progress: .1).whenData((value) => '$value'), + const AsyncLoading(progress: .1), ); expect( const AsyncError(21, StackTrace.empty) @@ -1477,11 +1503,10 @@ void main() { test('catches errors in data transformer and return AsyncError', () { expect( const AsyncValue.data(42).whenData( - // ignore: only_throw_errors - (value) => throw '42', + (value) => throw StateError('foo'), ), isA>() - .having((e) => e.error, 'error', '42') + .having((e) => e.error, 'error', isStateError) .having((e) => e.stackTrace, 'stackTrace', isNotNull), ); }); @@ -1490,7 +1515,6 @@ void main() { test('AsyncValue.asData', () { const value = AsyncValue.data(42); - // ignore: omit_local_variable_types, unused_local_variable, testing that assignment works, final AsyncData? data = value.asData; expect( @@ -1513,8 +1537,8 @@ void main() { null, ); expect( - () => const AsyncError('err', StackTrace.empty).value, - throwsA('err'), + const AsyncError('err', StackTrace.empty).value, + null, ); expect( @@ -1529,28 +1553,6 @@ void main() { ); }); - test('AsyncValue.valueOrNull', () { - expect(const AsyncValue.data(42).valueOrNull, 42); - expect( - const AsyncLoading().valueOrNull, - null, - ); - expect(const AsyncError('err', StackTrace.empty).valueOrNull, null); - - expect( - const AsyncError('err', StackTrace.empty) - .copyWithPrevious(const AsyncData(42)) - .valueOrNull, - 42, - ); - expect( - const AsyncLoading() - .copyWithPrevious(const AsyncData(42)) - .valueOrNull, - 42, - ); - }); - test('AsyncValue.guard emits the data when the created future completes', () async { await expectLater( @@ -1608,20 +1610,42 @@ void main() { throwsA(isA()), ); }); -} -class CustomLoading extends AsyncLoading { - const CustomLoading(); -} + test( + 'AsyncValue.guard emits the error when the created future fails and predicate is null', + () async { + final stack = StackTrace.current; -class CustomData extends AsyncData { - const CustomData(super.value); -} + await expectLater( + AsyncValue.guard(() => Future.error(42, stack)), + completion(AsyncError(42, stack)), + ); + }); + + test( + 'AsyncValue.guard emits the error when the created future fails and predicate is true', + () async { + final stack = StackTrace.current; + bool isInt(Object error) => error is int; -class CustomError extends AsyncError { - const CustomError(Object error, {required StackTrace stackTrace}) - : super( - error, - stackTrace, - ); + await expectLater( + AsyncValue.guard( + () => Future.error(42, stack), + isInt, + ), + completion(AsyncError(42, stack)), + ); + }); + + test('AsyncValue.guard rethrows exception if predicate is false,', () async { + bool isInt(Object error) => error is int; + + await expectLater( + AsyncValue.guard( + () => throw const FormatException(), + isInt, + ), + throwsA(isA()), + ); + }); } diff --git a/packages/riverpod/test/src/core/auto_dispose_test.dart b/packages/riverpod/test/src/core/auto_dispose_test.dart new file mode 100644 index 000000000..76f1df389 --- /dev/null +++ b/packages/riverpod/test/src/core/auto_dispose_test.dart @@ -0,0 +1,217 @@ +import 'package:mockito/mockito.dart'; +import 'package:riverpod/src/internals.dart'; +import 'package:test/test.dart'; + +import '../utils.dart'; +import 'provider_container_test.dart'; + +void main() { + group('AutoDispose', () { + test('Supports clearing the state of elements with only weak listeners', + () async { + final container = ProviderContainer.test(); + final onDispose = OnDisposeMock(); + var buildCount = 0; + final provider = Provider.autoDispose((ref) { + buildCount++; + ref.onDispose(onDispose.call); + return 0; + }); + + final element = container.readProviderElement(provider); + + final sub = container.listen( + provider, + weak: true, + (previous, value) {}, + ); + + expect(sub.read(), 0); + expect(buildCount, 1); + verifyZeroInteractions(onDispose); + + await container.pump(); + + verifyOnly(onDispose, onDispose()); + + expect(buildCount, 1); + expect(container.readProviderElement(provider), same(element)); + expect(element.stateResult, null); + + expect(sub.read(), 0); + expect(element.stateResult, ResultData(0)); + expect(buildCount, 2); + }); + + test( + 'onDispose is triggered only once if within autoDispose unmount, a dependency changed', + () async { + // regression test for https://github.com/rrousselGit/riverpod/issues/1064 + final container = ProviderContainer.test(); + final onDispose = OnDisposeMock(); + final dep = StateProvider((ref) => 0); + final provider = Provider.autoDispose((ref) { + ref.watch(dep); + ref.onDispose(onDispose.call); + }); + + when(onDispose()).thenAnswer((realInvocation) { + container.read(dep.notifier).state++; + }); + + container.read(provider); + verifyZeroInteractions(onDispose); + + // cause provider to be disposed + await container.pump(); + + verify(onDispose()).called(1); + verifyNoMoreInteractions(onDispose); + }); + + test('supports disposing of overridden families', () async { + // Regression test for https://github.com/rrousselGit/riverpod/issues/2480 + final provider = Provider.autoDispose.family( + (ref, _) => -1, + dependencies: const [], + ); + + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider.overrideWith((ref, arg) => 0)], + ); + + container.read(provider(0)); + + expect(container.pointerManager.readPointer(provider(0)), isNotNull); + expect(container.pointerManager.familyPointers[provider], isNotNull); + + await container.pump(); + + // The family pointer should still be there, as it comes from an override + expect(container.pointerManager.familyPointers[provider], isNotNull); + // The provider should've been disposed. + expect(container.pointerManager.readPointer(provider(0)), isNull); + }); + + test( + 'When a non-overridden autoDispose provider is disposed ' + 'and the associated ProviderContainer has a child ProviderContainer which overrides said provider, ' + 'the child container keeps its override', () async { +// Regression test for https://github.com/rrousselGit/riverpod/issues/1519 + + final provider = Provider.autoDispose( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final child = ProviderContainer.test( + parent: root, + overrides: [ + provider.overrideWith((ref) { + ref.keepAlive(); + return 42; + }), + ], + ); + + root.read(provider); + child.read(provider); + + await root.pump(); + + expect( + root.pointerManager.readPointer(provider), + isNull, + ); + + expect( + child.pointerManager.readPointer(provider), + isPointer(override: isNotNull, element: isNotNull), + ); + }); + + group('on unused providers', () { + test( + 'cleans up the pointers of a provider in the entire ProviderContainer tree', + () async { + final unrelated = Provider((_) => 42, dependencies: const []); + // Regression test for https://github.com/rrousselGit/riverpod/issues/1943 + final a = ProviderContainer.test(); + // b/c voluntarily do not use the Provider, but a/d do. This is to test + // that the disposal logic correctly cleans up the StateReaders + // in all ProviderContainers associated with the provider, even if + // some links between two ProviderContainers are not using the provider. + final b = ProviderContainer.test(parent: a, overrides: [unrelated]); + final c = ProviderContainer.test(parent: b, overrides: [unrelated]); + final d = ProviderContainer.test(parent: c, overrides: [unrelated]); + + final provider = Provider.autoDispose((ref) => 3); + + final subscription = d.listen( + provider, + (previous, next) {}, + fireImmediately: true, + ); + + expect(a.pointerManager.readPointer(provider), isNotNull); + expect(b.pointerManager.readPointer(provider), isNull); + expect(c.pointerManager.readPointer(provider), isNull); + expect(d.pointerManager.readPointer(provider), isNotNull); + + subscription.close(); + + expect(a.pointerManager.readPointer(provider), isNotNull); + expect(b.pointerManager.readPointer(provider), isNull); + expect(c.pointerManager.readPointer(provider), isNull); + expect(d.pointerManager.readPointer(provider), isNotNull); + + await a.pump(); + + expect(a.pointerManager.readPointer(provider), isNull); + expect(b.pointerManager.readPointer(provider), isNull); + expect(c.pointerManager.readPointer(provider), isNull); + expect(d.pointerManager.readPointer(provider), isNull); + + d.listen( + provider, + (previous, next) {}, + fireImmediately: true, + ); + + expect(a.pointerManager.readPointer(provider), isNotNull); + expect(b.pointerManager.readPointer(provider), isNull); + expect(c.pointerManager.readPointer(provider), isNull); + expect(d.pointerManager.readPointer(provider), isNotNull); + }); + + test( + 'if a dependency changed, the element is still disposed, ' + 'but without calling ref.onDispose again', () async { + final container = ProviderContainer.test(); + final onDispose = OnDisposeMock(); + final dep = StateProvider((ref) => 0); + final provider = Provider.autoDispose((ref) { + ref.onDispose(onDispose.call); + return ref.watch(dep); + }); + + container.read(provider); + verifyZeroInteractions(onDispose); + container.read(dep.notifier).state++; + + expect( + container.pointerManager.readPointer(provider), + isNotNull, + ); + + await container.pump(); + + verify(onDispose()).called(1); + + expect(container.pointerManager.readPointer(provider), isNull); + }); + }); + }); +} diff --git a/packages/riverpod/test/src/core/family_test.dart b/packages/riverpod/test/src/core/family_test.dart new file mode 100644 index 000000000..278f67e00 --- /dev/null +++ b/packages/riverpod/test/src/core/family_test.dart @@ -0,0 +1,75 @@ +import 'package:mockito/mockito.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:test/test.dart'; + +import '../matrix.dart'; +import '../utils.dart'; + +void main() { + group('ClassProvider', () { + notifierProviderFactory.createGroup((factory) { + test('overrideWithBuild', () { + final provider = factory.simpleTestProvider((ref, _) => 0); + final overrideWithBuild = + OverrideWithBuildMock, int, int>(-1); + + when(overrideWithBuild.call(any, any)).thenReturn(42); + + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithBuild(overrideWithBuild.call), + ], + ); + + verifyZeroInteractions(overrideWithBuild); + + container.read(provider); + + final [ref as Ref, notifier as TestNotifier] = verify( + overrideWithBuild.call(captureAny, captureAny), + ).captured; + + // ignore: invalid_use_of_protected_member + expect(ref, same(notifier.ref)); + + expect(notifier.state, 42); + }); + }); + }); + + group('ClassFamily', () { + notifierProviderFactory.createGroup((factory) { + if (!factory.isFamily) return; + + test('overrideWithBuild', () { + final provider = factory.simpleTestProvider((ref, _) => 0).from!; + provider as NotifierProviderFamily, int, + Object?>; + + final overrideWithBuild = + OverrideWithBuildMock, int, int>(-1); + + when(overrideWithBuild.call(any, any)).thenReturn(42); + + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithBuild(overrideWithBuild.call), + ], + ); + + verifyZeroInteractions(overrideWithBuild); + + container.read(provider(0)); + + final [ref as Ref, notifier as TestNotifier] = verify( + overrideWithBuild.call(captureAny, captureAny), + ).captured; + + // ignore: invalid_use_of_protected_member + expect(ref, same(notifier.ref)); + + expect(notifier.state, 42); + }); + }); + }); +} diff --git a/packages/riverpod/test/src/core/modifiers/future_test.dart b/packages/riverpod/test/src/core/modifiers/future_test.dart new file mode 100644 index 000000000..77e595c61 --- /dev/null +++ b/packages/riverpod/test/src/core/modifiers/future_test.dart @@ -0,0 +1,112 @@ +import 'package:mockito/mockito.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:test/test.dart'; + +import '../../utils.dart'; + +void main() { + group('provider.future', () { + group('handles listen(weak: true)', () { + test('closing the subscription updated element.weakDependents', () { + final container = ProviderContainer.test(); + final provider = FutureProvider((ref) => 0); + + final sub = container.listen( + provider.future, + weak: true, + (previous, value) {}, + ); + + expect( + container.readProviderElement(provider).weakDependents, + isNotEmpty, + ); + + sub.close(); + + expect( + container.readProviderElement(provider).weakDependents, + isEmpty, + ); + }); + + test( + 'calls mayNeedDispose in ProviderSubscription.read for the sake of listen(weak: true)', + () async { + final container = ProviderContainer.test(); + final onDispose = OnDisposeMock(); + final provider = FutureProvider.autoDispose((ref) { + ref.onDispose(onDispose.call); + return 0; + }); + + final element = container.readProviderElement(provider); + + final sub = container.listen( + provider.future, + weak: true, + (previous, value) {}, + ); + + expect(sub.read(), completion(0)); + verifyZeroInteractions(onDispose); + + await container.pump(); + + verifyOnly(onDispose, onDispose()); + }); + + test('common use-case ', () async { + var buildCount = 0; + final provider = FutureProvider((ref) { + buildCount++; + return 'Hello'; + }); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen( + provider.future, + listener.call, + weak: true, + ); + + verifyZeroInteractions(listener); + expect(buildCount, 0); + + container.read(provider); + + expect(buildCount, 1); + final [future as Future] = verifyOnly( + listener, + listener.call(argThat(isNull), captureAny), + ).captured; + expect(await future, 'Hello'); + }); + + test('calling `sub.read` on a weak listener will read the value', + () async { + final provider = FutureProvider((ref) => 'Hello'); + final container = ProviderContainer.test(); + final listener = Listener>(); + + final sub = container.listen( + provider.future, + listener.call, + weak: true, + ); + + verifyZeroInteractions(listener); + + expect(await sub.read(), 'Hello'); + + final [future as Future] = verifyOnly( + listener, + listener.call(argThat(isNull), captureAny), + ).captured; + + expect(await future, 'Hello'); + }); + }); + }); +} diff --git a/packages/riverpod/test/framework/select_async_test.dart b/packages/riverpod/test/src/core/modifiers/select_async_test.dart similarity index 83% rename from packages/riverpod/test/framework/select_async_test.dart rename to packages/riverpod/test/src/core/modifiers/select_async_test.dart index e984d2aca..9336d869b 100644 --- a/packages/riverpod/test/framework/select_async_test.dart +++ b/packages/riverpod/test/src/core/modifiers/select_async_test.dart @@ -1,22 +1,25 @@ import 'dart:async'; import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; import 'package:test/test.dart'; -import '../utils.dart'; +import '../../matrix.dart'; +import '../../utils.dart'; void main() { group('If disposed before a value could be emitted', () { test('resolves values with `sub.read()`', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final controller = StreamController(); final provider = Provider((ref) => ref); addTearDown(controller.close); final dep = StreamProvider((ref) => controller.stream); - final ref = container.read(provider); - final future = ref.watch(dep.selectAsync((data) => data * 2)); + container.listen(dep, (p, n) {}); + final ref = container.listen(provider, (a, b) {}); + final future = ref.read().watch(dep.selectAsync((data) => data * 2)); container.invalidate(provider); controller.add(21); @@ -25,14 +28,15 @@ void main() { }); test('resolves errors with `sub.read()`', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final controller = StreamController(); final provider = Provider((ref) => ref); addTearDown(controller.close); final dep = StreamProvider((ref) => controller.stream); - final ref = container.read(provider); - final future = ref.watch(dep.selectAsync((data) => data * 2)); + container.listen(dep, (p, n) {}); + final ref = container.listen(provider, (a, b) {}); + final future = ref.read().watch(dep.selectAsync((data) => data * 2)); container.invalidate(provider); controller.addError('err'); @@ -42,7 +46,7 @@ void main() { }); test('implements ProviderSubscription.read on AsyncData', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateProvider((ref) => 0); final provider = FutureProvider((ref) async => ref.watch(dep)); @@ -65,7 +69,7 @@ void main() { }); test('implements ProviderSubscription.read on AsyncError', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateProvider((ref) => 0); final provider = FutureProvider( (ref) => Future.error(ref.watch(dep)), @@ -91,7 +95,7 @@ void main() { }); test('when selector throws, returns a failing future', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final dep = StateProvider((ref) => 0); final provider = FutureProvider((ref) async => ref.watch(dep)); @@ -114,7 +118,7 @@ void main() { }); test('handles fireImmediately: true on AsyncLoading', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = FutureProvider((ref) async => 0); final listener = Listener>(); @@ -131,7 +135,7 @@ void main() { }); test('handles fireImmediately: true on AsyncData', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = FutureProvider((ref) => 0); final listener = Listener>(); @@ -148,7 +152,7 @@ void main() { }); test('handles fireImmediately: true on AsyncError', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = FutureProvider((ref) => throw StateError('0')); final listener = Listener>(); @@ -165,7 +169,7 @@ void main() { }); test('handles fireImmediately: false', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = FutureProvider((ref) async => 0); final listener = Listener>(); @@ -180,7 +184,7 @@ void main() { test( 'catching errors in the future is not necessary if the error is coming from AsyncError', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = FutureProvider((ref) => throw StateError('err')); container.listen( @@ -194,15 +198,15 @@ void main() { }); test('handles multiple AsyncLoading at once then data', () async { - final container = createContainer(); - late FutureProviderRef ref; - final provider = FutureProvider((r) { - ref = r; - final completer = Completer(); - ref.onDispose(() => completer.complete(84)); - - return completer.future; - }); + final container = ProviderContainer.test(); + final provider = AsyncNotifierProvider, int>( + () => DeferredAsyncNotifier((ref, self) { + final completer = Completer(); + ref.onDispose(() => completer.complete(84)); + + return completer.future; + }), + ); final sub = container.listen( provider.selectAsync((data) => data + 40), @@ -211,19 +215,19 @@ void main() { expect(sub.read(), completion(42)); - ref.state = const AsyncLoading() + final notifier = container.read(provider.notifier); + notifier.state = const AsyncLoading() .copyWithPrevious(const AsyncValue.data(0)); - ref.state = const AsyncLoading() + notifier.state = const AsyncLoading() .copyWithPrevious(const AsyncError('err', StackTrace.empty)); - ref.state = const AsyncLoading(); - - ref.state = const AsyncData(2); + notifier.state = const AsyncLoading(); + notifier.state = const AsyncData(2); // the previous unawaited `completion` should resolve with 2+40 }); test('can watch async selectors', () async { - final container = createContainer(); + final container = ProviderContainer.test(); var buildCount = 0; final dep = StateProvider((ref) => 0); final a = FutureProvider((ref) async => ref.watch(dep)); @@ -232,7 +236,9 @@ void main() { return ref.watch(a.selectAsync((value) => value % 10)); }); - expect(buildCount, 0); + container.listen(a, (p, n) {}); + container.listen(b, (p, n) {}); + expect(container.read(a), const AsyncLoading()); expect(container.read(b), const AsyncLoading()); expect(await container.read(b.future), 0); @@ -279,7 +285,7 @@ void main() { }); test('can watch async selectors (autoDispose)', () async { - final container = createContainer(); + final container = ProviderContainer.test(); var buildCount = 0; final dep = StateProvider((ref) => 0); final a = FutureProvider.autoDispose((ref) async => ref.watch(dep)); @@ -288,7 +294,9 @@ void main() { return ref.watch(a.selectAsync((value) => value % 10)); }); - expect(buildCount, 0); + container.listen(a, (p, n) {}); + container.listen(b, (p, n) {}); + expect(container.read(b), const AsyncLoading()); expect(container.read(b), const AsyncLoading()); expect(await container.read(b.future), 0); @@ -336,9 +344,11 @@ void main() { group('Supports ProviderContainer.read', () { test('and resolves with data', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = FutureProvider((ref) async => 0); + container.listen(provider, (p, n) {}); + expect( container.read(provider.selectAsync((data) => data.toString())), completion('0'), @@ -346,10 +356,12 @@ void main() { }); test('resolves with error', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = FutureProvider((ref) async => throw StateError('err')); + container.listen(provider, (p, n) {}); + expect( container.read(provider.selectAsync((data) => data)), throwsStateError, @@ -357,9 +369,11 @@ void main() { }); test('emits exceptions inside selectors as Future.error', () async { - final container = createContainer(); + final container = ProviderContainer.test(); final provider = FutureProvider((ref) async => 42); + container.listen(provider, (p, n) {}); + expect( container.read(provider.selectAsync((data) => throw StateError('err'))), throwsStateError, diff --git a/packages/riverpod/test/src/core/modifiers/select_test.dart b/packages/riverpod/test/src/core/modifiers/select_test.dart new file mode 100644 index 000000000..7d7118476 --- /dev/null +++ b/packages/riverpod/test/src/core/modifiers/select_test.dart @@ -0,0 +1,186 @@ +import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:test/test.dart'; + +import '../../utils.dart'; + +void main() { + group('provider.select', () { + test('handles when the selector throws', () { + final provider = Provider((ref) => Object()); + final container = ProviderContainer.test(); + + final errors = []; + container.read(provider); + final sub = container.listen( + provider.select((value) => throw StateError('Foo')), + (_, __) {}, + onError: (err, stack) => errors.add(err), + ); + + container.invalidate(provider); + + expect( + sub.read, + throwsA(isStateError.having((e) => e.message, 'message', 'Foo')), + ); + expect(errors, [isStateError.having((e) => e.message, 'message', 'Foo')]); + }); + + group('handles listen(weak: true)', () { + test( + 'supports calling ProviderSubscription.read when no value were emitted yet', + () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => 0); + + final sub = container.listen( + provider.select((value) => 42), + (previous, value) {}, + ); + + expect(sub.read(), 42); + }); + + test('closing the subscription updated element.hasListeners', () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => 0); + + final sub = container.listen( + provider.select((value) => 0), + weak: true, + (previous, value) {}, + ); + + expect( + container.readProviderElement(provider).weakDependents, + isNotEmpty, + ); + + sub.close(); + + expect( + container.readProviderElement(provider).weakDependents, + isEmpty, + ); + }); + + test( + 'calls mayNeedDispose in ProviderSubscription.read for the sake of listen(weak: true)', + () async { + final container = ProviderContainer.test(); + final onDispose = OnDisposeMock(); + final provider = Provider.autoDispose((ref) { + ref.onDispose(onDispose.call); + return 0; + }); + + final element = container.readProviderElement(provider); + + final sub = container.listen( + provider.select((value) => value), + weak: true, + (previous, value) {}, + ); + + expect(sub.read(), 0); + verifyZeroInteractions(onDispose); + + await container.pump(); + + verifyOnly(onDispose, onDispose()); + }); + + test('common use-case ', () { + final provider = StateProvider((ref) => 'Hello'); + final container = ProviderContainer.test(); + final listener = Listener(); + + container.listen( + provider.select((value) => value[0]), + listener.call, + weak: true, + ); + + verifyZeroInteractions(listener); + + container.read(provider); + + verifyOnly(listener, listener.call(null, 'H')); + + container.read(provider.notifier).state = 'World'; + + verifyOnly(listener, listener.call('H', 'W')); + + container.read(provider.notifier).state = 'Will not notify'; + + verifyNoMoreInteractions(listener); + }); + + test('calling `sub.read` on a weak listener will select the value', () { + final provider = StateProvider((ref) => 'Hello'); + final container = ProviderContainer.test(); + final listener = Listener(); + + final sub = container.listen( + provider.select((value) => value[0]), + listener.call, + weak: true, + ); + + verifyZeroInteractions(listener); + + expect(sub.read(), 'H'); + + verifyOnly(listener, listener.call(null, 'H')); + }); + + test( + 'after calling `sub.read`, notifications correctly compare the previous and new value ' + 'instead of considering that "previous" is missing.', () { + final provider = StateProvider((ref) => 'Hello'); + final container = ProviderContainer.test(); + final listener = Listener(); + + final sub = container.listen( + provider.select((value) => value[0]), + listener.call, + weak: true, + ); + + sub.read(); + + verifyOnly(listener, listener.call(null, 'H')); + + container.read(provider.notifier).state = 'Hi'; + + verifyNoMoreInteractions(listener); + }); + }); + }); + + group('_ProviderSelector', () { + test('handles pause/resume', () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => 0); + + final element = container.readProviderElement(provider); + + final sub = container.listen( + provider.select((value) => null), + (previous, next) {}, + ); + + expect(element.isActive, true); + + sub.pause(); + + expect(element.isActive, false); + + sub.resume(); + + expect(element.isActive, true); + }); + }); +} diff --git a/packages/riverpod/test/src/core/overrides_test.dart b/packages/riverpod/test/src/core/overrides_test.dart new file mode 100644 index 000000000..6b59579b5 --- /dev/null +++ b/packages/riverpod/test/src/core/overrides_test.dart @@ -0,0 +1,107 @@ +import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/framework.dart'; +import 'package:test/test.dart'; + +import '../../old/utils.dart'; + +void main() { + group('ProviderOverride', () { + test('TransitiveOverride.toString', () { + final provider = Provider((_) => 42); + + expect( + TransitiveProviderOverride(provider).toString(), + equalsIgnoringHashCodes('Provider#00000'), + ); + }); + + group('overrideWith', () { + test('toString', () { + final namelessProvider = Provider((_) => 42); + final namelessFamily = Provider.family((_, __) => 42); + final provider = Provider((_) => 42, name: 'myName'); + final family = Provider.family((_, __) => 42, name: 'myName'); + + expect( + namelessProvider.overrideWith((ref) => 42).toString(), + equalsIgnoringHashCodes('Provider#00000.overrideWith(...)'), + ); + expect( + namelessFamily(42).overrideWith((ref) => 42).toString(), + equalsIgnoringHashCodes('Provider#00000(42).overrideWith(...)'), + ); + + expect( + provider.overrideWith((ref) => 42).toString(), + 'myName.overrideWith(...)', + ); + expect( + family(42).overrideWith((ref) => 42).toString(), + 'myName(42).overrideWith(...)', + ); + }); + }); + + group('overrideWithValue', () { + test('toString', () { + final namelessProvider = Provider((_) => 42); + final namelessFamily = Provider.family((_, __) => 42); + final provider = Provider((_) => 42, name: 'myName'); + final family = Provider.family((_, __) => 42, name: 'myName'); + + expect( + namelessProvider.overrideWithValue(21).toString(), + equalsIgnoringHashCodes('Provider#00000.overrideWithValue(21)'), + ); + expect( + namelessFamily(42).overrideWithValue(21).toString(), + equalsIgnoringHashCodes( + 'Provider#00000(42).overrideWithValue(21)', + ), + ); + + expect( + provider.overrideWithValue(21).toString(), + 'myName.overrideWithValue(21)', + ); + expect( + family(42).overrideWithValue(21).toString(), + 'myName(42).overrideWithValue(21)', + ); + }); + }); + }); + + group('FamilyOverride', () { + test('TransitiveOverride.toString', () { + const f = Provider.family; + + final provider = Provider.family((_, b) => 42); + final provider2 = f.call((_, b) => 42); + + expect( + TransitiveFamilyOverride(provider2).toString(), + equalsIgnoringHashCodes('ProviderFamily#00000'), + ); + }); + + group('overrideWith', () { + test('toString', () { + final namelessFamily = Provider.family((_, __) => 42); + final family = Provider.family((_, __) => 42, name: 'myName'); + + expect( + namelessFamily.overrideWith((ref, arg) => 42).toString(), + equalsIgnoringHashCodes( + 'ProviderFamily#00000.overrideWith(...)', + ), + ); + + expect( + family.overrideWith((ref, _) => 42).toString(), + 'myName.overrideWith(...)', + ); + }); + }); + }); +} diff --git a/packages/riverpod/test/src/core/provider_container_test.dart b/packages/riverpod/test/src/core/provider_container_test.dart new file mode 100644 index 000000000..ea4c0c8dd --- /dev/null +++ b/packages/riverpod/test/src/core/provider_container_test.dart @@ -0,0 +1,3049 @@ +import 'dart:async'; + +import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/framework.dart'; +import 'package:test/test.dart'; + +import '../utils.dart'; + +const _sentinel = Object(); + +TypeMatcher<$ProviderPointer> isPointer({ + Object? override = _sentinel, + Object? element = _sentinel, + Object? targetContainer = _sentinel, +}) { + var matcher = isA<$ProviderPointer>(); + + if (override != _sentinel) { + matcher = matcher.having((p) => p.providerOverride, 'override', override); + } + + if (element != _sentinel) { + matcher = matcher.having((p) => p.element, 'element', element); + } + + if (targetContainer != _sentinel) { + matcher = matcher.having( + (p) => p.targetContainer, + 'targetContainer', + targetContainer, + ); + } + + return matcher; +} + +TypeMatcher isTransitiveFamilyOverride( + Object? family, +) { + return isA().having((f) => f.from, 'from', family); +} + +TypeMatcher isTransitiveProviderOverride([ + Object? provider = _sentinel, +]) { + var matcher = isA(); + + if (provider != _sentinel) { + matcher = matcher.having((f) => f.origin, 'origin', provider); + } + + return matcher; +} + +TypeMatcher isProviderDirectory({ + Object? override = _sentinel, + Object? pointers = _sentinel, + Object? targetContainer = _sentinel, +}) { + var matcher = isA(); + + if (override != _sentinel) { + matcher = matcher.having((p) => p.familyOverride, 'override', override); + } + + if (pointers != _sentinel) { + matcher = matcher.having((p) => p.pointers, 'pointers', pointers); + } + + if (targetContainer != _sentinel) { + matcher = matcher.having( + (p) => p.targetContainer, + 'targetContainer', + targetContainer, + ); + } + + return matcher; +} + +void main() { + group('ProviderPointerManager', () { + group('findDeepestTransitiveDependencyProviderContainer', () { + final transitiveDependency = Provider( + (_) => 0, + dependencies: const [], + ); + final dependency = Provider( + (_) => 0, + dependencies: [transitiveDependency], + ); + + final a = Provider( + (_) => 0, + dependencies: [dependency], + ); + + test('always returns null if has no dependency', () { + final provider = Provider((_) => 0); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + // Unrelated override, to avoid the container optimizing the pointer away + Provider((ref) => null, dependencies: const []), + ], + ); + + expect( + container.pointerManager + .findDeepestTransitiveDependencyProviderContainer(provider), + null, + ); + }); + + test('returns null if the dependency is overridden in the root container', + () { + final root = ProviderContainer.test( + overrides: [ + dependency.overrideWithValue(42), + ], + ); + + expect( + root.pointerManager + .findDeepestTransitiveDependencyProviderContainer(a), + null, + ); + }); + + test( + "for direct dependencies, returns the dependency's container if overridden", + () { + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + dependency.overrideWithValue(42), + ], + ); + final leaf = ProviderContainer.test( + parent: container, + overrides: [ + // Unrelated override, to avoid the container optimizing the pointer away + Provider((ref) => null, dependencies: const []), + ], + ); + + expect( + leaf.pointerManager + .findDeepestTransitiveDependencyProviderContainer(a), + container, + ); + }); + + test( + "for transitive dependencies, returns the dependency's container if overridden", + () { + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + transitiveDependency.overrideWithValue(42), + ], + ); + final leaf = ProviderContainer.test( + parent: container, + overrides: [ + // Unrelated override, to avoid the container optimizing the pointer away + Provider((ref) => null, dependencies: const []), + ], + ); + + expect( + leaf.pointerManager + .findDeepestTransitiveDependencyProviderContainer(a), + container, + ); + }); + + test( + 'if multiple dependencies are overridden, returns the deepest container', + () { + final dep2 = Provider( + (_) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + dependency.overrideWithValue(42), + ], + ); + final container2 = ProviderContainer.test( + parent: container, + overrides: [ + dep2.overrideWithValue(42), + ], + ); + final leaf = ProviderContainer.test( + parent: container2, + overrides: [ + // Unrelated override, to avoid the container optimizing the pointer away + Provider((ref) => null, dependencies: const []), + ], + ); + + final b = Provider( + (_) => 0, + dependencies: [dep2], + ); + + expect( + leaf.pointerManager + .findDeepestTransitiveDependencyProviderContainer(a), + // Does not care about dep2, so points to 'container' + container, + ); + + expect( + leaf.pointerManager + .findDeepestTransitiveDependencyProviderContainer(b), + container2, + ); + }); + }); + + group('upsertDirectory', () { + test('handles auto-scoping', () { + final dep = Provider( + (_) => 0, + dependencies: const [], + ); + final family = Provider.family( + (ref, id) => 0, + dependencies: [dep], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [dep.overrideWithValue(42)], + ); + + final directory = container.pointerManager.upsertDirectory(family(42)); + + expect( + directory, + isProviderDirectory( + targetContainer: container, + override: isTransitiveFamilyOverride(family), + pointers: isEmpty, + ), + ); + + expect( + container.pointerManager.familyPointers, + {family: directory}, + ); + + // Check that the root was unaffected + expect( + root.pointerManager.familyPointers, + isEmpty, + ); + expect( + root.pointerManager.orphanPointers.pointers, + isEmpty, + ); + }); + + test('on orphans, return orphanPointers', () { + final provider = Provider((_) => 0); + final container = ProviderContainer.test(); + + final directory = container.pointerManager.upsertDirectory(provider); + + expect(container.pointerManager.orphanPointers, directory); + + expect( + directory, + isProviderDirectory( + targetContainer: container, + override: null, + pointers: isEmpty, + ), + ); + }); + + test('on families, adds a new directory if not already present', () { + final provider = Provider.family((ref, _) => 0); + final container = ProviderContainer.test(); + + final directory = container.pointerManager.upsertDirectory( + provider(42), + ); + + expect( + container.pointerManager.familyPointers, + {provider: directory}, + ); + + expect( + directory, + isProviderDirectory( + targetContainer: container, + override: null, + pointers: isEmpty, + ), + ); + }); + + test('returns existing directory if called twice', () { + final provider = Provider.family((ref, _) => 0); + final container = ProviderContainer.test(); + + final directory = + container.pointerManager.upsertDirectory(provider(42)); + final directory2 = + container.pointerManager.upsertDirectory(provider(42)); + + expect(directory2, same(directory)); + }); + + test('returns override directory if present', () { + final provider = Provider.family((ref, _) => 0); + + final container = ProviderContainer.test( + overrides: [provider.overrideWith((ref, arg) => 0)], + ); + + final overrideDir = container.pointerManager.familyPointers[provider]!; + + final directory = container.pointerManager.upsertDirectory( + provider(42), + ); + + expect(directory, same(overrideDir)); + + expect( + directory, + isProviderDirectory( + targetContainer: container, + override: isNotNull, + pointers: isEmpty, + ), + ); + }); + }); + + group('upsertPointer', () { + test('on scoped providers, has no impact on the ancestor container', () { + final provider = Provider((_) => 0, dependencies: const []); + final family = Provider.family( + (ref, id) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider, family], + ); + + container.pointerManager.upsertPointer(provider); + container.pointerManager.upsertPointer(family(42)); + + expect(root.pointerManager.familyPointers, isEmpty); + expect(root.pointerManager.orphanPointers.pointers, isEmpty); + + expect( + container.pointerManager.orphanPointers.pointers, + { + provider: isPointer( + targetContainer: container, + override: provider, + ), + }, + ); + expect( + container.pointerManager.familyPointers, + { + family: isProviderDirectory( + targetContainer: container, + override: family, + pointers: {family(42): isPointer(targetContainer: container)}, + ), + }, + ); + }); + + group('auto-scoping', () { + test('handles auto-scoping', () { + final dep = Provider( + (_) => 0, + dependencies: const [], + ); + final family = Provider.family( + (ref, id) => 0, + dependencies: [dep], + ); + final provider = Provider((_) => 0, dependencies: [dep]); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [dep.overrideWithValue(42)], + ); + + final pointer = container.pointerManager.upsertPointer(family(42)); + final pointer2 = container.pointerManager.upsertPointer(provider); + + expect( + pointer, + isPointer( + targetContainer: container, + override: null, + ), + ); + expect( + pointer2, + isPointer( + targetContainer: container, + override: isTransitiveProviderOverride(provider), + ), + ); + + expect( + container.pointerManager.familyPointers, + { + family: isProviderDirectory( + targetContainer: container, + override: isTransitiveFamilyOverride(family), + pointers: { + family(42): pointer, + }, + ), + }, + ); + expect( + container.pointerManager.orphanPointers, + isProviderDirectory( + targetContainer: root, + override: null, + pointers: { + provider: pointer2, + dep: isNotNull, + }, + ), + ); + + // Check that the root was unaffected + expect( + root.pointerManager.familyPointers, + isEmpty, + ); + expect( + root.pointerManager.orphanPointers.pointers, + isEmpty, + ); + }); + + test('skips auto-scoping if the provider is manually overridden', () { + final dep = Provider( + (_) => 0, + dependencies: const [], + ); + final family = Provider.family( + (ref, id) => 0, + dependencies: [dep], + ); + final familyOverride = family.overrideWith((ref, arg) => 0); + final provider = Provider((_) => 0, dependencies: [dep]); + final providerOverride = provider.overrideWithValue(42); + + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( + parent: root, + overrides: [familyOverride, providerOverride], + ); + final container = ProviderContainer.test( + parent: mid, + overrides: [dep.overrideWithValue(42)], + ); + + container.pointerManager.upsertPointer(family(42)); + container.pointerManager.upsertPointer(provider); + + expect( + container.pointerManager.familyPointers, + { + family: isProviderDirectory( + targetContainer: mid, + override: familyOverride, + pointers: { + family(42): isPointer( + targetContainer: mid, + override: null, + ), + }, + ), + }, + ); + expect( + container.pointerManager.orphanPointers, + isProviderDirectory( + targetContainer: root, + override: null, + pointers: { + provider: isPointer( + targetContainer: mid, + override: providerOverride, + ), + dep: isNotNull, + }, + ), + ); + }); + + test('auto-scoping inserts at the correct container', () { + final dep = Provider((_) => 0, dependencies: const [], name: 'dep'); + final dep2 = Provider((_) => 0, dependencies: const [], name: 'dep2'); + + final a = Provider((ref) => 0, dependencies: [dep], name: 'a'); + final b = Provider((ref) => 0, dependencies: [dep2], name: 'b'); + final c = Provider.family( + (ref, id) => 0, + dependencies: [dep], + name: 'c', + ); + final d = Provider.family( + (ref, id) => 0, + dependencies: [dep2], + name: 'd', + ); + + final root = ProviderContainer.test(); + final mid = ProviderContainer.test(parent: root, overrides: [dep]); + final mid2 = ProviderContainer.test(parent: mid, overrides: [dep2]); + final leaf = ProviderContainer.test( + parent: mid2, + overrides: [ + // Disable scoping optimization + Provider((ref) => null, dependencies: const []), + ], + ); + + leaf.pointerManager.upsertPointer(a); + leaf.pointerManager.upsertPointer(b); + leaf.pointerManager.upsertPointer(c(0)); + leaf.pointerManager.upsertPointer(d(0)); + + expect( + leaf.pointerManager.orphanPointers, + isProviderDirectory( + targetContainer: root, + override: null, + pointers: allOf( + containsPair( + a, + isPointer( + targetContainer: mid, + override: isTransitiveProviderOverride(a), + ), + ), + containsPair( + b, + isPointer( + targetContainer: mid2, + override: isTransitiveProviderOverride(b), + ), + ), + ), + ), + ); + + expect( + leaf.pointerManager.familyPointers, + { + c: isProviderDirectory( + targetContainer: mid, + override: isTransitiveFamilyOverride(c), + pointers: { + c(0): isPointer(targetContainer: mid, override: null), + }, + ), + d: isProviderDirectory( + targetContainer: mid2, + override: isTransitiveFamilyOverride(d), + pointers: { + d(0): isPointer(targetContainer: mid2, override: null), + }, + ), + }, + ); + }); + + test('when overriding a family provider', () { + final a = Provider.family.autoDispose( + (ref, value) => 'root $value', + dependencies: [], + name: 'a', + ); + + final b = Provider.family.autoDispose( + (ref, value) => ref.watch(a(value)), + dependencies: [a], + name: 'b', + ); + + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [a('42').overrideWith((ref) => 'override 42')], + ); + + expect(container.read(b('42')), 'override 42'); + }); + + test('when overriding both a family and one provider from said family', + () { + final a = Provider.family.autoDispose( + (ref, value) => 'root $value', + dependencies: [], + name: 'a', + ); + final b = Provider.family.autoDispose( + (ref, value) => ref.watch(a(value)), + dependencies: [a], + name: 'b', + ); + + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( + parent: root, + overrides: [a.overrideWith((ref, _) => 'mid')], + ); + final container = ProviderContainer.test( + parent: mid, + overrides: [a('42').overrideWith((ref) => 'override 42')], + ); + + final mid2 = ProviderContainer.test( + parent: root, + overrides: [a('42').overrideWith((ref) => 'mid')], + ); + final container2 = ProviderContainer.test( + parent: mid2, + overrides: [a.overrideWith((ref, value) => 'override $value')], + ); + + expect(container.read(b('42')), 'override 42'); + expect(container2.read(b('21')), 'override 21'); + }); + }); + + test( + 'have ancestors and children share their pointers when not overridden', + () { + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + // An unrelated override, added to avoid the container optimizing + Provider((_) => 0, dependencies: const []), + ], + ); + final provider = Provider((ref) => 0); + final family = Provider.family((ref, id) => 0); + + root.read(provider); + root.read(family(42)); + container.read(provider); + container.read(family(42)); + + expect( + container.pointerManager.orphanPointers.pointers[provider], + same(root.pointerManager.orphanPointers.pointers[provider]), + ); + + expect( + container.pointerManager.familyPointers[family]!.pointers, + { + family(42): same( + root.pointerManager.familyPointers[family]!.pointers[family(42)], + ), + }, + ); + }); + + test('on orphans, insert in orphanPointers', () { + final provider = Provider((_) => 0); + final container = ProviderContainer.test(); + + final pointer = container.pointerManager.upsertPointer(provider); + + expect( + container.pointerManager.orphanPointers.pointers, + {provider: pointer}, + ); + + expect( + pointer, + isPointer(targetContainer: container, override: null), + ); + }); + + test('on families, adds a new pointer if not already present', () { + final provider = Provider.family((ref, _) => 0); + final container = ProviderContainer.test(); + + final pointer = container.pointerManager.upsertPointer( + provider(42), + ); + + expect( + container.pointerManager.familyPointers[provider]!.pointers, + {provider(42): pointer}, + ); + + expect( + pointer, + isPointer( + targetContainer: container, + override: null, + element: isNotNull, + ), + ); + }); + }); + + group('remove', () { + test('if called on a provider that is not mounted, is no-op', () {}); + + test('removes non-family providers from orphan list', () { + final provider = Provider((_) => 0); + final container = ProviderContainer.test(); + + final pointer = container.pointerManager.upsertPointer(provider); + + expect( + container.pointerManager.orphanPointers.pointers, + {provider: isPointer()}, + ); + + final removed = container.pointerManager.remove(provider); + + expect(removed, pointer); + expect( + container.pointerManager.orphanPointers.pointers, + isEmpty, + ); + }); + + test('removes family providers from family list', () { + final family = Provider.family((ref, _) => 0); + final container = ProviderContainer.test(); + + final pointer = container.pointerManager.upsertPointer(family(42)); + // Mounting two values to avoid testing for empty families + container.pointerManager.upsertPointer(family(21)); + + expect( + container.pointerManager.familyPointers[family]!.pointers, + {family(42): isPointer(), family(21): isPointer()}, + ); + + final removed = container.pointerManager.remove(family(42)); + + expect(removed, pointer); + expect( + container.pointerManager.familyPointers[family]!.pointers, + {family(21): isPointer()}, + ); + }); + + test('if a family becomes empty after a remove, remove the directory', + () { + final family = Provider.family((ref, _) => 0); + final container = ProviderContainer.test(); + + final pointer = container.pointerManager.upsertPointer(family(42)); + + expect( + container.pointerManager.familyPointers, + {family: isProviderDirectory()}, + ); + + final removed = container.pointerManager.remove(family(42)); + + expect(removed, pointer); + expect( + container.pointerManager.familyPointers, + isEmpty, + ); + }); + + test('if a family is not empty after a remove, keep the directory', () { + final family = Provider.family((ref, _) => 0); + final container = ProviderContainer.test(); + + final pointer = container.pointerManager.upsertPointer(family(42)); + container.pointerManager.upsertPointer(family(21)); + + expect( + container.pointerManager.familyPointers, + { + family: isProviderDirectory( + pointers: { + family(42): isPointer(), + family(21): isPointer(), + }, + ), + }, + ); + + final removed = container.pointerManager.remove(family(42)); + + expect(removed, pointer); + expect( + container.pointerManager.familyPointers, + { + family: isProviderDirectory(pointers: {family(21): isPointer()}), + }, + ); + }); + + test('if an orphan provider is from an override, keep the pointer', () { + final provider = Provider((_) => 0); + final override = provider.overrideWithValue(42); + final container = ProviderContainer.test( + overrides: [override], + ); + + final pointer = container.pointerManager.upsertPointer(provider); + + expect( + container.pointerManager.orphanPointers.pointers, + {provider: isPointer(override: override)}, + ); + + final removed = container.pointerManager.remove(provider); + + expect(removed, pointer); + expect( + container.pointerManager.orphanPointers.pointers, + {provider: isPointer(override: override)}, + ); + }); + + test('if a family provider is from a manual override, keep the pointer', + () { + final family = Provider.family((ref, _) => 0); + final override = family(21).overrideWith((ref) => 42); + final container = ProviderContainer.test( + overrides: [override], + ); + + final pointer = container.pointerManager.upsertPointer(family(21)); + + expect( + container.pointerManager.familyPointers[family]!.pointers, + {family(21): isPointer(override: override)}, + ); + + final removed = container.pointerManager.remove(family(21)); + + expect(removed, pointer); + expect( + container.pointerManager.familyPointers, + { + family: isProviderDirectory( + pointers: {family(21): isPointer(override: override)}, + ), + }, + ); + }); + + test( + 'if a family becomes empty after a remove but is from a manual override, ' + 'keep the directory', () { + final family = Provider.family((ref, _) => 0); + final override = family.overrideWith((ref, _) => 42); + final container = ProviderContainer.test( + overrides: [override], + ); + + final pointer = container.pointerManager.upsertPointer(family(21)); + + expect( + container.pointerManager.familyPointers, + { + family: isProviderDirectory( + override: override, + pointers: {family(21): isPointer()}, + ), + }, + ); + + final removed = container.pointerManager.remove(family(21)); + + expect(removed, pointer); + expect( + container.pointerManager.familyPointers, + { + family: isProviderDirectory( + override: override, + pointers: isEmpty, + ), + }, + ); + }); + + test( + 'if an orphan is from a transitive override, ' + 'removes the pointer', () { + final dep = Provider( + (_) => 0, + dependencies: const [], + ); + final provider = Provider((ref) => 0, dependencies: [dep]); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [dep], + ); + + final pointer = container.pointerManager.upsertPointer(provider); + + expect( + container.pointerManager.orphanPointers.pointers, + { + dep: isPointer(override: dep), + provider: isPointer( + override: isTransitiveProviderOverride(provider), + ), + }, + ); + + final removed = container.pointerManager.remove(provider); + + expect(removed, pointer); + expect( + container.pointerManager.orphanPointers.pointers, + {dep: isPointer(override: dep)}, + ); + }); + + test( + 'if a family is from a transitive override and becomes empty, ' + 'remove the directory', () { + final dep = Provider( + (_) => 0, + dependencies: const [], + ); + final family = Provider.family( + (ref, _) => 0, + dependencies: [dep], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [dep], + ); + + final pointer = container.pointerManager.upsertPointer(family(42)); + + expect( + container.pointerManager.familyPointers, + { + family: isProviderDirectory( + override: isTransitiveFamilyOverride(family), + pointers: {family(42): isPointer()}, + ), + }, + ); + + final removed = container.pointerManager.remove(family(42)); + + expect(removed, pointer); + expect( + container.pointerManager.familyPointers, + isEmpty, + ); + }); + }); + }); + + group('ProviderContainer', () { + group('constructor', () { + test('throws if trying to scope a provider/family with no dependencies', + () { + final provider = Provider((_) => 0); + final family = Provider.family((ref, _) => 0); + + final root = ProviderContainer.test(); + + expect( + () => ProviderContainer.test( + parent: root, + overrides: [provider], + ), + throwsA(isA()), + ); + + expect( + () => ProviderContainer.test( + parent: root, + overrides: [family], + ), + throwsA(isA()), + ); + + expect( + () => ProviderContainer.test( + parent: root, + overrides: [family(21)], + ), + throwsA(isA()), + ); + }); + + test('throws if "parent" is disposed', () { + final root = ProviderContainer(); + root.dispose(); + + expect( + () => ProviderContainer(parent: root), + throwsStateError, + ); + + expect( + root.children, + isEmpty, + reason: 'Invalid containers should not be added as children', + ); + }); + + test('if parent is null, assign "root" to "null"', () { + final container = ProviderContainer(); + addTearDown(container.dispose); + + expect(container.root, null); + }); + + test('if parent is not null, assign "root" to "parent.root"', () { + final root = ProviderContainer(); + addTearDown(root.dispose); + final container = ProviderContainer(parent: root); + addTearDown(container.dispose); + + expect(container.root, root); + }); + + test('assign "parent" to "this.parent"', () { + final root = ProviderContainer(); + addTearDown(root.dispose); + final container = ProviderContainer(parent: root); + addTearDown(container.dispose); + + expect(container.parent, root); + }); + + test('Adds "this" to "root.children"', () { + final root = ProviderContainer(); + addTearDown(root.dispose); + final container = ProviderContainer(parent: root); + addTearDown(container.dispose); + + expect(root.children, [container]); + }); + + group('overrides', () { + test( + 'throws if the same provider is overridden twice in the same container', + () { + final provider = Provider((ref) => 0); + + expect( + () => ProviderContainer.test( + overrides: [ + provider.overrideWithValue(42), + provider.overrideWithValue(21), + ], + ), + throwsA(isA()), + ); + }); + + test( + 'throws if the same family is overridden twice in the same container', + () { + final provider = Provider.family((ref, id) => 0); + + expect( + () => ProviderContainer.test( + overrides: [ + provider.overrideWith((ref, arg) => arg), + provider.overrideWith((ref, arg) => arg), + ], + ), + throwsA(isA()), + ); + }); + + test( + 'supports overriding an already overridden provider/family in a different container', + () { + final provider = Provider( + (ref) => 0, + dependencies: const [], + ); + final family = Provider.family( + (ref, id) => 0, + dependencies: const [], + ); + final root = ProviderContainer( + overrides: [ + provider.overrideWithValue(42), + family.overrideWith((ref, arg) => arg), + ], + ); + addTearDown(root.dispose); + + final container = ProviderContainer( + parent: root, + overrides: [ + provider.overrideWithValue(21), + family.overrideWith((ref, arg) => arg * 2), + ], + ); + addTearDown(container.dispose); + }); + + test( + 'supports overriding a provider from a family, and then the family', + () { + final family = Provider.family((ref, id) => 0); + final root = ProviderContainer( + overrides: [ + family(42).overrideWithValue(42), + family.overrideWith((ref, arg) => arg), + ], + ); + addTearDown(root.dispose); + }); + }); + }); + + group('retry', () { + test('inherits retry from parent if arg is null', () { + Duration? rootRetry(int count, Object error) => Duration.zero; + Duration? subRetry(int count, Object error) => Duration.zero; + + final root = ProviderContainer.test(retry: rootRetry); + final container = ProviderContainer.test(parent: root); + final container2 = ProviderContainer.test( + parent: root, + retry: subRetry, + ); + + expect(container.retry, root.retry); + expect(container2.retry, subRetry); + }); + }); + + test( + 'Reading a provider with deps does not mount those deps if unused by the provider', + () { + final dep = Provider((_) => 0); + final provider = Provider((ref) => 0, dependencies: [dep]); + + final container = ProviderContainer.test(); + + container.read(provider); + + expect( + container.pointerManager + .listProviderPointers() + .map((e) => e.element?.origin), + [provider], + ); + }); + + group('pointers', () { + test('has "container" pointing to "this"', () { + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + // An unrelated override, added to avoid the container optimizing + Provider((_) => 0, dependencies: const []), + ], + ); + + expect(root.pointerManager.container, root); + expect(container.pointerManager.container, container); + }); + + group('at the root, ', () { + test('orphansPointers.container points to the root', () { + final root = ProviderContainer.test(); + + expect(root.pointerManager.orphanPointers.targetContainer, root); + }); + }); + + group('on scoped containers', () { + test( + 'Inheriting a transitively overridden family which contains family(arg) overrides ' + 'preserves the family(arg) overrides.', () { + final dep = Provider((_) => 0, dependencies: const []); + final provider = Provider.family( + (ref, id) => 'root ${ref.watch(dep)}', + dependencies: [dep], + ); + + final root = ProviderContainer.test( + overrides: [ + provider(42).overrideWith((ref) { + return 'override ${ref.watch(dep)}'; + }), + ], + ); + + final container = ProviderContainer.test( + parent: root, + overrides: [dep.overrideWithValue(42)], + ); + + expect( + container.read(provider(42)), + 'override 0', + reason: 'provider(42) is manually overridden, ' + 'so this disables auto-scoping', + ); + expect(container.read(provider(21)), 'root 42'); + expect(root.read(provider(21)), 'root 0'); + expect(root.read(provider(42)), 'override 0'); + }); + + test('does not inherit transitive overrides', () { + final unrelated = Provider((_) => 0, dependencies: const []); + final dep = Provider( + (_) => 0, + dependencies: const [], + name: 'dep', + ); + final provider = Provider((_) => 0, dependencies: [dep], name: 'a'); + final family = Provider.family( + (_, d) => 0, + dependencies: [dep], + name: 'b', + ); + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( + parent: root, + overrides: [dep], + ); + + mid.pointerManager.upsertPointer(provider); + mid.pointerManager.upsertPointer(family(42)); + + final container = ProviderContainer.test( + parent: mid, + overrides: [ + // An unrelated override, added to avoid the container optimizing + unrelated, + ], + ); + + expect( + container.pointerManager.orphanPointers.pointers, + { + dep: isPointer(targetContainer: mid, override: dep), + unrelated: isPointer(), + }, + ); + expect( + container.pointerManager.familyPointers, + isEmpty, + ); + }); + + test('inherits overrides from its parents', () { + final a = Provider((_) => 0, name: 'a'); + final aOverride = a.overrideWithValue(1); + final b = Provider( + (_) => 0, + name: 'b', + dependencies: const [], + ); + final bOverride = b.overrideWithValue(2); + final c = Provider( + (_) => 0, + name: 'c', + dependencies: const [], + ); + final cOverride = c.overrideWithValue(3); + final aFamily = + Provider.family((_, __) => 0, name: 'aFamily'); + final aFamilyOverride = aFamily.overrideWith((_, __) => 1); + final aValueOverride = aFamily(1).overrideWith((_) => 2); + final bFamily = Provider.family( + (_, __) => 0, + name: 'bFamily', + dependencies: const [], + ); + final bFamilyOverride = bFamily.overrideWith((_, __) => 2); + final bValueOverride = bFamily(2).overrideWith((_) => 3); + final cFamily = Provider.family( + (_, __) => 0, + name: 'cFamily', + dependencies: const [], + ); + final cFamilyOverride = cFamily.overrideWith((_, __) => 3); + final cValueOverride = cFamily(3).overrideWith((_) => 4); + + final root = ProviderContainer.test( + overrides: [aOverride, aFamilyOverride, aValueOverride], + ); + final mid = ProviderContainer.test( + parent: root, + overrides: [bOverride, bFamilyOverride, bValueOverride], + ); + final container = ProviderContainer.test( + parent: mid, + overrides: [cOverride, cFamilyOverride, cValueOverride], + ); + + expect( + container.pointerManager.familyPointers, + { + aFamily: isProviderDirectory( + override: aFamilyOverride, + targetContainer: root, + pointers: { + aFamily(1): isPointer( + override: aValueOverride, + targetContainer: root, + element: null, + ), + }, + ), + bFamily: isProviderDirectory( + override: bFamilyOverride, + targetContainer: mid, + pointers: { + bFamily(2): isPointer( + override: bValueOverride, + targetContainer: mid, + element: null, + ), + }, + ), + cFamily: isProviderDirectory( + override: cFamilyOverride, + targetContainer: container, + pointers: { + cFamily(3): isPointer( + override: cValueOverride, + targetContainer: container, + element: null, + ), + }, + ), + }, + ); + + expect( + container.pointerManager.orphanPointers, + isProviderDirectory( + targetContainer: root, + override: null, + pointers: { + a: isPointer( + override: aOverride, + targetContainer: root, + element: null, + ), + b: isPointer( + override: bOverride, + targetContainer: mid, + element: null, + ), + c: isPointer( + override: cOverride, + targetContainer: container, + element: null, + ), + }, + ), + ); + }); + + test('with no overrides, uses an identical content as its parent', () { + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( + parent: root, + overrides: [Provider((_) => 0, dependencies: const [])], + ); + final container = ProviderContainer.test(parent: mid); + final container2 = ProviderContainer.test(parent: mid, overrides: []); + + expect( + container.pointerManager.orphanPointers, + same(mid.pointerManager.orphanPointers), + ); + expect( + container.pointerManager.familyPointers, + same(mid.pointerManager.familyPointers), + ); + + expect( + container2.pointerManager.familyPointers, + same(mid.pointerManager.familyPointers), + ); + expect( + container2.pointerManager.orphanPointers, + same(mid.pointerManager.orphanPointers), + ); + }); + + test('orphanPointers.containers are always equal to root', () { + final root = ProviderContainer.test(); + final provider = Provider( + (_) => 0, + dependencies: const [], + ); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); + + expect(root.pointerManager.orphanPointers.targetContainer, root); + expect( + container.pointerManager.orphanPointers.targetContainer, + root, + ); + + expect( + container.pointerManager.orphanPointers.pointers, + { + provider: isPointer( + targetContainer: container, + override: provider, + element: null, + ), + }, + ); + }); + + test('can scope a provider that is already scoped', () { + final provider = Provider((_) => 0, dependencies: const []); + final family = Provider.family( + (_, b) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + + final providerOverride1 = provider.overrideWithValue(1); + final familyOverride1 = family.overrideWith((ref, arg) => 1); + final mid = ProviderContainer.test( + parent: root, + overrides: [providerOverride1, familyOverride1], + ); + + final providerOverride2 = provider.overrideWithValue(1); + final familyOverride2 = family.overrideWith((ref, arg) => 1); + final leaf = ProviderContainer.test( + parent: mid, + overrides: [providerOverride2, familyOverride2], + ); + + expect( + leaf.pointerManager.orphanPointers.pointers, + { + provider: isPointer( + targetContainer: leaf, + override: providerOverride2, + element: null, + ), + }, + ); + expect( + leaf.pointerManager.familyPointers, + { + family: isProviderDirectory( + override: familyOverride2, + targetContainer: leaf, + pointers: isEmpty, + ), + }, + ); + + expect( + mid.pointerManager.orphanPointers.pointers, + { + provider: isPointer( + targetContainer: mid, + override: providerOverride1, + element: null, + ), + }, + ); + expect( + mid.pointerManager.familyPointers, + { + family: isProviderDirectory( + override: familyOverride1, + targetContainer: mid, + pointers: isEmpty, + ), + }, + ); + }); + }); + + test('adds non-family provider overrides to orphanPointers', () { + final provider = Provider( + (_) => 0, + dependencies: const [], + ); + final override = provider.overrideWithValue(42); + final root = ProviderContainer.test( + overrides: [override], + ); + final root2 = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root2, + overrides: [override], + ); + + expect( + root.pointerManager.orphanPointers, + isProviderDirectory( + targetContainer: root, + override: null, + pointers: { + provider: isPointer( + targetContainer: root, + override: override, + element: null, + ), + }, + ), + ); + + expect( + root2.pointerManager.orphanPointers.pointers, + isEmpty, + ); + expect( + container.pointerManager.orphanPointers, + isProviderDirectory( + targetContainer: root2, + override: null, + pointers: { + provider: isPointer( + targetContainer: container, + override: override, + element: null, + ), + }, + ), + ); + }); + + test('adds family overrides to familyPointers', () { + final provider = Provider.family( + (ref, _) => 0, + dependencies: const [], + ); + final override = provider.overrideWith((ref, arg) => 0); + final root = ProviderContainer.test( + overrides: [override], + ); + final root2 = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root2, + overrides: [override], + ); + + expect( + root.pointerManager.familyPointers, + { + provider: isProviderDirectory( + override: override, + targetContainer: root, + pointers: isEmpty, + ), + }, + ); + + expect( + root2.pointerManager.familyPointers, + isEmpty, + ); + expect( + container.pointerManager.familyPointers, + { + provider: isProviderDirectory( + override: override, + targetContainer: container, + pointers: isEmpty, + ), + }, + ); + }); + + test('adds family provider overrides to familyPointers.pointers', () { + final provider = Provider.family( + (ref, _) => 0, + dependencies: const [], + ); + final override = provider(42).overrideWith((ref) => 0); + final root = ProviderContainer.test( + overrides: [override], + ); + final root2 = ProviderContainer.test(); + + final container = ProviderContainer.test( + parent: root2, + overrides: [override], + ); + + expect( + root.pointerManager.familyPointers, + { + provider: isProviderDirectory( + override: null, + targetContainer: root, + pointers: { + provider(42): isPointer( + targetContainer: root, + override: override, + element: null, + ), + }, + ), + }, + ); + + expect( + container.pointerManager.familyPointers, + isNot(root2.pointerManager.familyPointers), + ); + + expect( + root2.pointerManager.familyPointers, + isEmpty, + ); + + expect( + container.pointerManager.familyPointers, + { + provider: isProviderDirectory( + override: null, + targetContainer: root2, + pointers: { + provider(42): isPointer( + targetContainer: container, + override: override, + element: null, + ), + }, + ), + }, + ); + }); + + test( + 'can override a family and a provider from that family in the same container', + () { + final family = Provider.family((ref, a) => 'Hello $a'); + final familyOverride = family.overrideWith((ref, a) => 'Hi $a'); + final beforeOverride = family(42).overrideWithValue('Bonjour 42'); + final afterOverride = family(21).overrideWithValue('Ola 42'); + + final container = ProviderContainer.test( + overrides: [ + beforeOverride, + familyOverride, + afterOverride, + ], + ); + + expect(container.pointerManager.familyPointers, { + family: isProviderDirectory( + override: familyOverride, + targetContainer: container, + pointers: { + family(42): isPointer( + override: beforeOverride, + targetContainer: container, + element: null, + ), + family(21): isPointer( + override: afterOverride, + targetContainer: container, + element: null, + ), + }, + ), + }); + }); + }); + + group('.test', () { + test('Auto-disposes the provider when the test ends', () { + late ProviderContainer container; + + addTearDown(() => expect(container.disposed, true)); + + container = ProviderContainer.test(); + + addTearDown(() => expect(container.disposed, false)); + }); + + test('Passes parameters', () { + final provider = Provider( + (ref) => 0, + dependencies: const [], + ); + final observer = _EmptyObserver(); + + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + observers: [observer], + overrides: [ + provider.overrideWithValue(1), + ], + ); + + expect(container.root, root); + expect(container.observers, [observer]); + expect(container.read(provider), 1); + }); + }); + + group('dispose', () { + test( + 'Handles cases where the ProviderContainer is disposed yet Scheduler.performDispose is invoked anyway', + () async { + // regression test for https://github.com/rrousselGit/riverpod/issues/1400 + final provider = Provider.autoDispose( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); + + container.read(provider); + container.dispose(); + + await root.pump(); + }); + + test( + 'after a child container is disposed, ' + 'ref.watch keeps working on providers associated with the ancestor container', + () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final provider = Provider((ref) => ref.watch(dep)); + final listener = Listener(); + final child = ProviderContainer.test(parent: container); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 0)); + + child.dispose(); + + container.read(dep.notifier).state++; + await container.pump(); + + verifyOnly(listener, listener(0, 1)); + }); + + test('does not compute provider states if not loaded yet', () { + var callCount = 0; + final provider = Provider((_) => callCount++); + + final container = ProviderContainer.test( + overrides: [provider], + ); + + container.dispose(); + + expect(callCount, 0); + }); + + test('Disposes its children first', () { + final rootOnDispose = OnDisposeMock(); + final childOnDispose = OnDisposeMock(); + final child2OnDispose = OnDisposeMock(); + final provider = Provider( + (ref) { + ref.onDispose(rootOnDispose.call); + return 0; + }, + dependencies: const [], + ); + + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [ + provider.overrideWith((ref) { + ref.onDispose(childOnDispose.call); + return 0; + }), + ], + ); + final container2 = ProviderContainer.test( + parent: root, + overrides: [ + provider.overrideWith((ref) { + ref.onDispose(child2OnDispose.call); + return 0; + }), + ], + ); + + root.listen(provider, (previous, next) {}); + container.listen(provider, (previous, next) {}); + container2.listen(provider, (previous, next) {}); + + container2.dispose(); + + verifyOnly(child2OnDispose, child2OnDispose.call()); + verifyZeroInteractions(childOnDispose); + verifyZeroInteractions(rootOnDispose); + expect(container.disposed, false); + expect(root.disposed, false); + + root.dispose(); + + verifyInOrder([ + childOnDispose.call(), + rootOnDispose.call(), + ]); + + expect(container.disposed, true); + expect(root.disposed, true); + }); + + test('removes "this" from "root.children"', () { + final root = ProviderContainer.test(); + final container = ProviderContainer.test(parent: root); + final leaf = ProviderContainer.test(parent: container); + final leaf2 = ProviderContainer.test(parent: container); + + expect(root.children, [container]); + expect(container.children, [leaf, leaf2]); + expect(leaf.children, isEmpty); + expect(leaf2.children, isEmpty); + + leaf.dispose(); + + expect(root.children, [container]); + expect(container.children, [leaf2]); + + leaf2.dispose(); + + expect(root.children, [container]); + expect(container.children, isEmpty); + + container.dispose(); + + expect(root.children, isEmpty); + }); + }); + + group('exists', () { + test('simple use-case', () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => 0); + + expect(container.exists(provider), false); + expect(container.getAllProviderElements(), isEmpty); + + container.read(provider); + + expect(container.exists(provider), true); + }); + + test('handles autoDispose', () async { + final provider = Provider.autoDispose((ref) => 0); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWith((ref) => 42), + ], + ); + + expect(container.exists(provider), false); + expect(container.getAllProviderElements(), isEmpty); + + container.read(provider); + + expect(container.exists(provider), true); + + await container.pump(); + + expect(container.getAllProviderElements(), isEmpty); + expect(container.exists(provider), false); + expect(container.getAllProviderElements(), isEmpty); + }); + + test('Handles uninitialized overrideWith', () { + final provider = Provider((ref) => 0); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWith((ref) => 42), + ], + ); + + expect(container.exists(provider), false); + expect(container.getAllProviderElements(), isEmpty); + + container.read(provider); + + expect(container.exists(provider), true); + }); + + test('handles nested providers', () { + final provider = Provider((ref) => 0); + final provider2 = Provider( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider2], + ); + + expect(container.exists(provider), false); + expect(container.exists(provider2), false); + expect(container.getAllProviderElements(), isEmpty); + expect(root.getAllProviderElements(), isEmpty); + + container.read(provider); + + expect(container.exists(provider), true); + expect(container.exists(provider2), false); + expect(container.getAllProviderElements(), isEmpty); + expect(root.getAllProviderElements().map((e) => e.origin), [provider]); + + container.read(provider2); + + expect(container.exists(provider2), true); + expect( + container.getAllProviderElements().map((e) => e.origin), + [provider2], + ); + expect(root.getAllProviderElements().map((e) => e.origin), [provider]); + }); + }); + + group('.pump', () { + test( + '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)); + final b = Provider( + (ref) => ref.watch(dep), + dependencies: const [], + ); + final aListener = Listener(); + final bListener = Listener(); + + final root = ProviderContainer.test(); + final scoped = ProviderContainer.test(parent: root, overrides: [b]); + + scoped.listen(a, aListener.call, fireImmediately: true); + scoped.listen(b, bListener.call, fireImmediately: true); + + verifyOnly(aListener, aListener(null, 0)); + verifyOnly(bListener, bListener(null, 0)); + + root.read(dep.notifier).state++; + await scoped.pump(); + + verifyOnly(aListener, aListener(0, 1)); + verifyOnly(bListener, bListener(0, 1)); + + scoped.read(dep.notifier).state++; + await scoped.pump(); + + verifyOnly(aListener, aListener(1, 2)); + verifyOnly(bListener, bListener(1, 2)); + }); + }); + + test('depth', () { + final root = ProviderContainer.test(); + final a = ProviderContainer.test(parent: root); + final b = ProviderContainer.test(parent: a); + final c = ProviderContainer.test(parent: a); + + final root2 = ProviderContainer.test(); + + expect(root.depth, 0); + expect(root2.depth, 0); + expect(a.depth, 1); + expect(b.depth, 2); + expect(c.depth, 2); + }); + + group('updateOverrides', () { + test('is not allowed to remove overrides ', () { + final provider = Provider((_) => 0); + + final container = ProviderContainer.test( + overrides: [provider.overrideWithValue(42)], + ); + + expect(container.read(provider), 42); + + expect( + () => container.updateOverrides([]), + throwsA(isAssertionError), + ); + }); + + test('changing the override type at a given index throws', () { + final provider = Provider((ref) => 0); + final family = Provider.family((ref, value) => 0); + final container = ProviderContainer.test(overrides: [family]); + + expect( + () => container.updateOverrides([provider]), + throwsA(isA()), + ); + }); + + test('does not compute provider states if not loaded yet', () { + var callCount = 0; + final provider = Provider((_) => callCount++); + + final container = ProviderContainer.test( + overrides: [provider], + ); + + container.updateOverrides([provider]); + + expect(callCount, 0); + + container.dispose(); + + expect(callCount, 0); + }); + + test('does not notify listeners if updated with the same value', () { + final provider = Provider((ref) => 0); + final container = ProviderContainer.test( + overrides: [provider.overrideWithValue(42)], + ); + final listener = Listener(); + + addTearDown(container.dispose); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 42)); + + container.updateOverrides([ + provider.overrideWithValue(42), + ]); + + expect(container.read(provider), 42); + verifyNoMoreInteractions(listener); + }); + + test('notify listeners when value changes', () { + final provider = Provider((ref) => 0); + final container = ProviderContainer.test( + overrides: [provider.overrideWithValue(42)], + ); + final listener = Listener(); + + addTearDown(container.dispose); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 42)); + + container.updateOverrides([ + provider.overrideWithValue(21), + ]); + + verifyOnly(listener, listener(42, 21)); + }); + + test('updating parent override when there is a child override is no-op', + () async { + final provider = Provider( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test( + overrides: [provider.overrideWithValue(21)], + ); + final container = ProviderContainer.test( + parent: root, + overrides: [provider.overrideWithValue(42)], + ); + final listener = Listener(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 42)); + + root.updateOverrides([ + provider.overrideWithValue(22), + ]); + + await container.pump(); + + verifyNoMoreInteractions(listener); + }); + + test('can update multiple ScopeProviders at once', () { + final provider = Provider((ref) => -1); + final provider2 = Provider((ref) => -1); + + final container = ProviderContainer.test( + overrides: [ + provider.overrideWithValue(21), + provider2.overrideWithValue(42), + ], + ); + + final listener = Listener(); + final listener2 = Listener(); + + container.listen(provider, listener.call, fireImmediately: true); + container.listen(provider2, listener2.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 21)); + verifyOnly(listener2, listener2(null, 42)); + + container.updateOverrides([ + provider.overrideWithValue(22), + provider2.overrideWithValue(43), + ]); + + verifyInOrder([ + listener(21, 22), + listener2(42, 43), + ]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test( + 'if listened from a child container, ' + 'updating the parent override correctly notifies listeners', () { + final provider = Provider( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test( + overrides: [provider.overrideWithValue(1)], + ); + final mid = ProviderContainer.test( + parent: root, + overrides: [ + provider.overrideWithValue(42), + ], + ); + final container = ProviderContainer.test(parent: mid); + final listener = Listener(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 42)); + + mid.updateOverrides([ + provider.overrideWithValue(21), + ]); + + verifyOnly(listener, listener(42, 21)); + }); + + test('throws if used on a disposed container', () { + final container = ProviderContainer.test(); + container.dispose(); + + expect( + () => container.updateOverrides([]), + throwsStateError, + ); + }); + }); + + group('invalidate', () { + group('invalidate', () { + test('can disposes of the element if not used anymore', () async { + final provider = Provider.autoDispose((r) { + r.keepAlive(); + return 0; + }); + final container = ProviderContainer.test(); + + container.read(provider); + container.invalidate(provider); + + await container.pump(); + + expect(container.getAllProviderElements(), isEmpty); + }); + }); + + test('supports asReload', () async { + final container = ProviderContainer.test(); + final provider = FutureProvider((r) async => 0); + + await container.read(provider.future); + expect(container.read(provider), const AsyncValue.data(0)); + + container.invalidate(provider, asReload: true); + + expect( + container.read(provider), + isA>().having((e) => e.value, 'value', 0), + ); + }); + }); + + group('listen', () { + test('when no onError is specified, fallbacks to handleUncaughtError', + () async { + final container = ProviderContainer.test(); + final isErrored = StateProvider((ref) => false); + final dep = Provider((ref) { + if (ref.watch(isErrored)) throw UnimplementedError(); + return 0; + }); + final listener = Listener(); + final errors = []; + + runZonedGuarded( + () => container.listen(dep, listener.call), + (err, stack) => errors.add(err), + ); + + verifyZeroInteractions(listener); + expect(errors, isEmpty); + + container.read(isErrored.notifier).state = true; + + await container.pump(); + + verifyZeroInteractions(listener); + expect(errors, [isUnimplementedError]); + }); + + test( + 'when no onError is specified, selectors fallbacks to handleUncaughtError', + () async { + final container = ProviderContainer.test(); + final isErrored = StateProvider((ref) => false); + final dep = Provider((ref) { + if (ref.watch(isErrored)) throw UnimplementedError(); + return 0; + }); + final listener = Listener(); + final errors = []; + + runZonedGuarded( + () => container.listen(dep.select((value) => value), listener.call), + (err, stack) => errors.add(err), + ); + + verifyZeroInteractions(listener); + expect(errors, isEmpty); + + container.read(isErrored.notifier).state = true; + + await container.pump(); + + verifyZeroInteractions(listener); + expect(errors, [isUnimplementedError]); + }); + + test('when rebuild throws, calls onError', () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final provider = Provider((ref) { + if (ref.watch(dep) != 0) { + throw UnimplementedError(); + } + return 0; + }); + final errorListener = ErrorListener(); + final listener = Listener(); + + container.listen(provider, listener.call, onError: errorListener.call); + + verifyZeroInteractions(errorListener); + verifyZeroInteractions(listener); + + container.read(dep.notifier).state++; + await container.pump(); + + verifyZeroInteractions(listener); + verifyOnly( + errorListener, + errorListener(isUnimplementedError, any), + ); + }); + + test('when rebuild throws on selector, calls onError', () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final provider = Provider((ref) { + if (ref.watch(dep) != 0) { + throw UnimplementedError(); + } + return 0; + }); + final errorListener = ErrorListener(); + final listener = Listener(); + + container.listen( + provider.select((value) => value), + listener.call, + onError: errorListener.call, + ); + + verifyZeroInteractions(errorListener); + verifyZeroInteractions(listener); + + container.read(dep.notifier).state++; + await container.pump(); + + verifyZeroInteractions(listener); + verifyOnly( + errorListener, + errorListener(isUnimplementedError, any), + ); + }); + + test( + 'when using selectors, `previous` is the latest notification instead of latest event', + () { + final container = ProviderContainer.test(); + final provider = StateNotifierProvider, int>( + (ref) => StateController(0), + ); + final listener = Listener(); + + container.listen( + provider.select((value) => value.isEven), + listener.call, + fireImmediately: true, + ); + + verifyOnly(listener, listener(null, true)); + + container.read(provider.notifier).state += 2; + + verifyNoMoreInteractions(listener); + + container.read(provider.notifier).state++; + + verifyOnly(listener, listener(true, false)); + }); + + test('expose previous and new value on change', () { + final container = ProviderContainer.test(); + final provider = StateNotifierProvider, int>( + (ref) => StateController(0), + ); + final listener = Listener(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 0)); + + container.read(provider.notifier).state++; + + verifyOnly(listener, listener(0, 1)); + }); + + test('can downcast the value', () async { + final listener = Listener(); + final dep = StateProvider((ref) => 0); + + final container = ProviderContainer.test(); + + container.listen(dep, listener.call); + + verifyZeroInteractions(listener); + + container.read(dep.notifier).state++; + await container.pump(); + + verifyOnly(listener, listener(0, 1)); + }); + + test( + 'if a listener adds a container.listen, the new listener is not called immediately', + () { + final provider = StateProvider((ref) => 0); + final container = ProviderContainer.test(); + + final listener = Listener(); + + container.listen(provider, (prev, value) { + listener(prev, value); + container.listen(provider, listener.call); + }); + + verifyZeroInteractions(listener); + + container.read(provider.notifier).state++; + + verify(listener(0, 1)).called(1); + + container.read(provider.notifier).state++; + + verify(listener(1, 2)).called(2); + }); + + test( + 'if a listener removes another provider.listen, the removed listener is not called', + () { + final provider = StateProvider((ref) => 0); + final container = ProviderContainer.test(); + + final listener = Listener(); + final listener2 = Listener(); + + final p = Provider((ref) { + ProviderSubscription? a; + ref.listen(provider, (prev, value) { + listener(prev, value); + a?.close(); + a = null; + }); + + a = ref.listen(provider, listener2.call); + }); + container.read(p); + + verifyZeroInteractions(listener); + verifyZeroInteractions(listener2); + + container.read(provider.notifier).state++; + + verifyOnly(listener, listener(0, 1)); + verifyZeroInteractions(listener2); + + container.read(provider.notifier).state++; + + verify(listener(1, 2)).called(1); + verifyNoMoreInteractions(listener2); + }); + + test( + 'if a listener adds a provider.listen, the new listener is not called immediately', + () { + final provider = StateProvider((ref) => 0); + final container = ProviderContainer.test(); + + final listener = Listener(); + + final p = Provider((ref) { + ref.listen(provider, (prev, value) { + listener(prev, value); + ref.listen(provider, listener.call); + }); + }); + container.read(p); + + verifyZeroInteractions(listener); + + container.read(provider.notifier).state++; + + verify(listener(0, 1)).called(1); + + container.read(provider.notifier).state++; + + verify(listener(1, 2)).called(2); + }); + + group('fireImmediately', () { + test('when no onError is specified, fallbacks to handleUncaughtError', + () { + final container = ProviderContainer.test(); + final dep = Provider((ref) => throw UnimplementedError()); + final listener = Listener(); + final errors = []; + + runZonedGuarded( + () { + container.listen( + dep, + listener.call, + fireImmediately: true, + ); + }, + (err, stack) => errors.add(err), + ); + + verifyZeroInteractions(listener); + expect(errors, [ + isUnimplementedError, + ]); + }); + + test( + 'when no onError is specified on selectors, fallbacks to handleUncaughtError', + () { + final container = ProviderContainer.test(); + final dep = Provider((ref) => throw UnimplementedError()); + final listener = Listener(); + final errors = []; + + runZonedGuarded( + () { + container.listen( + dep.select((value) => value), + listener.call, + fireImmediately: true, + ); + }, + (err, stack) => errors.add(err), + ); + + verifyZeroInteractions(listener); + expect(errors, [ + isUnimplementedError, + ]); + }); + + test('on provider that threw, fireImmediately calls onError', () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => throw UnimplementedError()); + final listener = Listener(); + final errorListener = ErrorListener(); + + container.listen( + provider, + listener.call, + onError: errorListener.call, + fireImmediately: true, + ); + + verifyZeroInteractions(listener); + verifyOnly( + errorListener, + errorListener(isUnimplementedError, argThat(isNotNull)), + ); + }); + + test('supports selectors', () { + final container = ProviderContainer.test(); + final provider = StateProvider((ref) => 0); + final listener = Listener(); + final listener2 = Listener(); + + container.listen( + provider.select((v) => v.isEven), + listener.call, + fireImmediately: true, + ); + container.listen(provider.select((v) => v.isEven), listener2.call); + + verifyOnly(listener, listener(null, true)); + verifyZeroInteractions(listener2); + + container.read(provider.notifier).state = 21; + + verifyOnly(listener, listener(true, false)); + verifyOnly(listener2, listener2(true, false)); + }); + + test('passing fireImmediately: false skips the initial value', () { + final provider = StateProvider((ref) => 0); + final listener = Listener(); + + final container = ProviderContainer.test(); + + container.listen(provider, listener.call); + + verifyZeroInteractions(listener); + }); + + test( + 'correctly listens to the provider if selector onError listener throws', + () async { + final dep = StateProvider((ref) => 0); + final provider = Provider((ref) { + if (ref.watch(dep) == 0) { + throw UnimplementedError(); + } + return ref.watch(dep); + }); + final listener = Listener(); + final errorListener = ErrorListener(); + var isFirstCall = true; + + final container = ProviderContainer.test(); + final errors = []; + + final sub = runZonedGuarded( + () => container.listen( + provider.select((value) => value), + listener.call, + onError: (err, stack) { + errorListener(err, stack); + if (isFirstCall) { + isFirstCall = false; + throw StateError('Some error'); + } + }, + fireImmediately: true, + ), + (err, stack) => errors.add(err), + ); + + container.listen(provider, (prev, value) {}); + + expect(sub, isNotNull); + verifyZeroInteractions(listener); + verifyOnly( + errorListener, + errorListener(argThat(isUnimplementedError), argThat(isNotNull)), + ); + expect(errors, [isStateError]); + + container.read(dep.notifier).state++; + await container.pump(); + + verifyNoMoreInteractions(errorListener); + verifyOnly(listener, listener(null, 1)); + }); + + test( + 'correctly listens to the provider if normal onError listener throws', + () async { + final dep = StateProvider((ref) => 0); + final provider = Provider((ref) { + if (ref.watch(dep) == 0) { + throw UnimplementedError(); + } + return ref.watch(dep); + }); + final listener = Listener(); + final errorListener = ErrorListener(); + var isFirstCall = true; + + final container = ProviderContainer.test(); + final errors = []; + + final sub = runZonedGuarded( + () => container.listen( + provider, + listener.call, + onError: (err, stack) { + errorListener(err, stack); + if (isFirstCall) { + isFirstCall = false; + throw StateError('Some error'); + } + }, + fireImmediately: true, + ), + (err, stack) => errors.add(err), + ); + + container.listen(provider, (prev, value) {}); + + expect(sub, isNotNull); + verifyZeroInteractions(listener); + verifyOnly( + errorListener, + errorListener(argThat(isUnimplementedError), argThat(isNotNull)), + ); + expect(errors, [isStateError]); + + container.read(dep.notifier).state++; + await container.pump(); + + verifyNoMoreInteractions(errorListener); + verifyOnly(listener, listener(null, 1)); + }); + + test('correctly listens to the provider if selector listener throws', + () { + final provider = StateProvider((ref) => 0); + final listener = Listener(); + var isFirstCall = true; + + final container = ProviderContainer.test(); + final errors = []; + + final sub = runZonedGuarded( + () => container.listen( + provider.select((value) => value), + (prev, value) { + listener(prev, value); + if (isFirstCall) { + isFirstCall = false; + throw StateError('Some error'); + } + }, + fireImmediately: true, + ), + (err, stack) => errors.add(err), + ); + + expect(sub, isNotNull); + verifyOnly(listener, listener(null, 0)); + expect(errors, [isStateError]); + + container.read(provider.notifier).state++; + + verifyOnly(listener, listener(0, 1)); + }); + + test('correctly listens to the provider if normal listener throws', () { + final provider = StateProvider((ref) => 0); + final listener = Listener(); + var isFirstCall = true; + + final container = ProviderContainer.test(); + final errors = []; + + final sub = runZonedGuarded( + () => container.listen( + provider, + (prev, value) { + listener(prev, value); + if (isFirstCall) { + isFirstCall = false; + throw StateError('Some error'); + } + }, + fireImmediately: true, + ), + (err, stack) => errors.add(err), + ); + + expect(sub, isNotNull); + verifyOnly(listener, listener(null, 0)); + expect(errors, [isStateError]); + + container.read(provider.notifier).state++; + + verifyOnly(listener, listener(0, 1)); + }); + + test('correctly listens to the provider if normal listener throws', () { + final provider = StateProvider((ref) => 0); + final listener = Listener(); + var isFirstCall = true; + + final container = ProviderContainer.test(); + final errors = []; + + final sub = runZonedGuarded( + () => container.listen( + provider, + (prev, notifier) { + listener(prev, notifier); + if (isFirstCall) { + isFirstCall = false; + throw StateError('Some error'); + } + }, + fireImmediately: true, + ), + (err, stack) => errors.add(err), + ); + + expect(sub, isNotNull); + verifyOnly(listener, listener(null, 0)); + expect(errors, [isStateError]); + + container.read(provider.notifier).state++; + + verifyOnly(listener, listener(0, 1)); + }); + }); + + test('.read on closed subscription throws', () { + final provider = StateProvider((_) => 0); + final container = ProviderContainer.test(); + final listener = Listener(); + + final sub = + container.listen(provider, listener.call, fireImmediately: true); + + verify(listener(null, 0)).called(1); + verifyNoMoreInteractions(listener); + + sub.close(); + container.read(provider.notifier).state++; + + expect(sub.read, throwsStateError); + + verifyNoMoreInteractions(listener); + }); + + test('.read on closed selector subscription throws', () { + final provider = StateProvider((_) => 0); + final container = ProviderContainer.test(); + final listener = Listener(); + + final sub = container.listen( + provider.select((value) => value * 2), + listener.call, + fireImmediately: true, + ); + + verify(listener(null, 0)).called(1); + verifyNoMoreInteractions(listener); + + sub.close(); + container.read(provider.notifier).state++; + + expect(sub.read, throwsStateError); + verifyNoMoreInteractions(listener); + }); + + test("doesn't trow when creating a provider that failed", () { + final container = ProviderContainer.test(); + final provider = Provider((ref) { + throw Error(); + }); + + final sub = container.listen(provider, (_, __) {}); + + expect(sub, isA>()); + }); + + test('selectors can close listeners', () { + final container = ProviderContainer.test(); + final provider = StateProvider((ref) => 0); + + expect( + container.readProviderElement(provider).hasNonWeakListeners, + false, + ); + + final sub = container.listen( + provider.select((count) => count.isEven), + (prev, isEven) {}, + ); + + expect( + container.readProviderElement(provider).hasNonWeakListeners, + true, + ); + + sub.close(); + + expect( + container.readProviderElement(provider).hasNonWeakListeners, + false, + ); + }); + + test('can watch selectors', () async { + final container = ProviderContainer.test(); + final provider = StateProvider((ref) => 0); + final isAdultSelector = Selector(false, (c) => c >= 18); + final isAdultListener = Listener(); + + final controller = container.read(provider.notifier); + container.listen( + provider.select(isAdultSelector.call), + isAdultListener.call, + fireImmediately: true, + ); + + verifyOnly(isAdultSelector, isAdultSelector(0)); + verifyOnly(isAdultListener, isAdultListener(null, false)); + + controller.state += 10; + + verifyOnly(isAdultSelector, isAdultSelector(10)); + verifyNoMoreInteractions(isAdultListener); + + controller.state += 10; + + verifyOnly(isAdultSelector, isAdultSelector(20)); + verifyOnly(isAdultListener, isAdultListener(false, true)); + + controller.state += 10; + + verifyOnly(isAdultSelector, isAdultSelector(30)); + verifyNoMoreInteractions(isAdultListener); + }); + + test('calls immediately the listener with the current value', () { + final provider = Provider((ref) => 0); + final listener = Listener(); + + final container = ProviderContainer.test(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 0)); + }); + + test('call listener when provider rebuilds', () async { + final controller = StreamController(); + addTearDown(controller.close); + final container = ProviderContainer.test(); + + final count = StateProvider((ref) => 0); + final provider = Provider((ref) => ref.watch(count)); + + container.listen( + provider, + (prev, value) => controller.add(value), + fireImmediately: true, + ); + + container.read(count.notifier).state++; + + await expectLater( + controller.stream, + emitsInOrder([0, 1]), + ); + }); + + test('call listener when provider emits an update', () async { + final container = ProviderContainer.test(); + + final count = StateProvider((ref) => 0); + final listener = Listener(); + + container.listen(count, listener.call); + + container.read(count.notifier).state++; + + verifyOnly(listener, listener(0, 1)); + + container.read(count.notifier).state++; + + verifyOnly(listener, listener(1, 2)); + }); + + test('supports selectors', () { + final container = ProviderContainer.test(); + + final count = StateProvider((ref) => 0); + final listener = Listener(); + + container.listen( + count.select((value) => value.isEven), + listener.call, + fireImmediately: true, + ); + + verifyOnly(listener, listener(null, true)); + + container.read(count.notifier).state = 2; + + verifyNoMoreInteractions(listener); + + container.read(count.notifier).state = 3; + + verifyOnly(listener, listener(true, false)); + }); + + test('can downcast the listener value', () { + final container = ProviderContainer.test(); + final provider = StateProvider((ref) => 0); + final listener = Listener(); + + container.listen(provider, listener.call); + + verifyZeroInteractions(listener); + + container.read(provider.notifier).state++; + + verifyOnly(listener, listener(any, any)); + }); + + test( + 'can close a ProviderSubscription multiple times with no effect', + () { + final container = ProviderContainer.test(); + final provider = + StateNotifierProvider, int>((ref) { + return StateController(0); + }); + final listener = Listener(); + + final controller = container.read(provider.notifier); + + final sub = container.listen(provider, listener.call); + + sub.close(); + sub.close(); + + controller.state++; + + verifyZeroInteractions(listener); + }, + ); + + test( + 'closing an already closed ProviderSubscription does not remove subscriptions with the same listener', + () { + final container = ProviderContainer.test(); + final provider = + StateNotifierProvider, int>((ref) { + return StateController(0); + }); + final listener = Listener(); + + final controller = container.read(provider.notifier); + + final sub = container.listen(provider, listener.call); + container.listen(provider, listener.call); + + controller.state++; + + verify(listener(0, 1)).called(2); + verifyNoMoreInteractions(listener); + + sub.close(); + sub.close(); + + controller.state++; + + verifyOnly(listener, listener(1, 2)); + }, + ); + }); + }); +} + +class _EmptyObserver extends ProviderObserver {} diff --git a/packages/riverpod/test/src/core/provider_element_test.dart b/packages/riverpod/test/src/core/provider_element_test.dart new file mode 100644 index 000000000..89af21735 --- /dev/null +++ b/packages/riverpod/test/src/core/provider_element_test.dart @@ -0,0 +1,643 @@ +import 'dart:async'; + +import 'package:mockito/mockito.dart'; +import 'package:riverpod/src/internals.dart'; +import 'package:test/test.dart'; + +import '../../third_party/fake_async.dart'; +import '../matrix.dart'; +import '../utils.dart'; + +void main() { + group('ProviderElement', () { + test( + 'pauses old subscriptions upon invalidation until the completion of the new computation', + () async { + final container = ProviderContainer.test(); + final depProvider = Provider.family((ref, _) => 0); + final futureP = FutureProvider(Future.value); + final streamP = StreamProvider(Stream.value); + final p = Provider((ref) => ref); + + final depP = container.readProviderElement(depProvider('p')); + final depF = container.readProviderElement(depProvider('f')); + final depS = container.readProviderElement(depProvider('s')); + + container.listen(p, (a, b) {}).read().watch(depProvider('p')); + (await container.listen(futureP.future, (a, b) {}).read()) + .watch(depProvider('f')); + (await container.listen(streamP.future, (a, b) {}).read()) + .watch(depProvider('s')); + + container.invalidate(p); + container.invalidate(futureP); + container.invalidate(streamP); + + expect(depP.hasNonWeakListeners, true); + expect(depF.hasNonWeakListeners, true); + expect(depS.hasNonWeakListeners, true); + expect(depP.isActive, false); + expect(depF.isActive, false); + expect(depS.isActive, false); + + await container.pump(); + + expect(depP.hasNonWeakListeners, false); + expect(depF.hasNonWeakListeners, true); + expect(depS.hasNonWeakListeners, true); + expect(depP.isActive, false); + expect(depF.isActive, false); + expect(depS.isActive, false); + + await container.read(streamP.future); + await container.read(futureP.future); + + expect(depP.hasNonWeakListeners, false); + expect(depF.hasNonWeakListeners, false); + expect(depS.hasNonWeakListeners, false); + expect(depP.isActive, false); + expect(depF.isActive, false); + expect(depS.isActive, false); + }); + + test('Only includes direct subscriptions in subscription lists', () { + final container = ProviderContainer.test(); + final provider = FutureProvider((ref) => 0); + final dep = Provider((ref) { + ref.watch(provider.future.select((value) => 0)); + }); + + container.read(dep); + + final providerElement = container.readProviderElement(provider); + final depElement = container.readProviderElement(dep); + + expect(providerElement.subscriptions, null); + expect( + providerElement.dependents, + [isA>()], + ); + expect(providerElement.weakDependents, isEmpty); + + expect(depElement.subscriptions, [ + isA>(), + ]); + expect(depElement.dependents, isEmpty); + expect(depElement.weakDependents, isEmpty); + }); + + group('retry', () { + test( + 'default retry delays from 200ms to 6.4 seconds', + () => fakeAsync((fake) async { + final container = ProviderContainer.test(); + var buildCount = 0; + final provider = Provider((ref) { + buildCount++; + throw StateError(''); + }); + + container.listen( + provider, + (prev, next) {}, + fireImmediately: true, + onError: (e, s) {}, + ); + + const times = [ + 200, + 400, + 800, + 1600, + 3200, + 6400, + ]; + + for (final (index, time) in times.indexed) { + fake.elapse(Duration(milliseconds: time - 10)); + expect(buildCount, index + 1, reason: 'expect retry time of $time'); + + fake.elapse(const Duration(milliseconds: 10)); + expect(buildCount, index + 2); + } + }), + ); + + group('custom retry', () { + test( + 'if returns null, stops retrying', + () => fakeAsync((fake) { + final container = ProviderContainer.test(retry: (_, __) => null); + var buildCount = 0; + final provider = Provider((ref) { + buildCount++; + throw StateError(''); + }); + + container.listen( + provider, + (prev, next) {}, + fireImmediately: true, + onError: (e, s) {}, + ); + expect(buildCount, 1); + + fake.elapse(const Duration(seconds: 100)); + + expect(buildCount, 1); + }), + ); + + test( + 'passes the correct retry count and error', + () => fakeAsync((fake) { + final retry = RetryMock(); + when(retry(any, any)).thenReturn(const Duration(milliseconds: 200)); + final container = ProviderContainer.test(retry: retry.call); + final provider = Provider((ref) => throw StateError('')); + + container.listen( + provider, + (prev, next) {}, + fireImmediately: true, + onError: (e, s) {}, + ); + + verifyOnly(retry, retry(0, isStateError)); + + fake.elapse(const Duration(milliseconds: 200)); + + verifyOnly(retry, retry(1, isStateError)); + + fake.elapse(const Duration(milliseconds: 200)); + + verifyOnly(retry, retry(2, isStateError)); + + container.invalidate(provider, asReload: true); + fake.elapse(const Duration(milliseconds: 50)); + + // On reload, resets counter to 0 + verifyOnly(retry, retry(0, isStateError)); + }), + ); + + test( + 'delays by the duration returned', + () => fakeAsync((fake) { + final container = ProviderContainer.test( + retry: (_, __) => const Duration(milliseconds: 3), + ); + final errorListener = ErrorListener(); + var msg = '0'; + final provider = Provider((ref) => throw StateError(msg)); + + container.listen( + provider, + (prev, next) {}, + fireImmediately: true, + onError: errorListener.call, + ); + + verifyOnly( + errorListener, + errorListener( + argThat(isStateErrorWith(message: '0')), + any, + ), + ); + + msg = '1'; + fake.elapse(const Duration(milliseconds: 1)); + + verifyNoMoreInteractions(errorListener); + + fake.elapse(const Duration(milliseconds: 1)); + + verifyNoMoreInteractions(errorListener); + + fake.elapse(const Duration(milliseconds: 1)); + + verifyOnly( + errorListener, + errorListener( + argThat(isStateErrorWith(message: '1')), + any, + ), + ); + }), + ); + + test( + 'if the retry function throws, stops retrying and report the error', + () => fakeAsync((fake) { + final errors = []; + runZonedGuarded( + () { + final container = ProviderContainer.test( + retry: (_, __) => throw StateError('Oops!'), + ); + final errorListener = ErrorListener(); + final provider = Provider((ref) => throw StateError('msg')); + + container.listen( + provider, + (prev, next) {}, + fireImmediately: true, + onError: errorListener.call, + ); + + verifyOnly( + errorListener, + errorListener( + argThat(isStateErrorWith(message: 'msg')), + any, + ), + ); + + fake.elapse(const Duration(milliseconds: 1)); + + verifyNoMoreInteractions(errorListener); + }, + (e, s) => errors.add(e), + ); + + expect( + errors, + equals([isStateErrorWith(message: 'Oops!')]), + ); + }), + ); + + test( + "If the provider specifies retry, uses the provider's " + 'logic instead of the container one.', () { + final retry1 = RetryMock(); + final retry2 = RetryMock(); + final container = ProviderContainer.test( + retry: retry1.call, + ); + + final provider = Provider( + retry: retry2.call, + (ref) => throw StateError(''), + ); + + container.listen( + provider, + (prev, next) {}, + fireImmediately: true, + onError: (e, s) {}, + ); + + verifyOnly(retry2, retry2(0, isStateError)); + verifyNoMoreInteractions(retry1); + }); + }); + + test( + 'disposes of the timer when the element is disposed', + () => fakeAsync((fake) { + final retry = RetryMock(); + when(retry(any, any)).thenReturn(const Duration(milliseconds: 200)); + final container = ProviderContainer.test(retry: retry.call); + final provider = Provider((ref) => throw StateError('')); + + container.listen( + provider, + (prev, next) {}, + fireImmediately: true, + onError: (e, s) {}, + ); + + expect(fake.pendingTimers, hasLength(1)); + + container.dispose(); + + expect(fake.pendingTimers, isEmpty); + }), + ); + }); + + test( + 'does not throw outdated error when a dependency is flushed while the dependent is building', + () async { + final container = ProviderContainer.test(); + final a = StateProvider((ref) => 0); + + final dep = Provider((ref) { + return ref.watch(a) + 10; + }); + final dependent = Provider((ref) { + if (ref.watch(a) > 0) { + ref.watch(dep); + // Voluntarily using "watch" twice. + // When `dep` is flushed, it could cause subsequent "watch" calls to throw + // because `dependent` is considered as outdated + return ref.watch(dep); + } + return null; + }); + final listener = Listener(); + + expect(container.read(dep), 10); + container.listen(dependent, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, null)); + + // schedules `dep` and `dependent` to rebuild + // Will build `dependent` before `dep` because `dependent` doesn't depend on `dep` yet + // And since nothing is watching `dep` at the moment, then `dependent` will + // rebuild before `dep` even though `dep` is its ancestor. + // This is fine since nothing is listening to `dep` yet, but it should + // not cause certain assertions to trigger + container.read(a.notifier).state++; + await container.pump(); + + verifyOnly(listener, listener(null, 11)); + }); + + group('readSelf', () { + test('throws on providers that threw', () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => throw UnimplementedError()); + + final element = container.readProviderElement(provider); + + expect( + element.readSelf, + throwsUnimplementedError, + ); + }); + }); + + group('visitChildren', () { + test('includes ref.watch dependents', () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => 0); + final dependent = Provider((ref) { + ref.watch(provider); + }); + final dependent2 = Provider((ref) { + ref.watch(provider); + }); + + container.read(dependent); + container.read(dependent2); + + final children = []; + + container.readProviderElement(provider).visitChildren(children.add); + expect( + children, + unorderedMatches([ + isA() + .having((e) => e.provider, 'provider', dependent), + isA() + .having((e) => e.provider, 'provider', dependent2), + ]), + ); + }); + + test('includes ref.listen dependents', () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => 0); + final dependent = Provider((ref) { + ref.listen(provider, (_, __) {}); + }); + final dependent2 = Provider((ref) { + ref.listen(provider, (_, __) {}); + }); + final dependent3 = Provider((ref) { + ref.listen(provider, (_, __) {}, weak: true); + }); + + container.read(dependent); + container.read(dependent2); + container.read(dependent3); + + final children = []; + + container.readProviderElement(provider).visitChildren(children.add); + + expect( + children, + unorderedMatches([ + isA() + .having((e) => e.provider, 'provider', dependent), + isA() + .having((e) => e.provider, 'provider', dependent2), + isA() + .having((e) => e.provider, 'provider', dependent3), + ]), + ); + }); + }); + + group('isActive', () { + test('Is paused if all watchers are paused', () { + final container = ProviderContainer.test(); + final provider = Provider(name: 'foo', (ref) => 0); + final dep = Provider(name: 'dep', (ref) => ref.watch(provider)); + final dep2 = Provider(name: 'dep2', (ref) => ref.watch(provider)); + + final depSub = container.listen(dep, (a, b) {}); + final dep2Sub = container.listen(dep2, (a, b) {}); + final element = container.readProviderElement(provider); + + expect(element.isActive, true); + + depSub.close(); + + expect(element.isActive, true); + + dep2Sub.close(); + + expect(element.isActive, false); + }); + + test('Is paused if all subscriptions are paused', () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => 0); + + final element = container.readProviderElement(provider); + + final sub = container.listen(provider, (_, __) {}); + final sub2 = container.listen(provider, (_, __) {}); + + expect(element.isActive, true); + + sub.pause(); + + expect(element.isActive, true); + + sub2.pause(); + + expect(element.isActive, false); + }); + + test('rejects weak listeners', () { + final provider = Provider((ref) => 0); + final container = ProviderContainer.test(); + + final element = container.readProviderElement(provider); + + expect(element.isActive, false); + + container.listen(provider, weak: true, (_, __) {}); + + expect(element.isActive, false); + }); + + test('includes provider listeners', () async { + final provider = Provider((ref) => 0); + final dep = Provider((ref) { + ref.listen(provider, (prev, value) {}); + }); + final container = ProviderContainer.test(); + + expect(container.readProviderElement(provider).isActive, false); + + container.listen(dep, (p, n) {}); + + expect(container.readProviderElement(provider).isActive, true); + }); + + test('includes provider dependents', () async { + final provider = Provider((ref) => 0); + final dep = Provider((ref) { + ref.watch(provider); + }); + final container = ProviderContainer.test(); + + expect(container.readProviderElement(provider).isActive, false); + + container.listen(dep, (p, n) {}); + + expect(container.readProviderElement(provider).isActive, true); + }); + + test('includes container listeners', () async { + final provider = Provider((ref) => 0); + final container = ProviderContainer.test(); + + expect(container.readProviderElement(provider).isActive, false); + + container.listen(provider, (_, __) {}); + + expect(container.readProviderElement(provider).isActive, true); + }); + }); + + group('hasNonWeakListeners', () { + test('excludes weak listeners', () { + final provider = Provider((ref) => 0); + final container = ProviderContainer.test(); + + final element = container.readProviderElement(provider); + + expect(element.hasNonWeakListeners, false); + + container.listen(provider, weak: true, (_, __) {}); + + expect(element.hasNonWeakListeners, false); + }); + + test('includes provider listeners', () async { + final provider = Provider((ref) => 0); + final dep = Provider((ref) { + ref.listen(provider, (prev, value) {}); + }); + final container = ProviderContainer.test(); + + expect( + container.readProviderElement(provider).hasNonWeakListeners, + false, + ); + + container.read(dep); + + expect( + container.readProviderElement(provider).hasNonWeakListeners, + true, + ); + }); + + test('includes provider dependents', () async { + final provider = Provider((ref) => 0); + final dep = Provider((ref) { + ref.watch(provider); + }); + final container = ProviderContainer.test(); + + expect( + container.readProviderElement(provider).hasNonWeakListeners, + false, + ); + + container.read(dep); + + expect( + container.readProviderElement(provider).hasNonWeakListeners, + true, + ); + }); + + test('includes container listeners', () async { + final provider = Provider((ref) => 0); + final container = ProviderContainer.test(); + + expect( + container.readProviderElement(provider).hasNonWeakListeners, + false, + ); + + container.listen(provider, (_, __) {}); + + expect( + container.readProviderElement(provider).hasNonWeakListeners, + true, + ); + }); + }); + + test('does not notify listeners twice when using fireImmediately', + () async { + final container = ProviderContainer.test(); + final listener = Listener(); + + final dep = StateProvider((ref) => 0); + final provider = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + ref.watch(dep); + return self.state = 0; + }), + ); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 0)); + }); + + test('does not notify listeners when rebuilding the state', () async { + final container = ProviderContainer.test(); + final listener = Listener(); + + final dep = StateProvider((ref) => 0); + final provider = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + ref.watch(dep); + return self.state = 0; + }), + ); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 0)); + + container.read(dep.notifier).state++; + await container.pump(); + + verifyNoMoreInteractions(listener); + }); + }); +} diff --git a/packages/riverpod/test/framework/provider_observer_test.dart b/packages/riverpod/test/src/core/provider_observer_test.dart similarity index 60% rename from packages/riverpod/test/framework/provider_observer_test.dart rename to packages/riverpod/test/src/core/provider_observer_test.dart index 8eae31284..ce00b940e 100644 --- a/packages/riverpod/test/framework/provider_observer_test.dart +++ b/packages/riverpod/test/src/core/provider_observer_test.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; import 'package:test/test.dart'; @@ -8,20 +9,8 @@ import '../utils.dart'; void main() { group('ProviderObserver', () { - test('life-cycles do nothing by default', () { - const observer = ConstObserver(); - - final provider = Provider((ref) => 0); - final container = createContainer(); - - observer.didAddProvider(provider, 0, container); - observer.didDisposeProvider(provider, container); - observer.didUpdateProvider(provider, 0, 0, container); - observer.providerDidFail(provider, 0, StackTrace.empty, container); - }); - - test('ProviderObservers can have const constructors', () { - final root = createContainer( + test('can have const constructors', () { + final root = ProviderContainer.test( observers: [ const ConstObserver(), ], @@ -35,7 +24,9 @@ void main() { () async { final observer = ObserverMock(); final observer2 = ObserverMock(); - final container = createContainer(observers: [observer, observer2]); + final container = ProviderContainer.test( + observers: [observer, observer2], + ); final dep = StateProvider((ref) => 0); final provider = Provider((ref) { if (ref.watch(dep) == 0) { @@ -54,19 +45,17 @@ void main() { verifyInOrder([ observer.didUpdateProvider( - provider, + argThat(isProviderObserverContext(provider, container)), null, 0, - container, ), observer2.didUpdateProvider( - provider, + argThat(isProviderObserverContext(provider, container)), null, 0, - container, ), ]); - verifyNever(observer.providerDidFail(any, any, any, any)); + verifyNever(observer.providerDidFail(any, any, any)); }); test( @@ -74,17 +63,18 @@ void main() { () { final provider = StateNotifierProvider, int>( (ref) => StateController(0), + dependencies: const [], ); final observer = ObserverMock('a'); final observer2 = ObserverMock('b'); final observer3 = ObserverMock('c'); - final root = createContainer(observers: [observer]); - final mid = createContainer( + final root = ProviderContainer.test(observers: [observer]); + final mid = ProviderContainer.test( parent: root, observers: [observer2], ); - final child = createContainer( + final child = ProviderContainer.test( parent: mid, overrides: [provider.overrideWith((ref) => StateController(42))], observers: [observer3], @@ -99,13 +89,37 @@ void main() { expect(child.read(provider.notifier).state++, 42); - verify(observer.didUpdateProvider(provider, 42, 43, child)).called(1); - verify(observer2.didUpdateProvider(provider, 42, 43, child)).called(1); - verify(observer3.didUpdateProvider(provider, 42, 43, child)).called(1); + verify( + observer.didUpdateProvider( + argThat(isProviderObserverContext(provider, child)), + 42, + 43, + ), + ).called(1); + verify( + observer2.didUpdateProvider( + argThat(isProviderObserverContext(provider, child)), + 42, + 43, + ), + ).called(1); + verify( + observer3.didUpdateProvider( + argThat(isProviderObserverContext(provider, child)), + 42, + 43, + ), + ).called(1); mid.read(provider.notifier).state++; - verify(observer.didUpdateProvider(provider, 0, 1, root)).called(1); + verify( + observer.didUpdateProvider( + argThat(isProviderObserverContext(provider, root)), + 0, + 1, + ), + ).called(1); verifyNoMoreInteractions(observer3); verifyNoMoreInteractions(observer2); @@ -114,7 +128,7 @@ void main() { test('handles computed provider update', () async { final observer = ObserverMock(); - final container = createContainer(observers: [observer]); + final container = ProviderContainer.test(observers: [observer]); final notifier = Counter(); final provider = StateNotifierProvider((_) => notifier); final computed = Provider((ref) => ref.watch(provider)); @@ -128,13 +142,17 @@ void main() { verifyOnly( observer, - observer.didUpdateProvider(computed, 0, 1, container), + observer.didUpdateProvider( + argThat(isProviderObserverContext(computed, container)), + 0, + 1, + ), ).called(1); }); test('handles direct provider update', () { final observer = ObserverMock(); - final container = createContainer(observers: [observer]); + final container = ProviderContainer.test(observers: [observer]); final notifier = Counter(); final provider = StateNotifierProvider((_) => notifier); @@ -145,7 +163,11 @@ void main() { verifyOnly( observer, - observer.didUpdateProvider(provider, 0, 1, container), + observer.didUpdateProvider( + argThat(isProviderObserverContext(provider, container)), + 0, + 1, + ), ).called(1); }); @@ -154,7 +176,7 @@ void main() { final observer2 = ObserverMock('b'); final provider = StateNotifierProvider((_) => Counter()); final counter = Counter(); - final container = createContainer( + final container = ProviderContainer.test( overrides: [ provider.overrideWith((ref) => counter), ], @@ -167,8 +189,14 @@ void main() { verify(listener(null, 0)).called(1); verifyNoMoreInteractions(listener); verifyInOrder([ - observer.didAddProvider(provider, 0, container), - observer2.didAddProvider(provider, 0, container), + observer.didAddProvider( + argThat(isProviderObserverContext(provider, container)), + 0, + ), + observer2.didAddProvider( + argThat(isProviderObserverContext(provider, container)), + 0, + ), ]); verifyNoMoreInteractions(observer); verifyNoMoreInteractions(observer2); @@ -177,8 +205,16 @@ void main() { verifyInOrder([ listener(0, 1), - observer.didUpdateProvider(provider, 0, 1, container), - observer2.didUpdateProvider(provider, 0, 1, container), + observer.didUpdateProvider( + argThat(isProviderObserverContext(provider, container)), + 0, + 1, + ), + observer2.didUpdateProvider( + argThat(isProviderObserverContext(provider, container)), + 0, + 1, + ), ]); verifyNoMoreInteractions(listener); verifyNoMoreInteractions(observer); @@ -187,15 +223,13 @@ void main() { test('guards didUpdateProviders', () { final observer = ObserverMock(); - when(observer.didUpdateProvider(any, any, any, any)) - .thenThrow('error1'); + when(observer.didUpdateProvider(any, any, any)).thenThrow('error1'); final observer2 = ObserverMock(); - when(observer2.didUpdateProvider(any, any, any, any)) - .thenThrow('error2'); + when(observer2.didUpdateProvider(any, any, any)).thenThrow('error2'); final observer3 = ObserverMock(); final provider = StateNotifierProvider((_) => Counter()); final counter = Counter(); - final container = createContainer( + final container = ProviderContainer.test( overrides: [provider.overrideWith((ref) => counter)], observers: [observer, observer2, observer3], ); @@ -211,9 +245,21 @@ void main() { expect(errors, ['error1', 'error2']); verifyInOrder([ - observer.didUpdateProvider(provider, 0, 1, container), - observer2.didUpdateProvider(provider, 0, 1, container), - observer3.didUpdateProvider(provider, 0, 1, container), + observer.didUpdateProvider( + argThat(isProviderObserverContext(provider, container)), + 0, + 1, + ), + observer2.didUpdateProvider( + argThat(isProviderObserverContext(provider, container)), + 0, + 1, + ), + observer3.didUpdateProvider( + argThat(isProviderObserverContext(provider, container)), + 0, + 1, + ), ]); verifyNoMoreInteractions(observer); verifyNoMoreInteractions(observer2); @@ -228,7 +274,7 @@ void main() { final isNegative = Provider((ref) { return ref.watch(provider).isNegative; }); - final container = createContainer(observers: [observer]); + final container = ProviderContainer.test(observers: [observer]); final isNegativeListener = Listener(); container.listen( @@ -244,33 +290,34 @@ void main() { await container.pump(); verifyInOrder([ - observer.didDisposeProvider(isNegative, container), + observer.didDisposeProvider( + argThat(isProviderObserverContext(isNegative, container)), + ), observer.didUpdateProvider( - provider, + argThat(isProviderObserverContext(provider, container)), 0, 1, - container, ), ]); verifyNoMoreInteractions(observer); - counter.setState(-10); + counter.state = -10; await container.pump(); verifyInOrder([ - observer.didDisposeProvider(isNegative, container), + observer.didDisposeProvider( + argThat(isProviderObserverContext(isNegative, container)), + ), observer.didUpdateProvider( - provider, + argThat(isProviderObserverContext(provider, container)), 1, -10, - container, ), isNegativeListener(false, true), observer.didUpdateProvider( - isNegative, + argThat(isProviderObserverContext(isNegative, container)), false, true, - container, ), ]); verifyNoMoreInteractions(isNegativeListener); @@ -285,17 +332,18 @@ void main() { final dep = StateProvider((ref) => 0); final provider = StateNotifierProvider, int>( (ref) => StateController(0), + dependencies: const [], ); final observer = ObserverMock('a'); final observer2 = ObserverMock('b'); final observer3 = ObserverMock('c'); - final root = createContainer(observers: [observer]); - final mid = createContainer( + final root = ProviderContainer.test(observers: [observer]); + final mid = ProviderContainer.test( parent: root, observers: [observer2], ); - final child = createContainer( + final child = ProviderContainer.test( parent: mid, overrides: [ provider.overrideWith((ref) { @@ -321,20 +369,54 @@ void main() { await child.pump(); verifyInOrder([ - observer.didDisposeProvider(provider, child), - observer.didUpdateProvider(dep, 0, 1, root), - observer.didUpdateProvider(provider, 42, null, child), - observer.providerDidFail(provider, 'error', StackTrace.empty, child), + observer.didDisposeProvider( + argThat(isProviderObserverContext(provider, child)), + ), + observer.didUpdateProvider( + argThat(isProviderObserverContext(dep, root)), + 0, + 1, + ), + observer.didUpdateProvider( + argThat(isProviderObserverContext(provider, child)), + 42, + null, + ), + observer.providerDidFail( + argThat(isProviderObserverContext(provider, child)), + 'error', + StackTrace.empty, + ), ]); verifyInOrder([ - observer2.didDisposeProvider(provider, child), - observer2.didUpdateProvider(provider, 42, null, child), - observer2.providerDidFail(provider, 'error', StackTrace.empty, child), + observer2.didDisposeProvider( + argThat(isProviderObserverContext(provider, child)), + ), + observer2.didUpdateProvider( + argThat(isProviderObserverContext(provider, child)), + 42, + null, + ), + observer2.providerDidFail( + argThat(isProviderObserverContext(provider, child)), + 'error', + StackTrace.empty, + ), ]); verifyInOrder([ - observer3.didDisposeProvider(provider, child), - observer3.didUpdateProvider(provider, 42, null, child), - observer3.providerDidFail(provider, 'error', StackTrace.empty, child), + observer3.didDisposeProvider( + argThat(isProviderObserverContext(provider, child)), + ), + observer3.didUpdateProvider( + argThat(isProviderObserverContext(provider, child)), + 42, + null, + ), + observer3.providerDidFail( + argThat(isProviderObserverContext(provider, child)), + 'error', + StackTrace.empty, + ), ]); verifyNoMoreInteractions(observer3); @@ -344,7 +426,7 @@ void main() { test('is called when FutureProvider emits an error', () async { final observer = ObserverMock(); - final container = createContainer(observers: [observer]); + final container = ProviderContainer.test(observers: [observer]); final provider = FutureProvider( (ref) => Future.error('error', StackTrace.empty), ); @@ -354,28 +436,25 @@ void main() { verifyInOrder([ observer.didAddProvider( - provider, + argThat(isProviderObserverContext(provider, container)), const AsyncLoading(), - container, ), observer.didUpdateProvider( - provider, + argThat(isProviderObserverContext(provider, container)), const AsyncLoading(), const AsyncError('error', StackTrace.empty), - container, ), observer.providerDidFail( - provider, + argThat(isProviderObserverContext(provider, container)), 'error', StackTrace.empty, - container, ), ]); }); test('is called when StreamProvider emits an error', () async { final observer = ObserverMock(); - final container = createContainer(observers: [observer]); + final container = ProviderContainer.test(observers: [observer]); final provider = StreamProvider( (ref) => Stream.error('error', StackTrace.empty), ); @@ -385,21 +464,18 @@ void main() { verifyInOrder([ observer.didAddProvider( - provider, + argThat(isProviderObserverContext(provider, container)), const AsyncLoading(), - container, ), observer.didUpdateProvider( - provider, + argThat(isProviderObserverContext(provider, container)), const AsyncLoading(), const AsyncError('error', StackTrace.empty), - container, ), observer.providerDidFail( - provider, + argThat(isProviderObserverContext(provider, container)), 'error', StackTrace.empty, - container, ), ]); }); @@ -407,7 +483,8 @@ void main() { test('is called on uncaught error during first initialization', () { final observer = ObserverMock(); final observer2 = ObserverMock(); - final container = createContainer(observers: [observer, observer2]); + final container = + ProviderContainer.test(observers: [observer, observer2]); final provider = Provider((ref) => throw UnimplementedError()); expect( @@ -416,19 +493,23 @@ void main() { ); verifyInOrder([ - observer.didAddProvider(provider, null, container), - observer2.didAddProvider(provider, null, container), + observer.didAddProvider( + argThat(isProviderObserverContext(provider, container)), + null, + ), + observer2.didAddProvider( + argThat(isProviderObserverContext(provider, container)), + null, + ), observer.providerDidFail( - provider, + argThat(isProviderObserverContext(provider, container)), argThat(isUnimplementedError), argThat(isNotNull), - container, ), observer2.providerDidFail( - provider, + argThat(isProviderObserverContext(provider, container)), argThat(isUnimplementedError), argThat(isNotNull), - container, ), ]); verifyNoMoreInteractions(observer); @@ -437,7 +518,8 @@ void main() { test('is called on uncaught error after update ', () async { final observer = ObserverMock(); final observer2 = ObserverMock(); - final container = createContainer(observers: [observer, observer2]); + final container = + ProviderContainer.test(observers: [observer, observer2]); final dep = StateProvider((ref) => 0); final provider = Provider((ref) { if (ref.watch(dep) != 0) { @@ -456,28 +538,24 @@ void main() { verifyInOrder([ observer.didUpdateProvider( - provider, + argThat(isProviderObserverContext(provider, container)), 0, null, - container, ), observer2.didUpdateProvider( - provider, + argThat(isProviderObserverContext(provider, container)), 0, null, - container, ), observer.providerDidFail( - provider, + argThat(isProviderObserverContext(provider, container)), argThat(isUnimplementedError), argThat(isNotNull), - container, ), observer2.providerDidFail( - provider, + argThat(isProviderObserverContext(provider, container)), argThat(isUnimplementedError), argThat(isNotNull), - container, ), ]); }); @@ -486,7 +564,7 @@ void main() { group('didAddProvider', () { test('when throwing during creation, receives `null` as value', () { final observer = ObserverMock(); - final container = createContainer(observers: [observer]); + final container = ProviderContainer.test(observers: [observer]); final provider = Provider((ref) => throw UnimplementedError()); expect( @@ -494,22 +572,30 @@ void main() { throwsUnimplementedError, ); - verify(observer.didAddProvider(provider, null, container)); + verify( + observer.didAddProvider( + argThat(isProviderObserverContext(provider, container)), + null, + ), + ); }); test( 'on scoped ProviderContainer, applies both child and ancestors observers', () { - final provider = Provider((ref) => 0); + final provider = Provider( + (ref) => 0, + dependencies: const [], + ); final observer = ObserverMock(); final observer2 = ObserverMock(); final observer3 = ObserverMock(); - final root = createContainer(observers: [observer]); - final mid = createContainer( + final root = ProviderContainer.test(observers: [observer]); + final mid = ProviderContainer.test( parent: root, observers: [observer2], ); - final child = createContainer( + final child = ProviderContainer.test( parent: mid, overrides: [provider.overrideWithValue(42)], observers: [observer3], @@ -518,14 +604,28 @@ void main() { expect(child.read(provider), 42); verifyInOrder([ - observer3.didAddProvider(provider, 42, child), - observer2.didAddProvider(provider, 42, child), - observer.didAddProvider(provider, 42, child), + observer3.didAddProvider( + argThat(isProviderObserverContext(provider, child)), + 42, + ), + observer2.didAddProvider( + argThat(isProviderObserverContext(provider, child)), + 42, + ), + observer.didAddProvider( + argThat(isProviderObserverContext(provider, child)), + 42, + ), ]); expect(mid.read(provider), 0); - verify(observer.didAddProvider(provider, 0, root)).called(1); + verify( + observer.didAddProvider( + argThat(isProviderObserverContext(provider, root)), + 0, + ), + ).called(1); verifyNoMoreInteractions(observer3); verifyNoMoreInteractions(observer2); @@ -536,19 +636,18 @@ void main() { final observer = ObserverMock(); final observer2 = ObserverMock(); final provider = Provider((_) => 42); - final container = createContainer(observers: [observer, observer2]); + final container = + ProviderContainer.test(observers: [observer, observer2]); expect(container.read(provider), 42); verifyInOrder([ observer.didAddProvider( - provider, + argThat(isProviderObserverContext(provider, container)), 42, - container, ), observer2.didAddProvider( - provider, + argThat(isProviderObserverContext(provider, container)), 42, - container, ), ]); verifyNoMoreInteractions(observer); @@ -557,12 +656,12 @@ void main() { test('guards against exceptions', () { final observer = ObserverMock(); - when(observer.didAddProvider(any, any, any)).thenThrow('error1'); + when(observer.didAddProvider(any, any)).thenThrow('error1'); final observer2 = ObserverMock(); - when(observer2.didAddProvider(any, any, any)).thenThrow('error2'); + when(observer2.didAddProvider(any, any)).thenThrow('error2'); final observer3 = ObserverMock(); final provider = Provider((_) => 42); - final container = createContainer( + final container = ProviderContainer.test( observers: [observer, observer2, observer3], ); @@ -577,9 +676,18 @@ void main() { expect(result, 42); expect(errors, ['error1', 'error2']); verifyInOrder([ - observer.didAddProvider(provider, 42, container), - observer2.didAddProvider(provider, 42, container), - observer3.didAddProvider(provider, 42, container), + observer.didAddProvider( + argThat(isProviderObserverContext(provider, container)), + 42, + ), + observer2.didAddProvider( + argThat(isProviderObserverContext(provider, container)), + 42, + ), + observer3.didAddProvider( + argThat(isProviderObserverContext(provider, container)), + 42, + ), ]); verifyNoMoreInteractions(observer); }); @@ -589,14 +697,19 @@ void main() { group('didDisposeProvider', () { test('supports invalidate', () { final observer = ObserverMock(); - final container = createContainer(observers: [observer]); + final container = ProviderContainer.test(observers: [observer]); final provider = Provider((ref) => 0); container.read(provider); clearInteractions(observer); container.invalidate(provider); - verifyOnly(observer, observer.didDisposeProvider(provider, container)); + verifyOnly( + observer, + observer.didDisposeProvider( + argThat(isProviderObserverContext(provider, container)), + ), + ); container.invalidate(provider); verifyNoMoreInteractions(observer); @@ -604,7 +717,7 @@ void main() { test('supports container dispose', () { final observer = ObserverMock(); - final container = createContainer(observers: [observer]); + final container = ProviderContainer.test(observers: [observer]); final provider = StateNotifierProvider((ref) => Counter()); container.read(provider); @@ -614,14 +727,16 @@ void main() { container.dispose(); verifyInOrder([ - observer.didDisposeProvider(provider, container), + observer.didDisposeProvider( + argThat(isProviderObserverContext(provider, container)), + ), ]); verifyNoMoreInteractions(observer); }); test('supports auto-dispose', () async { final observer = ObserverMock(); - final container = createContainer(observers: [observer]); + final container = ProviderContainer.test(observers: [observer]); final provider = StateNotifierProvider.autoDispose((ref) { return Counter(); }); @@ -634,16 +749,18 @@ void main() { await container.pump(); verifyInOrder([ - observer.didDisposeProvider(provider, container), + observer.didDisposeProvider( + argThat(isProviderObserverContext(provider, container)), + ), ]); verifyNoMoreInteractions(observer); }); test('is guarded', () { final observer = ObserverMock(); - when(observer.didDisposeProvider(any, any)).thenThrow('error1'); + when(observer.didDisposeProvider(any)).thenThrow('error1'); final observer2 = ObserverMock(); - when(observer2.didDisposeProvider(any, any)).thenThrow('error2'); + when(observer2.didDisposeProvider(any)).thenThrow('error2'); final observer3 = ObserverMock(); final onDispose = OnDisposeMock(); final provider = Provider((ref) { @@ -651,7 +768,7 @@ void main() { return 0; }); final provider2 = Provider((ref) => ref.watch(provider)); - final container = createContainer( + final container = ProviderContainer.test( observers: [observer, observer2, observer3], ); @@ -667,13 +784,25 @@ void main() { expect(errors, ['error1', 'error2', 'error1', 'error2']); verifyInOrder([ - observer.didDisposeProvider(provider2, container), - observer2.didDisposeProvider(provider2, container), - observer3.didDisposeProvider(provider2, container), + observer.didDisposeProvider( + argThat(isProviderObserverContext(provider2, container)), + ), + observer2.didDisposeProvider( + argThat(isProviderObserverContext(provider2, container)), + ), + observer3.didDisposeProvider( + argThat(isProviderObserverContext(provider2, container)), + ), onDispose(), - observer.didDisposeProvider(provider, container), - observer2.didDisposeProvider(provider, container), - observer3.didDisposeProvider(provider, container), + observer.didDisposeProvider( + argThat(isProviderObserverContext(provider, container)), + ), + observer2.didDisposeProvider( + argThat(isProviderObserverContext(provider, container)), + ), + observer3.didDisposeProvider( + argThat(isProviderObserverContext(provider, container)), + ), ]); verifyNoMoreInteractions(onDispose); verifyNoMoreInteractions(observer); @@ -692,8 +821,8 @@ class Counter extends StateNotifier { void increment() => state++; - // ignore: use_setters_to_change_properties - void setState(int value) => state = value; + @override + abstract int state; } class ConstObserver extends ProviderObserver { diff --git a/packages/riverpod/test/src/core/provider_subscription_test.dart b/packages/riverpod/test/src/core/provider_subscription_test.dart new file mode 100644 index 000000000..571ca481e --- /dev/null +++ b/packages/riverpod/test/src/core/provider_subscription_test.dart @@ -0,0 +1,152 @@ +import 'package:mockito/mockito.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:test/test.dart'; + +import '../matrix.dart'; +import '../utils.dart'; + +void main() { + group('_ProxySubscription', () { + test('handles pause/resume', () { + final container = ProviderContainer.test(); + final provider = FutureProvider((ref) => 0); + + final element = container.readProviderElement(provider); + + final sub = container.listen(provider.future, (previous, next) {}); + + expect(element.isActive, true); + + sub.pause(); + + expect(element.isActive, false); + + sub.resume(); + + expect(element.isActive, true); + }); + + test('closing a paused subscription unpauses the element', () { + final container = ProviderContainer.test(); + final provider = FutureProvider((ref) => 0); + + final element = container.readProviderElement(provider); + + final sub = container.listen(provider.future, (previous, next) {}); + + expect(element.isActive, true); + + sub.pause(); + + expect(element.isActive, false); + + sub.close(); + container.listen(provider.future, (previous, next) {}); + + expect(element.isActive, true); + }); + }); + + group('ProviderSubscription.resume', () { + test( + 'Resuming a paused subscription with no missed data event does not call listeners', + () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => 0); + final listener = Listener(); + + final sub = container.listen(provider, listener.call); + + sub.pause(); + + sub.resume(); + + verifyZeroInteractions(listener); + }); + + test('Resuming a paused subscription with missed data emits the last event', + () async { + final container = ProviderContainer.test(); + final provider = NotifierProvider, int>( + () => DeferredNotifier((ref, _) => 0), + ); + final listener = Listener(); + + final notifier = container.read(provider.notifier); + + final sub = container.listen(provider, listener.call); + + sub.pause(); + + notifier.state = 1; + notifier.state = 2; + + sub.resume(); + + verifyOnly(listener, listener(1, 2)); + + sub.resume(); + + verifyNoMoreInteractions(listener); + }); + + test( + 'Resuming a paused subscription with missed error emits the last error', + () async { + final container = ProviderContainer.test(); + late Error toThrow; + final stack = StackTrace.current; + final provider = Provider((ref) { + Error.throwWithStackTrace(toThrow, stack); + }); + final listener = Listener(); + final onError = ErrorListener(); + final err = Error(); + final err2 = Error(); + + toThrow = err; + + final sub = container.listen( + provider, + listener.call, + onError: onError.call, + ); + + sub.pause(); + + toThrow = err2; + try { + container.refresh(provider); + } catch (e) { + // Will rethrow the error, but we don't care about it here + } + + sub.resume(); + + verifyOnly(onError, onError(err2, stack)); + + sub.resume(); + + verifyNoMoreInteractions(onError); + verifyZeroInteractions(listener); + }); + + test('needs to be called as many times as pause() was called', () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => 0); + + final sub = container.listen(provider, (p, b) {}); + + sub.pause(); + sub.pause(); + sub.pause(); + + sub.resume(); + expect(sub.isPaused, true); + sub.resume(); + expect(sub.isPaused, true); + sub.resume(); + expect(sub.isPaused, false); + }); + }); +} diff --git a/packages/riverpod/test/src/core/provider_test.dart b/packages/riverpod/test/src/core/provider_test.dart new file mode 100644 index 000000000..e364ec729 --- /dev/null +++ b/packages/riverpod/test/src/core/provider_test.dart @@ -0,0 +1,37 @@ +import 'package:riverpod/riverpod.dart'; +import 'package:test/test.dart'; + +void main() { + group('ProviderBase', () { + test('allTransitiveDependencies', () { + final a = Provider((ref) => 0); + final b = Provider.family((ref, _) => 0, dependencies: [a]); + final c = Provider((ref) => 0, dependencies: [b]); + final d = Provider((ref) => 0, dependencies: [c]); + + expect(d.allTransitiveDependencies, containsAll([a, b, c])); + + expect(b.allTransitiveDependencies, isNotNull); + expect(b.dependencies, isNotNull); + expect(b(21).allTransitiveDependencies, isNull); + expect(b(21).dependencies, isNull); + }); + + group('addListener', () { + test('throws if specifying both weak and fireImmediately', () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => 0); + + expect( + () => container.listen( + provider, + (previous, value) {}, + weak: true, + fireImmediately: true, + ), + throwsA(isA()), + ); + }); + }); + }); +} diff --git a/packages/riverpod/test/src/core/proxy_provider_listenable_test.dart b/packages/riverpod/test/src/core/proxy_provider_listenable_test.dart new file mode 100644 index 000000000..91b29bbec --- /dev/null +++ b/packages/riverpod/test/src/core/proxy_provider_listenable_test.dart @@ -0,0 +1,28 @@ +import 'package:riverpod/riverpod.dart'; +import 'package:test/test.dart'; + +void main() { + group('_ProviderSelector', () { + test('handles pause/resume', () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => 0); + + final element = container.readProviderElement(provider); + + final sub = container.listen( + provider.select((value) => null), + (previous, next) {}, + ); + + expect(element.isActive, true); + + sub.pause(); + + expect(element.isActive, false); + + sub.resume(); + + expect(element.isActive, true); + }); + }); +} diff --git a/packages/riverpod/test/src/core/ref_test.dart b/packages/riverpod/test/src/core/ref_test.dart new file mode 100644 index 000000000..54f543f60 --- /dev/null +++ b/packages/riverpod/test/src/core/ref_test.dart @@ -0,0 +1,3129 @@ +import 'dart:async'; + +import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/framework.dart'; +import 'package:test/test.dart'; + +import '../matrix.dart'; +import '../utils.dart'; + +final refMethodsThatDependOnProviders = + )>{ + 'watch': (ref, p) => ref.watch(p), + 'read': (ref, p) => ref.read(p), + 'listen': (ref, p) => ref.listen(p, (prev, next) {}), + 'invalidate': (ref, p) => ref.invalidate(p), + 'refresh': (ref, p) => ref.refresh(p), + 'exists': (ref, p) => ref.exists(p), +}; +final refMethodsThatDependOnListenables = + )>{ + 'watch': (ref, p) => ref.watch(p), + 'read': (ref, p) => ref.read(p), + 'listen': (ref, p) => ref.listen(p, (prev, next) {}), +}; +final refMethodsThatDependOnProviderOrFamilies = + { + 'invalidate': (ref, p) => ref.invalidate(p), +}; + +void main() { + group('Ref', () { + test('asserts that a lifecycle cannot be used after a ref is unmounted', + () { + late Ref ref; + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final provider = Provider((r) { + r.watch(dep); + ref = r; + return Object(); + }); + + container.read(provider); + container.read(dep.notifier).state++; + + final another = Provider((ref) => 0); + + expect( + () => ref.watch(another), + throwsA(isA()), + ); + expect( + () => ref.invalidateSelf(), + throwsA(isA()), + ); + expect( + () => ref.invalidate(dep), + throwsA(isA()), + ); + expect( + () => ref.refresh(another), + throwsA(isA()), + ); + expect( + () => ref.read(another), + throwsA(isA()), + ); + expect( + () => ref.onDispose(() {}), + throwsA(isA()), + ); + expect( + () => ref.onAddListener(() {}), + throwsA(isA()), + ); + expect( + () => ref.onCancel(() {}), + throwsA(isA()), + ); + expect( + () => ref.onRemoveListener(() {}), + throwsA(isA()), + ); + expect( + () => ref.onResume(() {}), + throwsA(isA()), + ); + expect( + () => ref.notifyListeners(), + throwsA(isA()), + ); + expect( + () => ref.listen(another, (_, __) {}), + throwsA(isA()), + ); + expect( + () => ref.exists(another), + throwsA(isA()), + ); + + expect( + () => ref.keepAlive(), + throwsA(isA()), + ); + }); + + test('asserts that a lifecycle cannot be used inside selectors', () { + late Ref ref; + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final provider = Provider((r) { + r.watch(dep); + ref = r; + return Object(); + }); + + container.read(provider); + + container.read(dep.notifier).state++; + + final another = Provider((ref) => 0); + + expect( + () => container.read(provider.select((_) => ref.watch(another))), + throwsA(isA()), + ); + expect( + () => container.read(provider.select((_) => ref.invalidateSelf())), + throwsA(isA()), + ); + expect( + () => container.read(provider.select((_) => ref.invalidate(dep))), + throwsA(isA()), + ); + expect( + () => container.read(provider.select((_) => ref.refresh(another))), + throwsA(isA()), + ); + expect( + () => container.read(provider.select((_) => ref.read(another))), + throwsA(isA()), + ); + expect( + () => container.read(provider.select((_) => ref.onDispose(() {}))), + throwsA(isA()), + ); + expect( + () => container.read(provider.select((_) => ref.onAddListener(() {}))), + throwsA(isA()), + ); + expect( + () => container.read(provider.select((_) => ref.onCancel(() {}))), + throwsA(isA()), + ); + expect( + () => + container.read(provider.select((_) => ref.onRemoveListener(() {}))), + throwsA(isA()), + ); + expect( + () => container.read(provider.select((_) => ref.onResume(() {}))), + throwsA(isA()), + ); + expect( + () => container.read(provider.select((_) => ref.notifyListeners())), + throwsA(isA()), + ); + expect( + () => container + .read(provider.select((_) => ref.listen(another, (_, __) {}))), + throwsA(isA()), + ); + expect( + () => container.read(provider.select((_) => ref.exists(another))), + throwsA(isA()), + ); + + expect( + () => container.read(provider.select((_) => ref.keepAlive())), + throwsA(isA()), + ); + }); + + group('invalidate', () { + test('can disposes of the element if not used anymore', () async { + late Ref ref; + final dep = Provider((r) { + ref = r; + return 0; + }); + final provider = Provider.autoDispose((r) { + r.keepAlive(); + return 0; + }); + final container = ProviderContainer.test(); + + container.read(dep); + container.read(provider); + ref.invalidate(provider); + + await container.pump(); + + expect( + container.getAllProviderElements().map((e) => e.origin), + [dep], + ); + }); + + test('does not mount providers if they are not already mounted', + () async { + final container = ProviderContainer.test(); + final provider = FutureProvider((r) async => 0); + late Ref ref; + final dep = Provider((r) { + ref = r; + return 0; + }); + + container.read(dep); + + ref.invalidate(provider); + + expect( + container.getAllProviderElements().map((e) => e.origin), + [dep], + ); + }); + + test('supports asReload', () async { + final container = ProviderContainer.test(); + final provider = FutureProvider((r) async => 0); + late Ref ref; + final dep = Provider((r) { + ref = r; + return 0; + }); + + container.read(dep); + await container.read(provider.future); + expect(container.read(provider), const AsyncValue.data(0)); + + ref.invalidate(provider, asReload: true); + + expect( + container.read(provider), + isA>().having((e) => e.value, 'value', 0), + ); + }); + }); + + group('invalidateSelf', () { + test('calls dispose immediately', () { + final container = ProviderContainer.test(); + final listener = OnDisposeMock(); + late Ref ref; + final provider = Provider((r) { + ref = r; + ref.onDispose(listener.call); + }); + + container.read(provider); + verifyZeroInteractions(listener); + + ref.invalidateSelf(); + + verifyOnly(listener, listener()); + }); + + test('triggers a rebuild on next frame', () async { + final container = ProviderContainer.test(); + final listener = Listener(); + var result = 0; + late Ref ref; + final provider = Provider((r) { + ref = r; + return result; + }); + + container.listen(provider, listener.call); + verifyZeroInteractions(listener); + + ref.invalidateSelf(); + result = 1; + + verifyZeroInteractions(listener); + + await container.pump(); + + verifyOnly(listener, listener(0, 1)); + }); + + test('merges the rebuild with dependency change rebuild', () async { + final container = ProviderContainer.test(); + final listener = Listener(); + final dep = StateProvider((ref) => 0); + late Ref ref; + final provider = Provider((r) { + ref = r; + return ref.watch(dep); + }); + + container.listen(provider, listener.call); + verifyZeroInteractions(listener); + + ref.invalidateSelf(); + container.read(dep.notifier).state++; + + verifyZeroInteractions(listener); + + await container.pump(); + + verifyOnly(listener, listener(0, 1)); + }); + + test('can disposes of the element if not used anymore', () async { + late Ref ref; + final provider = Provider.autoDispose((r) { + ref = r; + r.keepAlive(); + return 0; + }); + final container = ProviderContainer.test(); + + container.read(provider); + ref.invalidateSelf(); + + await container.pump(); + + expect(container.getAllProviderElements(), isEmpty); + }); + + test('supports asReload', () async { + final container = ProviderContainer.test(); + late Ref ref; + final provider = FutureProvider((r) async { + ref = r; + return 0; + }); + + await container.read(provider.future); + expect(container.read(provider), const AsyncValue.data(0)); + + ref.invalidateSelf(asReload: true); + + expect( + container.read(provider), + isA>().having((e) => e.value, 'value', 0), + ); + }); + }); + + group( + 'asserts that a provider cannot depend on a provider that is not in its dependencies:', + () { + for (final entry in refMethodsThatDependOnProviders.entries) { + final method = entry.key; + final call = entry.value; + + test('Using `$method` when passing a provider', () { + final transitiveDep = Provider((ref) => 0, dependencies: const []); + final dep = Provider((ref) => 0, dependencies: [transitiveDep]); + final depFamily = Provider.family( + (ref, id) => 0, + dependencies: const [], + ); + final unrelatedScoped = Provider((ref) => 0, dependencies: const []); + final unrelatedScopedFamily = Provider.family( + (ref, i) => 0, + dependencies: const [], + ); + final nonScopedProvider = Provider((ref) => 0); + final provider = Provider( + (ref) => ref, + dependencies: [dep, depFamily], + ); + final family = Provider.family( + (ref, id) => ref, + dependencies: [dep, depFamily], + ); + + final container = ProviderContainer.test(); + final ref = container.read(provider); + final ref2 = container.read(family(0)); + + // accepts providers that are part of its dependencies + call(ref, dep); + call(ref2, dep); + call(ref, depFamily(42)); + call(ref2, depFamily(42)); + + // accepts non-scoped providers + call(ref, nonScopedProvider); + call(ref2, nonScopedProvider); + + // rejects providers that are not part of its dependencies + expect( + () => call(ref, transitiveDep), + throwsA(isA()), + ); + expect( + () => call(ref2, transitiveDep), + throwsA(isA()), + ); + expect( + () => call(ref, unrelatedScoped), + throwsA(isA()), + ); + expect( + () => call(ref2, unrelatedScoped), + throwsA(isA()), + ); + expect( + () => call(ref2, unrelatedScopedFamily(42)), + throwsA(isA()), + ); + }); + } + + for (final entry in refMethodsThatDependOnListenables.entries) { + final method = entry.key; + final call = entry.value; + + test('Using `$method` when passing a listenable', () async { + final transitiveDep = FutureProvider( + (ref) => 0, + dependencies: const [], + ); + final dep = FutureProvider((ref) => 0, dependencies: [transitiveDep]); + final depFamily = FutureProvider.family( + (ref, id) => 0, + dependencies: const [], + ); + final unrelatedScoped = FutureProvider( + (ref) => 0, + dependencies: const [], + ); + final nonScopedProvider = FutureProvider((ref) => 0); + final provider = FutureProvider( + (ref) => ref, + dependencies: [dep, depFamily], + ); + + final container = ProviderContainer.test(); + final ref = container.read(provider).requireValue; + + // accepts providers that are part of its dependencies + call(ref, dep.select((value) => 0)); + call(ref, dep.selectAsync((value) => 0)); + call(ref, depFamily(42).select((value) => 0)); + + // accepts non-scoped providers + call(ref, nonScopedProvider.select((value) => 0)); + + // rejects providers that are not part of its dependencies + await expectLater( + () => call(ref, unrelatedScoped.select((value) => 0)), + throwsA(isA()), + ); + await expectLater( + () => call(ref, unrelatedScoped.selectAsync((value) => 0)), + throwsA(isA()), + ); + await expectLater( + () => call(ref, unrelatedScoped.future), + throwsA(isA()), + ); + }); + } + + for (final entry in refMethodsThatDependOnProviderOrFamilies.entries) { + final method = entry.key; + final call = entry.value; + + test('Using `$method` when passing a family (not `family(arg)`)', () { + final transitiveDep = Provider.family( + (ref, i) => 0, + dependencies: const [], + ); + final dep = Provider.family( + (ref, id) => 0, + dependencies: [transitiveDep], + ); + final unrelatedScoped = Provider.family( + (ref, i) => 0, + dependencies: const [], + ); + final nonScopedProvider = Provider.family((ref, i) => 0); + + final provider = Provider( + (ref) => ref, + dependencies: [dep], + ); + + final container = ProviderContainer.test(); + final ref = container.read(provider); + + // accepts providers that are part of its dependencies + call(ref, dep); + + // accepts non-scoped providers + call(ref, nonScopedProvider); + + // rejects providers that are not part of its dependencies + expect( + () => call(ref, transitiveDep), + throwsA(isA()), + ); + expect( + () => call(ref, unrelatedScoped), + throwsA(isA()), + ); + }); + } + }); + + group('.exists', () { + test('Returns true if available on ancestor container', () { + final root = ProviderContainer.test(); + final container = ProviderContainer.test(parent: root); + final provider = Provider((ref) => 0); + + root.read(provider); + + expect(container.exists(provider), true); + expect(root.exists(provider), true); + }); + + test('simple use-case', () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => 0); + final refProvider = Provider((ref) => ref); + + final ref = container.read(refProvider); + + expect( + container.getAllProviderElements().map((e) => e.origin), + [refProvider], + ); + expect(container.exists(refProvider), true); + expect(ref.exists(provider), false); + + ref.read(provider); + + expect(ref.exists(provider), true); + }); + }); + + group('listenSelf', () { + test('does not break autoDispose', () async { + final container = ProviderContainer.test(); + + final provider = + NotifierProvider.autoDispose, void>( + () => DeferredNotifier((ref, self) { + self.listenSelf((previous, next) {}); + }), + ); + + container.read(provider); + expect(container.getAllProviderElements(), [anything]); + + await container.pump(); + + expect(container.getAllProviderElements(), isEmpty); + }); + + test('listens to mutations post build', () async { + final container = ProviderContainer.test(); + final listener = Listener(); + final listener2 = Listener(); + + final provider = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + self.listenSelf(listener.call); + self.listenSelf(listener2.call); + + return 0; + }), + ); + + container.read(provider); + + verifyInOrder([ + listener(null, 0), + listener2(null, 0), + ]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + container.read(provider.notifier).state = 42; + + verifyInOrder([ + listener(0, 42), + listener2(0, 42), + ]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('listens to rebuild', () async { + final container = ProviderContainer.test(); + final listener = Listener(); + final listener2 = Listener(); + var result = 0; + + final provider = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + self.listenSelf(listener.call); + self.listenSelf(listener2.call); + + return result; + }), + ); + + container.read(provider); + + verifyInOrder([ + listener(null, 0), + listener2(null, 0), + ]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + result = 42; + container.refresh(provider); + + verifyInOrder([ + listener(0, 42), + listener2(0, 42), + ]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('notify listeners independently from updateShouldNotify', () async { + final container = ProviderContainer.test(); + final listener = Listener(); + final listener2 = Listener(); + + final provider = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + self.listenSelf(listener.call); + self.listenSelf(listener2.call); + + return 0; + }), + ); + + container.read(provider); + + verifyInOrder([ + listener(null, 0), + listener2(null, 0), + ]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + container.refresh(provider); + + verifyInOrder([ + listener(0, 0), + listener2(0, 0), + ]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('clears state listeners on rebuild', () async { + final container = ProviderContainer.test(); + final listener = Listener(); + final listener2 = Listener(); + var result = 0; + + final provider = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + if (result == 0) { + self.listenSelf(listener.call); + } else { + self.listenSelf(listener2.call); + } + + return result; + }), + ); + + container.read(provider); + + verifyOnly(listener, listener(null, 0)); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + result = 42; + container.refresh(provider); + + verifyOnly(listener2, listener2(0, 42)); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('listens to errors', () { + final container = ProviderContainer.test(); + final listener = Listener(); + final errorListener = ErrorListener(); + final errorListener2 = ErrorListener(); + var error = 42; + + final provider = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + self.listenSelf(listener.call, onError: errorListener.call); + self.listenSelf((prev, next) {}, onError: errorListener2.call); + + Error.throwWithStackTrace(error, StackTrace.empty); + }), + ); + + expect(() => container.read(provider), throwsA(42)); + + verifyZeroInteractions(listener); + verifyInOrder([ + errorListener(42, StackTrace.empty), + errorListener2(42, StackTrace.empty), + ]); + verifyNoMoreInteractions(errorListener); + verifyNoMoreInteractions(errorListener2); + + error = 21; + expect(() => container.refresh(provider), throwsA(21)); + + verifyZeroInteractions(listener); + + verifyInOrder([ + errorListener(21, StackTrace.empty), + errorListener2(21, StackTrace.empty), + ]); + verifyNoMoreInteractions(errorListener); + verifyNoMoreInteractions(errorListener2); + }); + + test('executes error listener before other listeners', () { + final container = ProviderContainer.test(); + final errorListener = ErrorListener(); + final errorListener2 = ErrorListener(); + Exception? error; + + final provider = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + self.listenSelf((prev, next) {}, onError: errorListener.call); + + if (error != null) { + Error.throwWithStackTrace(error, StackTrace.empty); + } + + return 0; + }), + ); + + container.listen( + provider, + (prev, next) {}, + onError: errorListener2.call, + ); + + verifyZeroInteractions(errorListener); + verifyZeroInteractions(errorListener2); + + error = Exception(); + expect(() => container.refresh(provider), throwsA(error)); + + verifyInOrder([ + errorListener(error, StackTrace.empty), + errorListener2(error, StackTrace.empty), + ]); + verifyNoMoreInteractions(errorListener); + verifyNoMoreInteractions(errorListener2); + }); + + test('executes state listener before other listeners', () { + final container = ProviderContainer.test(); + final listener = Listener(); + final listener2 = Listener(); + var result = 0; + + final provider = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + self.listenSelf(listener.call); + return result; + }), + ); + + container.listen(provider, listener2.call, fireImmediately: true); + + verifyInOrder([ + listener(null, 0), + listener2(null, 0), + ]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + result = 42; + container.refresh(provider); + + verifyInOrder([ + listener(0, 42), + listener2(0, 42), + ]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('listeners are not allowed to modify the state', () {}); + }); + + group('listen', () { + test('does not invoke value listeners if paused', () { + final container = ProviderContainer.test(); + final listener = Listener(); + late Ref ref; + final provider = Provider((r) { + ref = r; + return 0; + }); + + final sub = container.listen(provider, listener.call); + sub.pause(); + + verifyZeroInteractions(listener); + + ref.notifyListeners(); + + verifyZeroInteractions(listener); + }); + + test('does not invoke error listeners if paused', () { + final container = ProviderContainer.test(); + final listener = ErrorListener(); + late Ref ref; + var throws = false; + final provider = Provider((r) { + ref = r; + if (throws) throw StateError('err'); + }); + + final sub = container.listen( + provider, + (a, b) {}, + onError: listener.call, + ); + + sub.pause(); + + verifyZeroInteractions(listener); + + throws = true; + try { + container.refresh(provider); + } catch (e) { + // We just want to trigger onError listener + } + verifyZeroInteractions(listener); + }); + + group('weak', () { + test('Mounts the element but does not initialize the provider', () { + final container = ProviderContainer.test(); + + final dep = StateProvider((ref) => 0); + final provider = Provider((ref) { + ref.listen( + dep, + weak: true, + (previous, next) {}, + ); + return 0; + }); + + container.read(provider); + + expect( + container.pointerManager.orphanPointers.pointers[dep]!.element, + isA>() + .having((e) => e.stateResult, 'stateResult', null), + ); + }); + + test('when finally mounting an element, notifies weak listeners', () { + final container = ProviderContainer.test(); + + final listener = Listener(); + final dep = StateProvider((ref) => 0); + final provider = Provider((ref) { + ref.listen(dep, weak: true, listener.call); + return 0; + }); + + container.read(provider); + + verifyZeroInteractions(listener); + + // Flush the provider + container.read(dep); + + verifyOnly(listener, listener(null, 0)); + }); + + test('when finally rebuilding a dirty element, notifies weak listeners', + () { + final container = ProviderContainer.test(); + + final listener = Listener(); + var result = 0; + final dep = StateProvider((ref) => result); + final provider = Provider((ref) { + ref.listen(dep, weak: true, listener.call); + return 0; + }); + + container.read(dep); + container.invalidate(dep); + result = 1; + + container.read(provider); + + verifyZeroInteractions(listener); + + // Flush the provider + container.read(dep); + + verifyOnly(listener, listener(0, 1)); + }); + + test( + 'adding a weak listener on an invalidated provider does not rebuild it', + () { + final container = ProviderContainer.test(); + var buildCount = 0; + final dep = StateProvider((ref) { + buildCount++; + return 0; + }); + final provider = Provider((ref) { + ref.listen(dep, weak: true, (previous, next) {}); + return 0; + }); + + container.read(dep); + container.invalidate(dep); + expect(buildCount, 1); + + container.read(provider); + + expect(buildCount, 1); + }); + + test('closing the subscription removes the listener', () { + final container = ProviderContainer.test(); + final provider = Provider((ref) => Object()); + final listener = Listener(); + + final sub = container.listen( + provider, + weak: true, + listener.call, + ); + sub.close(); + + container.read(provider); + container.refresh(provider); + + verifyZeroInteractions(listener); + }); + + test('does not count towards the pause mechanism', () async { + final container = ProviderContainer.test(); + + final listener = Listener(); + final provider = Provider((ref) => Object()); + + container.listen(provider, weak: true, listener.call); + container.invalidate(provider); + + await container.pump(); + + verifyZeroInteractions(listener); + }); + }); + + test('ref.listen on outdated provider causes it to rebuild', () { + final dep = StateProvider((ref) => 0); + var buildCount = 0; + final provider = Provider((ref) { + buildCount++; + return ref.watch(dep); + }); + final listener = Listener(); + final another = Provider((ref) { + ref.listen(provider, listener.call, fireImmediately: true); + }); + final container = ProviderContainer.test(); + + expect(container.read(provider), 0); + expect(buildCount, 1); + + container.read(dep.notifier).state = 42; + + expect(buildCount, 1); + + container.read(another); + + expect(buildCount, 2); + verifyOnly(listener, listener(null, 42)); + }); + + test('can downcast the value', () async { + final listener = Listener(); + final dep = StateProvider((ref) => 0); + final provider = Provider((ref) { + ref.listen(dep, listener.call); + }); + + final container = ProviderContainer.test(); + container.read(provider); + + verifyZeroInteractions(listener); + + container.read(dep.notifier).state++; + await container.pump(); + + verifyOnly(listener, listener(0, 1)); + }); + + test('can listen selectors', () async { + final container = ProviderContainer.test(); + final provider = + StateNotifierProvider, int>((ref) { + return StateController(0); + }); + final isEvenSelector = Selector(false, (c) => c.isEven); + final isEvenListener = Listener(); + var buildCount = 0; + + final another = Provider((ref) { + buildCount++; + ref.listen( + provider.select(isEvenSelector.call), + isEvenListener.call, + ); + return 0; + }); + + container.read(another); + + expect(buildCount, 1); + verifyZeroInteractions(isEvenListener); + verifyOnly(isEvenSelector, isEvenSelector(0)); + + container.read(provider.notifier).state = 2; + + verifyOnly(isEvenSelector, isEvenSelector(2)); + verifyZeroInteractions(isEvenListener); + + container.read(provider.notifier).state = 3; + + verifyOnly(isEvenSelector, isEvenSelector(3)); + verifyOnly(isEvenListener, isEvenListener(true, false)); + + container.read(provider.notifier).state = 4; + + verifyOnly(isEvenSelector, isEvenSelector(4)); + verifyOnly(isEvenListener, isEvenListener(false, true)); + + await container.pump(); + + expect(buildCount, 1); + }); + + test('listen on selectors supports fireImmediately', () async { + final container = ProviderContainer.test(); + final provider = + StateNotifierProvider, int>((ref) { + return StateController(0); + }); + final isEvenSelector = Selector(false, (c) => c.isEven); + final isEvenListener = Listener(); + var buildCount = 0; + + final another = Provider((ref) { + buildCount++; + ref.listen( + provider.select(isEvenSelector.call), + isEvenListener.call, + fireImmediately: true, + ); + return 0; + }); + + container.read(another); + + expect(buildCount, 1); + verifyOnly(isEvenListener, isEvenListener(null, true)); + verifyOnly(isEvenSelector, isEvenSelector(0)); + + container.read(provider.notifier).state = 2; + + verifyOnly(isEvenSelector, isEvenSelector(2)); + verifyNoMoreInteractions(isEvenListener); + + container.read(provider.notifier).state = 3; + + verifyOnly(isEvenSelector, isEvenSelector(3)); + verifyOnly(isEvenListener, isEvenListener(true, false)); + + await container.pump(); + + expect(buildCount, 1); + }); + + test( + 'when rebuild throws identical error/stack, listeners are still notified', + () { + final container = ProviderContainer.test(); + const stack = StackTrace.empty; + final listener = Listener(); + final errorListener = ErrorListener(); + final provider = Provider((ref) { + Error.throwWithStackTrace(42, stack); + }); + + container.listen( + provider, + listener.call, + onError: errorListener.call, + fireImmediately: true, + ); + + verifyZeroInteractions(listener); + verifyOnly(errorListener, errorListener(42, stack)); + + expect(() => container.refresh(provider), throwsA(42)); + + verifyZeroInteractions(listener); + verifyOnly(errorListener, errorListener(42, stack)); + }); + + test('cannot listen itself', () { + final container = ProviderContainer.test(); + final listener = Listener(); + late NotifierProvider, int> provider; + provider = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + ref.listen(provider, (previous, next) {}); + return 0; + }), + ); + + expect(() => container.read(provider), throwsA(isAssertionError)); + + container.read(provider.notifier).state = 42; + + verifyZeroInteractions(listener); + }); + + test('expose previous and new value on change', () { + final container = ProviderContainer.test(); + final dep = StateNotifierProvider, int>( + (ref) => StateController(0), + ); + final listener = Listener(); + final provider = Provider((ref) { + ref.listen(dep, listener.call, fireImmediately: true); + }); + + container.read(provider); + + verifyOnly(listener, listener(null, 0)); + + container.read(dep.notifier).state++; + + verifyOnly(listener, listener(0, 1)); + }); + + test( + 'calling ref.listen on a provider with an outdated dependency flushes it, then add the listener', + () { + final container = ProviderContainer.test(); + var buildCount = 0; + final dep2 = StateNotifierProvider, int>( + (ref) => StateController(0), + ); + final dep = Provider((ref) { + buildCount++; + return ref.watch(dep2); + }); + final listener = Listener(); + final provider = Provider((ref) { + ref.listen(dep, listener.call); + }); + + container.read(dep); + container.read(dep2.notifier).state++; // mark `dep` as outdated + + expect(buildCount, 1); + verifyZeroInteractions(listener); + + container.read(provider); + + expect(buildCount, 2); + verifyZeroInteractions(listener); + }); + + test( + 'when using selectors, `previous` is the latest notification instead of latest event', + () { + final container = ProviderContainer.test(); + final dep = StateNotifierProvider, int>( + (ref) => StateController(0), + ); + final listener = Listener(); + final provider = Provider((ref) { + ref.listen( + dep.select((value) => value.isEven), + listener.call, + fireImmediately: true, + ); + }); + + container.read(provider); + verifyOnly(listener, listener(null, true)); + + container.read(dep.notifier).state += 2; + + verifyNoMoreInteractions(listener); + + container.read(dep.notifier).state++; + + verifyOnly(listener, listener(true, false)); + }); + + test('when no onError is specified, fallbacks to handleUncaughtError', + () async { + final container = ProviderContainer.test(); + final isErrored = StateProvider((ref) => false); + final dep = Provider((ref) { + if (ref.watch(isErrored)) throw UnimplementedError(); + return 0; + }); + final listener = Listener(); + final errors = []; + final provider = Provider((ref) { + runZonedGuarded( + () => ref.listen(dep, listener.call), + (err, stack) => errors.add(err), + ); + }); + + container.listen(provider, (a, b) {}); + + verifyZeroInteractions(listener); + expect(errors, isEmpty); + + container.read(isErrored.notifier).state = true; + + await container.pump(); + + verifyZeroInteractions(listener); + expect(errors, [isUnimplementedError]); + }); + + test( + 'when no onError is specified, selectors fallbacks to handleUncaughtError', + () async { + final container = ProviderContainer.test(); + final isErrored = StateProvider((ref) => false); + final dep = Provider((ref) { + if (ref.watch(isErrored)) throw UnimplementedError(); + return 0; + }); + final listener = Listener(); + final errors = []; + final provider = Provider((ref) { + runZonedGuarded( + () => ref.listen(dep.select((value) => value), listener.call), + (err, stack) => errors.add(err), + ); + }); + + container.listen(provider, (p, n) {}); + + verifyZeroInteractions(listener); + expect(errors, isEmpty); + + container.read(isErrored.notifier).state = true; + + await container.pump(); + + verifyZeroInteractions(listener); + expect(errors, [isUnimplementedError]); + }); + + test('when rebuild throws, calls onError', () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final provider = Provider((ref) { + if (ref.watch(dep) != 0) { + throw UnimplementedError(); + } + return 0; + }); + final errorListener = ErrorListener(); + final listener = Listener(); + + final a = Provider((ref) { + ref.listen(provider, listener.call, onError: errorListener.call); + }); + + container.listen(a, (p, n) {}); + + verifyZeroInteractions(errorListener); + verifyZeroInteractions(listener); + + container.read(dep.notifier).state++; + await container.pump(); + + verifyZeroInteractions(listener); + verifyOnly( + errorListener, + errorListener(isUnimplementedError, any), + ); + }); + + test('when rebuild throws on selector, calls onError', () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final provider = Provider((ref) { + if (ref.watch(dep) != 0) { + throw UnimplementedError(); + } + return 0; + }); + final errorListener = ErrorListener(); + final listener = Listener(); + + final a = Provider((ref) { + ref.listen( + provider.select((value) => value), + listener.call, + onError: errorListener.call, + ); + }); + + container.listen(a, (a, b) {}); + + verifyZeroInteractions(errorListener); + verifyZeroInteractions(listener); + + container.read(dep.notifier).state++; + await container.pump(); + + verifyZeroInteractions(listener); + verifyOnly( + errorListener, + errorListener(isUnimplementedError, any), + ); + }); + + group('fireImmediately', () { + test('when no onError is specified, fallbacks to handleUncaughtError', + () { + final container = ProviderContainer.test(); + final dep = Provider((ref) => throw UnimplementedError()); + final listener = Listener(); + final errors = []; + final provider = Provider((ref) { + runZonedGuarded( + () { + ref.listen( + dep, + listener.call, + fireImmediately: true, + ); + }, + (err, stack) => errors.add(err), + ); + }); + + container.read(provider); + + verifyZeroInteractions(listener); + expect(errors, [ + isUnimplementedError, + ]); + }); + + test( + 'when no onError is specified on selectors, fallbacks to handleUncaughtError', + () { + final container = ProviderContainer.test(); + final dep = Provider((ref) => throw UnimplementedError()); + final listener = Listener(); + final errors = []; + final provider = Provider((ref) { + runZonedGuarded( + () { + ref.listen( + dep.select((value) => value), + listener.call, + fireImmediately: true, + ); + }, + (err, stack) => errors.add(err), + ); + }); + + container.read(provider); + + verifyZeroInteractions(listener); + expect(errors, [ + isUnimplementedError, + ]); + }); + + test('on provider that threw, fireImmediately calls onError', () { + final container = ProviderContainer.test(); + final dep = Provider((ref) => throw UnimplementedError()); + final listener = Listener(); + final errorListener = ErrorListener(); + final provider = Provider((ref) { + ref.listen( + dep, + listener.call, + onError: errorListener.call, + fireImmediately: true, + ); + }); + + container.read(provider); + + verifyZeroInteractions(listener); + verifyOnly( + errorListener, + errorListener(isUnimplementedError, argThat(isNotNull)), + ); + }); + + test( + 'when selecting provider that threw, fireImmediately calls onError', + () { + final container = ProviderContainer.test(); + final dep = Provider((ref) => throw UnimplementedError()); + final listener = Listener(); + final errorListener = ErrorListener(); + final provider = Provider((ref) { + ref.listen( + dep.select((value) => 0), + listener.call, + onError: errorListener.call, + fireImmediately: true, + ); + }); + + container.read(provider); + + verifyZeroInteractions(listener); + verifyOnly( + errorListener, + errorListener(isUnimplementedError, argThat(isNotNull)), + ); + }); + + test('correctly listens to the provider if selector listener throws', + () { + final dep = StateProvider((ref) => 0); + final listener = Listener(); + var isFirstCall = true; + + final container = ProviderContainer.test(); + final errors = []; + + ProviderSubscription? sub; + + final provider = Provider((ref) { + sub = runZonedGuarded( + () => ref.listen( + dep.select((value) => value), + (prev, value) { + listener(prev, value); + if (isFirstCall) { + isFirstCall = false; + throw StateError('Some error'); + } + }, + fireImmediately: true, + ), + (err, stack) => errors.add(err), + ); + }); + + container.listen(provider, (prev, value) {}); + + expect(sub, isNotNull); + verifyOnly(listener, listener(null, 0)); + expect(errors, [isStateError]); + + container.read(dep.notifier).state++; + verifyOnly(listener, listener(0, 1)); + }); + + test('correctly listens to the provider if normal listener throws', () { + final dep = StateProvider((ref) => 0); + final listener = Listener(); + var isFirstCall = true; + + final container = ProviderContainer.test(); + final errors = []; + + ProviderSubscription? sub; + + final provider = Provider((ref) { + sub = runZonedGuarded( + () => ref.listen( + dep, + (prev, value) { + listener(prev, value); + if (isFirstCall) { + isFirstCall = false; + throw StateError('Some error'); + } + }, + fireImmediately: true, + ), + (err, stack) => errors.add(err), + ); + }); + + container.listen(provider, (prev, value) {}); + + expect(sub, isNotNull); + verifyOnly(listener, listener(null, 0)); + expect(errors, [isStateError]); + + container.read(dep.notifier).state++; + verifyOnly(listener, listener(0, 1)); + }); + + test( + 'correctly listens to the provider if selector onError listener throws', + () async { + final dep = StateProvider((ref) => 0); + final dep2 = Provider((ref) { + if (ref.watch(dep) == 0) { + throw UnimplementedError(); + } + return ref.watch(dep); + }); + final listener = Listener(); + final errorListener = ErrorListener(); + var isFirstCall = true; + + final container = ProviderContainer.test(); + final errors = []; + + ProviderSubscription? sub; + + final provider = Provider((ref) { + sub = runZonedGuarded( + () => ref.listen( + dep2.select((value) => value), + listener.call, + onError: (err, stack) { + errorListener(err, stack); + if (isFirstCall) { + isFirstCall = false; + throw StateError('Some error'); + } + }, + fireImmediately: true, + ), + (err, stack) => errors.add(err), + ); + }); + + container.listen(provider, (p, n) {}); + + expect(sub, isNotNull); + verifyZeroInteractions(listener); + verifyOnly( + errorListener, + errorListener(argThat(isUnimplementedError), argThat(isNotNull)), + ); + expect(errors, [isStateError]); + + container.read(dep.notifier).state++; + await container.pump(); + + verifyNoMoreInteractions(errorListener); + verifyOnly(listener, listener(null, 1)); + }); + + test( + 'correctly listens to the provider if normal onError listener throws', + () async { + final dep = StateProvider((ref) => 0); + final dep2 = Provider((ref) { + if (ref.watch(dep) == 0) { + throw UnimplementedError(); + } + return ref.watch(dep); + }); + final listener = Listener(); + final errorListener = ErrorListener(); + var isFirstCall = true; + + final container = ProviderContainer.test(); + final errors = []; + + ProviderSubscription? sub; + + final provider = Provider((ref) { + sub = runZonedGuarded( + () => ref.listen( + dep2, + listener.call, + onError: (err, stack) { + errorListener(err, stack); + if (isFirstCall) { + isFirstCall = false; + throw StateError('Some error'); + } + }, + fireImmediately: true, + ), + (err, stack) => errors.add(err), + ); + }); + + container.listen(provider, (p, n) {}); + + expect(sub, isNotNull); + verifyZeroInteractions(listener); + verifyOnly( + errorListener, + errorListener(argThat(isUnimplementedError), argThat(isNotNull)), + ); + expect(errors, [isStateError]); + + container.read(dep.notifier).state++; + await container.pump(); + + verifyNoMoreInteractions(errorListener); + verifyOnly(listener, listener(null, 1)); + }); + }); + }); + + group('keepAlive', () { + test( + 'Does not cause an infinite loop if aborted directly in the callback', + () async { + final container = ProviderContainer.test(); + var buildCount = 0; + var disposeCount = 0; + final provider = Provider.autoDispose((ref) { + buildCount++; + ref.onDispose(() => disposeCount++); + final link = ref.keepAlive(); + link.close(); + return 'value'; + }); + + container.read(provider); + + expect(buildCount, 1); + expect(disposeCount, 0); + expect( + container.getAllProviderElements().map((e) => e.provider), + [provider], + ); + + await container.pump(); + + expect(buildCount, 1); + expect(disposeCount, 1); + expect( + container.getAllProviderElements().map((e) => e.provider), + isEmpty, + ); + }); + + test('when the provider rebuilds, links are cleared', () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + KeepAliveLink? a; + + final provider = Provider.autoDispose((ref) { + ref.watch(dep); + a ??= ref.keepAlive(); + }); + + container.read(provider); + await container.pump(); + + expect( + container.getAllProviderElements().map((e) => e.provider), + contains(provider), + ); + + container.read(dep.notifier).state++; + // manually trigger rebuild, as the provider is not listened + container.read(provider); + await container.pump(); + + expect( + container.getAllProviderElements().map((e) => e.provider), + isNot(contains(provider)), + ); + }); + + test('maintains the state of the provider until all links are closed', + () async { + final container = ProviderContainer.test(); + late KeepAliveLink a; + late KeepAliveLink b; + + final provider = Provider.autoDispose((ref) { + a = ref.keepAlive(); + b = ref.keepAlive(); + }); + + container.read(provider); + + expect( + container.getAllProviderElements().map((e) => e.provider), + [provider], + ); + + await container.pump(); + + expect( + container.getAllProviderElements().map((e) => e.provider), + [provider], + ); + + a.close(); + await container.pump(); + + expect( + container.getAllProviderElements().map((e) => e.provider), + [provider], + ); + + b.close(); + await container.pump(); + + expect( + container.getAllProviderElements(), + isEmpty, + ); + }); + + test( + 'when closing KeepAliveLink, does not dispose the provider if it is still being listened to', + () async { + final container = ProviderContainer.test(); + late KeepAliveLink a; + + final provider = Provider.autoDispose((ref) { + a = ref.keepAlive(); + }); + + final sub = container.listen(provider, (previous, next) {}); + + a.close(); + await container.pump(); + + expect( + container.getAllProviderElements().map((e) => e.provider), + [provider], + ); + + sub.close(); + await container.pump(); + + expect( + container.getAllProviderElements().map((e) => e.provider), + isEmpty, + ); + }); + + test( + 'when closing the last KeepAliveLink, then immediately adding a new link, ' + 'the provider will not be disposed.', () async { + final container = ProviderContainer.test(); + late KeepAliveLink a; + late Ref ref; + + final provider = Provider.autoDispose((r) { + ref = r; + a = ref.keepAlive(); + }); + + container.read(provider); + + a.close(); + final b = ref.keepAlive(); + await container.pump(); + + expect( + container.getAllProviderElements().map((e) => e.provider), + [provider], + ); + + b.close(); + await container.pump(); + + expect( + container.getAllProviderElements().map((e) => e.provider), + isEmpty, + ); + }); + }); + + group('refresh', () { + test('refreshes a provider and return the new state', () { + var value = 0; + final state = Provider((ref) => value); + late Ref ref; + final provider = Provider((r) { + ref = r; + }); + final container = ProviderContainer.test(); + + container.read(provider); + + expect(container.read(state), 0); + + value = 42; + expect(ref.refresh(state), 42); + expect(container.read(state), 42); + }); + }); + + group('.notifyListeners', () { + test('If called after initialization, notify listeners', () { + final observer = ObserverMock(); + final listener = Listener(); + final selfListener = Listener(); + final container = ProviderContainer.test(observers: [observer]); + final provider = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + self.listenSelf(selfListener.call); + return 0; + }), + ); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly( + observer, + observer.didAddProvider( + argThat(isProviderObserverContext(provider, container)), + 0, + ), + ); + verifyOnly(listener, listener(null, 0)); + verifyOnly(selfListener, selfListener(null, 0)); + + container.read(provider.notifier).ref.notifyListeners(); + + verifyOnly(listener, listener(0, 0)); + verifyOnly(selfListener, selfListener(0, 0)); + verifyOnly( + observer, + observer.didUpdateProvider( + argThat(isProviderObserverContext(provider, container)), + 0, + 0, + ), + ); + }); + + test( + 'can be invoked during first initialization, and does not notify listeners', + () { + final observer = ObserverMock(); + final selfListener = Listener(); + final listener = Listener(); + final container = ProviderContainer.test(observers: [observer]); + + final provider = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + self.listenSelf(selfListener.call); + ref.notifyListeners(); + return 0; + }), + ); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly( + observer, + observer.didAddProvider( + argThat(isProviderObserverContext(provider, container)), + 0, + ), + ); + verifyOnly(listener, listener(null, 0)); + verifyOnly(selfListener, selfListener(null, 0)); + }); + + test( + 'can be invoked during a re-initialization, and does not notify listeners', + () { + final observer = ObserverMock(); + final listener = Listener(); + final selfListener = Listener(); + final container = ProviderContainer.test(observers: [observer]); + var callNotifyListeners = false; + const firstValue = 'first'; + const secondValue = 'second'; + var result = firstValue; + + final provider = NotifierProvider, Object>( + () => DeferredNotifier((ref, self) { + self.listenSelf(selfListener.call); + if (callNotifyListeners) { + ref.notifyListeners(); + } + return result; + }), + ); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly( + observer, + observer.didAddProvider( + argThat(isProviderObserverContext(provider, container)), + firstValue, + ), + ); + verifyOnly(selfListener, selfListener(null, firstValue)); + verifyOnly(listener, listener(null, firstValue)); + + result = secondValue; + callNotifyListeners = true; + container.refresh(provider); + + verifyOnly(selfListener, selfListener(firstValue, secondValue)); + verifyOnly(listener, listener(firstValue, secondValue)); + verify( + observer.didDisposeProvider( + argThat(isProviderObserverContext(provider, container)), + ), + ); + verify( + observer.didUpdateProvider( + argThat(isProviderObserverContext(provider, container)), + firstValue, + secondValue, + ), + ).called(1); + verifyNoMoreInteractions(observer); + }); + }); + + group('.refresh', () { + test('Throws if a circular dependency is detected', () { + // Regression test for https://github.com/rrousselGit/riverpod/issues/2336 + late Ref ref; + final a = Provider((r) { + ref = r; + return 0; + }); + final b = Provider((r) => r.watch(a)); + final container = ProviderContainer.test(); + + container.read(b); + + expect( + () => ref.refresh(b), + throwsA(isA()), + ); + }); + }); + + group('.invalidate', () { + test('Throws if a circular dependency is detected', () { + // Regression test for https://github.com/rrousselGit/riverpod/issues/2336 + late Ref ref; + final a = Provider((r) { + ref = r; + return 0; + }); + final b = Provider((r) => r.watch(a)); + final container = ProviderContainer.test(); + + container.read(b); + + expect( + () => ref.invalidate(b), + throwsA(isA()), + ); + }); + + test('Circular dependency ignores families', () { + late Ref ref; + final a = Provider((r) { + ref = r; + return 0; + }); + final b = Provider.family((r, id) => r.watch(a)); + final container = ProviderContainer.test(); + + container.read(b(0)); + + expect( + () => ref.invalidate(b), + returnsNormally, + ); + }); + + test('triggers a rebuild on next frame', () async { + final container = ProviderContainer.test(); + final listener = Listener(); + var result = 0; + final provider = Provider((r) => result); + late Ref ref; + final another = Provider((r) { + ref = r; + }); + + container.listen(provider, listener.call); + container.read(another); + verifyZeroInteractions(listener); + + ref.invalidate(provider); + ref.invalidate(provider); + result = 1; + + verifyZeroInteractions(listener); + + await container.pump(); + + verifyOnly(listener, listener(0, 1)); + }); + + group('on families', () { + test('recomputes providers associated with the family', () async { + final container = ProviderContainer.test(); + final listener = Listener(); + final listener2 = Listener(); + final listener3 = Listener(); + var result = 0; + final unrelated = Provider((ref) => result); + final provider = Provider.family((r, i) => '$result-$i'); + late Ref ref; + final another = Provider((r) { + ref = r; + }); + + container.read(another); + + container.listen(provider(0), listener.call, fireImmediately: true); + container.listen(provider(1), listener2.call, fireImmediately: true); + container.listen(unrelated, listener3.call, fireImmediately: true); + + verifyOnly(listener, listener(null, '0-0')); + verifyOnly(listener2, listener2(null, '0-1')); + verifyOnly(listener3, listener3(null, 0)); + + ref.invalidate(provider); + ref.invalidate(provider); + result = 1; + + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + verifyNoMoreInteractions(listener3); + + await container.pump(); + + verifyOnly(listener, listener('0-0', '1-0')); + verifyOnly(listener2, listener2('0-1', '1-1')); + verifyNoMoreInteractions(listener3); + }); + + test('clears only on the closest family override', () async { + var result = 0; + final provider = Provider.family( + (r, i) => result, + dependencies: const [], + ); + late Ref ref; + final another = Provider((r) => ref = r, dependencies: [provider]); + + final listener = Listener(); + final listener2 = Listener(); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider, another], + ); + + container.read(another); + root.listen(provider(0), listener.call, fireImmediately: true); + container.listen(provider(1), listener2.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 0)); + verifyOnly(listener2, listener2(null, 0)); + + ref.invalidate(provider); + result = 1; + + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + await container.pump(); + + verifyOnly(listener2, listener2(0, 1)); + verifyNoMoreInteractions(listener); + }); + }); + }); + + group('.onRemoveListener', () { + test('returns a way to unregister the listener', () { + final container = ProviderContainer.test(); + final listener = OnRemoveListener(); + late RemoveListener remove; + final provider = Provider((ref) { + remove = ref.onRemoveListener(listener.call); + }); + + final sub = container.listen(provider, (previous, next) {}); + + remove(); + + sub.close(); + + verifyZeroInteractions(listener); + }); + + test('is called on read', () { + final container = ProviderContainer.test(); + final listener = OnRemoveListener(); + final provider = Provider((ref) { + ref.onRemoveListener(listener.call); + }); + + container.read(provider); + + verifyOnly(listener, listener()); + }); + + test('calls listeners when container.listen subscriptions are closed', + () { + final container = ProviderContainer.test(); + final listener = OnRemoveListener(); + final listener2 = OnRemoveListener(); + final provider = Provider((ref) { + ref.onRemoveListener(listener.call); + ref.onRemoveListener(listener2.call); + }); + + final sub = container.listen(provider, (previous, next) {}); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + sub.close(); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + final sub2 = container.listen(provider, (previous, next) {}); + + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + sub2.close(); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('calls listeners when ref.listen subscriptions are closed', () { + final container = ProviderContainer.test(); + final listener = OnRemoveListener(); + final listener2 = OnRemoveListener(); + final dep = Provider( + name: 'dep', + (ref) { + ref.onRemoveListener(listener.call); + ref.onRemoveListener(listener2.call); + }, + ); + late Ref ref; + final provider = Provider( + name: 'provider', + (r) { + ref = r; + }, + ); + + // initialize ref + container.read(provider); + + final sub = ref.listen(dep, (previous, next) {}); + + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + sub.close(); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + final sub2 = ref.listen(dep, (previous, next) {}); + + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + sub2.close(); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('calls listeners when ref.watch subscriptions are removed', () { + final container = ProviderContainer.test(); + final listener = OnRemoveListener(); + final listener2 = OnRemoveListener(); + final dep = Provider( + name: 'dep', + (ref) { + ref.onRemoveListener(listener.call); + ref.onRemoveListener(listener2.call); + }, + ); + late Ref ref; + final provider = Provider( + name: 'provider', + (r) => ref = r, + ); + + // initialize refs + container.read(provider); + + ref.watch(dep); + + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + container.refresh(provider); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('listeners are cleared on rebuild', () { + final container = ProviderContainer.test(); + final listener = OnRemoveListener(); + final listener2 = OnRemoveListener(); + var isSecondBuild = false; + final provider = Provider((ref) { + if (isSecondBuild) { + ref.onRemoveListener(listener2.call); + } else { + ref.onRemoveListener(listener.call); + } + }); + + container.read(provider); + verifyOnly(listener, listener()); + verifyZeroInteractions(listener2); + + isSecondBuild = true; + container.refresh(provider); + + // Removed sub from refresh + verify(listener2()).called(1); + + final sub = container.listen(provider, (previous, next) {}); + sub.close(); + + verify(listener2()).called(1); + verifyNoMoreInteractions(listener2); + verifyNoMoreInteractions(listener); + }); + + test('if a listener throws, still calls all listeners', () { + final errors = []; + final container = ProviderContainer.test(); + final listener = OnRemoveListener(); + final listener2 = OnRemoveListener(); + when(listener()).thenThrow(42); + final provider = Provider((ref) { + ref.onRemoveListener(listener.call); + ref.onRemoveListener(listener2.call); + }); + + final sub = container.listen(provider, (prev, next) {}); + + runZonedGuarded( + sub.close, + (err, stack) => errors.add(err), + ); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + expect(errors, [42]); + }); + }); + + group('.onAddListener', () { + test('returns a way to unregister the listener', () { + final container = ProviderContainer.test(); + final listener = OnAddListener(); + late RemoveListener remove; + final provider = Provider((ref) { + remove = ref.onAddListener(listener.call); + }); + + container.listen(provider, (previous, next) {}); + clearInteractions(listener); + + remove(); + + container.listen(provider, (previous, next) {}); + + verifyZeroInteractions(listener); + }); + + test('is called on read', () { + final container = ProviderContainer.test(); + final listener = OnAddListener(); + final provider = Provider((ref) { + ref.onAddListener(listener.call); + }); + + container.read(provider); + + verifyOnly(listener, listener()); + }); + + test('calls listeners when container.listen is invoked', () { + final container = ProviderContainer.test(); + final listener = OnAddListener(); + final listener2 = OnAddListener(); + final provider = Provider((ref) { + ref.onAddListener(listener.call); + ref.onAddListener(listener2.call); + }); + + container.listen(provider, (previous, next) {}); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + container.listen(provider, (previous, next) {}); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('calls listeners when new ref.listen is invoked', () { + final container = ProviderContainer.test(); + final listener = OnAddListener(); + final listener2 = OnAddListener(); + final dep = Provider( + name: 'dep', + (ref) { + ref.onAddListener(listener.call); + ref.onAddListener(listener2.call); + }, + ); + late Ref ref; + final provider = Provider( + name: 'provider', + (r) => ref = r, + ); + + // initialize ref + container.read(provider); + + ref.listen(dep, (previous, next) {}); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + ref.listen(dep, (previous, next) {}); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('calls listeners when new ref.watch is invoked', () { + final container = ProviderContainer.test(); + final listener = OnAddListener(); + final listener2 = OnAddListener(); + final dep = Provider( + name: 'dep', + (ref) { + ref.onAddListener(listener.call); + ref.onAddListener(listener2.call); + }, + ); + late Ref ref; + final provider = Provider( + name: 'provider', + (r) => ref = r, + ); + late Ref ref2; + final provider2 = Provider( + name: 'provider', + (r) => ref2 = r, + ); + + // initialize refs + container.read(provider); + container.read(provider2); + + ref.watch(dep); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + ref.watch(dep); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + ref2.watch(dep); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('listeners are cleared on rebuild', () { + final container = ProviderContainer.test(); + final listener = OnAddListener(); + final listener2 = OnAddListener(); + var isSecondBuild = false; + final provider = Provider((ref) { + if (isSecondBuild) { + ref.onAddListener(listener2.call); + } else { + ref.onAddListener(listener.call); + } + }); + + container.read(provider); + verifyOnly(listener, listener()); + + isSecondBuild = true; + container.refresh(provider); + + // Added refresh listener + verifyOnly(listener2, listener2()); + + container.listen(provider, (previous, next) {}); + + verify(listener2()).called(1); + verifyNoMoreInteractions(listener2); + verifyNoMoreInteractions(listener); + }); + + test('if a listener throws, still calls all listeners', () { + final errors = []; + final container = ProviderContainer.test(); + final listener = OnAddListener(); + final listener2 = OnAddListener(); + when(listener()).thenThrow(42); + final provider = Provider((ref) { + ref.onAddListener(listener.call); + ref.onAddListener(listener2.call); + }); + + runZonedGuarded( + () => container.listen(provider, (prev, next) {}), + (err, stack) => errors.add(err), + ); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + expect(errors, [42]); + }); + }); + + group('.onResume', () { + test('returns a way to unregister the listener', () { + final container = ProviderContainer.test(); + final listener = OnResume(); + late RemoveListener remove; + final provider = Provider((ref) { + remove = ref.onResume(listener.call); + }); + + final sub = container.listen(provider, (previous, next) {}); + + remove(); + + sub.pause(); + sub.resume(); + + verifyZeroInteractions(listener); + }); + + test('is not called on initial subscription', () { + final container = ProviderContainer.test(); + final listener = OnResume(); + final provider = Provider((ref) { + ref.onResume(listener.call); + }); + + container.listen(provider, (previous, next) {}); + + verifyZeroInteractions(listener); + }); + + test('calls listeners on the first new container.listen after a cancel', + () { + final container = ProviderContainer.test(); + final listener = OnResume(); + final listener2 = OnResume(); + final provider = Provider((ref) { + ref.onResume(listener.call); + ref.onResume(listener2.call); + }); + + final sub = container.listen(provider, (previous, next) {}); + sub.close(); + + verifyZeroInteractions(listener); + verifyZeroInteractions(listener2); + + container.listen(provider, (previous, next) {}); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + container.listen(provider, (previous, next) {}); + + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('calls listeners on the first new ref.listen after a cancel', () { + final container = ProviderContainer.test(); + final listener = OnResume(); + final listener2 = OnResume(); + final dep = Provider( + name: 'dep', + (ref) { + ref.onResume(listener.call); + ref.onResume(listener2.call); + }, + ); + late Ref ref; + final provider = Provider( + name: 'provider', + (r) => ref = r, + ); + + // initialize ref + container.read(provider); + + final sub = ref.listen(dep, (previous, next) {}); + sub.close(); + + verifyZeroInteractions(listener); + + ref.listen(dep, (previous, next) {}); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + + ref.listen(dep, (previous, next) {}); + + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('calls listeners when ref.watch is invoked after a cancel', () { + final container = ProviderContainer.test(); + final listener = OnAddListener(); + final listener2 = OnAddListener(); + final dep = Provider( + name: 'dep', + (ref) { + ref.onAddListener(listener.call); + ref.onAddListener(listener2.call); + }, + ); + late Ref ref; + final provider = Provider( + name: 'provider', + (r) => ref = r, + ); + + // initialize refs + container.read(provider); + + final sub = container.listen(provider, (previous, next) {}); + sub.close(); + + verifyZeroInteractions(listener); + + ref.watch(dep); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('listeners are cleared on rebuild', () { + final container = ProviderContainer.test(); + final listener = OnResume(); + final listener2 = OnResume(); + var isSecondBuild = false; + final provider = Provider((ref) { + if (isSecondBuild) { + ref.onResume(listener2.call); + } else { + ref.onResume(listener.call); + } + }); + + container.read(provider); + isSecondBuild = true; + container.invalidate(provider); + + final sub = container.listen(provider, (previous, next) {}); + sub.close(); + + verifyZeroInteractions(listener); + verifyZeroInteractions(listener2); + + container.listen(provider, (previous, next) {}); + + verify(listener2()).called(1); + verifyNoMoreInteractions(listener2); + verifyZeroInteractions(listener); + }); + + test('internal resume status is cleared on rebuild', () { + final container = ProviderContainer.test(); + final listener = OnResume(); + final provider = Provider((ref) { + ref.onResume(listener.call); + }); + + final sub = container.listen(provider, (previous, next) {}); + sub.close(); + + container.invalidate(provider); + + final sub2 = container.listen(provider, (previous, next) {}); + sub2.close(); + + verifyZeroInteractions(listener); + + container.listen(provider, (previous, next) {}); + + verifyOnly(listener, listener()); + }); + + test('if a listener throws, still calls all listeners', () { + final errors = []; + final container = ProviderContainer.test(); + final listener = OnResume(); + final listener2 = OnResume(); + when(listener()).thenThrow(42); + final provider = Provider((ref) { + ref.onResume(listener.call); + ref.onResume(listener2.call); + }); + + final sub = container.listen(provider, (previous, next) {}); + sub.close(); + + verifyZeroInteractions(listener); + verifyZeroInteractions(listener2); + + runZonedGuarded( + () => container.listen(provider, (prev, next) {}), + (err, stack) => errors.add(err), + ); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + expect(errors, [42]); + }); + }); + + group('.onCancel', () { + test('returns a way to unregister the listener', () { + final container = ProviderContainer.test(); + final listener = OnCancelMock(); + late RemoveListener remove; + final provider = Provider((ref) { + remove = ref.onCancel(listener.call); + }); + + final sub = container.listen(provider, (previous, next) {}); + + remove(); + + sub.close(); + + verifyZeroInteractions(listener); + }); + + test('is called when all container listeners are removed', () { + final container = ProviderContainer.test(); + final listener = OnCancelMock(); + final listener2 = OnCancelMock(); + final provider = Provider((ref) { + ref.onCancel(listener.call); + ref.onCancel(listener2.call); + }); + + final sub = container.listen(provider, (previous, next) {}); + final sub2 = container.listen(provider, (previous, next) {}); + + verifyZeroInteractions(listener); + verifyZeroInteractions(listener2); + + sub.close(); + + verifyZeroInteractions(listener2); + + sub2.close(); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('is called when all provider listeners are removed', () { + final container = ProviderContainer.test(); + final listener = OnCancelMock(); + final listener2 = OnCancelMock(); + final dep = Provider((ref) { + ref.onCancel(listener.call); + ref.onCancel(listener2.call); + }); + late Ref ref; + final provider = Provider((r) { + ref = r; + }); + + container.read(provider); + final sub = ref.listen(dep, (previous, next) {}); + final sub2 = ref.listen(dep, (previous, next) {}); + + verifyZeroInteractions(listener); + verifyZeroInteractions(listener2); + + sub.close(); + + verifyZeroInteractions(listener2); + + sub2.close(); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + }); + + test('is called when all provider dependencies are removed', () async { + final container = ProviderContainer.test(); + final listener = OnCancelMock(); + final listener2 = OnCancelMock(); + final resume = OnResume(); + final dep = Provider(name: 'dep', (ref) { + ref.onCancel(listener.call); + ref.onCancel(listener2.call); + ref.onResume(resume.call); + }); + var watching = true; + final provider = Provider(name: 'provider', (ref) { + if (watching) ref.watch(dep); + }); + final provider2 = Provider(name: 'provider2', (ref) { + if (watching) ref.watch(dep); + }); + + container.listen(provider, (p, n) {}); + container.listen(provider2, (p, n) {}); + + watching = false; + // remove the dependency provider<>dep + container.invalidate(provider); + await container.pump(); + + verifyZeroInteractions(listener); + verifyZeroInteractions(listener2); + + // remove the dependency provider2<>dep + container.invalidate(provider2); + await container.pump(); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + verifyZeroInteractions(resume); + }); + + test('is called when using container.read', () async { + final container = ProviderContainer.test(); + final listener = OnCancelMock(); + final provider = Provider((ref) { + ref.onCancel(listener.call); + }); + + container.read(provider); + + verifyOnly(listener, listener()); + }); + + test('listeners are cleared on rebuild', () { + final container = ProviderContainer.test(); + final listener = OnCancelMock(); + final listener2 = OnCancelMock(); + var isSecondBuild = false; + final provider = Provider((ref) { + if (isSecondBuild) { + ref.onCancel(listener2.call); + } else { + ref.onCancel(listener.call); + } + }); + + container.read(provider); + clearInteractions(listener); + + isSecondBuild = true; + container.invalidate(provider); + + verifyZeroInteractions(listener); + verifyZeroInteractions(listener2); + + final sub = container.listen(provider, (previous, next) {}); + + verifyZeroInteractions(listener); + verifyZeroInteractions(listener2); + + sub.close(); + + verify(listener2()).called(1); + verifyNoMoreInteractions(listener2); + verifyZeroInteractions(listener); + }); + + test('if a listener throws, still calls all listeners', () { + final errors = []; + final container = ProviderContainer.test(); + final listener = OnCancelMock(); + final listener2 = OnCancelMock(); + when(listener()).thenThrow(42); + final provider = Provider((ref) { + ref.onCancel(listener.call); + ref.onCancel(listener2.call); + }); + + final sub = container.listen(provider, (previous, next) {}); + + verifyZeroInteractions(listener); + verifyZeroInteractions(listener2); + + runZonedGuarded( + sub.close, + (err, stack) => errors.add(err), + ); + + verifyInOrder([listener(), listener2()]); + verifyNoMoreInteractions(listener); + verifyNoMoreInteractions(listener2); + expect(errors, [42]); + }); + }); + + group('.onDispose', () { + test('returns a way to unregister the listener', () async { + final container = ProviderContainer.test(); + final listener = OnDisposeMock(); + late RemoveListener remove; + final provider = Provider.autoDispose((ref) { + remove = ref.onDispose(listener.call); + }); + + final sub = container.listen(provider, (previous, next) {}); + + remove(); + + sub.close(); + await container.pump(); + + verifyZeroInteractions(listener); + }); + + test("can't use ref inside onDispose", () { + final provider2 = Provider((ref) => 0); + final provider = Provider((ref) { + ref.onDispose(() { + ref.watch(provider2); + }); + return ref; + }); + final container = ProviderContainer.test(); + + container.read(provider); + + final errors = []; + runZonedGuarded(container.dispose, (err, _) => errors.add(err)); + + expect(errors, [isA()]); + }); + + test( + 'calls all the listeners in order when the ProviderContainer is disposed', + () { + final onDispose = OnDisposeMock(); + final onDispose2 = OnDisposeMock(); + final provider = Provider((ref) { + ref.onDispose(onDispose.call); + ref.onDispose(onDispose2.call); + }); + + final container = ProviderContainer(); + addTearDown(container.dispose); + + container.read(provider); // register the onDispose hooks + + verifyZeroInteractions(onDispose); + verifyZeroInteractions(onDispose2); + + container.dispose(); + + verifyInOrder([ + onDispose(), + onDispose2(), + ]); + verifyNoMoreInteractions(onDispose); + verifyNoMoreInteractions(onDispose2); + }); + + test('calls all listeners in order when one of its dependency changed', + () async { + final onDispose = OnDisposeMock(); + final onDispose2 = OnDisposeMock(); + + final count = StateProvider((ref) => 0); + final provider = Provider((ref) { + ref.watch(count); + ref.onDispose(onDispose.call); + ref.onDispose(onDispose2.call); + }); + + final container = ProviderContainer(); + addTearDown(container.dispose); + + container.read(provider); // register the onDispose hooks + + verifyZeroInteractions(onDispose); + verifyZeroInteractions(onDispose2); + + container.read(count.notifier).state++; + await container.pump(); + + verifyInOrder([ + onDispose(), + onDispose2(), + ]); + verifyNoMoreInteractions(onDispose); + verifyNoMoreInteractions(onDispose2); + }); + + test('does not call listeners again if more than one dependency changed', + () { + final onDispose = OnDisposeMock(); + + final count = StateProvider((ref) => 0); + final count2 = StateProvider((ref) => 0); + final provider = Provider((ref) { + ref.watch(count); + ref.watch(count2); + ref.onDispose(onDispose.call); + }); + + final container = ProviderContainer(); + addTearDown(container.dispose); + + container.read(provider); // register the onDispose hooks + + verifyZeroInteractions(onDispose); + + container.read(count.notifier).state++; + container.read(count2.notifier).state++; + + verifyOnly(onDispose, onDispose()); + }); + + test( + 'does not call listeners again if a dependency changed then ProviderContainer was disposed', + () async { + final onDispose = OnDisposeMock(); + var buildCount = 0; + + final count = StateProvider((ref) => 0); + final provider = Provider((ref) { + buildCount++; + ref.watch(count); + ref.onDispose(onDispose.call); + }); + + final container = ProviderContainer(); + addTearDown(container.dispose); + + container.read(provider); // register the onDispose hooks + expect(buildCount, 1); + + verifyZeroInteractions(onDispose); + + container.read(count.notifier).state++; + // no pump() because that would rebuild the provider, which means it would + // need to be disposed once again. + + verifyOnly(onDispose, onDispose()); + + container.dispose(); + + expect(buildCount, 1); + verifyNoMoreInteractions(onDispose); + }); + }); + + group('mounted', () { + test('stays false on older refs while new refs are building', () { + final container = ProviderContainer.test(); + late Ref ref; + final provider = Provider((r) { + ref = r; + return 0; + }); + + container.read(provider); + final oldRef = ref; + + container.refresh(provider); + + expect(oldRef.mounted, false); + expect(ref.mounted, true); + }); + + test('is false during onDispose caused by ref.watch', () { + final container = ProviderContainer.test(); + bool? mounted; + late Ref ref; + final dep = StateProvider((ref) => 0); + final provider = Provider((r) { + ref = r; + ref.watch(dep); + ref.onDispose(() => mounted = ref.mounted); + }); + + container.read(provider); + expect(mounted, null); + + container.read(dep.notifier).state++; + + expect(mounted, false); + }); + + test('is false during onDispose caused by container dispose', () { + final container = ProviderContainer.test(); + bool? mounted; + late Ref ref; + final dep = StateProvider((ref) => 0); + final provider = Provider((r) { + ref = r; + ref.watch(dep); + ref.onDispose(() => mounted = ref.mounted); + }); + + container.read(provider); + expect(mounted, null); + + container.dispose(); + + expect(mounted, false); + }); + + test('is false in between rebuilds', () { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + late Ref ref; + final provider = Provider((r) { + ref = r; + ref.watch(dep); + }); + + container.read(provider); + expect(ref.mounted, true); + + container.read(dep.notifier).state++; + + expect(ref.mounted, false); + }); + }); + }); +} diff --git a/packages/riverpod/test/framework/scope_test.dart b/packages/riverpod/test/src/core/scope_test.dart similarity index 56% rename from packages/riverpod/test/framework/scope_test.dart rename to packages/riverpod/test/src/core/scope_test.dart index ba5af3013..5286d8cc2 100644 --- a/packages/riverpod/test/framework/scope_test.dart +++ b/packages/riverpod/test/src/core/scope_test.dart @@ -1,40 +1,33 @@ // Tests related to scoping providers -import 'package:expect_error/expect_error.dart'; import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/framework.dart' show ProviderContainerTest; +import 'package:riverpod/src/internals.dart' + show $ProviderElement, ProviderElement; import 'package:test/test.dart'; -import '../utils.dart'; +import 'provider_container_test.dart'; Future main() async { - final library = await Library.parseFromStacktrace(); - - test( - 'transitive dependencies includes the transitive dependencies of families', - () { - final a = Provider((ref) => 0); - final b = Provider.family((ref, _) => 0, dependencies: [a]); - final c = Provider((ref) => 0, dependencies: [b]); - - expect(c.allTransitiveDependencies, containsAll([a, b])); - }); - test( 'reading a provider from a scoped container, ' 'then adding a new container with an override, ' 'then reading from the new container correctly auto-scope again', () { - final a = Provider((ref) => 0); + final a = Provider( + (ref) => 0, + dependencies: const [], + ); final b = Provider((ref) => ref.watch(a) + 10, dependencies: [a]); - final root = createContainer(); - final mid = createContainer( + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( parent: root, overrides: [a.overrideWithValue(42)], ); expect(mid.read(b), 52); - final child = createContainer( + final child = ProviderContainer.test( parent: mid, overrides: [a.overrideWithValue(84)], ); @@ -46,15 +39,18 @@ Future main() async { 'reading a provider from a scoped container, ' 'then reading from container further down the tree correctly auto-scope again', () { - final a = Provider((ref) => 0); + final a = Provider( + (ref) => 0, + dependencies: const [], + ); final b = Provider((ref) => ref.watch(a) + 10, dependencies: [a]); - final root = createContainer(); - final mid = createContainer( + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( parent: root, overrides: [a.overrideWithValue(42)], ); - final child = createContainer( + final child = ProviderContainer.test( parent: mid, overrides: [a.overrideWithValue(84)], ); @@ -68,23 +64,34 @@ Future main() async { 'then adding a new container with the same family overridden again, ' 'then reading from the new container correctly obtains the new override', () { - final a = Provider.family((ref, id) => id); + final a = Provider.family( + (ref, id) => id, + dependencies: const [], + ); - final root = createContainer(); - final mid = createContainer( + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( parent: root, overrides: [ - a.overrideWithProvider((argument) => Provider((ref) => argument + 10)), + a.overrideWith((ref, argument) => argument + 10), ], ); expect(mid.read(a(10)), 20); - final child = createContainer( + final override2 = a.overrideWith((ref, argument) => argument + 20); + final child = ProviderContainer.test( parent: mid, - overrides: [ - a.overrideWithProvider((argument) => Provider((ref) => argument + 20)), - ], + overrides: [override2], + ); + + expect( + child.pointerManager.familyPointers[a], + isProviderDirectory( + override: override2, + targetContainer: child, + pointers: {}, + ), ); expect(child.read(a(10)), 30); @@ -94,20 +101,23 @@ Future main() async { 'reading a family override from a scoped container, ' 'then reading from container further down the tree correctly uses the deepest override', () { - final a = Provider.family((ref, id) => id); + final a = Provider.family( + (ref, id) => id, + dependencies: const [], + ); - final root = createContainer(); - final mid = createContainer( + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( parent: root, overrides: [ - a.overrideWithProvider((argument) => Provider((ref) => argument + 10)), + a.overrideWith((ref, argument) => argument + 10), ], ); - final child = createContainer( + final child = ProviderContainer.test( parent: mid, overrides: [ - a.overrideWithProvider((argument) => Provider((ref) => argument + 20)), + a.overrideWith((ref, argument) => argument + 20), ], ); @@ -119,17 +129,18 @@ Future main() async { 'reading a family override from a scoped container, ' 'then reading from container further down the tree reuse the provider state when possible', () { - final a = Provider.family((ref, id) => id); + final a = Provider.family( + (ref, id) => id, + dependencies: const [], + ); var overrideBuildCount = 0; - final root = createContainer(); - final mid = createContainer( + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( parent: root, overrides: [ - a.overrideWithProvider((argument) { - return Provider((ref) { - overrideBuildCount++; - return argument + 10; - }); + a.overrideWith((ref, argument) { + overrideBuildCount++; + return argument + 10; }), ], ); @@ -138,7 +149,7 @@ Future main() async { expect(mid.read(a(10)), 20); expect(overrideBuildCount, 1); - final child = createContainer(parent: mid); + final child = ProviderContainer.test(parent: mid); expect(child.read(a(10)), 20); expect(overrideBuildCount, 1); @@ -148,22 +159,25 @@ Future main() async { test('can override a provider with a reference to the provider directly', () { final provider = Provider((ref) => 0); - final container = createContainer(); - final child = createContainer(overrides: [provider]); + final container = ProviderContainer.test(); + final child = ProviderContainer.test(overrides: [provider]); expect(child.read(provider), 0); expect(child.getAllProviderElements(), [ - isA>() + isA<$ProviderElement>() .having((e) => e.provider, 'provider', provider), ]); expect(container.getAllProviderElements(), isEmpty); }); test('use latest override on mount', () { - final provider = Provider((ref) => 0); - final root = createContainer(); - final container = createContainer( + final provider = Provider( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [provider.overrideWithValue(42)], ); @@ -176,9 +190,12 @@ Future main() async { }); test('updating scoped override does not mount the provider', () { - final provider = Provider((ref) => 0); - final root = createContainer(); - final container = createContainer( + final provider = Provider( + (ref) => 0, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [provider.overrideWithValue(42)], ); @@ -195,13 +212,16 @@ Future main() async { 'does not re-initialize a provider if read by an intermediary container', () { var callCount = 0; - final provider = Provider((ref) { - callCount++; - return 42; - }); - final root = createContainer(); - final mid = createContainer(parent: root, overrides: [provider]); - final container = createContainer(parent: mid); + final provider = Provider( + (ref) { + callCount++; + return 42; + }, + dependencies: const [], + ); + final root = ProviderContainer.test(); + final mid = ProviderContainer.test(parent: root, overrides: [provider]); + final container = ProviderContainer.test(parent: mid); expect(mid.read(provider), 42); expect(callCount, 1); @@ -215,12 +235,16 @@ Future main() async { group('Scoping family', () { test('use latest override on mount', () { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = Provider.family( (ref, value) => '$value ${ref.watch(dep)}', + dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ dep.overrideWithValue(1), @@ -237,12 +261,16 @@ Future main() async { }); test('updating scoped override does not mount the provider', () { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = Provider.family( (ref, value) => '$value ${ref.watch(dep)}', + dependencies: const [], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [ dep.overrideWithValue(1), @@ -261,13 +289,13 @@ Future main() async { test('can override a family with a reference to the provider directly', () { final provider = Provider.family((ref, param) => 0); - final container = createContainer(); - final child = createContainer(overrides: [provider]); + final container = ProviderContainer.test(); + final child = ProviderContainer.test(overrides: [provider]); expect(child.read(provider(0)), 0); expect(child.getAllProviderElements(), [ - isA>() + isA<$ProviderElement>() .having((e) => e.provider, 'provider', provider(0)), ]); expect(container.getAllProviderElements(), isEmpty); @@ -276,21 +304,27 @@ Future main() async { test( 'does not re-initialize a provider if read by an intermediary container', () { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); var callCount = 0; - final provider = Provider.family((ref, value) { - callCount++; - return '$value ${ref.watch(dep)}'; - }); - final root = createContainer(); - final mid = createContainer( + final provider = Provider.family( + (ref, value) { + callCount++; + return '$value ${ref.watch(dep)}'; + }, + dependencies: [dep], + ); + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( parent: root, overrides: [ dep.overrideWithValue(1), provider, ], ); - final container = createContainer(parent: mid); + final container = ProviderContainer.test(parent: mid); expect(mid.read(provider(0)), '0 1'); expect(callCount, 1); @@ -313,13 +347,17 @@ Future main() async { }, dependencies: [dep], ); - final container = createContainer(); + final container = ProviderContainer.test(); expect(container.read(provider(42)), 'foo'); }); test('auto scope direct provider dependencies', () { - final dep = Provider((ref) => 0, name: 'dep'); + final dep = Provider( + (ref) => 0, + name: 'dep', + dependencies: const [], + ); var buildCount = 0; final provider = Provider( name: 'provider', @@ -329,13 +367,13 @@ Future main() async { return ref.watch(dep); }, ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); - final child = createContainer(parent: container); - final subChild = createContainer(parent: child); + final child = ProviderContainer.test(parent: container); + final subChild = ProviderContainer.test(parent: child); expect(buildCount, 0); expect(container.read(provider), 42); @@ -347,7 +385,7 @@ Future main() async { expect(subChild.read(provider), 42); expect(buildCount, 2); - final lateChild = createContainer(parent: container); + final lateChild = ProviderContainer.test(parent: container); expect(lateChild.read(provider), 42); expect(buildCount, 2); @@ -356,7 +394,11 @@ Future main() async { test( 'auto scope still works if the first read of the auto-override is through a child container', () { - final dep = Provider((ref) => 0, name: 'dep'); + final dep = Provider( + (ref) => 0, + name: 'dep', + dependencies: const [], + ); var buildCount = 0; final provider = Provider( dependencies: [dep], @@ -366,13 +408,13 @@ Future main() async { return ref.watch(dep); }, ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); - final child = createContainer(parent: container); - final subChild = createContainer(parent: child); + final child = ProviderContainer.test(parent: container); + final subChild = ProviderContainer.test(parent: child); expect(subChild.read(provider), 42); expect(buildCount, 1); @@ -392,6 +434,7 @@ Future main() async { depBuildCount++; return 0; }, + dependencies: const [], ); var dep2BuildCount = 0; final dep2 = Provider.family( @@ -421,13 +464,13 @@ Future main() async { }, ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); - final child = createContainer(parent: container); - final subChild = createContainer(parent: child); + final child = ProviderContainer.test(parent: container); + final subChild = ProviderContainer.test(parent: child); expect(buildCount, 0); @@ -467,7 +510,7 @@ Future main() async { expect(dep2BuildCount, 2); expect(depBuildCount, 1); - final lateChild = createContainer(parent: container); + final lateChild = ProviderContainer.test(parent: container); expect(lateChild.read(provider), '84'); expect(container.read(dep3), 84); @@ -478,8 +521,16 @@ Future main() async { test( 'when provider depends on multiple overrides, is placed on the deepest container', () { - final dep = Provider((ref) => 0, name: 'dep'); - final dep2 = Provider((ref) => 0, name: 'dep2'); + final dep = Provider( + (ref) => 0, + name: 'dep', + dependencies: const [], + ); + final dep2 = Provider( + (ref) => 0, + name: 'dep2', + dependencies: const [], + ); final a = Provider( (ref) => ref.watch(dep) + ref.watch(dep2), dependencies: [dep, dep2], @@ -493,16 +544,16 @@ Future main() async { ], // checking that the 'dependencies' order doesn't matter name: 'b', ); - final root = createContainer(); - final mid = createContainer( + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); - final mid2 = createContainer( + final mid2 = ProviderContainer.test( parent: mid, overrides: [dep2.overrideWithValue(21)], ); - final container = createContainer(parent: mid2); + final container = ProviderContainer.test(parent: mid2); expect(container.read(a), 63); expect(container.read(b), 21); @@ -511,40 +562,44 @@ Future main() async { expect( mid2.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', dep2), - isA>() - .having((e) => e.origin, 'origin', a), - isA>() - .having((e) => e.origin, 'origin', b), + isA().having((e) => e.origin, 'origin', dep2), + isA().having((e) => e.origin, 'origin', a), + isA().having((e) => e.origin, 'origin', b), ]), ); expect(mid.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', dep), + isA().having((e) => e.origin, 'origin', dep), ]); expect(root.getAllProviderElements(), isEmpty); }); test('skips containers with overrides that do not match the "dependencies"', () { - final dep = Provider((ref) => 0, name: 'dep'); - final dep2 = Provider((ref) => 0, name: 'dep2'); + final dep = Provider( + (ref) => 0, + name: 'dep', + dependencies: const [], + ); + final dep2 = Provider( + (ref) => 0, + name: 'dep2', + dependencies: const [], + ); final a = Provider( (ref) => ref.watch(dep), dependencies: [dep], name: 'a', ); - final root = createContainer(); - final mid = createContainer( + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); - final mid2 = createContainer( + final mid2 = ProviderContainer.test( parent: mid, overrides: [dep2.overrideWithValue(21)], ); - final container = createContainer(parent: mid2); + final container = ProviderContainer.test(parent: mid2); expect(container.read(a), 42); @@ -553,10 +608,8 @@ Future main() async { expect( mid.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', dep), - isA>() - .having((e) => e.origin, 'origin', a), + isA().having((e) => e.origin, 'origin', dep), + isA().having((e) => e.origin, 'origin', a), ]), ); expect(root.getAllProviderElements(), isEmpty); @@ -566,14 +619,17 @@ Future main() async { 'when a provider with dependencies is overridden with a value, ' 'it is no longer automatically overridden if a lower container overrides a dependency', () { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = Provider((ref) => ref.watch(dep), dependencies: [dep]); - final root = createContainer(); - final mid = createContainer( + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( parent: root, overrides: [provider.overrideWithValue(42)], ); - final container = createContainer( + final container = ProviderContainer.test( parent: mid, overrides: [dep.overrideWithValue(84)], ); @@ -585,24 +641,26 @@ Future main() async { expect( mid.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', provider), ], ); }); test('auto scope direct family dependencies', () { - final family = Provider.family((ref, id) => id * 2); + final family = Provider.family( + (ref, id) => id * 2, + dependencies: const [], + ); final provider = Provider( (ref) => ref.watch(family(21)), dependencies: [family], ); - final root = createContainer(); - final mid = createContainer( + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( parent: root, overrides: [family], ); - final container = createContainer(parent: mid); + final container = ProviderContainer.test(parent: mid); expect(container.read(provider), 42); expect(mid.read(provider), 42); @@ -611,27 +669,28 @@ Future main() async { expect( mid.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), - isA>() - .having((e) => e.origin, 'origin', family(21)), + isA().having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', family(21)), ]), ); }); test('auto scope transitive family dependencies', () { - final family = Provider.family((ref, id) => id * 2); + final family = Provider.family( + (ref, id) => id * 2, + dependencies: [], + ); final dep = Provider( (ref) => ref.watch(family(21)), dependencies: [family], ); final provider = Provider((ref) => ref.watch(dep), dependencies: [dep]); - final root = createContainer(); - final mid = createContainer( + final root = ProviderContainer.test(); + final mid = ProviderContainer.test( parent: root, overrides: [family], ); - final container = createContainer(parent: mid); + final container = ProviderContainer.test(parent: mid); expect(container.read(provider), 42); expect(mid.read(provider), 42); @@ -640,24 +699,24 @@ Future main() async { expect( mid.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), - isA>() - .having((e) => e.origin, 'origin', dep), - isA>() - .having((e) => e.origin, 'origin', family(21)), + isA().having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', dep), + isA().having((e) => e.origin, 'origin', family(21)), ]), ); }); test('can auto-scope autoDispose providers', () async { - final dep = Provider((ref) => 0); + final dep = Provider( + (ref) => 0, + dependencies: const [], + ); final provider = Provider.autoDispose( (ref) => ref.watch(dep), dependencies: [dep], ); - final root = createContainer(); - final container = createContainer( + final root = ProviderContainer.test(); + final container = ProviderContainer.test( parent: root, overrides: [dep.overrideWithValue(42)], ); @@ -666,10 +725,8 @@ Future main() async { expect( container.getAllProviderElements(), unorderedEquals([ - isA>() - .having((e) => e.origin, 'origin', provider), - isA>() - .having((e) => e.origin, 'origin', dep), + isA().having((e) => e.origin, 'origin', provider), + isA().having((e) => e.origin, 'origin', dep), ]), ); expect(root.getAllProviderElements(), isEmpty); @@ -679,130 +736,32 @@ Future main() async { expect( container.getAllProviderElements(), [ - isA>() - .having((e) => e.origin, 'origin', dep), + isA().having((e) => e.origin, 'origin', dep), ], ); expect(root.getAllProviderElements(), isEmpty); }); - - test('accepts only providers or families', () async { - expect( - library.withCode( - ''' -import 'package:riverpod/riverpod.dart'; - -final a = Provider((ref) => 0); - -final b = Provider( - (ref) => 0, - dependencies: [ - // expect-error: LIST_ELEMENT_TYPE_NOT_ASSIGNABLE - 42, - // expect-error: LIST_ELEMENT_TYPE_NOT_ASSIGNABLE - a.select((value) => 42), - ], -); -''', - ), - compiles, - ); - }); - - test( - 'does not throw if trying to watch a non-scoped provider that is not in the dependencies list', - () { - final container = createContainer(); - final dep = Provider((ref) => 0); - final provider = Provider( - (ref) => ref.watch(dep), - dependencies: const [], - ); - - expect(container.read(provider), 0); - }); - - test( - 'Throw if trying to watch a scoped provider that is not in the dependencies list', - () { - final container = createContainer(); - final dep = Provider((ref) => 0, dependencies: const []); - final dep2 = Provider((ref) => 0, dependencies: [dep]); - final provider = Provider( - dependencies: [dep], - (ref) => ref.watch(dep2), - ); - - expect( - () => container.read(provider), - throwsA(isA()), - ); - }); - - test( - 'Throw if trying to listen a scoped provider that is not in the dependencies list', - () { - final container = createContainer(); - final dep = Provider((ref) => 0, dependencies: const []); - final dep2 = Provider((ref) => 0, dependencies: [dep]); - final provider = Provider( - dependencies: [dep], - (ref) => ref.listen(dep2, (_, __) {}), - ); - - expect( - () => container.read(provider), - throwsA(isA()), - ); - }); - - test( - 'Throw if trying to read a scoped provider that is not in the dependencies list', - () { - final container = createContainer(); - final dep = Provider((ref) => 0, dependencies: const []); - final dep2 = Provider((ref) => 0, dependencies: [dep]); - final provider = Provider( - dependencies: [dep], - (ref) => ref.read(dep2), - ); - - expect( - () => container.read(provider), - throwsA(isA()), - ); - }); - }); - - test( - 'throw if non-family overrideWithProvider returns a provider with dependencies', - () { - final provider = Provider((ref) => 0); - final a = Provider((ref) => 0); - - expect( - // ignore: deprecated_member_use_from_same_package - () => provider.overrideWithProvider( - Provider((ref) => 0, dependencies: [a]), - ), - throwsA(isA()), - ); }); test('does not auto-scope provider overrides', () { - final a = Provider((ref) => 0); - final another = Provider((ref) => 42); + final a = Provider( + (ref) => 0, + dependencies: const [], + ); + final another = Provider( + (ref) => 42, + dependencies: const [], + ); final b = Provider((ref) => ref.watch(a), dependencies: [a]); final c = Provider((ref) => ref.watch(a), dependencies: [a]); - final root = createContainer( + final root = ProviderContainer.test( overrides: [ b.overrideWithValue(21), - // ignore: deprecated_member_use_from_same_package - c.overrideWithProvider(Provider((ref) => ref.watch(another) + 10)), + c.overrideWith((ref) => ref.watch(another) + 10), ], ); - final container = createContainer( + final container = ProviderContainer.test( parent: root, overrides: [ a.overrideWithValue(42), @@ -816,21 +775,27 @@ final b = Provider( }); test('does not auto-scope family overrides', () { - final a = Provider((ref) => 0); - final another = Provider((ref) => 42); + final a = Provider( + (ref) => 0, + dependencies: const [], + ); + final another = Provider( + (ref) => 42, + dependencies: const [], + ); final b = Provider.family( (ref, _) => ref.watch(a), - dependencies: [a], + dependencies: [a, another], ); - final root = createContainer( + final root = ProviderContainer.test( overrides: [ - b.overrideWithProvider( - (value) => Provider((ref) => ref.watch(another) + value), + b.overrideWith( + (ref, value) => ref.watch(another) + value, ), ], ); - final container = createContainer( + final container = ProviderContainer.test( parent: root, overrides: [ a.overrideWithValue(42), @@ -841,4 +806,121 @@ final b = Provider( expect(container.read(a), 42); expect(container.read(b(10)), 52); }); + + test('scoped autoDispose override preserve the override after one disposal', + () async { + final provider = Provider.autoDispose( + (ref) => 0, + dependencies: const [], + ); + + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); + + container.read(provider); + expect(root.getAllProviderElements(), isEmpty); + expect(container.getAllProviderElements(), isNotEmpty); + + await container.pump(); + + expect(root.getAllProviderElements(), isEmpty); + expect(container.getAllProviderElements(), isEmpty); + + container.read(provider); + + expect(root.getAllProviderElements(), isEmpty); + expect(container.getAllProviderElements(), isNotEmpty); + }); + + test( + 'scoped autoDispose override through intermediary unused container preserve the override after one disposal', + () async { + final provider = Provider.autoDispose( + (ref) => 0, + dependencies: const [], + ); + + final root = ProviderContainer.test(); + final mid = ProviderContainer.test(parent: root, overrides: [provider]); + final container = ProviderContainer.test(parent: mid); + + container.read(provider); + expect(root.getAllProviderElements(), isEmpty); + expect(mid.getAllProviderElements(), isNotEmpty); + expect(container.getAllProviderElements(), isEmpty); + + await container.pump(); + + expect(root.getAllProviderElements(), isEmpty); + expect(mid.getAllProviderElements(), isEmpty); + expect(container.getAllProviderElements(), isEmpty); + + container.read(provider); + + expect(root.getAllProviderElements(), isEmpty); + expect(mid.getAllProviderElements(), isNotEmpty); + expect(container.getAllProviderElements(), isEmpty); + }); + + test( + 'scoped autoDispose override preserve family override after one disposal', + () async { + final provider = Provider.autoDispose.family( + (ref, _) => 0, + dependencies: const [], + ); + + final root = ProviderContainer.test(); + final container = ProviderContainer.test( + parent: root, + overrides: [provider], + ); + + container.read(provider(0)); + expect(root.getAllProviderElements(), isEmpty); + expect(container.getAllProviderElements(), isNotEmpty); + + await container.pump(); + + expect(root.getAllProviderElements(), isEmpty); + expect(container.getAllProviderElements(), isEmpty); + + container.read(provider(0)); + + expect(root.getAllProviderElements(), isEmpty); + expect(container.getAllProviderElements(), isNotEmpty); + }); + + test( + 'scoped autoDispose override through intermediary unused container preserve family override after one disposal', + () async { + final provider = Provider.autoDispose.family( + (ref, _) => 0, + dependencies: const [], + ); + + final root = ProviderContainer.test(); + final mid = ProviderContainer.test(parent: root, overrides: [provider]); + final container = ProviderContainer.test(parent: mid); + + container.read(provider(0)); + expect(root.getAllProviderElements(), isEmpty); + expect(mid.getAllProviderElements(), isNotEmpty); + expect(container.getAllProviderElements(), isEmpty); + + await container.pump(); + + expect(root.getAllProviderElements(), isEmpty); + expect(mid.getAllProviderElements(), isEmpty); + expect(container.getAllProviderElements(), isEmpty); + + container.read(provider(0)); + + expect(root.getAllProviderElements(), isEmpty); + expect(mid.getAllProviderElements(), isNotEmpty); + expect(container.getAllProviderElements(), isEmpty); + }); } diff --git a/packages/riverpod/test/framework/uni_directional_test.dart b/packages/riverpod/test/src/core/uni_directional_test.dart similarity index 70% rename from packages/riverpod/test/framework/uni_directional_test.dart rename to packages/riverpod/test/src/core/uni_directional_test.dart index 7bd6e4abf..2bf07a19f 100644 --- a/packages/riverpod/test/framework/uni_directional_test.dart +++ b/packages/riverpod/test/src/core/uni_directional_test.dart @@ -2,105 +2,46 @@ import 'package:mockito/mockito.dart'; import 'package:riverpod/src/internals.dart'; import 'package:test/test.dart'; +import '../matrix.dart'; import '../utils.dart'; void main() { - late ProviderContainer container; - setUp(() { - container = ProviderContainer(); - }); - tearDown(() { - container.dispose(); - }); - test( - 'Catches circular dependency when dependencies are setup during provider initialization', + 'Catches sync circular dependency when the dependency is not yet mounted', () { // regression for #1766 - final container = createContainer(); + final container = ProviderContainer.test(); - final authInterceptorProvider = Provider((ref) => ref); + final c = Provider((ref) => ref); - final dioProvider = Provider((ref) { - ref.watch(authInterceptorProvider); + final a = Provider((ref) { + ref.watch(c); return 0; }); - final accessTokenProvider = Provider((ref) { - return ref.watch(dioProvider); + final b = Provider((ref) { + return ref.watch(a); }); - container.read(dioProvider); - final interceptor = container.read(authInterceptorProvider); + container.read(a); + final ref = container.read(c); expect( - () => interceptor.read(accessTokenProvider), + () => ref.read(b), throwsA(isA()), ); }); - group('ProviderContainer.debugVsyncs', () { - // test('are called before modifying a provider', () { - // final provider = StateProvider((ref) => 0); - // final container = ProviderContainer(); - // final vsync = VsyncMock(); - // final vsync2 = VsyncMock(); - - // container.debugVsyncs.addAll([vsync, vsync2]); - - // final state = container.read(provider); - - // verifyZeroInteractions(vsync); - // verifyZeroInteractions(vsync2); - - // state.state++; - - // verifyOnly(vsync, vsync()); - // verifyOnly(vsync2, vsync2()); - // }); - - // test('are not called when flushing a provider', () { - // final dep = StateProvider((ref) => 0); - // final provider = Provider((ref) { - // return ref.watch(dep); - // }); - // final container = ProviderContainer(); - // final vsync = VsyncMock(); - - // final sub = container.listen(provider, (_) {}); - // container.read(dep.notifier).state++; - - // container.debugVsyncs.add(vsync); - - // sub.flush(); - - // verifyZeroInteractions(vsync); - // }); - - // test('are not called when re-creating a provider', () { - // final provider = Provider((ref) => 0); - // final container = ProviderContainer(); - // final vsync = VsyncMock(); - - // final sub = container.listen(provider, (_) {}); - // container.refresh(provider); - - // container.debugVsyncs.add(vsync); - - // sub.flush(); - - // verifyZeroInteractions(vsync); - // }); - }); - test('rebuilding a provider can modify other providers', () async { final dep = StateProvider((ref) => 0); final provider = Provider((ref) => ref.watch(dep)); - final another = StateProvider((ref) { - ref.listen(provider, (prev, value) => ref.controller.state++); - return 0; - }); - final container = createContainer(); + final another = NotifierProvider, int>( + () => DeferredNotifier((ref, self) { + ref.listen(provider, (prev, value) => self.state++); + return 0; + }), + ); + final container = ProviderContainer.test(); expect(container.read(another), 0); @@ -147,6 +88,45 @@ void main() { }); }); + group('ref.listen cannot end-up in a circular dependency', () { + test('direct dependency', () { + final provider = Provider((ref) => ref); + final provider2 = Provider((ref) => ref); + final container = ProviderContainer(); + + final ref = container.read(provider); + final ref2 = container.read(provider2); + + ref.watch(provider2); + expect( + () => ref2.listen(provider, (a, b) {}), + throwsA(isA()), + ); + }); + + test('indirect dependency', () { + final provider = Provider((ref) => ref); + final provider2 = Provider((ref) => ref); + final provider3 = Provider((ref) => ref); + final provider4 = Provider((ref) => ref); + final container = ProviderContainer(); + + final ref = container.read(provider); + final ref2 = container.read(provider2); + final ref3 = container.read(provider3); + final ref4 = container.read(provider4); + + ref.listen(provider2, (a, b) {}); + ref2.listen(provider3, (a, b) {}); + ref3.listen(provider4, (a, b) {}); + + expect( + () => ref4.listen(provider, (a, b) {}), + throwsA(isA()), + ); + }); + }); + group('ref.read cannot end-up in a circular dependency', () { test('direct dependency', () { final provider = Provider((ref) => ref); @@ -186,6 +166,7 @@ void main() { }); test("initState can't dirty ancestors", () { + final container = ProviderContainer.test(); final ancestor = StateProvider((_) => 0); final child = Provider((ref) { ref.watch(ancestor.notifier).state++; @@ -196,6 +177,7 @@ void main() { }); test("initState can't dirty siblings", () { + final container = ProviderContainer.test(); final ancestor = StateProvider((_) => 0, name: 'ancestor'); final counter = Counter(); final sibling = StateNotifierProvider( @@ -222,6 +204,7 @@ void main() { }); test("initState can't mark dirty other provider", () { + final container = ProviderContainer.test(); final provider = StateProvider((ref) => 0); final provider2 = Provider((ref) { ref.watch(provider.notifier).state = 42; @@ -234,6 +217,7 @@ void main() { }); test("nested initState can't mark dirty other providers", () { + final container = ProviderContainer.test(); final counter = Counter(); final provider = StateNotifierProvider((_) => counter); final nested = Provider((_) => 0); @@ -249,6 +233,7 @@ void main() { }); test('auto dispose can dirty providers', () async { + final container = ProviderContainer.test(); final counter = Counter(); final provider = StateNotifierProvider((_) => counter); var didDispose = false; @@ -273,6 +258,7 @@ void main() { }); test("Provider can't dirty anything on create", () { + final container = ProviderContainer.test(); final counter = Counter(); final provider = StateNotifierProvider((_) => counter); late List errors; @@ -291,7 +277,3 @@ void main() { expect(errors, isNotEmpty); }); } - -// class VsyncMock extends Mock { -// void call(); -// } diff --git a/packages/riverpod/test/framework/visit_states_test.dart b/packages/riverpod/test/src/core/visit_states_test.dart similarity index 86% rename from packages/riverpod/test/framework/visit_states_test.dart rename to packages/riverpod/test/src/core/visit_states_test.dart index 9ba94c783..57df08a7f 100644 --- a/packages/riverpod/test/framework/visit_states_test.dart +++ b/packages/riverpod/test/src/core/visit_states_test.dart @@ -6,8 +6,6 @@ import 'package:riverpod/riverpod.dart'; import 'package:test/test.dart'; import 'package:trotter/trotter.dart'; -import '../utils.dart'; - void main() { // A // | @@ -28,7 +26,7 @@ void main() { final perm = Permutations(3, [a, b, c]); for (final permutation in perm()) { - final container = createContainer(); + final container = ProviderContainer.test(); permutation.forEach(container.read); expect(compute(container), [a, b, c]); } @@ -58,7 +56,7 @@ void main() { final perm = Permutations(4, [a, b, c, d]); for (final permutation in perm()) { - final container = createContainer(); + final container = ProviderContainer.test(); permutation.forEach(container.read); expect( compute(container), @@ -76,13 +74,16 @@ void main() { test('linear across two containers', () { final a = Provider((ref) => A()); - final b = Provider((ref) { - ref.watch(a); - return B(); - }); + final b = Provider( + (ref) { + ref.watch(a); + return B(); + }, + dependencies: const [], + ); - final parent = createContainer(); - final container = createContainer(parent: parent, overrides: [b]); + final parent = ProviderContainer.test(); + final container = ProviderContainer.test(parent: parent, overrides: [b]); container.read(b); expect(compute(container), [b]); @@ -95,21 +96,25 @@ void main() { test('branching across two containers', () { final a = Provider((ref) => A()); - final b = Provider((ref) { - return B(); - }); + final b = Provider( + (ref) => B(), + dependencies: const [], + ); - final c = Provider((ref) { - ref.watch(a); - ref.watch(b); - return C(); - }); + final c = Provider( + (ref) { + ref.watch(a); + ref.watch(b); + return C(); + }, + dependencies: [b], + ); - final parent = createContainer(); + final parent = ProviderContainer.test(); final perm = Permutations(2, [b, c]); for (final permutation in perm()) { - final container = createContainer( + final container = ProviderContainer.test( parent: parent, overrides: permutation, ); @@ -171,7 +176,7 @@ void main() { final perm = Permutations(7, [a, b, c, d, e, f, g]); for (final permutation in perm()) { - final container = createContainer(); + final container = ProviderContainer.test(); permutation.forEach(container.read); expect( compute(container), @@ -215,7 +220,7 @@ void main() { final perm = Permutations(4, [a, b, c, d]); for (final permutation in perm()) { - final container = createContainer(); + final container = ProviderContainer.test(); permutation.forEach(container.read); expect( compute(container), @@ -250,7 +255,7 @@ void main() { final perm = Permutations(4, [a, b, c, d]); for (final permutation in perm()) { - final container = createContainer(); + final container = ProviderContainer.test(); permutation.forEach(container.read); expect( compute(container), @@ -290,7 +295,7 @@ void main() { final perm = Permutations(5, [a, b, c, d, e]); for (final permutation in perm()) { - final container = createContainer(); + final container = ProviderContainer.test(); permutation.forEach(container.read); expect( compute(container), diff --git a/packages/riverpod/test/src/matrix.dart b/packages/riverpod/test/src/matrix.dart new file mode 100644 index 000000000..202f2a010 --- /dev/null +++ b/packages/riverpod/test/src/matrix.dart @@ -0,0 +1,184 @@ +import 'dart:async'; + +import 'package:riverpod/src/internals.dart'; +import 'package:state_notifier/state_notifier.dart'; +import 'package:test/test.dart' hide Retry; + +part 'matrix/async_notifier_provider.dart'; +part 'matrix/stream_notifier_provider.dart'; +part 'matrix/notifier_provider.dart'; + +class TestMatrix> { + TestMatrix(this.values); + + final Map values; + + void createGroup(void Function(T factory) cb) { + for (final entry in values.entries) { + group(entry.key, () => cb(entry.value)); + } + } +} + +class TestFactory { + TestFactory({ + required this.value, + required this.isAutoDispose, + required this.isFamily, + }); + + final T value; + final bool isAutoDispose; + final bool isFamily; +} + +typedef ProviderFactory = ProviderT Function([Object? arg]) + Function( + BaseT Function(Ref? ref, Object? arg) create, { + String? name, + Iterable? dependencies, + Retry? retry, +}); + +extension $Modifiers on ProviderBase { + Refreshable? get notifier { + final that = this; + return switch (that) { + $ClassProvider() => that.notifier, + _ => null, + }; + } + + Refreshable>? get future { + final that = this; + return switch (that) { + $FutureModifier() => that.future, + _ => null, + }; + } +} + +final providerFactory = >>[ + (create, {name, dependencies, retry}) => ([arg]) { + return Provider( + (ref) => create(ref, arg), + name: name, + dependencies: dependencies, + retry: retry, + ); + }, + (create, {name, dependencies, retry}) => ([arg]) { + return Provider.autoDispose( + (ref) => create(ref, arg), + name: name, + dependencies: dependencies, + retry: retry, + ); + }, + (create, {name, dependencies, retry}) => ([arg]) { + return Provider.family( + (ref, arg) => create(ref, arg), + name: name, + dependencies: dependencies, + retry: retry, + )(arg); + }, + (create, {name, dependencies, retry}) => ([arg]) { + return Provider.autoDispose.family( + (ref, arg) => create(ref, arg), + name: name, + dependencies: dependencies, + retry: retry, + )(arg); + }, +]; + +final futureProviderFactories = + , FutureProvider>>[ + (create, {name, dependencies, retry}) => ([arg]) { + return FutureProvider( + (ref) => create(ref, arg), + name: name, + dependencies: dependencies, + retry: retry, + ); + }, + (create, {name, dependencies, retry}) => ([arg]) { + return FutureProvider.autoDispose( + (ref) => create(ref, arg), + name: name, + dependencies: dependencies, + retry: retry, + ); + }, + (create, {name, dependencies, retry}) => ([arg]) { + return FutureProvider.family( + (ref, arg) => create(ref, arg), + name: name, + dependencies: dependencies, + retry: retry, + )(arg); + }, + (create, {name, dependencies, retry}) => ([arg]) { + return FutureProvider.autoDispose.family( + (ref, arg) => create(ref, arg), + name: name, + dependencies: dependencies, + retry: retry, + )(arg); + }, +]; + +final streamProviderFactories = + , StreamProvider>>[ + (create, {name, dependencies, retry}) => ([arg]) { + return StreamProvider( + (ref) => create(ref, arg), + name: name, + dependencies: dependencies, + retry: retry, + ); + }, + (create, {name, dependencies, retry}) => ([arg]) { + return StreamProvider.autoDispose( + (ref) => create(ref, arg), + name: name, + dependencies: dependencies, + retry: retry, + ); + }, + (create, {name, dependencies, retry}) => ([arg]) { + return StreamProvider.family( + (ref, arg) => create(ref, arg), + name: name, + dependencies: dependencies, + retry: retry, + )(arg); + }, + (create, {name, dependencies, retry}) => ([arg]) { + return StreamProvider.autoDispose.family( + (ref, arg) => create(ref, arg), + name: name, + dependencies: dependencies, + retry: retry, + )(arg); + }, +]; + +final asyncProviderFactory = + >>>[ + for (final factory in futureProviderFactories) + (create, {name, dependencies, retry}) => factory( + (ref, arg) async => create(ref, arg), + name: name, + dependencies: dependencies, + retry: retry, + ), + for (final factory in streamProviderFactories) + (create, {name, dependencies, retry}) => factory( + (ref, arg) => Stream.value(create(ref, arg)), + name: name, + dependencies: dependencies, + retry: retry, + ), +]; diff --git a/packages/riverpod/test/src/matrix/async_notifier_provider.dart b/packages/riverpod/test/src/matrix/async_notifier_provider.dart new file mode 100644 index 000000000..193dfdee7 --- /dev/null +++ b/packages/riverpod/test/src/matrix/async_notifier_provider.dart @@ -0,0 +1,224 @@ +part of '../matrix.dart'; + +final asyncNotifierProviderFactory = TestMatrix( + { + 'AsyncNotifierProvider': AsyncNotifierTestFactory( + isAutoDispose: false, + isFamily: false, + deferredNotifier: DeferredAsyncNotifier.new, + deferredProvider: (create, {updateShouldNotify, retry}) { + return AsyncNotifierProvider, StateT>( + retry: retry, + () => DeferredAsyncNotifier( + create, + updateShouldNotify: updateShouldNotify, + ), + ); + }, + provider: (create) => + AsyncNotifierProvider, StateT>( + () => create() as AsyncNotifier, + ), + value: (create, {name, dependencies, retry}) => ([arg]) { + return AsyncNotifierProvider, Object?>( + () => create(null, arg) as AsyncNotifier, + name: name, + dependencies: dependencies, + retry: retry, + ); + }, + ), + 'AsyncNotifierProvider.autoDispose': AsyncNotifierTestFactory( + isAutoDispose: true, + isFamily: false, + deferredNotifier: DeferredAsyncNotifier.new, + deferredProvider: (create, {updateShouldNotify, retry}) { + return AsyncNotifierProvider.autoDispose, + StateT>( + retry: retry, + () => DeferredAsyncNotifier( + create, + updateShouldNotify: updateShouldNotify, + ), + ); + }, + provider: (create) { + return AsyncNotifierProvider.autoDispose, StateT>( + () => create() as AsyncNotifier, + ); + }, + value: (create, {name, dependencies, retry}) => ([arg]) { + return AsyncNotifierProvider.autoDispose, + Object?>( + () => create(null, arg) as AsyncNotifier, + name: name, + dependencies: dependencies, + retry: retry, + ); + }, + ), + 'AsyncNotifierProvider.family': AsyncNotifierTestFactory( + isAutoDispose: false, + isFamily: true, + deferredNotifier: DeferredFamilyAsyncNotifier.new, + deferredProvider: (create, {updateShouldNotify, retry}) { + return AsyncNotifierProvider.family, + StateT, Object?>( + retry: retry, + () => DeferredFamilyAsyncNotifier( + create, + updateShouldNotify: updateShouldNotify, + ), + ).call(42); + }, + provider: (create) { + return AsyncNotifierProvider.family< + FamilyAsyncNotifier, StateT, Object?>( + () => create() as FamilyAsyncNotifier, + ).call(42); + }, + value: (create, {name, dependencies, retry}) => ([arg]) { + return AsyncNotifierProvider.family< + FamilyAsyncNotifier, Object?, Object?>( + () => create(null, arg) as FamilyAsyncNotifier, + name: name, + dependencies: dependencies, + retry: retry, + )(arg); + }, + ), + 'AsyncNotifierProvider.autoDispose.family': AsyncNotifierTestFactory( + isAutoDispose: true, + isFamily: true, + deferredNotifier: DeferredFamilyAsyncNotifier.new, + deferredProvider: (create, {updateShouldNotify, retry}) { + return AsyncNotifierProvider.family + .autoDispose, StateT, Object?>( + retry: retry, + () => DeferredFamilyAsyncNotifier( + create, + updateShouldNotify: updateShouldNotify, + ), + ) + .call(42); + }, + provider: (create) { + return AsyncNotifierProvider.autoDispose + .family, StateT, Object?>( + () => create() as FamilyAsyncNotifier, + ) + .call(42); + }, + value: (create, {name, dependencies, retry}) => ([arg]) { + return AsyncNotifierProvider.autoDispose + .family, Object?, Object?>( + () => create(null, arg) as FamilyAsyncNotifier, + name: name, + dependencies: dependencies, + retry: retry, + )(arg); + }, + ), + }, +); + +abstract class TestAsyncNotifier implements $AsyncNotifier { + // Removing protected + @override + AsyncValue get state; + + @override + set state(AsyncValue value); +} + +class DeferredAsyncNotifier extends AsyncNotifier + implements TestAsyncNotifier { + DeferredAsyncNotifier( + this._create, { + bool Function(AsyncValue, AsyncValue)? updateShouldNotify, + }) : _updateShouldNotify = updateShouldNotify; + + final FutureOr Function(Ref ref, $AsyncNotifier self) _create; + final bool Function( + AsyncValue previousState, + AsyncValue newState, + )? _updateShouldNotify; + + @override + FutureOr build() => _create(ref, this); + + @override + bool updateShouldNotify( + AsyncValue previousState, + AsyncValue newState, + ) => + _updateShouldNotify?.call(previousState, newState) ?? + super.updateShouldNotify(previousState, newState); +} + +class DeferredFamilyAsyncNotifier + extends FamilyAsyncNotifier + implements TestAsyncNotifier { + DeferredFamilyAsyncNotifier( + this._create, { + bool Function(AsyncValue, AsyncValue)? updateShouldNotify, + }) : _updateShouldNotify = updateShouldNotify; + + final FutureOr Function(Ref ref, $AsyncNotifier self) _create; + + final bool Function( + AsyncValue previousState, + AsyncValue newState, + )? _updateShouldNotify; + + @override + FutureOr build(int arg) => _create(ref, this); + + @override + bool updateShouldNotify( + AsyncValue previousState, + AsyncValue newState, + ) => + _updateShouldNotify?.call(previousState, newState) ?? + super.updateShouldNotify(previousState, newState); +} + +class AsyncNotifierTestFactory extends TestFactory< + ProviderFactory<$AsyncNotifier, ProviderBase>> { + AsyncNotifierTestFactory({ + required super.isAutoDispose, + required super.isFamily, + required super.value, + required this.deferredNotifier, + required this.deferredProvider, + required this.provider, + }); + + final TestAsyncNotifier Function( + FutureOr Function(Ref ref, $AsyncNotifier self) create, + ) deferredNotifier; + + final $AsyncNotifierProvider, StateT> + Function( + FutureOr Function(Ref ref, $AsyncNotifier self) create, { + bool Function(AsyncValue, AsyncValue)? updateShouldNotify, + Retry? retry, + }) deferredProvider; + + final $AsyncNotifierProvider<$AsyncNotifier, StateT> Function( + $AsyncNotifier Function() create, + ) provider; + + $AsyncNotifierProvider, StateT> + simpleTestProvider( + FutureOr Function(Ref ref, $AsyncNotifier self) create, { + bool Function(AsyncValue, AsyncValue)? updateShouldNotify, + Retry? retry, + }) { + return deferredProvider( + (ref, self) => create(ref, self), + updateShouldNotify: updateShouldNotify, + retry: retry, + ); + } +} diff --git a/packages/riverpod/test/src/matrix/notifier_mixin.dart b/packages/riverpod/test/src/matrix/notifier_mixin.dart new file mode 100644 index 000000000..e54813854 --- /dev/null +++ b/packages/riverpod/test/src/matrix/notifier_mixin.dart @@ -0,0 +1,48 @@ +// This is a file testing that mixins can be applied on notifiers. +// No need to run anything, just checking that it compiles. + +import 'dart:async'; + +import 'package:riverpod/riverpod.dart'; + +mixin MyMixin on NotifierBase { + @override + // ignore: unnecessary_overrides + B runBuild() { + return super.runBuild(); + } +} + +class Sync extends Notifier with MyMixin { + @override + int build() => 42; +} + +class SyncFamily extends FamilyNotifier with MyMixin { + @override + int build(int arg) => 42; +} + +class Async extends AsyncNotifier + with MyMixin, FutureOr> { + @override + FutureOr build() => Future.value(42); +} + +class AsyncFamily extends FamilyAsyncNotifier + with MyMixin, FutureOr> { + @override + FutureOr build(int arg) => Future.value(42); +} + +class StreamN extends StreamNotifier + with MyMixin, Stream> { + @override + Stream build() => Stream.value(42); +} + +class StreamFamily extends FamilyStreamNotifier + with MyMixin, Stream> { + @override + Stream build(int arg) => Stream.value(42); +} diff --git a/packages/riverpod/test/src/matrix/notifier_provider.dart b/packages/riverpod/test/src/matrix/notifier_provider.dart new file mode 100644 index 000000000..bdc31dc61 --- /dev/null +++ b/packages/riverpod/test/src/matrix/notifier_provider.dart @@ -0,0 +1,225 @@ +part of '../matrix.dart'; + +final notifierProviderFactory = TestMatrix( + { + 'NotifierProvider': NotifierTestFactory( + isAutoDispose: false, + isFamily: false, + deferredNotifier: (create) => + DeferredNotifier((ref, self) => create(ref, self)), + deferredProvider: (create, {updateShouldNotify}) { + return NotifierProvider, StateT>( + () => DeferredNotifier( + (ref, self) => create(ref, self), + updateShouldNotify: updateShouldNotify, + ), + ); + }, + provider: (create) => NotifierProvider, StateT>( + () => create() as Notifier, + ), + value: (create, {name, dependencies, retry}) => ([arg]) { + return NotifierProvider, Object?>( + () => create(null, arg) as Notifier, + name: name, + dependencies: dependencies, + retry: retry, + ); + }, + ), + 'NotifierProvider.autoDispose': NotifierTestFactory( + isAutoDispose: true, + isFamily: false, + deferredNotifier: (create) => + DeferredNotifier((ref, self) => create(ref, self)), + deferredProvider: (create, {updateShouldNotify}) { + return NotifierProvider.autoDispose, StateT>( + () => DeferredNotifier( + (ref, self) => create(ref, self), + updateShouldNotify: updateShouldNotify, + ), + ); + }, + provider: (create) { + return NotifierProvider.autoDispose, StateT>( + () => create() as Notifier, + ); + }, + value: (create, {name, dependencies, retry}) => ([arg]) { + return NotifierProvider.autoDispose, Object?>( + () => create(null, arg) as Notifier, + name: name, + dependencies: dependencies, + retry: retry, + ); + }, + ), + 'NotifierProvider.family': NotifierTestFactory( + isAutoDispose: false, + isFamily: true, + deferredNotifier: DeferredFamilyNotifier.new, + deferredProvider: (create, {updateShouldNotify}) { + return NotifierProvider.family, StateT, + Object?>( + () => DeferredFamilyNotifier( + create, + updateShouldNotify: updateShouldNotify, + ), + ).call(42); + }, + provider: (create) { + return NotifierProvider.family, StateT, + Object?>( + () => create() as FamilyNotifier, + ).call(42); + }, + value: (create, {name, dependencies, retry}) => ([arg]) { + return NotifierProvider.family, + Object?, Object?>( + () => create(null, arg) as FamilyNotifier, + name: name, + dependencies: dependencies, + retry: retry, + )(arg); + }, + ), + 'NotifierProvider.autoDispose.family': NotifierTestFactory( + isAutoDispose: true, + isFamily: true, + deferredNotifier: DeferredFamilyNotifier.new, + deferredProvider: (create, {updateShouldNotify}) { + return NotifierProvider.family + .autoDispose, StateT, Object?>( + () => DeferredFamilyNotifier( + create, + updateShouldNotify: updateShouldNotify, + ), + ) + .call(42); + }, + provider: (create) { + return NotifierProvider.autoDispose + .family, StateT, Object?>( + () => create() as FamilyNotifier, + ) + .call(42); + }, + value: (create, {name, dependencies, retry}) => ([arg]) { + return NotifierProvider.autoDispose + .family, Object?, Object?>( + () => create(null, arg) as FamilyNotifier, + name: name, + dependencies: dependencies, + retry: retry, + )(arg); + }, + ), + }, +); + +abstract class TestNotifier implements $Notifier { + // Removing protected + @override + StateT get state; + + @override + set state(StateT value); + + @override + RemoveListener listenSelf( + void Function(StateT? previous, StateT next) listener, { + void Function(Object error, StackTrace stackTrace)? onError, + }); +} + +class DeferredNotifier extends Notifier + implements TestNotifier { + DeferredNotifier( + this._create, { + bool Function(StateT, StateT)? updateShouldNotify, + }) : _updateShouldNotify = updateShouldNotify; + + final StateT Function(Ref ref, DeferredNotifier self) _create; + final bool Function( + StateT previousState, + StateT newState, + )? _updateShouldNotify; + + @override + Ref get ref; + + @override + RemoveListener listenSelf( + void Function(StateT? previous, StateT next) listener, { + void Function(Object error, StackTrace stackTrace)? onError, + }); + + @override + StateT build() => _create(ref, this); + + @override + bool updateShouldNotify(StateT previousState, StateT newState) => + _updateShouldNotify?.call(previousState, newState) ?? + super.updateShouldNotify(previousState, newState); +} + +class DeferredFamilyNotifier extends FamilyNotifier + implements TestNotifier { + DeferredFamilyNotifier( + this._create, { + bool Function(StateT, StateT)? updateShouldNotify, + }) : _updateShouldNotify = updateShouldNotify; + + final StateT Function(Ref ref, DeferredFamilyNotifier self) _create; + + final bool Function( + StateT previousState, + StateT newState, + )? _updateShouldNotify; + + @override + StateT build(int arg) => _create(ref, this); + + @override + bool updateShouldNotify( + StateT previousState, + StateT newState, + ) => + _updateShouldNotify?.call(previousState, newState) ?? + super.updateShouldNotify(previousState, newState); +} + +class NotifierTestFactory extends TestFactory< + ProviderFactory<$Notifier, ProviderBase>> { + NotifierTestFactory({ + required super.isAutoDispose, + required super.isFamily, + required super.value, + required this.deferredNotifier, + required this.deferredProvider, + required this.provider, + }); + + final TestNotifier Function( + StateT Function(Ref ref, $Notifier self) create, + ) deferredNotifier; + + final $NotifierProvider, StateT> Function( + StateT Function(Ref ref, $Notifier self) create, { + bool Function(StateT, StateT)? updateShouldNotify, + }) deferredProvider; + + final $NotifierProvider<$Notifier, StateT> Function( + $Notifier Function() create, + ) provider; + + $NotifierProvider, StateT> simpleTestProvider( + StateT Function(Ref ref, $Notifier self) create, { + bool Function(StateT, StateT)? updateShouldNotify, + }) { + return deferredProvider( + (ref, self) => create(ref, self), + updateShouldNotify: updateShouldNotify, + ); + } +} diff --git a/packages/riverpod/test/src/matrix/stream_notifier_provider.dart b/packages/riverpod/test/src/matrix/stream_notifier_provider.dart new file mode 100644 index 000000000..0bdf165fd --- /dev/null +++ b/packages/riverpod/test/src/matrix/stream_notifier_provider.dart @@ -0,0 +1,232 @@ +part of '../matrix.dart'; + +final streamNotifierProviderFactory = TestMatrix( + { + 'StreamNotifierProvider': StreamNotifierTestFactory( + isAutoDispose: false, + isFamily: false, + deferredNotifier: DeferredStreamNotifier.new, + deferredProvider: (create, {updateShouldNotify, retry}) { + return StreamNotifierProvider, StateT>( + retry: retry, + () => DeferredStreamNotifier( + create, + updateShouldNotify: updateShouldNotify, + ), + ); + }, + provider: (create) => + StreamNotifierProvider, StateT>( + () => create() as StreamNotifier, + ), + value: (create, {name, dependencies, retry}) => ([arg]) { + return StreamNotifierProvider, Object?>( + () => create(null, arg) as StreamNotifier, + name: name, + dependencies: dependencies, + retry: retry, + ); + }, + ), + 'StreamNotifierProvider.autoDispose': StreamNotifierTestFactory( + isAutoDispose: true, + isFamily: false, + deferredNotifier: DeferredStreamNotifier.new, + deferredProvider: (create, {updateShouldNotify, retry}) { + return StreamNotifierProvider.autoDispose< + DeferredStreamNotifier, StateT>( + retry: retry, + () => DeferredStreamNotifier( + create, + updateShouldNotify: updateShouldNotify, + ), + ); + }, + provider: (create) { + return StreamNotifierProvider.autoDispose, + StateT>( + () => create() as StreamNotifier, + ); + }, + value: (create, {name, dependencies, retry}) => ([arg]) { + return StreamNotifierProvider.autoDispose, + Object?>( + retry: retry, + () => create(null, arg) as StreamNotifier, + name: name, + dependencies: dependencies, + ); + }, + ), + 'StreamNotifierProvider.family': StreamNotifierTestFactory( + isAutoDispose: false, + isFamily: true, + deferredNotifier: DeferredFamilyStreamNotifier.new, + deferredProvider: (create, {updateShouldNotify, retry}) { + return StreamNotifierProvider.family< + DeferredFamilyStreamNotifier, StateT, Object?>( + retry: retry, + () => DeferredFamilyStreamNotifier( + create, + updateShouldNotify: updateShouldNotify, + ), + ).call(42); + }, + provider: (create) { + return StreamNotifierProvider.family< + FamilyStreamNotifier, StateT, Object?>( + () => create() as FamilyStreamNotifier, + ).call(42); + }, + value: (create, {name, dependencies, retry}) => ([arg]) { + return StreamNotifierProvider.family< + FamilyStreamNotifier, Object?, Object?>( + retry: retry, + () => create(null, arg) as FamilyStreamNotifier, + name: name, + dependencies: dependencies, + )(arg); + }, + ), + 'StreamNotifierProvider.autoDispose.family': StreamNotifierTestFactory( + isAutoDispose: true, + isFamily: true, + deferredNotifier: DeferredFamilyStreamNotifier.new, + deferredProvider: (create, {updateShouldNotify, retry}) { + return StreamNotifierProvider.family + .autoDispose, StateT, Object?>( + retry: retry, + () => DeferredFamilyStreamNotifier( + create, + updateShouldNotify: updateShouldNotify, + ), + ) + .call(42); + }, + provider: (create) { + return StreamNotifierProvider.autoDispose + .family, StateT, Object?>( + () => create() as FamilyStreamNotifier, + ) + .call(42); + }, + value: (create, {name, dependencies, retry}) => ([arg]) { + return StreamNotifierProvider.autoDispose + .family, Object?, Object?>( + retry: retry, + () => create(null, arg) as FamilyStreamNotifier, + name: name, + dependencies: dependencies, + )(arg); + }, + ), + }, +); + +abstract class TestStreamNotifier implements $StreamNotifier { + // Removing protected + @override + AsyncValue get state; + + @override + set state(AsyncValue value); +} + +class DeferredStreamNotifier extends StreamNotifier + implements TestStreamNotifier { + DeferredStreamNotifier( + this._create, { + bool Function(AsyncValue, AsyncValue)? updateShouldNotify, + }) : _updateShouldNotify = updateShouldNotify; + + final Stream Function( + Ref ref, + DeferredStreamNotifier self, + ) _create; + final bool Function( + AsyncValue previousState, + AsyncValue newState, + )? _updateShouldNotify; + + @override + Stream build() => _create(ref, this); + + @override + bool updateShouldNotify( + AsyncValue previousState, + AsyncValue newState, + ) => + _updateShouldNotify?.call(previousState, newState) ?? + super.updateShouldNotify(previousState, newState); +} + +class DeferredFamilyStreamNotifier + extends FamilyStreamNotifier + implements TestStreamNotifier { + DeferredFamilyStreamNotifier( + this._create, { + bool Function(AsyncValue, AsyncValue)? updateShouldNotify, + }) : _updateShouldNotify = updateShouldNotify; + + final Stream Function( + Ref ref, + DeferredFamilyStreamNotifier self, + ) _create; + + final bool Function( + AsyncValue previousState, + AsyncValue newState, + )? _updateShouldNotify; + + @override + Stream build(int arg) => _create(ref, this); + + @override + bool updateShouldNotify( + AsyncValue previousState, + AsyncValue newState, + ) => + _updateShouldNotify?.call(previousState, newState) ?? + super.updateShouldNotify(previousState, newState); +} + +class StreamNotifierTestFactory extends TestFactory< + ProviderFactory<$StreamNotifier, ProviderBase>> { + StreamNotifierTestFactory({ + required super.isAutoDispose, + required super.isFamily, + required super.value, + required this.deferredNotifier, + required this.deferredProvider, + required this.provider, + }); + + final TestStreamNotifier Function( + Stream Function(Ref ref, $StreamNotifier self) create, + ) deferredNotifier; + + final $StreamNotifierProvider, StateT> + Function( + Stream Function(Ref ref, $StreamNotifier self) create, { + bool Function(AsyncValue, AsyncValue)? updateShouldNotify, + Retry? retry, + }) deferredProvider; + + final $StreamNotifierProvider<$StreamNotifier, StateT> + Function( + $StreamNotifier Function() create, + ) provider; + + $StreamNotifierProvider, StateT> + simpleTestProvider( + Stream Function(Ref ref, $StreamNotifier self) create, { + bool Function(AsyncValue, AsyncValue)? updateShouldNotify, + Retry? retry, + }) { + return deferredProvider( + (ref, self) => create(ref, self), + updateShouldNotify: updateShouldNotify, + retry: retry, + ); + } +} diff --git a/packages/riverpod/test/src/providers/async_notifier_test.dart b/packages/riverpod/test/src/providers/async_notifier_test.dart new file mode 100644 index 000000000..ba0422e23 --- /dev/null +++ b/packages/riverpod/test/src/providers/async_notifier_test.dart @@ -0,0 +1,1373 @@ +// ignore_for_file: invalid_use_of_protected_member, void_checks, prefer_const_constructors, avoid_types_on_closure_parameters + +import 'dart:async'; + +import 'package:meta/meta.dart'; +import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/framework.dart' show UnmountedRefException; +import 'package:riverpod/src/providers/async_notifier.dart' show $AsyncNotifier; +import 'package:test/test.dart'; + +import '../../third_party/fake_async.dart'; +import '../matrix.dart'; +import '../utils.dart'; + +void main() { + test('Throws if using notifier properties in its constructor', () { + final errors = captureErrors([ + () => CtorNotifier().state, + () => CtorNotifier().state = const AsyncData(42), + () => CtorNotifier().future, + () => CtorNotifier().ref, + () => FamilyCtorNotifier().state, + () => FamilyCtorNotifier().state = const AsyncData(42), + () => FamilyCtorNotifier().future, + () => FamilyCtorNotifier().ref, + ]); + }); + + asyncNotifierProviderFactory.createGroup((factory) { + if (factory.isFamily) { + test('sets provider(arg) dependencies/allTransitiveDependencies to null', + () { + final provider = factory.value( + (_, arg) => factory.deferredNotifier((ref, _) => 0), + dependencies: [], + ); + + expect(provider().dependencies, null); + expect(provider().allTransitiveDependencies, null); + }); + } + + test('locks .future notification during build', () async { + final container = ProviderContainer.test(); + FutureOr Function(Ref ref, $AsyncNotifier self) build = + (ref, self) => 0; + final provider = factory.simpleTestProvider( + (ref, self) => build(ref, self), + ); + final futureListener = Listener>(); + + container.listen(provider.future, futureListener.call); + + build = (ref, self) { + self.state = const AsyncData(42); + self.state = const AsyncData(21); + return 84; + }; + container.invalidate(provider); + await container.pump(); + + final result = verify(futureListener(captureAny, captureAny))..called(1); + await expectLater(result.captured.first, completion(0)); + await expectLater(result.captured.last, completion(84)); + }); + + group('retry', () { + test( + 'handles retry', + () => fakeAsync((fake) async { + final container = ProviderContainer.test(); + var err = Exception('foo'); + final stack = StackTrace.current; + final provider = factory.simpleTestProvider( + (ref, self) => Error.throwWithStackTrace(err, stack), + retry: (_, __) => const Duration(seconds: 1), + ); + final listener = Listener>(); + + container.listen(provider, fireImmediately: true, listener.call); + await container.read(provider.future).catchError((e) => 0); + + verifyOnly( + listener, + listener(any, AsyncValue.error(err, stack)), + ); + + err = Exception('bar'); + + fake.elapse(const Duration(seconds: 1)); + fake.flushMicrotasks(); + + await container.read(provider.future).catchError((e) => 0); + + verifyOnly( + listener, + listener(any, AsyncValue.error(err, stack)), + ); + }), + ); + + test( + 'manually setting the state to an error does not cause a retry', + () => fakeAsync((fake) async { + final container = ProviderContainer.test(); + var retryCount = 0; + final provider = factory.simpleTestProvider( + (ref, self) => 0, + retry: (_, __) { + retryCount++; + return const Duration(seconds: 1); + }, + ); + final listener = Listener>(); + + container.listen(provider, fireImmediately: true, listener.call); + + expect(retryCount, 0); + + container.read(provider.notifier).state = AsyncValue.error( + Error(), + StackTrace.current, + ); + + expect(retryCount, 0); + }), + ); + }); + + test('resets progress to 0 if restarting while the future is pending', () { + final container = ProviderContainer.test(); + final completer = Completer(); + addTearDown(() => completer.complete(42)); + + final provider = factory.simpleTestProvider((ref, self) { + return completer.future; + }); + + expect(container.read(provider), const AsyncValue.loading()); + + container.read(provider.notifier).state = + const AsyncValue.loading(progress: .2); + + container.refresh(provider); + + expect(container.read(provider), const AsyncValue.loading()); + }); + + test('Does not skip return value if ref.state was set', () async { + final provider = factory.simpleTestProvider((ref, self) async { + await Future.value(); + self.state = const AsyncData(1); + await Future.value(); + self.state = const AsyncData(2); + await Future.value(); + return 3; + }); + final container = ProviderContainer.test(); + final listener = Listener>(); + // Completer used for the sole purpose of being able to await `provider.future` + // Since `provider` emits `AsyncData` before the future completes, then + // `provider.future` completes early. + // As such, awaiting `provider.future` isn't enough to fully await the FutureProvider + final completer = Completer(); + + container.listen>( + provider, + (prev, next) { + if (next.value == 3) completer.complete(); + listener(prev, next); + }, + fireImmediately: true, + ); + + await completer.future; + + verifyInOrder([ + listener(null, const AsyncLoading()), + listener(const AsyncLoading(), const AsyncData(1)), + listener(const AsyncData(1), const AsyncData(2)), + listener(const AsyncData(2), const AsyncData(3)), + ]); + }); + + test('Cannot share a Notifier instance between providers ', () { + final container = ProviderContainer.test(); + final notifier = factory.deferredNotifier((ref, _) => 0); + + final provider = factory.provider(() => notifier); + final provider2 = factory.provider(() => notifier); + + container.read(provider); + + expect( + container.read(provider2), + isA>(), + ); + }); + + test('Can read state inside onDispose', () { + final container = ProviderContainer.test(); + late TestAsyncNotifier notifier; + late List errors; + final provider = factory.simpleTestProvider((ref, _) { + ref.onDispose(() { + errors = captureErrors([ + () => notifier.state, + () => notifier.state = const AsyncData(42), + () => notifier.future, + ]); + }); + return 0; + }); + + container.listen(provider.notifier, (prev, next) {}); + notifier = container.read(provider.notifier); + + container.dispose(); + + expect( + errors, + everyElement(isA()), + ); + }); + + test('Using the notifier after dispose throws', () { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider((ref, _) => 0); + + container.listen(provider.notifier, (prev, next) {}); + final notifier = container.read(provider.notifier); + + container.dispose(); + + expect(notifier.ref.mounted, false); + expect( + () => notifier.state, + throwsA(isA()), + ); + expect( + () => notifier.future, + throwsA(isA()), + ); + expect( + () => notifier.state = const AsyncData(42), + throwsA(isA()), + ); + expect( + () => notifier.update((p1) => 42), + throwsA(isA()), + ); + }); + + test('Can assign `AsyncLoading` to `AsyncValue`', () { + // Regression test for https://github.com/rrousselGit/riverpod/issues/2120 + final provider = factory.simpleTestProvider((ref, _) => 42); + final container = ProviderContainer.test(); + + final sub = container.listen(provider.notifier, (prev, next) {}); + + expect(sub.read().state, const AsyncData(42)); + + sub.read().state = const AsyncLoading(); + + expect( + sub.read().state, + isA>() + .having((e) => e.hasValue, 'hasValue', true) + .having((e) => e.value, 'value', 42), + ); + }); + + test('Can assign `AsyncData` to `AsyncValue`', () { + // Regression test for https://github.com/rrousselGit/riverpod/issues/2120 + final provider = factory.simpleTestProvider((ref, _) => 42); + final container = ProviderContainer.test(); + + final sub = container.listen(provider.notifier, (prev, next) {}); + + expect(sub.read().state, const AsyncData(42)); + + sub.read().state = const AsyncData(42); + + expect( + sub.read().state, + isA>() + .having((e) => e.hasValue, 'hasValue', true) + .having((e) => e.value, 'value', 42), + ); + }); + + test('Can assign `AsyncError` to `AsyncValue`', () { + // Regression test for https://github.com/rrousselGit/riverpod/issues/2120 + final provider = factory.simpleTestProvider((ref, _) => 42); + final container = ProviderContainer.test(); + + final sub = container.listen(provider.notifier, (prev, next) {}); + + expect(sub.read().state, const AsyncData(42)); + + sub.read().state = AsyncError(21, StackTrace.current); + + expect( + sub.read().state, + isA>() + .having((e) => e.hasValue, 'hasValue', true) + .having((e) => e.value, 'value', 42) + .having((e) => e.hasError, 'hasError', true) + .having((e) => e.error, 'error', 21), + ); + }); + + group('supports AsyncValue transition', () { + test( + 'performs seamless copyWithPrevious if triggered by ref.invalidate/ref.refresh', + () async { + final container = ProviderContainer.test(); + var count = 0; + final provider = factory.simpleTestProvider( + (ref, _) => Future.value(count++), + ); + + container.listen(provider, (previous, next) {}); + + await expectLater(container.read(provider.future), completion(0)); + expect(container.read(provider), const AsyncData(0)); + + expect( + container.refresh(provider), + const AsyncLoading().copyWithPrevious(const AsyncData(0)), + ); + + await expectLater(container.read(provider.future), completion(1)); + expect(container.read(provider), const AsyncData(1)); + + container.invalidate(provider); + + expect( + container.read(provider), + const AsyncLoading().copyWithPrevious(const AsyncData(1)), + ); + await expectLater(container.read(provider.future), completion(2)); + expect(container.read(provider), const AsyncData(2)); + }); + + test( + 'performs seamless:false copyWithPrevious on `state = AsyncLoading()`', + () async { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider( + (ref, _) => Future.value(0), + ); + + final sub = container.listen(provider.notifier, (previous, next) {}); + + await expectLater(container.read(provider.future), completion(0)); + expect(container.read(provider), const AsyncData(0)); + + sub.read().state = const AsyncLoading(); + + expect( + sub.read().state, + const AsyncLoading() + .copyWithPrevious(const AsyncData(0), isRefresh: false), + ); + }); + + test( + 'performs seamless:false copyWithPrevious if triggered by a dependency change', + () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final provider = factory.simpleTestProvider( + (ref, _) => Future.value(ref.watch(dep)), + ); + + container.listen(provider, (previous, next) {}); + + await expectLater(container.read(provider.future), completion(0)); + expect(container.read(provider), const AsyncData(0)); + + container.read(dep.notifier).state++; + expect( + container.read(provider), + const AsyncLoading() + .copyWithPrevious(const AsyncData(0), isRefresh: false), + ); + + await expectLater(container.read(provider.future), completion(1)); + expect(container.read(provider), const AsyncData(1)); + }); + + test('performs seamless data > loading > error transition', () async { + final container = ProviderContainer.test(); + var result = Future.value(42); + final provider = FutureProvider((ref) => result); + + final sub = container.listen(provider.future, (_, __) {}); + + expect(container.read(provider), const AsyncLoading()); + expect(await sub.read(), 42); + expect(container.read(provider), const AsyncData(42)); + + result = Future.error('err', StackTrace.empty); + container.invalidate(provider); + + expect( + container.read(provider), + const AsyncLoading().copyWithPrevious(const AsyncData(42)), + ); + await expectLater(sub.read(), throwsA('err')); + expect( + container.read(provider), + const AsyncError('err', StackTrace.empty) + .copyWithPrevious(const AsyncData(42)), + ); + }); + + test( + 'performs seamless:false copyWithPrevious if both triggered by a dependency change and ref.refresh', + () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final provider = factory.simpleTestProvider( + (ref, _) => Future.value(ref.watch(dep)), + ); + + container.listen(provider, (previous, next) {}); + + await expectLater(container.read(provider.future), completion(0)); + expect(container.read(provider), const AsyncData(0)); + + container.read(dep.notifier).state++; + expect( + container.refresh(provider), + const AsyncLoading() + .copyWithPrevious(const AsyncData(0), isRefresh: false), + ); + + await expectLater(container.read(provider.future), completion(1)); + expect(container.read(provider), const AsyncData(1)); + }); + }); + + test('does not notify listeners when refreshed during loading', () async { + final provider = factory.simpleTestProvider((ref, _) => Future.value(0)); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, const AsyncLoading())); + + container.refresh(provider); + + await container.read(provider.future); + + verifyOnly( + listener, + listener(const AsyncLoading(), const AsyncData(0)), + ); + }); + + group('listenSelf', () { + test('can remove the listener', () async { + final container = ProviderContainer.test(); + final listener = Listener>(); + late final RemoveListener remove; + final provider = factory.simpleTestProvider((ref, self) { + remove = self.listenSelf(listener.call); + return 0; + }); + + container.listen(provider.notifier, (previous, next) {}); + clearInteractions(listener); + + remove(); + + container.read(provider.notifier).state = const AsyncData(42); + + verifyZeroInteractions(listener); + }); + + test('supports listenSelf', () { + final listener = Listener>(); + final onError = ErrorListener(); + final provider = factory.simpleTestProvider((ref, self) { + self.listenSelf(listener.call, onError: onError.call); + Error.throwWithStackTrace(42, StackTrace.empty); + }); + final container = ProviderContainer.test(); + + container.listen(provider, (previous, next) {}); + + verifyOnly( + listener, + listener(null, const AsyncError(42, StackTrace.empty)), + ); + verifyZeroInteractions(onError); + + container.read(provider.notifier).state = const AsyncData(42); + + verifyNoMoreInteractions(onError); + verifyOnly( + listener, + listener( + const AsyncError(42, StackTrace.empty), + const AsyncData(42), + ), + ); + }); + }); + + test( + 'converts AsyncNotifier.build into an AsyncData if the future completes', + () async { + final provider = factory.simpleTestProvider((ref, _) => Future.value(0)); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, const AsyncLoading())); + expect( + container.read(provider.notifier).state, + const AsyncLoading(), + ); + + expect(await container.read(provider.future), 0); + + verifyOnly( + listener, + listener(const AsyncLoading(), const AsyncData(0)), + ); + expect( + container.read(provider.notifier).state, + const AsyncData(0), + ); + }); + + test('converts AsyncNotifier.build into an AsyncError if the future fails', + () async { + final provider = factory.simpleTestProvider( + (ref, _) => Future.error(0, StackTrace.empty), + ); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, const AsyncLoading())); + expect( + container.read(provider.notifier).state, + const AsyncLoading(), + ); + + await expectLater(container.read(provider.future), throwsA(0)); + + verifyOnly( + listener, + listener(const AsyncLoading(), const AsyncError(0, StackTrace.empty)), + ); + expect( + container.read(provider.notifier).state, + const AsyncError(0, StackTrace.empty), + ); + }); + + test('supports cases where the AsyncNotifier constructor throws', () async { + final provider = factory.provider( + () => Error.throwWithStackTrace(0, StackTrace.empty), + ); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly( + listener, + listener(null, const AsyncError(0, StackTrace.empty)), + ); + expect( + () => container.read(provider.notifier), + throwsA(0), + ); + + await expectLater(container.read(provider.future), throwsA(0)); + }); + + test( + 'synchronously emits AsyncData if AsyncNotifier.build emits synchronously', + () async { + final provider = factory.simpleTestProvider((ref, _) => 0); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, const AsyncData(0))); + expect(container.read(provider.notifier).state, const AsyncData(0)); + await expectLater(container.read(provider.future), completion(0)); + }); + + test( + 'synchronously emits AsyncError if AsyncNotifier.build throws synchronously', + () async { + final provider = factory.simpleTestProvider( + (ref, _) => Error.throwWithStackTrace(42, StackTrace.empty), + ); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly( + listener, + listener(null, const AsyncError(42, StackTrace.empty)), + ); + expect( + container.read(provider.notifier).state, + const AsyncError(42, StackTrace.empty), + ); + await expectLater(container.read(provider.future), throwsA(42)); + }); + + test( + 'stops listening to the previous future data when the provider rebuilds', + () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final completers = { + 0: Completer.sync(), + 1: Completer.sync(), + }; + final provider = factory.simpleTestProvider( + (ref, _) => completers[ref.watch(dep)]!.future, + ); + final listener = Listener>(); + + container.listen(provider, listener.call); + + expect( + container.read(provider.future), + completion(21), + reason: 'The provider rebuilt while the future was still pending, ' + 'so .future should resolve with the next value', + ); + verifyZeroInteractions(listener); + expect(container.read(provider), const AsyncLoading()); + + container.read(dep.notifier).state++; + completers[0]!.complete(42); + + verifyZeroInteractions(listener); + + expect(container.read(provider.future), completion(21)); + expect(container.read(provider), const AsyncLoading()); + + completers[1]!.complete(21); + + expect(await container.read(provider.future), 21); + expect(container.read(provider), const AsyncData(21)); + }); + + test( + 'stops listening to the previous future error when the provider rebuilds', + () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final completers = { + 0: Completer.sync(), + 1: Completer.sync(), + }; + final provider = factory.simpleTestProvider( + (ref, _) => completers[ref.watch(dep)]!.future, + ); + final listener = Listener>(); + + container.listen(provider, listener.call); + + expect( + container.read(provider.future), + throwsA(21), + reason: 'The provider rebuilt while the future was still pending, ' + 'so .future should resolve with the next value', + ); + verifyZeroInteractions(listener); + expect(container.read(provider), const AsyncLoading()); + + container.read(dep.notifier).state++; + completers[0]!.completeError(42, StackTrace.empty); + + verifyZeroInteractions(listener); + + expect(container.read(provider.future), throwsA(21)); + expect(container.read(provider), const AsyncLoading()); + + completers[1]!.completeError(21, StackTrace.empty); + + await expectLater(container.read(provider.future), throwsA(21)); + expect( + container.read(provider), + const AsyncError(21, StackTrace.empty), + ); + }); + + group('AsyncNotifier.state', () { + test( + 'when manually modifying the state, the new exposed value contains the previous state when possible', + () async { + final provider = factory.simpleTestProvider((ref, _) => 0); + final container = ProviderContainer.test(); + + final sub = container.listen(provider.notifier, (previous, next) {}); + + final newState = AsyncData(84); + final newLoading = AsyncLoading(); + final newError = AsyncError(84, StackTrace.empty); + + sub.read().state = newState; + + expect(sub.read().state, same(newState)); + + sub.read().state = newLoading; + + expect( + sub.read().state, + const AsyncLoading() + .copyWithPrevious(newState, isRefresh: false), + ); + + sub.read().state = newError; + + expect( + sub.read().state, + newError.copyWithPrevious( + const AsyncLoading() + .copyWithPrevious(newState, isRefresh: false), + ), + ); + }); + + test('can be read inside build', () { + final dep = StateProvider((ref) => 0); + late AsyncValue state; + final provider = factory.provider( + () { + late TestAsyncNotifier notifier; + return notifier = factory.deferredNotifier((ref, _) { + state = notifier.state; + return Future.value(ref.watch(dep)); + }); + }, + ); + final container = ProviderContainer.test(); + + container.listen(provider, (previous, next) {}); + + expect(state, const AsyncLoading()); + + container.read(provider.notifier).state = const AsyncData(42); + container.refresh(provider); + + expect( + state, + const AsyncLoading().copyWithPrevious(const AsyncData(42)), + ); + }); + + test('notifies listeners when the setter is called', () { + final provider = factory.simpleTestProvider((ref, _) => 0); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen(provider, listener.call); + + verifyZeroInteractions(listener); + + container.read(provider.notifier).state = const AsyncData(42); + + verifyOnly( + listener, + listener(const AsyncData(0), const AsyncData(42)), + ); + }); + }); + + group('AsyncNotifier.future', () { + test('If the notifier is recreated with an error, rethrows the new error', + () async { + final container = ProviderContainer.test(); + final listener = Listener>(); + var body = () => factory.deferredNotifier((ref, _) => 0); + final provider = factory.provider(() => body()); + + container.listen(provider.future, listener.call); + + await expectLater( + container.read(provider.future), + completion(0), + ); + verifyZeroInteractions(listener); + + body = () => throw StateError('foo'); + container.invalidate(provider); + + await expectLater( + container.read(provider.future), + throwsA(isA()), + ); + verify(listener(any, any)).called(1); + }); + + test( + 'when disposed during loading, resolves with the content of AsyncNotifier.build', + () async { + final container = ProviderContainer.test(); + final completer = Completer.sync(); + final provider = factory.simpleTestProvider( + (ref, _) => completer.future, + ); + + final future = container.read(provider.future); + container.dispose(); + + completer.complete(42); + + await expectLater(future, completion(42)); + }); + + test( + 'when disposed during loading, resolves with the error of AsyncNotifier.build', + () async { + final container = ProviderContainer.test(); + final completer = Completer.sync(); + final provider = factory.simpleTestProvider( + (ref, _) => completer.future, + ); + + final future = container.read(provider.future); + + container.dispose(); + + completer.completeError(42); + + await expectLater(future, throwsA(42)); + }); + + test( + 'going data > loading while the future is still pending. ' + 'Resolves with last future result', + () async { + final container = ProviderContainer.test(); + final completer = Completer.sync(); + final provider = factory.simpleTestProvider( + (ref, _) => completer.future, + ); + + container.read(provider); + container.read(provider.notifier).state = const AsyncData(42); + container.read(provider.notifier).state = const AsyncLoading(); + + final future = container.read(provider.future); + + container.dispose(); + + completer.complete(42); + + await expectLater(future, completion(42)); + }, + ); + + test( + 'if going back to loading after future resolved, throws StateError', + () async { + final container = ProviderContainer.test(); + final completer = Completer.sync(); + final provider = factory.simpleTestProvider( + (ref, _) => completer.future, + ); + + container.read(provider); + + completer.complete(42); + + container.read(provider.notifier).state = const AsyncData(42); + container.read(provider.notifier).state = const AsyncLoading(); + + final future = container.read(provider.future); + + container.dispose(); + + await expectLater(future, throwsStateError); + }, + ); + + test( + 'resolves with the new state if AsyncNotifier.state is modified during loading', + () async { + final container = ProviderContainer.test(); + final completer = Completer.sync(); + final provider = factory.simpleTestProvider( + (ref, _) => completer.future, + ); + final listener = Listener>(); + + final sub = container.listen(provider.notifier, (previous, next) {}); + container.listen(provider.future, listener.call); + + expect(sub.read().future, completion(21)); + + sub.read().state = const AsyncData(21); + + completer.complete(42); + + expect(sub.read().future, completion(42)); + final capture = + verifyOnly(listener, listener(captureAny, captureAny)).captured; + + expect(capture.length, 2); + expect(capture.first, completion(21)); + expect(capture.last, completion(42)); + }); + + test('resolves with the new state when notifier.state is changed', + () async { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider((ref, _) => 0); + final listener = Listener>(); + + final sub = container.listen(provider.notifier, (previous, next) {}); + container.listen( + provider.future, + listener.call, + fireImmediately: true, + ); + + await expectLater(sub.read().future, completion(0)); + verifyOnly( + listener, + listener(argThat(equals(null)), argThat(completion(0))), + ); + + sub.read().state = const AsyncData(1); + + await expectLater(sub.read().future, completion(1)); + }); + + test('returns a Future identical to that of .future', () { + final listener = OnBuildMock(); + final dep = StateProvider((ref) => 0); + final provider = factory.simpleTestProvider( + (ref, _) { + listener(); + return Future.value(ref.watch(dep)); + }, + ); + final container = ProviderContainer.test(); + + container.listen(provider.notifier, (previous, next) {}); + final notifier = container.read(provider.notifier); + + expect(notifier.future, same(container.read(provider.future))); + }); + }); + + group('AsyncNotifierProvider.notifier', () { + test('If the notifier is recreated with an error, rethrows the new error', + () async { + final container = ProviderContainer.test(); + final listener = Listener<$AsyncNotifier>(); + final onError = ErrorListener(); + var body = () => factory.deferredNotifier((ref, _) => 0); + final provider = factory.provider(() => body()); + + container.listen( + provider.notifier, + listener.call, + onError: onError.call, + ); + + await expectLater(container.read(provider.notifier), isNotNull); + verifyZeroInteractions(listener); + verifyZeroInteractions(onError); + + body = () => throw StateError('foo'); + container.invalidate(provider); + + await expectLater( + () => container.read(provider.notifier), + throwsA(isA()), + ); + verifyZeroInteractions(listener); + verifyOnly(onError, onError(isA(), any)).called(1); + }); + + test( + 'Notifies listeners whenever `build` is re-executed, due to recreating a new notifier.', + () async { + final notifierListener = Listener<$AsyncNotifier>(); + final dep = StateProvider((ref) => 0); + final provider = factory.provider(() { + return factory.deferredNotifier( + (ref, _) => Future.value(ref.watch(dep)), + ); + }); + final container = ProviderContainer.test(); + + final sub = container.listen(provider.notifier, notifierListener.call); + final initialNotifier = sub.read(); + + expect(initialNotifier.ref.mounted, true); + + // Skip the loading + await container.read(provider.future); + verifyNoMoreInteractions(notifierListener); + + container.refresh(provider); + final newNotifier = sub.read(); + + expect(newNotifier, isNot(same(initialNotifier))); + verifyOnly( + notifierListener, + notifierListener(initialNotifier, newNotifier), + ).called(1); + expect(initialNotifier.ref.mounted, false); + expect(newNotifier.ref.mounted, true); + }); + }); + + test( + 'Can override AsyncNotifier.updateShouldNotify to change the default filter logic', + () { + final provider = factory.simpleTestProvider>( + (ref, _) => Equal(42), + updateShouldNotify: (a, b) => a != b, + ); + final container = ProviderContainer.test(); + final listener = Listener>>(); + + container.listen(provider, listener.call); + final notifier = container.read(provider.notifier); + + // voluntarily assigning the same value + final self = notifier.state; + notifier.state = self; + + verifyZeroInteractions(listener); + + notifier.state = AsyncData(Equal(42)); + + verifyZeroInteractions(listener); + + notifier.state = AsyncData(Equal(21)); + + verifyOnly( + listener, + listener(AsyncData(Equal(42)), AsyncData(Equal(21))), + ); + }); + + group('AsyncNotifier.update', () { + test('passes in the latest state', () async { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider( + (ref, _) => 0, + ); + + final sub = container.listen(provider.notifier, (prev, next) {}); + + expect( + container.read(provider), + const AsyncData(0), + ); + + await expectLater( + sub.read().update((prev) => prev + 1), + completion(1), + ); + await expectLater( + sub.read().future, + completion(1), + ); + await expectLater( + sub.read().update((prev) => prev + 1), + completion(2), + ); + }); + + test('can specify onError to handle error scenario', () async { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider( + (ref, _) => Error.throwWithStackTrace(42, StackTrace.empty), + ); + var callCount = 0; + Object? actualErr; + Object? actualStack; + + final sub = container.listen(provider.notifier, (prev, next) {}); + + expect( + container.read(provider), + const AsyncError(42, StackTrace.empty), + ); + + await expectLater( + sub.read().update( + (prev) { + callCount++; + return prev; + }, + onError: (err, stack) { + actualErr = err; + actualStack = stack; + return 21; + }, + ), + completion(21), + ); + expect(callCount, 0); + expect(actualErr, 42); + expect(actualStack, StackTrace.empty); + expect(container.read(provider), const AsyncData(21)); + }); + + test('executes immediately with current state if a state is available', + () async { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider((ref, _) => 1); + + final sub = container.listen(provider.notifier, (prev, next) {}); + + expect(container.read(provider), const AsyncData(1)); + + await expectLater( + sub.read().update((prev) => prev + 1), + completion(2), + ); + expect(container.read(provider), const AsyncData(2)); + }); + + test('executes immediately with current state if an error is available', + () async { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider( + (ref, _) => Error.throwWithStackTrace(42, StackTrace.empty), + ); + var callCount = 0; + + final sub = container.listen(provider.notifier, (prev, next) {}); + + expect( + container.read(provider), + const AsyncError(42, StackTrace.empty), + ); + + await expectLater( + sub.read().update((prev) { + callCount++; + return prev + 1; + }), + throwsA(42), + ); + + expect(callCount, 0); + expect( + container.read(provider), + const AsyncError(42, StackTrace.empty), + ); + }); + + test('awaits the future resolution if in loading state', () async { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider( + (ref, _) => Future.value(42), + ); + + final sub = container.listen(provider.notifier, (prev, next) {}); + + expect(container.read(provider), const AsyncLoading()); + + await expectLater( + sub.read().update((prev) => prev + 1), + completion(43), + ); + expect(container.read(provider), const AsyncData(43)); + }); + }); + }); + + test('supports overrideWith', () { + final provider = AsyncNotifierProvider, int>( + () => DeferredAsyncNotifier((ref, _) => 0), + ); + final autoDispose = + AsyncNotifierProvider.autoDispose, int>( + () => DeferredAsyncNotifier((ref, _) => 0), + ); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWith(() => DeferredAsyncNotifier((ref, _) => 42)), + autoDispose.overrideWith( + () => DeferredAsyncNotifier((ref, _) => 84), + ), + ], + ); + + expect(container.read(provider).value, 42); + expect(container.read(autoDispose).value, 84); + }); + + test('supports family overrideWith', () { + final family = AsyncNotifierProvider.family< + DeferredFamilyAsyncNotifier, int, int>( + () => DeferredFamilyAsyncNotifier((ref, _) => 0), + ); + final autoDisposeFamily = AsyncNotifierProvider.autoDispose + .family, int, int>( + () => DeferredFamilyAsyncNotifier((ref, _) => 0), + ); + final container = ProviderContainer.test( + overrides: [ + family.overrideWith( + () => DeferredFamilyAsyncNotifier((ref, _) => 42), + ), + autoDisposeFamily.overrideWith( + () => DeferredFamilyAsyncNotifier((ref, _) => 84), + ), + ], + ); + + expect(container.read(family(10)).value, 42); + expect(container.read(autoDisposeFamily(10)).value, 84); + }); + + group('modifiers', () { + void canBeAssignedToRefreshable( + Refreshable provider, + ) {} + + void canBeAssignedToProviderListenable( + ProviderListenable provider, + ) {} + + test('provider', () { + final provider = AsyncNotifierProvider, int>( + () => DeferredAsyncNotifier((ref, _) => 0), + ); + + provider.select((AsyncValue value) => 0); + provider.selectAsync((int value) => 0); + + canBeAssignedToProviderListenable>(provider); + canBeAssignedToRefreshable>(provider); + + canBeAssignedToProviderListenable>(provider.future); + canBeAssignedToRefreshable>(provider.future); + + canBeAssignedToProviderListenable>(provider.notifier); + canBeAssignedToRefreshable>(provider.notifier); + }); + + test('autoDispose', () { + final autoDispose = + AsyncNotifierProvider.autoDispose, int>( + () => DeferredAsyncNotifier((ref, _) => 0), + ); + + autoDispose.select((AsyncValue value) => 0); + autoDispose.selectAsync((int value) => 0); + + canBeAssignedToProviderListenable>(autoDispose); + canBeAssignedToRefreshable>(autoDispose); + + canBeAssignedToProviderListenable>(autoDispose.future); + canBeAssignedToRefreshable>(autoDispose.future); + + canBeAssignedToProviderListenable>( + autoDispose.notifier, + ); + canBeAssignedToRefreshable>( + autoDispose.notifier, + ); + }); + + test('family', () { + final family = AsyncNotifierProvider.family< + DeferredFamilyAsyncNotifier, String, int>( + () => DeferredFamilyAsyncNotifier((ref, _) => '0'), + ); + + family(0).select((AsyncValue value) => 0); + family(0).selectAsync((String value) => 0); + + canBeAssignedToProviderListenable>(family(0)); + canBeAssignedToRefreshable>(family(0)); + + canBeAssignedToProviderListenable>(family(0).future); + canBeAssignedToRefreshable>(family(0).future); + + canBeAssignedToProviderListenable>( + family(0).notifier, + ); + canBeAssignedToRefreshable>( + family(0).notifier, + ); + }); + + test('autoDisposeFamily', () { + expect( + AsyncNotifierProvider.autoDispose.family, + same(AsyncNotifierProvider.family.autoDispose), + ); + + final autoDisposeFamily = AsyncNotifierProvider.autoDispose + .family, String, int>( + () => DeferredFamilyAsyncNotifier((ref, _) => '0'), + ); + + autoDisposeFamily(0).select((AsyncValue value) => 0); + autoDisposeFamily(0).selectAsync((String value) => 0); + + canBeAssignedToProviderListenable>( + autoDisposeFamily(0), + ); + canBeAssignedToRefreshable>( + autoDisposeFamily(0), + ); + + canBeAssignedToProviderListenable>( + autoDisposeFamily(0).future, + ); + canBeAssignedToRefreshable>( + autoDisposeFamily(0).future, + ); + + canBeAssignedToProviderListenable>( + autoDisposeFamily(0).notifier, + ); + canBeAssignedToRefreshable>( + autoDisposeFamily(0).notifier, + ); + }); + }); +} + +@immutable +class Equal { + const Equal(this.value); + + final T value; + + @override + bool operator ==(Object other) => other is Equal && other.value == value; + + @override + int get hashCode => Object.hash(runtimeType, value); + + @override + String toString() => 'Equal($value)'; +} + +class CtorNotifier extends AsyncNotifier { + @override + FutureOr build() => 0; +} + +class FamilyCtorNotifier extends FamilyAsyncNotifier { + @override + FutureOr build(int arg) => 0; +} diff --git a/packages/riverpod/test/src/providers/future_provider_test.dart b/packages/riverpod/test/src/providers/future_provider_test.dart new file mode 100644 index 000000000..2a8f2722e --- /dev/null +++ b/packages/riverpod/test/src/providers/future_provider_test.dart @@ -0,0 +1,46 @@ +import 'package:mockito/mockito.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:test/test.dart'; + +import '../../third_party/fake_async.dart'; +import '../utils.dart'; + +void main() { + group('FutureProvider', () { + group('retry', () { + test( + 'handles retry', + () => fakeAsync((fake) async { + final container = ProviderContainer.test(); + var err = Exception('foo'); + final stack = StackTrace.current; + final provider = FutureProvider( + (ref) => Error.throwWithStackTrace(err, stack), + retry: (_, __) => const Duration(seconds: 1), + ); + final listener = Listener>(); + + container.listen(provider, fireImmediately: true, listener.call); + await container.read(provider.future).catchError((e) => 0); + + verifyOnly( + listener, + listener(any, AsyncValue.error(err, stack)), + ); + + err = Exception('bar'); + + fake.elapse(const Duration(seconds: 1)); + fake.flushMicrotasks(); + + await container.read(provider.future).catchError((e) => 0); + + verifyOnly( + listener, + listener(any, AsyncValue.error(err, stack)), + ); + }), + ); + }); + }); +} diff --git a/packages/riverpod/test/src/providers/notifier_test.dart b/packages/riverpod/test/src/providers/notifier_test.dart new file mode 100644 index 000000000..0d5dca1b8 --- /dev/null +++ b/packages/riverpod/test/src/providers/notifier_test.dart @@ -0,0 +1,725 @@ +// ignore_for_file: avoid_types_on_closure_parameters, invalid_use_of_protected_member, prefer_const_constructors + +import 'package:meta/meta.dart'; +import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/framework.dart' show UnmountedRefException; +import 'package:riverpod/src/providers/notifier.dart' show $Notifier; +import 'package:test/test.dart'; + +import '../matrix.dart'; +import '../utils.dart'; + +void main() { + test('Throws if using notifier properties in its constructor', () { + final errors = captureErrors([ + () => CtorNotifier().state, + () => CtorNotifier().state = 42, + () => CtorNotifier().ref, + () => FamilyCtorNotifier().state, + () => FamilyCtorNotifier().state = 42, + () => FamilyCtorNotifier().ref, + ]); + }); + + notifierProviderFactory.createGroup((factory) { + test('Cannot share a Notifier instance between providers ', () { + final container = ProviderContainer.test(); + final notifier = factory.deferredNotifier((ref, _) => 0); + + final provider = factory.provider(() => notifier); + final provider2 = factory.provider(() => notifier); + + container.read(provider); + + expect( + () => container.read(provider2), + throwsA(isA()), + ); + }); + + test('Cannot read properties inside onDispose', () { + final container = ProviderContainer.test(); + late TestNotifier notifier; + late List errors; + final provider = factory.simpleTestProvider((ref, _) { + ref.onDispose(() { + errors = captureErrors([ + () => notifier.state, + () => notifier.state = 42, + ]); + }); + return 0; + }); + + container.listen(provider.notifier, (prev, next) {}); + notifier = container.read(provider.notifier); + + container.dispose(); + + expect( + errors, + everyElement(isA()), + ); + }); + + test('Using the notifier after dispose throws', () { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider((ref, _) => 0); + + container.listen(provider.notifier, (prev, next) {}); + final notifier = container.read(provider.notifier); + + container.dispose(); + + expect(notifier.ref.mounted, false); + expect( + () => notifier.state, + throwsA(isA()), + ); + expect( + () => notifier.state = 42, + throwsA(isA()), + ); + }); + + test( + 'throws if the same Notifier instance is reused in different providers', + () { + // Regression test for https://github.com/rrousselGit/riverpod/issues/2617 + final container = ProviderContainer.test(); + + final notifier = factory.deferredNotifier((ref, _) => 0); + + final provider = factory.provider(() => notifier); + final provider2 = factory.provider(() => notifier); + + container.read(provider); + + expect( + () => container.read(provider2), + throwsA(isA()), + ); + }); + + group('Notifier.stateOrNull', () { + test('returns null during first build until state= is set', () { + final stateInBuild = []; + + final provider = factory.provider(() { + late TestNotifier notifier; + return notifier = factory.deferredNotifier((ref, _) { + stateInBuild.add(notifier.stateOrNull); + return 0; + }); + }); + final container = ProviderContainer.test(); + + final sub = container.listen( + provider.notifier, + (_, __) {}, + ); + + expect(stateInBuild, [null]); + + expect(sub.read().stateOrNull, 0); + }); + + test('returns null if Notifier.build threw', () { + final provider = factory.simpleTestProvider( + (ref, _) => throw Exception('42'), + ); + final container = ProviderContainer.test(); + + final sub = container.listen( + provider.notifier, + (_, __) {}, + ); + + expect(sub.read().stateOrNull, null); + }); + + test( + 'returns the previous state if using inside Notifier.build ' + 'after the state was already initialized', () { + final stateInBuild = []; + + final provider = factory.provider(() { + late TestNotifier notifier; + return notifier = factory.deferredNotifier((ref, _) { + stateInBuild.add(notifier.stateOrNull); + return 0; + }); + }); + final container = ProviderContainer.test(); + + final sub = container.listen( + provider.notifier, + (_, __) {}, + ); + + sub.read().state = 42; + container.refresh(provider); + + expect(stateInBuild, [null, 42]); + }); + + test('Post build, returns the current state', () { + final provider = factory.simpleTestProvider( + (ref, _) => 0, + ); + final container = ProviderContainer.test(); + + final sub = container.listen( + provider.notifier, + (_, __) {}, + ); + + expect(sub.read().stateOrNull, 0); + + sub.read().state = 42; + + expect(sub.read().stateOrNull, 42); + }); + + test( + 'On invalidated providers, rebuilds the notifier and return the new state', + () { + final provider = factory.simpleTestProvider( + (ref, _) => 0, + ); + final container = ProviderContainer.test(); + + final sub = container.listen( + provider.notifier, + (_, __) {}, + ); + + sub.read().state = 42; + + expect(sub.read().stateOrNull, 42); + + container.invalidate(provider); + + expect(sub.read().stateOrNull, 0); + }); + }); + + test( + 'uses notifier.build as initial state and update listeners when state changes', + () { + final provider = factory.simpleTestProvider((ref, _) => 0); + final container = ProviderContainer.test(); + final listener = Listener(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 0)); + + container.read(provider.notifier).state++; + + verifyOnly(listener, listener(0, 1)); + }); + + group('.notifier', () { + test( + 'Notifies listeners whenever `build` is re-executed, due to recreating a new notifier.', + () async { + final notifierListener = Listener<$Notifier>(); + final dep = StateProvider((ref) => 0); + final provider = factory.provider(() { + return factory.deferredNotifier((ref, _) => ref.watch(dep)); + }); + final container = ProviderContainer.test(); + + final sub = container.listen(provider.notifier, notifierListener.call); + final initialNotifier = sub.read(); + + expect(initialNotifier.ref.mounted, true); + + container.refresh(provider); + final newNotifier = sub.read(); + + expect(newNotifier, isNot(same(initialNotifier))); + verifyOnly( + notifierListener, + notifierListener(initialNotifier, newNotifier), + ).called(1); + expect(initialNotifier.ref.mounted, false); + expect(newNotifier.ref.mounted, true); + }); + }); + + test('calls notifier.build on every watch update', () async { + final dep = StateProvider((ref) => 0); + final provider = factory.simpleTestProvider((ref, _) { + return ref.watch(dep); + }); + final container = ProviderContainer.test(); + final listener = Listener(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, 0)); + + container.read(dep.notifier).update((state) => state + 1); + + verifyNoMoreInteractions(listener); + + await container.pump(); + + verifyOnly(listener, listener(0, 1)); + }); + + test('After a state initialization error, the notifier is still available', + () { + final provider = factory.simpleTestProvider((ref, _) { + throw StateError('Hey'); + }); + final container = ProviderContainer.test(); + + expect( + () => container.read(provider), + throwsStateError, + ); + + container.read(provider.notifier); + }); + + test('handles fail to initialize the notifier', () { + final err = UnimplementedError(); + final stack = StackTrace.current; + final provider = factory.provider( + () => Error.throwWithStackTrace(err, stack), + ); + final container = ProviderContainer.test(); + final listener = ErrorListener(); + + expect( + () => container.read(provider.notifier), + throwsUnimplementedError, + ); + expect( + () => container.read(provider), + throwsUnimplementedError, + ); + + final stateSub = container.listen( + provider, + (previous, next) {}, + onError: listener.call, + ); + + verifyNoMoreInteractions(listener); + + container.listen( + provider, + (previous, next) {}, + onError: listener.call, + fireImmediately: true, + ); + + verifyOnly(listener, listener(err, stack)); + + final notifierSub = container.listen( + provider.notifier, + (previous, next) {}, + onError: listener.call, + ); + + verifyNoMoreInteractions(listener); + + container.listen( + provider.notifier, + (previous, next) {}, + onError: listener.call, + fireImmediately: true, + ); + + verifyOnly(listener, listener(err, stack)); + + expect(stateSub.read, throwsUnimplementedError); + expect(notifierSub.read, throwsUnimplementedError); + }); + + test('can read/set the current state within the notifier', () { + final provider = factory.simpleTestProvider((ref, _) => 0); + final container = ProviderContainer.test(); + final listener = Listener(); + + container.listen(provider, listener.call, fireImmediately: true); + final notifier = container.read(provider.notifier); + + expect(notifier.state, 0); + verifyOnly(listener, listener(null, 0)); + + notifier.state++; + + expect(notifier.state, 1); + verifyOnly(listener, listener(0, 1)); + + notifier.state++; + + expect(notifier.state, 2); + verifyOnly(listener, listener(1, 2)); + }); + + test( + 'Reading the state inside the notifier rethrows initialization error, if any', + () { + final provider = factory + .simpleTestProvider((ref, _) => throw UnimplementedError()); + final container = ProviderContainer.test(); + + final notifier = container.read(provider.notifier); + + expect(() => notifier.state, throwsUnimplementedError); + }); + + test( + 'Setting the state after an initialization error allow listening the state again', + () { + final err = UnimplementedError(); + final stack = StackTrace.current; + final provider = factory.simpleTestProvider( + (ref, _) => Error.throwWithStackTrace(err, stack), + ); + final container = ProviderContainer.test(); + final listener = Listener(); + final onError = ErrorListener(); + + container.listen( + provider, + listener.call, + onError: onError.call, + fireImmediately: true, + ); + final notifier = container.read(provider.notifier); + + verifyOnly(onError, onError(err, stack)); + verifyZeroInteractions(listener); + + expect(() => notifier.state, throwsUnimplementedError); + + notifier.state = 0; + + verifyOnly(listener, listener(null, 0)); + verifyNoMoreInteractions(onError); + expect(notifier.state, 0); + expect(container.read(provider), 0); + + container.listen( + provider, + listener.call, + onError: onError.call, + fireImmediately: true, + ); + + verifyOnly(listener, listener(null, 0)); + verifyNoMoreInteractions(onError); + }); + + test('supports ref.refresh(provider)', () { + final provider = factory.simpleTestProvider((ref, _) => 0); + final container = ProviderContainer.test(); + + expect(container.read(provider), 0); + + container.read(provider.notifier).state = 42; + + expect(container.read(provider), 42); + + expect(container.refresh(provider), 0); + expect(container.read(provider), 0); + expect(container.read(provider.notifier).state, 0); + }); + + group('listenSelf', () { + test('can remove the data listener', () async { + final container = ProviderContainer.test(); + final listener = Listener(); + late final RemoveListener remove; + final provider = factory.simpleTestProvider((ref, self) { + remove = self.listenSelf(listener.call); + return 0; + }); + + container.listen(provider.notifier, (previous, next) {}); + clearInteractions(listener); + + remove(); + + container.read(provider.notifier).state = 42; + + verifyZeroInteractions(listener); + }); + + test('can remove the error listener', () async { + final container = ProviderContainer.test(); + final listener = ErrorListener(); + final provider = factory.simpleTestProvider((ref, self) { + final remove = self.listenSelf((a, b) {}, onError: listener.call); + remove(); + + throw StateError(''); + }); + + container.listen(provider.notifier, (previous, next) {}); + + verifyZeroInteractions(listener); + }); + + test('supports listenSelf((State? prev, State next) {})', () { + final listener = Listener(); + final onError = ErrorListener(); + final provider = factory.simpleTestProvider((ref, self) { + self.listenSelf(listener.call, onError: onError.call); + Error.throwWithStackTrace(42, StackTrace.empty); + }); + final container = ProviderContainer.test(); + + expect(() => container.read(provider), throwsA(42)); + + verifyOnly(onError, onError(42, StackTrace.empty)); + }); + }); + + test('filters state update by identical by default', () { + final provider = + factory.simpleTestProvider>((ref, _) => Equal(42)); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen(provider, listener.call); + final notifier = container.read(provider.notifier); + final firstState = notifier.state; + + // voluntarily assigning the same value + final self = notifier.state; + notifier.state = self; + + verifyZeroInteractions(listener); + + final secondState = notifier.state = Equal(42); + + verifyOnly(listener, listener(firstState, secondState)); + }); + + test( + 'Can override Notifier.updateShouldNotify to change the default filter logic', + () { + final provider = factory.simpleTestProvider>( + (ref, _) => Equal(42), + updateShouldNotify: (a, b) => a != b, + ); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen(provider, listener.call); + final notifier = container.read(provider.notifier); + + // voluntarily assigning the same value + final self = notifier.state; + notifier.state = self; + + verifyZeroInteractions(listener); + + notifier.state = Equal(42); + + verifyZeroInteractions(listener); + + notifier.state = Equal(21); + + verifyOnly(listener, listener(Equal(42), Equal(21))); + }); + + test('can override Notifier.build', () {}); + + if (factory.isAutoDispose) { + group('autoDispose', () { + test('keeps state alive if notifier is listened', () async { + final container = ProviderContainer.test(); + final onDispose = OnDisposeMock(); + final provider = factory.simpleTestProvider((ref, _) { + ref.onDispose(onDispose.call); + return 0; + }); + + final sub = container.listen(provider, (prev, next) {}); + verifyZeroInteractions(onDispose); + expect(container.getAllProviderElements().single.origin, provider); + + await container.pump(); + + verifyZeroInteractions(onDispose); + expect(container.getAllProviderElements().single.origin, provider); + + sub.close(); + await container.pump(); + + verifyOnly(onDispose, onDispose()); + expect(container.getAllProviderElements(), isEmpty); + }); + }); + } + }); + + test('supports overrideWith', () { + final provider = NotifierProvider, int>( + () => DeferredNotifier((ref, _) => 0), + ); + final autoDispose = + NotifierProvider.autoDispose, int>( + () => DeferredNotifier((ref, _) => 0), + ); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWith(() => DeferredNotifier((ref, _) => 42)), + autoDispose.overrideWith( + () => DeferredNotifier((ref, _) => 84), + ), + ], + ); + + expect(container.read(provider), 42); + expect(container.read(autoDispose), 84); + }); + + test('supports family overrideWith', () { + final family = + NotifierProvider.family, int, int>( + () => DeferredFamilyNotifier((ref, _) => 0), + ); + final autoDisposeFamily = NotifierProvider.autoDispose + .family, int, int>( + () => DeferredFamilyNotifier((ref, _) => 0), + ); + final container = ProviderContainer.test( + overrides: [ + family.overrideWith( + () => DeferredFamilyNotifier((ref, _) => 42), + ), + autoDisposeFamily.overrideWith( + () => DeferredFamilyNotifier((ref, _) => 84), + ), + ], + ); + + expect(container.read(family(10)), 42); + expect(container.read(autoDisposeFamily(10)), 84); + }); + + group('modifiers', () { + void canBeAssignedToRefreshable( + Refreshable provider, + ) {} + + void canBeAssignedToProviderListenable( + ProviderListenable provider, + ) {} + + test('provider', () { + final provider = NotifierProvider, int>( + () => DeferredNotifier((ref, _) => 0), + ); + + provider.select((int value) => 0); + + canBeAssignedToProviderListenable(provider); + canBeAssignedToRefreshable(provider); + + canBeAssignedToProviderListenable>(provider.notifier); + canBeAssignedToRefreshable>(provider.notifier); + }); + + test('autoDispose', () { + final autoDispose = + NotifierProvider.autoDispose, int>( + () => DeferredNotifier((ref, _) => 0), + ); + + autoDispose.select((int value) => 0); + + canBeAssignedToProviderListenable(autoDispose); + canBeAssignedToRefreshable(autoDispose); + + canBeAssignedToProviderListenable>( + autoDispose.notifier, + ); + canBeAssignedToRefreshable>( + autoDispose.notifier, + ); + }); + + test('family', () { + final family = + NotifierProvider.family, String, int>( + () => DeferredFamilyNotifier((ref, _) => '0'), + ); + + family(0).select((String value) => 0); + + canBeAssignedToProviderListenable(family(0)); + canBeAssignedToRefreshable(family(0)); + + canBeAssignedToProviderListenable>( + family(0).notifier, + ); + canBeAssignedToRefreshable>( + family(0).notifier, + ); + }); + + test('autoDisposeFamily', () { + expect( + NotifierProvider.autoDispose.family, + same(NotifierProvider.family.autoDispose), + ); + + final autoDisposeFamily = NotifierProvider.autoDispose + .family, String, int>( + () => DeferredFamilyNotifier((ref, _) => '0'), + ); + + autoDisposeFamily(0).select((String value) => 0); + + canBeAssignedToProviderListenable( + autoDisposeFamily(0), + ); + canBeAssignedToRefreshable( + autoDisposeFamily(0), + ); + + canBeAssignedToProviderListenable>( + autoDisposeFamily(0).notifier, + ); + canBeAssignedToRefreshable>( + autoDisposeFamily(0).notifier, + ); + }); + }); +} + +@immutable +class Equal { + const Equal(this.value); + + final T value; + + @override + bool operator ==(Object other) => other is Equal && other.value == value; + + @override + int get hashCode => Object.hash(runtimeType, value); +} + +class CtorNotifier extends Notifier { + @override + int build() => 0; +} + +class FamilyCtorNotifier extends FamilyNotifier { + @override + int build(int arg) => 0; +} diff --git a/packages/riverpod/test/src/providers/stream_notifier_test.dart b/packages/riverpod/test/src/providers/stream_notifier_test.dart new file mode 100644 index 000000000..7ee32bebe --- /dev/null +++ b/packages/riverpod/test/src/providers/stream_notifier_test.dart @@ -0,0 +1,1225 @@ +// ignore_for_file: avoid_types_on_closure_parameters, invalid_use_of_protected_member + +import 'dart:async'; + +import 'package:meta/meta.dart'; +import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/src/framework.dart' show UnmountedRefException; +import 'package:riverpod/src/providers/stream_notifier.dart' + show $StreamNotifier; +import 'package:test/test.dart'; + +import '../../third_party/fake_async.dart'; +import '../matrix.dart'; +import '../utils.dart'; + +void main() { + test('Throws if using notifier properties in its constructor', () { + final errors = captureErrors([ + () => CtorNotifier().state, + () => CtorNotifier().state = const AsyncData(42), + () => CtorNotifier().future, + () => CtorNotifier().ref, + () => FamilyCtorNotifier().state, + () => FamilyCtorNotifier().state = const AsyncData(42), + () => FamilyCtorNotifier().future, + () => FamilyCtorNotifier().ref, + ]); + }); + + streamNotifierProviderFactory.createGroup((factory) { + test('closes the StreamSubscription upon disposing the provider', () async { + final onCancel = OnCancelMock(); + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider((ref, _) { + final controller = StreamController(); + ref.onDispose(() { + controller.addError(42); + controller.close(); + }); + + return DelegatingStream( + controller.stream, + onSubscriptionCancel: onCancel.call, + ); + }); + + container.listen(provider, (previous, next) {}); + final future = container.read(provider.future); + + container.dispose(); + + verifyOnly(onCancel, onCancel()); + + await expectLater(future, throwsStateError); + }); + + test('Pauses the Stream when the provider is paused', () { + final streamController = StreamController(); + addTearDown(streamController.close); + + final onSubPause = OnPause(); + final onSubResume = OnResume(); + + final container = ProviderContainer.test(); + + final provider = factory.simpleTestProvider((ref, _) { + return DelegatingStream( + streamController.stream, + onSubscriptionPause: onSubPause.call, + onSubscriptionResume: onSubResume.call, + ); + }); + + final sub = container.listen(provider, (previous, next) {}); + + verifyZeroInteractions(onSubPause); + verifyZeroInteractions(onSubResume); + + sub.pause(); + + verifyOnly(onSubPause, onSubPause()); + verifyZeroInteractions(onSubResume); + + sub.resume(); + + verifyOnly(onSubResume, onSubResume()); + verifyNoMoreInteractions(onSubPause); + }); + group('retry', () { + test( + 'handles retry', + () => fakeAsync((fake) async { + final container = ProviderContainer.test(); + var err = Exception('foo'); + final stack = StackTrace.current; + final provider = factory.deferredProvider( + (ref, _) => Error.throwWithStackTrace(err, stack), + retry: (_, __) => const Duration(seconds: 1), + ); + final listener = Listener>(); + + container.listen(provider, fireImmediately: true, listener.call); + await container.read(provider.future).catchError((e) => 0); + + verifyOnly( + listener, + listener(any, AsyncValue.error(err, stack)), + ); + + err = Exception('bar'); + + fake.elapse(const Duration(seconds: 1)); + fake.flushMicrotasks(); + + await container.read(provider.future).catchError((e) => 0); + + verifyOnly( + listener, + listener(any, AsyncValue.error(err, stack)), + ); + }), + ); + + test( + 'manually setting the state to an error does not cause a retry', + () => fakeAsync((fake) async { + final container = ProviderContainer.test(); + var retryCount = 0; + late Ref r; + final provider = factory.simpleTestProvider( + retry: (_, __) { + retryCount++; + return const Duration(seconds: 1); + }, + (ref, self) { + self.state = const AsyncValue.data(0); + return const Stream.empty(); + }, + ); + final listener = Listener>(); + + container.listen(provider, fireImmediately: true, listener.call); + + expect(retryCount, 0); + + container.read(provider.notifier).state = AsyncValue.error( + Error(), + StackTrace.current, + ); + + expect(retryCount, 0); + }), + ); + }); + + test('Cannot share a Notifier instance between providers ', () { + final container = ProviderContainer.test(); + final notifier = factory.deferredNotifier((ref, _) => Stream.value(0)); + + final provider = factory.provider(() => notifier); + final provider2 = factory.provider(() => notifier); + + container.read(provider); + + expect( + container.read(provider2), + isA>(), + ); + }); + + test('Cannot properties inside onDispose', () { + final container = ProviderContainer.test(); + late TestStreamNotifier notifier; + late List errors; + final provider = factory.simpleTestProvider((ref, self) { + ref.onDispose(() { + errors = captureErrors([ + () => notifier.state, + () => notifier.state = const AsyncData(42), + () => notifier.future, + ]); + }); + return Stream.value(0); + }); + + container.listen(provider.notifier, (prev, next) {}); + notifier = container.read(provider.notifier); + + container.dispose(); + + expect( + errors, + everyElement(isA()), + ); + }); + + test('Using the notifier after dispose throws', () { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider( + (ref, self) => Stream.value(0), + ); + + container.listen(provider.notifier, (prev, next) {}); + final notifier = container.read(provider.notifier); + + container.dispose(); + + expect(notifier.ref.mounted, false); + expect( + () => notifier.state, + throwsA(isA()), + ); + expect( + () => notifier.future, + throwsA(isA()), + ); + expect( + () => notifier.state = const AsyncData(42), + throwsA(isA()), + ); + expect( + () => notifier.update((p1) => 42), + throwsA(isA()), + ); + }); + + group('supports AsyncValue transition', () { + test( + 'performs seamless copyWithPrevious if triggered by ref.invalidate/ref.refresh', + () async { + final container = ProviderContainer.test(); + var count = 0; + final provider = factory.simpleTestProvider( + (ref, self) => Stream.value(count++), + ); + + container.listen(provider, (previous, next) {}); + + await expectLater(container.read(provider.future), completion(0)); + expect(container.read(provider), const AsyncData(0)); + + expect( + container.refresh(provider), + const AsyncLoading().copyWithPrevious(const AsyncData(0)), + ); + + await expectLater(container.read(provider.future), completion(1)); + expect(container.read(provider), const AsyncData(1)); + + container.invalidate(provider); + + expect( + container.read(provider), + const AsyncLoading().copyWithPrevious(const AsyncData(1)), + ); + await expectLater(container.read(provider.future), completion(2)); + expect(container.read(provider), const AsyncData(2)); + }); + + test( + 'performs seamless:false copyWithPrevious on `state = AsyncLoading()`', + () async { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider( + (ref, self) => Stream.value(0), + ); + + final sub = container.listen(provider.notifier, (previous, next) {}); + + await expectLater(container.read(provider.future), completion(0)); + expect(container.read(provider), const AsyncData(0)); + + sub.read().state = const AsyncLoading(); + + expect( + sub.read().state, + const AsyncLoading() + .copyWithPrevious(const AsyncData(0), isRefresh: false), + ); + }); + + test( + 'performs seamless:false copyWithPrevious if triggered by a dependency change', + () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final provider = factory.simpleTestProvider( + (ref, self) => Stream.value(ref.watch(dep)), + ); + + container.listen(provider, (previous, next) {}); + + await expectLater(container.read(provider.future), completion(0)); + expect(container.read(provider), const AsyncData(0)); + + container.read(dep.notifier).state++; + expect( + container.read(provider), + const AsyncLoading() + .copyWithPrevious(const AsyncData(0), isRefresh: false), + ); + + await expectLater(container.read(provider.future), completion(1)); + expect(container.read(provider), const AsyncData(1)); + }); + + test( + 'performs seamless:false copyWithPrevious if both triggered by a dependency change and ref.refresh', + () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final provider = factory.simpleTestProvider( + (ref, self) => Stream.value(ref.watch(dep)), + ); + + container.listen(provider, (previous, next) {}); + + await expectLater(container.read(provider.future), completion(0)); + expect(container.read(provider), const AsyncData(0)); + + container.read(dep.notifier).state++; + expect( + container.refresh(provider), + const AsyncLoading() + .copyWithPrevious(const AsyncData(0), isRefresh: false), + ); + + await expectLater(container.read(provider.future), completion(1)); + expect(container.read(provider), const AsyncData(1)); + }); + }); + + test('does not notify listeners when refreshed during loading', () async { + final provider = factory.simpleTestProvider( + (ref, self) => Stream.value(0), + ); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, const AsyncLoading())); + + container.refresh(provider); + + await container.read(provider.future); + + verifyOnly( + listener, + listener(const AsyncLoading(), const AsyncData(0)), + ); + }); + + group('listenSelf', () { + test('can remove the listener', () async { + final container = ProviderContainer.test(); + final listener = Listener>(); + late final RemoveListener remove; + final provider = factory.simpleTestProvider((ref, self) { + remove = self.listenSelf(listener.call); + return Stream.value(42); + }); + + container.listen(provider.notifier, (previous, next) {}); + clearInteractions(listener); + + remove(); + + container.read(provider.notifier).state = const AsyncData(42); + + verifyZeroInteractions(listener); + }); + + test('supports listenSelf', () { + final listener = Listener>(); + final onError = ErrorListener(); + final provider = factory.simpleTestProvider((ref, self) { + self.listenSelf(listener.call, onError: onError.call); + Error.throwWithStackTrace(42, StackTrace.empty); + }); + final container = ProviderContainer.test(); + + container.listen(provider, (previous, next) {}); + + verifyOnly( + listener, + listener(null, const AsyncError(42, StackTrace.empty)), + ); + verifyZeroInteractions(onError); + + container.read(provider.notifier).state = const AsyncData(42); + + verifyNoMoreInteractions(onError); + verifyOnly( + listener, + listener( + const AsyncError(42, StackTrace.empty), + const AsyncData(42), + ), + ); + }); + }); + + test( + 'converts StreamNotifier.build into an AsyncData if the future completes', + () async { + final provider = factory.simpleTestProvider( + (ref, self) => Stream.value(0), + ); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, const AsyncLoading())); + expect( + container.read(provider.notifier).state, + const AsyncLoading(), + ); + + expect(await container.read(provider.future), 0); + + verifyOnly( + listener, + listener(const AsyncLoading(), const AsyncData(0)), + ); + expect( + container.read(provider.notifier).state, + const AsyncData(0), + ); + }); + + test('converts StreamNotifier.build into an AsyncError if the future fails', + () async { + final provider = factory.simpleTestProvider( + (ref, _) => Stream.error(0, StackTrace.empty), + ); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly(listener, listener(null, const AsyncLoading())); + expect( + container.read(provider.notifier).state, + const AsyncLoading(), + ); + + await expectLater(container.read(provider.future), throwsA(0)); + + verifyOnly( + listener, + listener(const AsyncLoading(), const AsyncError(0, StackTrace.empty)), + ); + expect( + container.read(provider.notifier).state, + const AsyncError(0, StackTrace.empty), + ); + }); + + test('supports cases where the StreamNotifier constructor throws', + () async { + final provider = factory.provider( + () => Error.throwWithStackTrace(0, StackTrace.empty), + ); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly( + listener, + listener(null, const AsyncError(0, StackTrace.empty)), + ); + expect( + () => container.read(provider.notifier), + throwsA(0), + ); + + await expectLater(container.read(provider.future), throwsA(0)); + }); + + test( + 'synchronously emits AsyncError if StreamNotifier.build throws synchronously', + () async { + final provider = factory.simpleTestProvider( + (ref, _) => Error.throwWithStackTrace(42, StackTrace.empty), + ); + final container = ProviderContainer.test(); + final listener = Listener>(); + + container.listen(provider, listener.call, fireImmediately: true); + + verifyOnly( + listener, + listener(null, const AsyncError(42, StackTrace.empty)), + ); + expect( + container.read(provider.notifier).state, + const AsyncError(42, StackTrace.empty), + ); + await expectLater(container.read(provider.future), throwsA(42)); + }); + + test( + 'stops listening to the previous future data when the provider rebuilds', + () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final completers = { + 0: Completer.sync(), + 1: Completer.sync(), + }; + final provider = factory.simpleTestProvider( + (ref, _) => Stream.fromFuture(completers[ref.watch(dep)]!.future), + ); + final listener = Listener>(); + + container.listen(provider, listener.call); + + expect( + container.read(provider.future), + completion(21), + reason: 'The provider rebuilt while the future was still pending, ' + 'so .future should resolve with the next value', + ); + verifyZeroInteractions(listener); + expect(container.read(provider), const AsyncLoading()); + + container.read(dep.notifier).state++; + completers[0]!.complete(42); + + verifyZeroInteractions(listener); + + expect(container.read(provider.future), completion(21)); + expect(container.read(provider), const AsyncLoading()); + + completers[1]!.complete(21); + + expect(await container.read(provider.future), 21); + expect(container.read(provider), const AsyncData(21)); + }); + + test( + 'stops listening to the previous future error when the provider rebuilds', + () async { + final container = ProviderContainer.test(); + final dep = StateProvider((ref) => 0); + final completers = { + 0: Completer.sync(), + 1: Completer.sync(), + }; + final provider = factory.simpleTestProvider( + (ref, _) => Stream.fromFuture(completers[ref.watch(dep)]!.future), + ); + final listener = Listener>(); + + container.listen(provider, listener.call); + + expect( + container.read(provider.future), + throwsA(21), + reason: 'The provider rebuilt while the future was still pending, ' + 'so .future should resolve with the next value', + ); + verifyZeroInteractions(listener); + expect(container.read(provider), const AsyncLoading()); + + container.read(dep.notifier).state++; + completers[0]!.completeError(42, StackTrace.empty); + + verifyZeroInteractions(listener); + + expect(container.read(provider.future), throwsA(21)); + expect(container.read(provider), const AsyncLoading()); + + completers[1]!.completeError(21, StackTrace.empty); + + await expectLater(container.read(provider.future), throwsA(21)); + expect( + container.read(provider), + const AsyncError(21, StackTrace.empty), + ); + }); + + group('StreamNotifier.state', () { + test( + 'when manually modifying the state, the new exposed value contains the previous state when possible', + () async { + final provider = factory.simpleTestProvider( + (ref, _) => Stream.value(0), + ); + final container = ProviderContainer.test(); + + final sub = container.listen(provider.notifier, (previous, next) {}); + await container.read(provider.future); + + // ignore: prefer_const_constructors, not using `const` as we voluntarily break identity to test `identical` + final newState = AsyncData(84); + // ignore: prefer_const_constructors, not using `const` as we voluntarily break identity to test `identical` + final newLoading = AsyncLoading(); + // ignore: prefer_const_constructors, not using `const` as we voluntarily break identity to test `identical` + final newError = AsyncError(84, StackTrace.empty); + + sub.read().state = newState; + + expect(sub.read().state, same(newState)); + + sub.read().state = newLoading; + + expect( + sub.read().state, + const AsyncLoading() + .copyWithPrevious(newState, isRefresh: false), + ); + + sub.read().state = newError; + + expect( + sub.read().state, + newError.copyWithPrevious( + const AsyncLoading() + .copyWithPrevious(newState, isRefresh: false), + ), + ); + }); + + test('can be read inside build', () { + final dep = StateProvider((ref) => 0); + late AsyncValue state; + final provider = factory.provider( + () { + late TestStreamNotifier notifier; + return notifier = factory.deferredNotifier((ref, _) { + state = notifier.state; + return Stream.value(ref.watch(dep)); + }); + }, + ); + final container = ProviderContainer.test(); + + container.listen(provider, (previous, next) {}); + + expect(state, const AsyncLoading()); + + container.read(provider.notifier).state = const AsyncData(42); + container.refresh(provider); + + expect( + state, + const AsyncLoading().copyWithPrevious(const AsyncData(42)), + ); + }); + + test('notifies listeners when the setter is called', () async { + final provider = factory.simpleTestProvider( + (ref, self) => Stream.value(0), + ); + final container = ProviderContainer.test(); + final listener = Listener>(); + + // Skip the loading + await container.listen(provider.future, (previous, next) {}).read(); + + container.listen(provider, listener.call); + + verifyZeroInteractions(listener); + + container.read(provider.notifier).state = const AsyncData(42); + + verifyOnly( + listener, + listener(const AsyncData(0), const AsyncData(42)), + ); + }); + }); + + group('StreamNotifier.future', () { + test( + 'when disposed during loading, resolves with the content of StreamNotifier.build', + () async { + final container = ProviderContainer.test(); + final completer = Completer.sync(); + addTearDown(() => completer.complete(42)); + final provider = factory.simpleTestProvider( + (ref, _) => Stream.fromFuture(completer.future), + ); + + final future = container.read(provider.future); + expect(future, throwsA(isStateError)); + + container.dispose(); + }); + + test( + 'going data > loading while the future is still pending. ' + 'Resolves with error', () async { + final container = ProviderContainer.test(); + final completer = Completer.sync(); + addTearDown(() => completer.complete(42)); + final provider = factory.simpleTestProvider( + (ref, _) => Stream.fromFuture(completer.future), + ); + + container.read(provider); + container.read(provider.notifier).state = const AsyncData(42); + container.read(provider.notifier).state = const AsyncLoading(); + + final future = container.read(provider.future); + expect(future, throwsA(isStateError)); + + container.dispose(); + }); + + test('if going back to loading after future resolved, throws StateError', + () async { + final container = ProviderContainer.test(); + final completer = Completer.sync(); + final provider = factory.simpleTestProvider( + (ref, _) => Stream.fromFuture(completer.future), + ); + + container.listen(provider, (previous, next) {}); + + completer.complete(42); + + container.read(provider.notifier).state = const AsyncData(42); + container.read(provider.notifier).state = const AsyncLoading(); + + final future = container.read(provider.future); + + container.dispose(); + + await expectLater(future, throwsStateError); + }); + + test( + 'resolves with the new state if StreamNotifier.state is modified during loading', + () async { + final container = ProviderContainer.test(); + final completer = Completer.sync(); + final provider = factory.simpleTestProvider( + (ref, _) => Stream.fromFuture(completer.future), + ); + final listener = Listener>(); + + final sub = container.listen(provider.notifier, (previous, next) {}); + container.listen(provider.future, listener.call); + + expect(sub.read().future, completion(21)); + + sub.read().state = const AsyncData(21); + + completer.complete(42); + + expect(sub.read().future, completion(42)); + final capture = + verifyOnly(listener, listener(captureAny, captureAny)).captured; + + expect(capture.length, 2); + expect(capture.first, completion(21)); + expect(capture.last, completion(42)); + }); + + test('resolves with the new state when notifier.state is changed', + () async { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider( + (ref, _) => Stream.value(0), + ); + final listener = Listener>(); + + final sub = container.listen(provider.notifier, (previous, next) {}); + container.listen( + provider.future, + listener.call, + fireImmediately: true, + ); + + await expectLater(sub.read().future, completion(0)); + verifyOnly( + listener, + listener(argThat(equals(null)), argThat(completion(0))), + ); + + sub.read().state = const AsyncData(1); + + await expectLater(sub.read().future, completion(1)); + }); + + test('returns a Future identical to that of .future', () { + final listener = OnBuildMock(); + final dep = StateProvider((ref) => 0); + final provider = factory.simpleTestProvider((ref, _) { + listener(); + return Stream.value(ref.watch(dep)); + }); + final container = ProviderContainer.test(); + + container.listen(provider.notifier, (previous, next) {}); + final notifier = container.read(provider.notifier); + + expect(notifier.future, same(container.read(provider.future))); + }); + }); + + group('StreamNotifierProvider.notifier', () { + test( + 'Notifies listeners whenever `build` is re-executed, due to recreating a new notifier.', + () async { + final notifierListener = Listener<$StreamNotifier>(); + final dep = StateProvider((ref) => 0); + final provider = factory.provider(() { + return factory.deferredNotifier( + (ref, _) => Stream.value(ref.watch(dep)), + ); + }); + final container = ProviderContainer.test(); + + final sub = container.listen(provider.notifier, notifierListener.call); + final initialNotifier = sub.read(); + + expect(initialNotifier.ref.mounted, true); + + // Skip the loading + await container.read(provider.future); + verifyNoMoreInteractions(notifierListener); + + container.refresh(provider); + final newNotifier = sub.read(); + + expect(newNotifier, isNot(same(initialNotifier))); + verifyOnly( + notifierListener, + notifierListener(initialNotifier, newNotifier), + ).called(1); + expect(initialNotifier.ref.mounted, false); + expect(newNotifier.ref.mounted, true); + }); + }); + + test( + 'Can override StreamNotifier.updateShouldNotify to change the default filter logic', + () async { + final provider = factory.simpleTestProvider>( + (ref, _) => Stream.value(Equal(42)), + updateShouldNotify: (a, b) => a != b, + ); + final container = ProviderContainer.test(); + final listener = Listener>>(); + + // Skip the loading + await container.listen(provider.future, (previous, next) {}).read(); + + container.listen(provider, listener.call); + final notifier = container.read(provider.notifier); + + // voluntarily assigning the same value + final self = notifier.state; + notifier.state = self; + + verifyZeroInteractions(listener); + + notifier.state = AsyncData(Equal(42)); + + verifyZeroInteractions(listener); + + notifier.state = AsyncData(Equal(21)); + + verifyOnly( + listener, + listener(AsyncData(Equal(42)), AsyncData(Equal(21))), + ); + }); + + group('AsyncNotifier.update', () { + test('passes in the latest state', () async { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider( + (ref, _) => Stream.value(0), + ); + + // Skip the loading + await container.listen(provider.future, (previous, next) {}).read(); + + final sub = container.listen(provider.notifier, (prev, next) {}); + + expect( + container.read(provider), + const AsyncData(0), + ); + + await expectLater( + sub.read().update((prev) => prev + 1), + completion(1), + ); + await expectLater( + sub.read().future, + completion(1), + ); + await expectLater( + sub.read().update((prev) => prev + 1), + completion(2), + ); + }); + + test('can specify onError to handle error scenario', () async { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider( + (ref, _) => Error.throwWithStackTrace(42, StackTrace.empty), + ); + var callCount = 0; + Object? actualErr; + Object? actualStack; + + final sub = container.listen(provider.notifier, (prev, next) {}); + + expect( + container.read(provider), + const AsyncError(42, StackTrace.empty), + ); + + await expectLater( + sub.read().update( + (prev) { + callCount++; + return prev; + }, + onError: (err, stack) { + actualErr = err; + actualStack = stack; + return 21; + }, + ), + completion(21), + ); + expect(callCount, 0); + expect(actualErr, 42); + expect(actualStack, StackTrace.empty); + expect(container.read(provider), const AsyncData(21)); + }); + + test('executes immediately with current state if a state is available', + () async { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider( + (ref, _) => Stream.value(1), + ); + + // Skip the loading + await container.listen(provider.future, (previous, next) {}).read(); + + final sub = container.listen(provider.notifier, (prev, next) {}); + + expect(container.read(provider), const AsyncData(1)); + + await expectLater( + sub.read().update((prev) => prev + 1), + completion(2), + ); + expect(container.read(provider), const AsyncData(2)); + }); + + test('executes immediately with current state if an error is available', + () async { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider( + (ref, _) => Error.throwWithStackTrace(42, StackTrace.empty), + ); + var callCount = 0; + + final sub = container.listen(provider.notifier, (prev, next) {}); + + expect( + container.read(provider), + const AsyncError(42, StackTrace.empty), + ); + + await expectLater( + sub.read().update((prev) { + callCount++; + return prev + 1; + }), + throwsA(42), + ); + + expect(callCount, 0); + expect( + container.read(provider), + const AsyncError(42, StackTrace.empty), + ); + }); + + test('awaits the future resolution if in loading state', () async { + final container = ProviderContainer.test(); + final provider = factory.simpleTestProvider( + (ref, _) => Stream.value(42), + ); + + final sub = container.listen(provider.notifier, (prev, next) {}); + + expect(container.read(provider), const AsyncLoading()); + + await expectLater( + sub.read().update((prev) => prev + 1), + completion(43), + ); + expect(container.read(provider), const AsyncData(43)); + }); + }); + }); + + test('supports overrideWith', () async { + final provider = StreamNotifierProvider, int>( + () => DeferredStreamNotifier((ref, _) => Stream.value(0)), + ); + final autoDispose = + StreamNotifierProvider.autoDispose, int>( + () => DeferredStreamNotifier((ref, _) => Stream.value(0)), + ); + final container = ProviderContainer.test( + overrides: [ + provider.overrideWith( + () => DeferredStreamNotifier((ref, _) => Stream.value(42)), + ), + autoDispose.overrideWith( + () => DeferredStreamNotifier((ref, _) => Stream.value(84)), + ), + ], + ); + + // Skip the loading + await container.listen(provider.future, (previous, next) {}).read(); + await container.listen(autoDispose.future, (previous, next) {}).read(); + + expect(container.read(provider).value, 42); + expect(container.read(autoDispose).value, 84); + }); + + test('supports family overrideWith', () async { + final family = StreamNotifierProvider.family< + DeferredFamilyStreamNotifier, int, int>( + () => DeferredFamilyStreamNotifier((ref, _) => Stream.value(0)), + ); + final autoDisposeFamily = StreamNotifierProvider.autoDispose + .family, int, int>( + () => DeferredFamilyStreamNotifier((ref, _) => Stream.value(0)), + ); + final container = ProviderContainer.test( + overrides: [ + family.overrideWith( + () => DeferredFamilyStreamNotifier((ref, _) => Stream.value(42)), + ), + autoDisposeFamily.overrideWith( + () => DeferredFamilyStreamNotifier( + (ref, _) => Stream.value(84), + ), + ), + ], + ); + + // Skip the loading + await container.listen(family(10).future, (previous, next) {}).read(); + await container + .listen(autoDisposeFamily(10).future, (previous, next) {}) + .read(); + + expect(container.read(family(10)).value, 42); + expect(container.read(autoDisposeFamily(10)).value, 84); + }); + + group('AutoDispose variant', () { + test('can watch autoDispose providers', () async { + final dep = Provider.autoDispose((ref) => 0); + final provider = + StreamNotifierProvider.autoDispose, int>( + () => DeferredStreamNotifier((ref, _) { + return Stream.value(ref.watch(dep)); + }), + ); + final container = ProviderContainer.test(); + + // Skip the loading + await container.listen(provider.future, (previous, next) {}).read(); + + expect(container.read(provider), const AsyncData(0)); + }); + }); + + group('modifiers', () { + void canBeAssignedToRefreshable( + Refreshable provider, + ) {} + + void canBeAssignedToProviderListenable( + ProviderListenable provider, + ) {} + + test('provider', () { + final provider = StreamNotifierProvider, int>( + () => DeferredStreamNotifier((ref, _) => Stream.value(0)), + ); + + provider.select((AsyncValue value) => 0); + provider.selectAsync((int value) => 0); + + canBeAssignedToProviderListenable>(provider); + canBeAssignedToRefreshable>(provider); + + canBeAssignedToProviderListenable>(provider.future); + canBeAssignedToRefreshable>(provider.future); + + canBeAssignedToProviderListenable>(provider.notifier); + canBeAssignedToRefreshable>(provider.notifier); + }); + + test('autoDispose', () { + final autoDispose = + StreamNotifierProvider.autoDispose, int>( + () => DeferredStreamNotifier((ref, _) => Stream.value(0)), + ); + + autoDispose.select((AsyncValue value) => 0); + autoDispose.selectAsync((int value) => 0); + + canBeAssignedToProviderListenable>(autoDispose); + canBeAssignedToRefreshable>(autoDispose); + + canBeAssignedToProviderListenable>(autoDispose.future); + canBeAssignedToRefreshable>(autoDispose.future); + + canBeAssignedToProviderListenable>( + autoDispose.notifier, + ); + canBeAssignedToRefreshable>( + autoDispose.notifier, + ); + }); + + test('family', () { + final family = StreamNotifierProvider.family< + DeferredFamilyStreamNotifier, String, int>( + () => DeferredFamilyStreamNotifier((ref, _) => Stream.value('0')), + ); + + family(0).select((AsyncValue value) => 0); + family(0).selectAsync((String value) => 0); + + canBeAssignedToProviderListenable>(family(0)); + canBeAssignedToRefreshable>(family(0)); + + canBeAssignedToProviderListenable>(family(0).future); + canBeAssignedToRefreshable>(family(0).future); + + canBeAssignedToProviderListenable>( + family(0).notifier, + ); + canBeAssignedToRefreshable>( + family(0).notifier, + ); + }); + + test('autoDisposeFamily', () { + expect( + StreamNotifierProvider.autoDispose.family, + same(StreamNotifierProvider.family.autoDispose), + ); + + final autoDisposeFamily = StreamNotifierProvider.autoDispose + .family, String, int>( + () => DeferredFamilyStreamNotifier((ref, _) => Stream.value('0')), + ); + + autoDisposeFamily(0).select((AsyncValue value) => 0); + autoDisposeFamily(0).selectAsync((String value) => 0); + + canBeAssignedToProviderListenable>( + autoDisposeFamily(0), + ); + canBeAssignedToRefreshable>( + autoDisposeFamily(0), + ); + + canBeAssignedToProviderListenable>( + autoDisposeFamily(0).future, + ); + canBeAssignedToRefreshable>( + autoDisposeFamily(0).future, + ); + + canBeAssignedToProviderListenable>( + autoDisposeFamily(0).notifier, + ); + canBeAssignedToRefreshable>( + autoDisposeFamily(0).notifier, + ); + }); + }); +} + +@immutable +class Equal { + // ignore: prefer_const_constructors_in_immutables + Equal(this.value); + + final T value; + + @override + bool operator ==(Object other) => other is Equal && other.value == value; + + @override + int get hashCode => Object.hash(runtimeType, value); +} + +class CtorNotifier extends StreamNotifier { + @override + Stream build() => Stream.value(0); +} + +class FamilyCtorNotifier extends FamilyStreamNotifier { + @override + Stream build(int arg) => Stream.value(0); +} diff --git a/packages/riverpod/test/src/utils.dart b/packages/riverpod/test/src/utils.dart new file mode 100644 index 000000000..bdabd4732 --- /dev/null +++ b/packages/riverpod/test/src/utils.dart @@ -0,0 +1,254 @@ +import 'dart:async'; + +import 'package:mockito/mockito.dart'; +import 'package:riverpod/legacy.dart'; +import 'package:riverpod/src/internals.dart'; +import 'package:test/test.dart' hide Retry; + +export '../old/utils.dart' + show ObserverMock, isProviderObserverContext, isMutationContext; + +typedef RemoveListener = void Function(); + +List captureErrors(List cb) { + final errors = []; + for (final fn in cb) { + try { + fn(); + errors.add(null); + } catch (e) { + errors.add(e); + } + } + return errors; +} + +class StreamSubscriptionView implements StreamSubscription { + StreamSubscriptionView(this.inner); + + final StreamSubscription inner; + + @override + Future asFuture([E? futureValue]) => inner.asFuture(futureValue); + + @override + Future cancel() => inner.cancel(); + + @override + bool get isPaused => inner.isPaused; + + @override + void onData(void Function(T data)? handleData) => inner.onData(handleData); + + @override + void onDone(void Function()? handleDone) => inner.onDone(handleDone); + + @override + void onError(Function? handleError) => inner.onError(handleError); + + @override + void pause([Future? resumeSignal]) => inner.pause(resumeSignal); + + @override + void resume() => inner.resume(); +} + +class _DelegatingStreamSubscription extends StreamSubscriptionView { + _DelegatingStreamSubscription( + super.inner, { + this.onSubscriptionPause, + this.onSubscriptionResume, + this.onSubscriptionCancel, + }); + + final void Function()? onSubscriptionPause; + final void Function()? onSubscriptionResume; + final void Function()? onSubscriptionCancel; + + @override + Future cancel() { + onSubscriptionCancel?.call(); + return super.cancel(); + } + + @override + void pause([Future? resumeSignal]) { + onSubscriptionPause?.call(); + super.pause(resumeSignal); + } + + @override + void resume() { + onSubscriptionResume?.call(); + super.resume(); + } +} + +class DelegatingStream extends StreamView { + DelegatingStream( + super.stream, { + this.onSubscriptionPause, + this.onSubscriptionResume, + this.onSubscriptionCancel, + }); + + final void Function()? onSubscriptionPause; + final void Function()? onSubscriptionResume; + final void Function()? onSubscriptionCancel; + + @override + StreamSubscription listen( + void Function(T event)? onData, { + Function? onError, + void Function()? onDone, + bool? cancelOnError, + }) { + return _DelegatingStreamSubscription( + super.listen( + onData, + onError: onError, + onDone: onDone, + cancelOnError: cancelOnError, + ), + onSubscriptionPause: onSubscriptionPause, + onSubscriptionResume: onSubscriptionResume, + onSubscriptionCancel: onSubscriptionCancel, + ); + } +} + +class OverrideWithBuildMock extends Mock { + OverrideWithBuildMock(this.fallback); + + final CreatedT fallback; + + CreatedT call(Ref? ref, NotifierT? value) { + return super.noSuchMethod( + Invocation.method(#call, [ref, value]), + returnValue: fallback, + returnValueForMissingStub: fallback, + ) as CreatedT; + } +} + +class RetryMock extends Mock { + RetryMock([Retry? retry]) { + if (retry != null) { + when(call(any, any)).thenAnswer( + (i) => retry( + i.positionalArguments[0] as int, + i.positionalArguments[1] as Object, + ), + ); + } + } + + Duration? call(int? retryCount, Object? error); +} + +class OnBuildMock extends Mock { + void call(); +} + +class OnDisposeMock extends Mock { + void call(); +} + +class OnCancelMock extends Mock { + void call(); +} + +class OnResume extends Mock { + void call(); +} + +class OnPause extends Mock { + void call(); +} + +class OnAddListener extends Mock { + void call(); +} + +class OnRemoveListener extends Mock { + void call(); +} + +/// Syntax sugar for: +/// +/// ```dart +/// verify(mock()).called(1); +/// verifyNoMoreInteractions(mock); +/// ``` +VerifyOnly get verifyOnly { + final verification = verify; + + return (mock, invocation) { + final result = verification(invocation); + result.called(1); + verifyNoMoreInteractions(mock); + return result; + }; +} + +typedef VerifyOnly = VerificationResult Function( + Mock mock, + T matchingInvocations, +); + +class Listener extends Mock { + void call(T? previous, T? next); +} + +final isAssertionError = isA(); + +Matcher isStateErrorWith({String? message}) { + var matcher = isA(); + + if (message != null) { + matcher = matcher.having((e) => e.message, 'message', message); + } + + return matcher; +} + +class ErrorListener extends Mock { + void call(Object? error, StackTrace? stackTrace); +} + +class Selector extends Mock { + Selector(this.fake, Output Function(Input) selector) { + when(call(any)).thenAnswer((i) { + return selector( + i.positionalArguments.first as Input, + ); + }); + } + + final Output fake; + + Output call(Input? value) { + return super.noSuchMethod( + Invocation.method(#call, [value]), + returnValue: fake, + returnValueForMissingStub: fake, + ) as Output; + } +} + +class Counter extends StateNotifier { + Counter([super.initialValue = 0]); + + void increment() => state++; + + @override + int get state => super.state; + @override + set state(int value) => super.state = value; +} + +List errorsOf(void Function() cb) { + final errors = []; + runZonedGuarded(cb, (err, _) => errors.add(err)); + return [...errors]; +} diff --git a/packages/riverpod/test/third_party/fake_async.dart b/packages/riverpod/test/third_party/fake_async.dart index c9f0b3e88..251510050 100644 --- a/packages/riverpod/test/third_party/fake_async.dart +++ b/packages/riverpod/test/third_party/fake_async.dart @@ -197,7 +197,6 @@ class FakeAsync { var absoluteTimeout = _elapsed + timeout; _fireTimersWhile((timer) { if (timer._nextCall > absoluteTimeout) { - // TODO(nweiz): Make this a [TimeoutException]. throw StateError('Exceeded timeout $timeout while flushing timers'); } diff --git a/packages/riverpod_analyzer_utils/CHANGELOG.md b/packages/riverpod_analyzer_utils/CHANGELOG.md index 17d565315..b43a512d0 100644 --- a/packages/riverpod_analyzer_utils/CHANGELOG.md +++ b/packages/riverpod_analyzer_utils/CHANGELOG.md @@ -1,3 +1,19 @@ +## Unreleased build + +- **Breaking**: Rewrote all RiverpodAst nodes to instead be extensions on `AstNodes`. + Too many changes to detail everything. I'm the only one who uses this package anyway. + If you're reading this, have a nice day! +- Added support for parsing `@mutation` + +## 1.0.0-dev.1 - 2023-11-20 + +- **Breaking** `LegacyProviderDeclarationElement.providerType` is now nullable. +- Fix crash when parsing classes with a `ProviderBase` field. + +## 1.0.0-dev.0 - 2023-10-30 + +- Added `GeneratorProviderDeclarationElement.isFamily` + ## 0.5.9 - 2025-01-08 Support latest analyzer @@ -42,13 +58,7 @@ Bump custom_lint ## 0.4.3 - 2023-10-28 -- `GeneratorProviderDeclaration.createdTypeDisplayString` now always - return `FutureOr` on asynchronous providers. -- Fixing typos - -## 0.4.2 - 2023-10-21 - -- Type `provider.node` as `AnnotatedNode` +- Added `GeneratorProviderDeclarationElement.isFamily` ## 0.4.1 - 2023-10-06 diff --git a/packages/riverpod_analyzer_utils/lib/riverpod_analyzer_utils.dart b/packages/riverpod_analyzer_utils/lib/riverpod_analyzer_utils.dart index a79bfdb58..e2bdd7b46 100644 --- a/packages/riverpod_analyzer_utils/lib/riverpod_analyzer_utils.dart +++ b/packages/riverpod_analyzer_utils/lib/riverpod_analyzer_utils.dart @@ -1,3 +1,9 @@ -export 'src/riverpod_ast.dart' hide ObjectUtils; -export 'src/riverpod_element.dart'; +export 'src/errors.dart' hide ErrorReporter; +export 'src/nodes.dart' + hide + parseFirstProviderFor, + parseLegacyProviderType, + parseProviderFor, + CollectionRiverpodAst, + RiverpodAnalysisResult; export 'src/riverpod_types.dart'; diff --git a/packages/riverpod_analyzer_utils/lib/src/analyzer_utils.dart b/packages/riverpod_analyzer_utils/lib/src/analyzer_utils.dart new file mode 100644 index 000000000..1d9bd8d1f --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/analyzer_utils.dart @@ -0,0 +1,35 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:meta/meta.dart'; + +@internal +class Box { + Box(this.value); + final T value; +} + +@internal +extension AstUtils on AstNode { + Iterable get ancestors sync* { + var parent = this.parent; + while (parent != null) { + yield parent; + parent = parent.parent; + } + } +} + +@internal +extension ExpandoUtils on Expando> { + R upsert( + AstNode key, + R Function() create, + ) { + // Using a record to differentiate "null value" from "no value". + final existing = this[key]; + if (existing != null) return existing.value; + + final created = create(); + this[key] = Box(created); + return created; + } +} diff --git a/packages/riverpod_analyzer_utils/lib/src/argument_list_utils.dart b/packages/riverpod_analyzer_utils/lib/src/argument_list_utils.dart index 838584c82..1a7a81f1a 100644 --- a/packages/riverpod_analyzer_utils/lib/src/argument_list_utils.dart +++ b/packages/riverpod_analyzer_utils/lib/src/argument_list_utils.dart @@ -1,4 +1,5 @@ import 'package:analyzer/dart/ast/ast.dart'; +import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; /// Utilities for [ArgumentList] to help with specific argument retrieval. @@ -13,4 +14,12 @@ extension ArgumentListUtils on ArgumentList { Iterable namedArguments() { return arguments.whereType(); } + + NamedExpression? named(String name) { + return namedArguments().firstWhereOrNull((e) => e.name.label.name == name); + } + + Expression? positional(int index) { + return positionalArguments().elementAtOrNull(index); + } } diff --git a/packages/riverpod_analyzer_utils/lib/src/element_util.dart b/packages/riverpod_analyzer_utils/lib/src/element_util.dart new file mode 100644 index 000000000..ca3f70f7e --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/element_util.dart @@ -0,0 +1,53 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/nullability_suffix.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:collection/collection.dart'; +import 'package:meta/meta.dart'; + +import 'errors.dart'; +import 'riverpod_types.dart'; + +@internal +extension LibraryElementX on CompilationUnit { + static final _asyncValueCache = Expando(); + + LibraryElement? get _library => declaredElement?.library; + + Element? findElementWithNameFromRiverpod(String name) { + return _library!.importedLibraries + .map((e) => e.exportNamespace.get(name)) + .firstWhereOrNull( + (element) => element != null && isFromRiverpod.isExactly(element), + ); + } + + ClassElement? findAsyncValue() { + final cache = _asyncValueCache[this]; + if (cache != null) return cache; + + final result = findElementWithNameFromRiverpod('AsyncValue'); + if (result == null) { + errorReporter( + RiverpodAnalysisError( + 'No AsyncValue accessible in the library. ' + 'Did you forget to import Riverpod?', + targetNode: this, + code: null, + ), + ); + return null; + } + + return _asyncValueCache[this] = result as ClassElement?; + } + + DartType? createdTypeToValueType(DartType? typeArg) { + final asyncValue = findAsyncValue(); + + return asyncValue?.instantiate( + typeArguments: [if (typeArg != null) typeArg], + nullabilitySuffix: NullabilitySuffix.none, + ); + } +} diff --git a/packages/riverpod_analyzer_utils/lib/src/errors.dart b/packages/riverpod_analyzer_utils/lib/src/errors.dart index ba9ba5c00..eed83d768 100644 --- a/packages/riverpod_analyzer_utils/lib/src/errors.dart +++ b/packages/riverpod_analyzer_utils/lib/src/errors.dart @@ -1,29 +1,50 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; +import 'package:meta/meta.dart'; +@internal typedef ErrorReporter = void Function(RiverpodAnalysisError); -ErrorReporter? errorReporter; +ErrorReporter errorReporter = (error) { + throw UnsupportedError( + 'RiverpodAnalysisError found but no errorReporter specified: $error', + ); +}; + +enum RiverpodAnalysisErrorCode { + missingNotifierBuild, + abstractNotifier, + missingNotifierDefaultConstructor, + notifierDefaultConstructorHasRequiredParameters, + providerDependencyListParseError, + providerOrFamilyExpressionParseError, + invalidRetryArgument, + mutationReturnTypeMismatch, + mutationIsStatic, + mutationIsAbstract, +} class RiverpodAnalysisError { RiverpodAnalysisError( this.message, { this.targetNode, this.targetElement, + required this.code, }); final String message; final AstNode? targetNode; final Element? targetElement; + final RiverpodAnalysisErrorCode? code; @override String toString() { var trailing = ''; if (targetElement != null) { - trailing += '\nelement: $targetElement (${targetElement.runtimeType})'; + trailing += ' ; element: $targetElement (${targetElement.runtimeType})'; } if (targetNode != null) { - trailing += '\nelement: $targetNode (${targetNode.runtimeType})'; + trailing += ' ; node: $targetNode (${targetNode.runtimeType})'; } return 'RiverpodAnalysisError: $message$trailing'; diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes.dart b/packages/riverpod_analyzer_utils/lib/src/nodes.dart new file mode 100644 index 000000000..753e7d115 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes.dart @@ -0,0 +1,72 @@ +library nodes; + +import 'dart:async'; +import 'dart:convert'; + +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/syntactic_entity.dart'; +import 'package:analyzer/dart/ast/token.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; +import 'package:analyzer/dart/constant/value.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/nullability_suffix.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:collection/collection.dart'; +import 'package:crypto/crypto.dart'; +import 'package:meta/meta.dart'; + +import '../riverpod_analyzer_utils.dart'; +import 'analyzer_utils.dart'; +import 'argument_list_utils.dart'; +import 'element_util.dart'; +import 'object_extensions.dart'; + +part 'nodes/widgets/state.dart'; +part 'nodes/widgets/stateful_widget.dart'; +part 'nodes/widgets/stateless_widget.dart'; +part 'nodes/widgets/widget.dart'; + +part 'nodes/dependencies.dart'; +part 'nodes/providers/function.dart'; +part 'nodes/providers/legacy.dart'; +part 'nodes/providers/notifier.dart'; +part 'nodes/providers/providers.dart'; +part 'nodes/providers/identifiers.dart'; + +part 'nodes/provider_for.dart'; +part 'nodes/provider_or_family.dart'; +part 'nodes/annotation.dart'; +part 'nodes/provider_listenable.dart'; +part 'nodes/ref_invocation.dart'; +part 'nodes/widget_ref_invocation.dart'; + +part 'nodes/scopes/overrides.dart'; +part 'nodes/scopes/provider_container.dart'; +part 'nodes/scopes/provider_scope.dart'; + +part 'nodes.g.dart'; + +const _ast = Object(); + +extension RawTypeX on DartType { + /// Returns whether this type is a `Raw` typedef from `package:riverpod_annotation`. + bool get isRaw { + final alias = this.alias; + if (alias == null) return false; + return alias.element.name == 'Raw' && + isFromRiverpodAnnotation.isExactly(alias.element); + } +} + +class _Cache { + final _cacheExpando = Expando<(Object?,)>(); + + R call(Object e, R Function() create) { + final existing = _cacheExpando[e]; + if (existing != null) return existing.$1 as R; + + final created = create(); + _cacheExpando[e] = (created,); + return created; + } +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes.g.dart b/packages/riverpod_analyzer_utils/lib/src/nodes.g.dart new file mode 100644 index 000000000..e0694f4e5 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes.g.dart @@ -0,0 +1,1066 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'nodes.dart'; + +// ************************************************************************** +// _LintVisitorGenerator +// ************************************************************************** + +mixin RiverpodAstVisitor { + void visitWidgetDeclaration(WidgetDeclaration node) {} + void visitStateDeclaration(StateDeclaration node) {} + void visitAccumulatedDependencyList(AccumulatedDependencyList node) {} + void visitIdentifierDependencies(IdentifierDependencies node) {} + void visitNamedTypeDependencies(NamedTypeDependencies node) {} + void visitDependenciesAnnotation(DependenciesAnnotation node) {} + void visitFunctionalProviderDeclaration(FunctionalProviderDeclaration node) {} + void visitLegacyProviderDeclaration(LegacyProviderDeclaration node) {} + void visitClassBasedProviderDeclaration(ClassBasedProviderDeclaration node) {} + void visitGeneratorProviderDeclaration(GeneratorProviderDeclaration node) {} + void visitProviderIdentifier(ProviderIdentifier node) {} + void visitRiverpodAnnotation(RiverpodAnnotation node) {} + void visitProviderListenableExpression(ProviderListenableExpression node) {} + void visitRefInvocation(RefInvocation node) {} + void visitWidgetRefInvocation(WidgetRefInvocation node) {} + void visitProviderOverrideExpression(ProviderOverrideExpression node) {} + void visitProviderOverrideList(ProviderOverrideList node) {} + void visitProviderContainerInstanceCreationExpression( + ProviderContainerInstanceCreationExpression node) {} + void visitProviderScopeInstanceCreationExpression( + ProviderScopeInstanceCreationExpression node) {} +} + +abstract class RecursiveRiverpodAstVisitor extends GeneralizingAstVisitor + with RiverpodAstVisitor { + @override + void visitClassDeclaration(ClassDeclaration node) { + if (node.widget case final value?) { + visitWidgetDeclaration(value); + return; + } + + if (node.state case final value?) { + visitStateDeclaration(value); + return; + } + + if (node.provider case final value?) { + visitClassBasedProviderDeclaration(value); + return; + } + + super.visitClassDeclaration(node); + } + + @override + void visitNode(AstNode node) { + if (node.accumulatedDependencies case final value?) { + visitAccumulatedDependencyList(value); + return; + } + + super.visitNode(node); + } + + @override + void visitIdentifier(Identifier node) { + if (node.identifierDependencies case final value?) { + visitIdentifierDependencies(value); + return; + } + + super.visitIdentifier(node); + } + + @override + void visitNamedType(NamedType node) { + if (node.typeAnnotationDependencies case final value?) { + visitNamedTypeDependencies(value); + return; + } + + super.visitNamedType(node); + } + + @override + void visitAnnotation(Annotation node) { + if (node.dependencies case final value?) { + visitDependenciesAnnotation(value); + return; + } + + if (node.riverpod case final value?) { + visitRiverpodAnnotation(value); + return; + } + + super.visitAnnotation(node); + } + + @override + void visitFunctionDeclaration(FunctionDeclaration node) { + if (node.provider case final value?) { + visitFunctionalProviderDeclaration(value); + return; + } + + super.visitFunctionDeclaration(node); + } + + @override + void visitVariableDeclaration(VariableDeclaration node) { + if (node.provider case final value?) { + visitLegacyProviderDeclaration(value); + return; + } + + super.visitVariableDeclaration(node); + } + + @override + void visitDeclaration(Declaration node) { + if (node.provider case final value?) { + visitGeneratorProviderDeclaration(value); + return; + } + + super.visitDeclaration(node); + } + + @override + void visitSimpleIdentifier(SimpleIdentifier node) { + if (node.provider case final value?) { + visitProviderIdentifier(value); + return; + } + + super.visitSimpleIdentifier(node); + } + + @override + void visitExpression(Expression node) { + if (node.providerListenable case final value?) { + visitProviderListenableExpression(value); + return; + } + + if (node.overrides case final value?) { + visitProviderOverrideList(value); + return; + } + + super.visitExpression(node); + } + + @override + void visitMethodInvocation(MethodInvocation node) { + if (node.refInvocation case final value?) { + visitRefInvocation(value); + return; + } + + if (node.widgetRefInvocation case final value?) { + visitWidgetRefInvocation(value); + return; + } + + super.visitMethodInvocation(node); + } + + @override + void visitCollectionElement(CollectionElement node) { + if (node.providerOverride case final value?) { + visitProviderOverrideExpression(value); + return; + } + + super.visitCollectionElement(node); + } + + @override + void visitInstanceCreationExpression(InstanceCreationExpression node) { + if (node.providerContainer case final value?) { + visitProviderContainerInstanceCreationExpression(value); + return; + } + + if (node.providerScope case final value?) { + visitProviderScopeInstanceCreationExpression(value); + return; + } + + super.visitInstanceCreationExpression(node); + } + + void visitWidgetDeclaration(WidgetDeclaration node) { + super.visitClassDeclaration(node.node); + } + + void visitStateDeclaration(StateDeclaration node) { + super.visitClassDeclaration(node.node); + } + + void visitAccumulatedDependencyList(AccumulatedDependencyList node) { + super.visitNode(node.node); + } + + void visitIdentifierDependencies(IdentifierDependencies node) { + super.visitIdentifier(node.node); + } + + void visitNamedTypeDependencies(NamedTypeDependencies node) { + super.visitNamedType(node.node); + } + + void visitDependenciesAnnotation(DependenciesAnnotation node) { + super.visitAnnotation(node.node); + } + + void visitFunctionalProviderDeclaration(FunctionalProviderDeclaration node) { + super.visitFunctionDeclaration(node.node); + } + + void visitLegacyProviderDeclaration(LegacyProviderDeclaration node) { + super.visitVariableDeclaration(node.node); + } + + void visitClassBasedProviderDeclaration(ClassBasedProviderDeclaration node) { + super.visitClassDeclaration(node.node); + } + + void visitGeneratorProviderDeclaration(GeneratorProviderDeclaration node) { + super.visitDeclaration(node.node); + } + + void visitProviderIdentifier(ProviderIdentifier node) { + super.visitSimpleIdentifier(node.node); + } + + void visitRiverpodAnnotation(RiverpodAnnotation node) { + super.visitAnnotation(node.node); + } + + void visitProviderListenableExpression(ProviderListenableExpression node) { + super.visitExpression(node.node); + } + + void visitRefInvocation(RefInvocation node) { + super.visitMethodInvocation(node.node); + } + + void visitWidgetRefInvocation(WidgetRefInvocation node) { + super.visitMethodInvocation(node.node); + } + + void visitProviderOverrideExpression(ProviderOverrideExpression node) { + super.visitCollectionElement(node.node); + } + + void visitProviderOverrideList(ProviderOverrideList node) { + super.visitExpression(node.node); + } + + void visitProviderContainerInstanceCreationExpression( + ProviderContainerInstanceCreationExpression node) { + super.visitInstanceCreationExpression(node.node); + } + + void visitProviderScopeInstanceCreationExpression( + ProviderScopeInstanceCreationExpression node) { + super.visitInstanceCreationExpression(node.node); + } +} + +abstract class SimpleRiverpodAstVisitor extends RecursiveRiverpodAstVisitor { + @override + void visitNode(AstNode node) {} +} + +abstract class UnimplementedRiverpodAstVisitor + extends SimpleRiverpodAstVisitor { + void visitWidgetDeclaration(WidgetDeclaration node) => + throw UnimplementedError(); + void visitStateDeclaration(StateDeclaration node) => + throw UnimplementedError(); + void visitAccumulatedDependencyList(AccumulatedDependencyList node) => + throw UnimplementedError(); + void visitIdentifierDependencies(IdentifierDependencies node) => + throw UnimplementedError(); + void visitNamedTypeDependencies(NamedTypeDependencies node) => + throw UnimplementedError(); + void visitDependenciesAnnotation(DependenciesAnnotation node) => + throw UnimplementedError(); + void visitFunctionalProviderDeclaration(FunctionalProviderDeclaration node) => + throw UnimplementedError(); + void visitLegacyProviderDeclaration(LegacyProviderDeclaration node) => + throw UnimplementedError(); + void visitClassBasedProviderDeclaration(ClassBasedProviderDeclaration node) => + throw UnimplementedError(); + void visitGeneratorProviderDeclaration(GeneratorProviderDeclaration node) => + throw UnimplementedError(); + void visitProviderIdentifier(ProviderIdentifier node) => + throw UnimplementedError(); + void visitRiverpodAnnotation(RiverpodAnnotation node) => + throw UnimplementedError(); + void visitProviderListenableExpression(ProviderListenableExpression node) => + throw UnimplementedError(); + void visitRefInvocation(RefInvocation node) => throw UnimplementedError(); + void visitWidgetRefInvocation(WidgetRefInvocation node) => + throw UnimplementedError(); + void visitProviderOverrideExpression(ProviderOverrideExpression node) => + throw UnimplementedError(); + void visitProviderOverrideList(ProviderOverrideList node) => + throw UnimplementedError(); + void visitProviderContainerInstanceCreationExpression( + ProviderContainerInstanceCreationExpression node) => + throw UnimplementedError(); + void visitProviderScopeInstanceCreationExpression( + ProviderScopeInstanceCreationExpression node) => + throw UnimplementedError(); +} + +@internal +class CollectionRiverpodAst extends SimpleRiverpodAstVisitor { + final Map> riverpodAst = {}; + List? _pendingList; + + @override + void visitClassDeclaration( + ClassDeclaration node, + ) { + final list = riverpodAst.putIfAbsent('ClassDeclaration', () => []); + final previousList = list; + _pendingList = list; + super.visitClassDeclaration(node); + _pendingList = previousList; + } + + @override + void visitNode( + AstNode node, + ) { + final list = riverpodAst.putIfAbsent('AstNode', () => []); + final previousList = list; + _pendingList = list; + super.visitNode(node); + _pendingList = previousList; + } + + @override + void visitIdentifier( + Identifier node, + ) { + final list = riverpodAst.putIfAbsent('Identifier', () => []); + final previousList = list; + _pendingList = list; + super.visitIdentifier(node); + _pendingList = previousList; + } + + @override + void visitNamedType( + NamedType node, + ) { + final list = riverpodAst.putIfAbsent('NamedType', () => []); + final previousList = list; + _pendingList = list; + super.visitNamedType(node); + _pendingList = previousList; + } + + @override + void visitAnnotation( + Annotation node, + ) { + final list = riverpodAst.putIfAbsent('Annotation', () => []); + final previousList = list; + _pendingList = list; + super.visitAnnotation(node); + _pendingList = previousList; + } + + @override + void visitFunctionDeclaration( + FunctionDeclaration node, + ) { + final list = riverpodAst.putIfAbsent('FunctionDeclaration', () => []); + final previousList = list; + _pendingList = list; + super.visitFunctionDeclaration(node); + _pendingList = previousList; + } + + @override + void visitVariableDeclaration( + VariableDeclaration node, + ) { + final list = riverpodAst.putIfAbsent('VariableDeclaration', () => []); + final previousList = list; + _pendingList = list; + super.visitVariableDeclaration(node); + _pendingList = previousList; + } + + @override + void visitDeclaration( + Declaration node, + ) { + final list = riverpodAst.putIfAbsent('Declaration', () => []); + final previousList = list; + _pendingList = list; + super.visitDeclaration(node); + _pendingList = previousList; + } + + @override + void visitSimpleIdentifier( + SimpleIdentifier node, + ) { + final list = riverpodAst.putIfAbsent('SimpleIdentifier', () => []); + final previousList = list; + _pendingList = list; + super.visitSimpleIdentifier(node); + _pendingList = previousList; + } + + @override + void visitExpression( + Expression node, + ) { + final list = riverpodAst.putIfAbsent('Expression', () => []); + final previousList = list; + _pendingList = list; + super.visitExpression(node); + _pendingList = previousList; + } + + @override + void visitMethodInvocation( + MethodInvocation node, + ) { + final list = riverpodAst.putIfAbsent('MethodInvocation', () => []); + final previousList = list; + _pendingList = list; + super.visitMethodInvocation(node); + _pendingList = previousList; + } + + @override + void visitCollectionElement( + CollectionElement node, + ) { + final list = riverpodAst.putIfAbsent('CollectionElement', () => []); + final previousList = list; + _pendingList = list; + super.visitCollectionElement(node); + _pendingList = previousList; + } + + @override + void visitInstanceCreationExpression( + InstanceCreationExpression node, + ) { + final list = + riverpodAst.putIfAbsent('InstanceCreationExpression', () => []); + final previousList = list; + _pendingList = list; + super.visitInstanceCreationExpression(node); + _pendingList = previousList; + } + + void visitWidgetDeclaration(WidgetDeclaration node) { + _pendingList!.add(node); + } + + void visitStateDeclaration(StateDeclaration node) { + _pendingList!.add(node); + } + + void visitAccumulatedDependencyList(AccumulatedDependencyList node) { + _pendingList!.add(node); + } + + void visitIdentifierDependencies(IdentifierDependencies node) { + _pendingList!.add(node); + } + + void visitNamedTypeDependencies(NamedTypeDependencies node) { + _pendingList!.add(node); + } + + void visitDependenciesAnnotation(DependenciesAnnotation node) { + _pendingList!.add(node); + } + + void visitFunctionalProviderDeclaration(FunctionalProviderDeclaration node) { + _pendingList!.add(node); + } + + void visitLegacyProviderDeclaration(LegacyProviderDeclaration node) { + _pendingList!.add(node); + } + + void visitClassBasedProviderDeclaration(ClassBasedProviderDeclaration node) { + _pendingList!.add(node); + } + + void visitGeneratorProviderDeclaration(GeneratorProviderDeclaration node) { + _pendingList!.add(node); + } + + void visitProviderIdentifier(ProviderIdentifier node) { + _pendingList!.add(node); + } + + void visitRiverpodAnnotation(RiverpodAnnotation node) { + _pendingList!.add(node); + } + + void visitProviderListenableExpression(ProviderListenableExpression node) { + _pendingList!.add(node); + } + + void visitRefInvocation(RefInvocation node) { + _pendingList!.add(node); + } + + void visitWidgetRefInvocation(WidgetRefInvocation node) { + _pendingList!.add(node); + } + + void visitProviderOverrideExpression(ProviderOverrideExpression node) { + _pendingList!.add(node); + } + + void visitProviderOverrideList(ProviderOverrideList node) { + _pendingList!.add(node); + } + + void visitProviderContainerInstanceCreationExpression( + ProviderContainerInstanceCreationExpression node) { + _pendingList!.add(node); + } + + void visitProviderScopeInstanceCreationExpression( + ProviderScopeInstanceCreationExpression node) { + _pendingList!.add(node); + } +} + +@internal +class RiverpodAnalysisResult extends RecursiveRiverpodAstVisitor { + final List errors = []; + + final widgetDeclarations = []; + @override + void visitWidgetDeclaration( + WidgetDeclaration node, + ) { + super.visitWidgetDeclaration(node); + widgetDeclarations.add(node); + } + + final stateDeclarations = []; + @override + void visitStateDeclaration( + StateDeclaration node, + ) { + super.visitStateDeclaration(node); + stateDeclarations.add(node); + } + + final accumulatedDependencyLists = []; + @override + void visitAccumulatedDependencyList( + AccumulatedDependencyList node, + ) { + super.visitAccumulatedDependencyList(node); + accumulatedDependencyLists.add(node); + } + + final identifierDependenciesList = []; + @override + void visitIdentifierDependencies( + IdentifierDependencies node, + ) { + super.visitIdentifierDependencies(node); + identifierDependenciesList.add(node); + } + + final namedTypeDependenciesList = []; + @override + void visitNamedTypeDependencies( + NamedTypeDependencies node, + ) { + super.visitNamedTypeDependencies(node); + namedTypeDependenciesList.add(node); + } + + final dependenciesAnnotations = []; + @override + void visitDependenciesAnnotation( + DependenciesAnnotation node, + ) { + super.visitDependenciesAnnotation(node); + dependenciesAnnotations.add(node); + } + + final functionalProviderDeclarations = []; + @override + void visitFunctionalProviderDeclaration( + FunctionalProviderDeclaration node, + ) { + super.visitFunctionalProviderDeclaration(node); + functionalProviderDeclarations.add(node); + } + + final legacyProviderDeclarations = []; + @override + void visitLegacyProviderDeclaration( + LegacyProviderDeclaration node, + ) { + super.visitLegacyProviderDeclaration(node); + legacyProviderDeclarations.add(node); + } + + final classBasedProviderDeclarations = []; + @override + void visitClassBasedProviderDeclaration( + ClassBasedProviderDeclaration node, + ) { + super.visitClassBasedProviderDeclaration(node); + classBasedProviderDeclarations.add(node); + } + + final generatorProviderDeclarations = []; + @override + void visitGeneratorProviderDeclaration( + GeneratorProviderDeclaration node, + ) { + super.visitGeneratorProviderDeclaration(node); + generatorProviderDeclarations.add(node); + } + + final providerIdentifiers = []; + @override + void visitProviderIdentifier( + ProviderIdentifier node, + ) { + super.visitProviderIdentifier(node); + providerIdentifiers.add(node); + } + + final riverpodAnnotations = []; + @override + void visitRiverpodAnnotation( + RiverpodAnnotation node, + ) { + super.visitRiverpodAnnotation(node); + riverpodAnnotations.add(node); + } + + final providerListenableExpressions = []; + @override + void visitProviderListenableExpression( + ProviderListenableExpression node, + ) { + super.visitProviderListenableExpression(node); + providerListenableExpressions.add(node); + } + + final refInvocations = []; + @override + void visitRefInvocation( + RefInvocation node, + ) { + super.visitRefInvocation(node); + refInvocations.add(node); + } + + final widgetRefInvocations = []; + @override + void visitWidgetRefInvocation( + WidgetRefInvocation node, + ) { + super.visitWidgetRefInvocation(node); + widgetRefInvocations.add(node); + } + + final providerOverrideExpressions = []; + @override + void visitProviderOverrideExpression( + ProviderOverrideExpression node, + ) { + super.visitProviderOverrideExpression(node); + providerOverrideExpressions.add(node); + } + + final providerOverrideLists = []; + @override + void visitProviderOverrideList( + ProviderOverrideList node, + ) { + super.visitProviderOverrideList(node); + providerOverrideLists.add(node); + } + + final providerContainerInstanceCreationExpressions = + []; + @override + void visitProviderContainerInstanceCreationExpression( + ProviderContainerInstanceCreationExpression node, + ) { + super.visitProviderContainerInstanceCreationExpression(node); + providerContainerInstanceCreationExpressions.add(node); + } + + final providerScopeInstanceCreationExpressions = + []; + @override + void visitProviderScopeInstanceCreationExpression( + ProviderScopeInstanceCreationExpression node, + ) { + super.visitProviderScopeInstanceCreationExpression(node); + providerScopeInstanceCreationExpressions.add(node); + } +} + +class RiverpodAstRegistry { + static final _cache = Expando>>(); + + void run(AstNode node) { + final previousErrorReporter = errorReporter; + try { + final errors = _cache.upsert( + node, + () => [], + ); + + final visitor = _RiverpodAstRegistryVisitor(this); + errorReporter = errors.add; + + node.accept(visitor); + for (final error in errors) { + visitor._runSubscriptions(error, _onRiverpodAnalysisError); + } + } finally { + errorReporter = previousErrorReporter; + } + } + + final _onRiverpodAnalysisError = []; + void addRiverpodAnalysisError( + void Function(RiverpodAnalysisError node) cb, + ) { + _onRiverpodAnalysisError.add(cb); + } + + final _onWidgetDeclaration = []; + void addWidgetDeclaration(void Function(WidgetDeclaration node) cb) { + _onWidgetDeclaration.add(cb); + } + + final _onStateDeclaration = []; + void addStateDeclaration(void Function(StateDeclaration node) cb) { + _onStateDeclaration.add(cb); + } + + final _onAccumulatedDependencyList = + []; + void addAccumulatedDependencyList( + void Function(AccumulatedDependencyList node) cb) { + _onAccumulatedDependencyList.add(cb); + } + + final _onIdentifierDependencies = []; + void addIdentifierDependencies( + void Function(IdentifierDependencies node) cb) { + _onIdentifierDependencies.add(cb); + } + + final _onNamedTypeDependencies = []; + void addNamedTypeDependencies(void Function(NamedTypeDependencies node) cb) { + _onNamedTypeDependencies.add(cb); + } + + final _onDependenciesAnnotation = []; + void addDependenciesAnnotation( + void Function(DependenciesAnnotation node) cb) { + _onDependenciesAnnotation.add(cb); + } + + final _onFunctionalProviderDeclaration = + []; + void addFunctionalProviderDeclaration( + void Function(FunctionalProviderDeclaration node) cb) { + _onFunctionalProviderDeclaration.add(cb); + } + + final _onLegacyProviderDeclaration = + []; + void addLegacyProviderDeclaration( + void Function(LegacyProviderDeclaration node) cb) { + _onLegacyProviderDeclaration.add(cb); + } + + final _onClassBasedProviderDeclaration = + []; + void addClassBasedProviderDeclaration( + void Function(ClassBasedProviderDeclaration node) cb) { + _onClassBasedProviderDeclaration.add(cb); + } + + final _onGeneratorProviderDeclaration = + []; + void addGeneratorProviderDeclaration( + void Function(GeneratorProviderDeclaration node) cb) { + _onGeneratorProviderDeclaration.add(cb); + } + + final _onProviderIdentifier = []; + void addProviderIdentifier(void Function(ProviderIdentifier node) cb) { + _onProviderIdentifier.add(cb); + } + + final _onRiverpodAnnotation = []; + void addRiverpodAnnotation(void Function(RiverpodAnnotation node) cb) { + _onRiverpodAnnotation.add(cb); + } + + final _onProviderListenableExpression = + []; + void addProviderListenableExpression( + void Function(ProviderListenableExpression node) cb) { + _onProviderListenableExpression.add(cb); + } + + final _onRefInvocation = []; + void addRefInvocation(void Function(RefInvocation node) cb) { + _onRefInvocation.add(cb); + } + + final _onWidgetRefInvocation = []; + void addWidgetRefInvocation(void Function(WidgetRefInvocation node) cb) { + _onWidgetRefInvocation.add(cb); + } + + final _onProviderOverrideExpression = + []; + void addProviderOverrideExpression( + void Function(ProviderOverrideExpression node) cb) { + _onProviderOverrideExpression.add(cb); + } + + final _onProviderOverrideList = []; + void addProviderOverrideList(void Function(ProviderOverrideList node) cb) { + _onProviderOverrideList.add(cb); + } + + final _onProviderContainerInstanceCreationExpression = + []; + void addProviderContainerInstanceCreationExpression( + void Function(ProviderContainerInstanceCreationExpression node) cb) { + _onProviderContainerInstanceCreationExpression.add(cb); + } + + final _onProviderScopeInstanceCreationExpression = + []; + void addProviderScopeInstanceCreationExpression( + void Function(ProviderScopeInstanceCreationExpression node) cb) { + _onProviderScopeInstanceCreationExpression.add(cb); + } +} + +class _RiverpodAstRegistryVisitor extends RecursiveRiverpodAstVisitor { + _RiverpodAstRegistryVisitor(this._registry); + + final RiverpodAstRegistry _registry; + + void _runSubscriptions( + R value, + List subscriptions, + ) { + for (final sub in subscriptions) { + try { + sub(value); + } catch (e, stack) { + Zone.current.handleUncaughtError(e, stack); + } + } + } + + @override + void visitWidgetDeclaration(WidgetDeclaration node) { + super.visitWidgetDeclaration(node); + _runSubscriptions( + node, + _registry._onWidgetDeclaration, + ); + } + + @override + void visitStateDeclaration(StateDeclaration node) { + super.visitStateDeclaration(node); + _runSubscriptions( + node, + _registry._onStateDeclaration, + ); + } + + @override + void visitAccumulatedDependencyList(AccumulatedDependencyList node) { + super.visitAccumulatedDependencyList(node); + _runSubscriptions( + node, + _registry._onAccumulatedDependencyList, + ); + } + + @override + void visitIdentifierDependencies(IdentifierDependencies node) { + super.visitIdentifierDependencies(node); + _runSubscriptions( + node, + _registry._onIdentifierDependencies, + ); + } + + @override + void visitNamedTypeDependencies(NamedTypeDependencies node) { + super.visitNamedTypeDependencies(node); + _runSubscriptions( + node, + _registry._onNamedTypeDependencies, + ); + } + + @override + void visitDependenciesAnnotation(DependenciesAnnotation node) { + super.visitDependenciesAnnotation(node); + _runSubscriptions( + node, + _registry._onDependenciesAnnotation, + ); + } + + @override + void visitFunctionalProviderDeclaration(FunctionalProviderDeclaration node) { + super.visitFunctionalProviderDeclaration(node); + _runSubscriptions( + node, + _registry._onFunctionalProviderDeclaration, + ); + } + + @override + void visitLegacyProviderDeclaration(LegacyProviderDeclaration node) { + super.visitLegacyProviderDeclaration(node); + _runSubscriptions( + node, + _registry._onLegacyProviderDeclaration, + ); + } + + @override + void visitClassBasedProviderDeclaration(ClassBasedProviderDeclaration node) { + super.visitClassBasedProviderDeclaration(node); + _runSubscriptions( + node, + _registry._onClassBasedProviderDeclaration, + ); + } + + @override + void visitGeneratorProviderDeclaration(GeneratorProviderDeclaration node) { + super.visitGeneratorProviderDeclaration(node); + _runSubscriptions( + node, + _registry._onGeneratorProviderDeclaration, + ); + } + + @override + void visitProviderIdentifier(ProviderIdentifier node) { + super.visitProviderIdentifier(node); + _runSubscriptions( + node, + _registry._onProviderIdentifier, + ); + } + + @override + void visitRiverpodAnnotation(RiverpodAnnotation node) { + super.visitRiverpodAnnotation(node); + _runSubscriptions( + node, + _registry._onRiverpodAnnotation, + ); + } + + @override + void visitProviderListenableExpression(ProviderListenableExpression node) { + super.visitProviderListenableExpression(node); + _runSubscriptions( + node, + _registry._onProviderListenableExpression, + ); + } + + @override + void visitRefInvocation(RefInvocation node) { + super.visitRefInvocation(node); + _runSubscriptions( + node, + _registry._onRefInvocation, + ); + } + + @override + void visitWidgetRefInvocation(WidgetRefInvocation node) { + super.visitWidgetRefInvocation(node); + _runSubscriptions( + node, + _registry._onWidgetRefInvocation, + ); + } + + @override + void visitProviderOverrideExpression(ProviderOverrideExpression node) { + super.visitProviderOverrideExpression(node); + _runSubscriptions( + node, + _registry._onProviderOverrideExpression, + ); + } + + @override + void visitProviderOverrideList(ProviderOverrideList node) { + super.visitProviderOverrideList(node); + _runSubscriptions( + node, + _registry._onProviderOverrideList, + ); + } + + @override + void visitProviderContainerInstanceCreationExpression( + ProviderContainerInstanceCreationExpression node) { + super.visitProviderContainerInstanceCreationExpression(node); + _runSubscriptions( + node, + _registry._onProviderContainerInstanceCreationExpression, + ); + } + + @override + void visitProviderScopeInstanceCreationExpression( + ProviderScopeInstanceCreationExpression node) { + super.visitProviderScopeInstanceCreationExpression(node); + _runSubscriptions( + node, + _registry._onProviderScopeInstanceCreationExpression, + ); + } +} + +// ignore_for_file: type=lint diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/annotation.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/annotation.dart new file mode 100644 index 000000000..3420d9360 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/annotation.dart @@ -0,0 +1,202 @@ +part of '../nodes.dart'; + +extension RiverpodAnnotatedAnnotatedNodeOfX on AnnotatedNode { + static final _cache = Expando>(); + + RiverpodAnnotation? get riverpod { + return _cache.upsert(this, () { + return metadata.map((e) => e.riverpod).nonNulls.firstOrNull; + }); + } +} + +@_ast +extension RiverpodAnnotatedAnnotatedNodeX on Annotation { + static final _cache = Expando>(); + + RiverpodAnnotation? get riverpod { + return _cache.upsert(this, () { + final elementAnnotation = this.elementAnnotation; + final element = this.element; + if (element == null || elementAnnotation == null) return null; + if (element is! ExecutableElement || + !riverpodType.isExactlyType(element.returnType)) { + // The annotation is not an @Riverpod + return null; + } + + final riverpodAnnotationElement = RiverpodAnnotationElement._parse( + elementAnnotation, + ); + if (riverpodAnnotationElement == null) return null; + + final dependenciesNode = arguments?.named('dependencies'); + + final dependencyList = dependenciesNode.let( + (e) => e.expression.providerDependencyList, + ); + + final AstNode? retryNode = arguments?.named('retry')?.expression; + if (retryNode is! SimpleIdentifier?) { + errorReporter( + RiverpodAnalysisError( + 'The "retry" argument must be a variable.', + targetNode: retryNode, + code: RiverpodAnalysisErrorCode.invalidRetryArgument, + ), + ); + } + + return RiverpodAnnotation._( + node: this, + element: riverpodAnnotationElement, + keepAliveNode: arguments?.named('keepAlive'), + dependenciesNode: dependenciesNode, + dependencyList: dependencyList, + retryNode: retryNode as SimpleIdentifier?, + ); + }); + } +} + +final class RiverpodAnnotation { + RiverpodAnnotation._({ + required this.node, + required this.element, + required this.keepAliveNode, + required this.dependenciesNode, + required this.dependencyList, + required this.retryNode, + }); + + final Annotation node; + final RiverpodAnnotationElement element; + final NamedExpression? keepAliveNode; + final NamedExpression? dependenciesNode; + final ProviderDependencyList? dependencyList; + final SimpleIdentifier? retryNode; +} + +final class RiverpodAnnotationElement { + RiverpodAnnotationElement._({ + required this.keepAlive, + required this.dependencies, + required this.allTransitiveDependencies, + required this.element, + }); + + static final _cache = _Cache(); + + static RiverpodAnnotationElement? _parse(ElementAnnotation element) { + return _cache(element, () { + final type = element.element.cast()?.returnType; + if (type == null || !riverpodType.isExactlyType(type)) return null; + + final constant = element.computeConstantValue(); + if (constant == null) return null; + + final keepAlive = constant.getField('keepAlive'); + if (keepAlive == null) return null; + + final dependencies = constant.getField('dependencies'); + if (dependencies == null) return null; + + final dependencyList = dependencies.toDependencyList( + from: element.element, + ); + final allTransitiveDependencies = dependencyList == null + ? null + : { + ...dependencyList, + ...dependencyList.expand( + (e) => e.annotation.allTransitiveDependencies ?? const {}, + ), + }; + + return RiverpodAnnotationElement._( + keepAlive: keepAlive.toBoolValue()!, + element: element, + dependencies: dependencyList, + allTransitiveDependencies: allTransitiveDependencies, + ); + }); + } + + static RiverpodAnnotationElement? _of(Element element) { + return element.metadata.map(_parse).nonNulls.firstOrNull; + } + + final bool keepAlive; + final ElementAnnotation element; + final List? dependencies; + final Set? allTransitiveDependencies; +} + +extension MutationAnnotatedNodeOfX on AnnotatedNode { + static final _cache = Expando>(); + + MutationAnnotation? get mutation { + return _cache.upsert(this, () { + return metadata.map((e) => e.mutation).nonNulls.firstOrNull; + }); + } +} + +extension MutationAnnotationX on Annotation { + static final _cache = Expando>(); + + MutationAnnotation? get mutation { + return _cache.upsert(this, () { + final elementAnnotation = this.elementAnnotation; + final element = this.element; + if (element == null || elementAnnotation == null) return null; + if (element is! ExecutableElement || + !mutationType.isExactlyType(element.returnType)) { + // The annotation is not an @Mutation + return null; + } + + final mutationAnnotationElement = MutationAnnotationElement._parse( + elementAnnotation, + ); + if (mutationAnnotationElement == null) return null; + + return MutationAnnotation._( + node: this, + element: mutationAnnotationElement, + ); + }); + } +} + +final class MutationAnnotation { + MutationAnnotation._({required this.node, required this.element}); + + final Annotation node; + final MutationAnnotationElement element; +} + +final class MutationAnnotationElement { + MutationAnnotationElement._({ + required this.element, + }); + + static final _cache = _Cache(); + + static MutationAnnotationElement? _parse(ElementAnnotation element) { + return _cache(element, () { + final type = element.element.cast()?.returnType; + if (type == null || !mutationType.isExactlyType(type)) return null; + + return MutationAnnotationElement._( + element: element, + ); + }); + } + + static MutationAnnotationElement? _of(Element element) { + return element.metadata.map(_parse).nonNulls.firstOrNull; + } + + final ElementAnnotation element; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/dependencies.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/dependencies.dart new file mode 100644 index 000000000..474132174 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/dependencies.dart @@ -0,0 +1,501 @@ +part of '../nodes.dart'; + +extension on CollectionElement { + static final _cache = Expando>(); + + ProviderDependency? get providerDependency { + return _cache.upsert(this, () { + final that = this; + if (that is! Expression) { + errorReporter( + RiverpodAnalysisError( + 'if/for/spread operators as not supported.', + targetNode: that, + code: RiverpodAnalysisErrorCode.providerDependencyListParseError, + ), + ); + return null; + } + + if (that is! SimpleIdentifier) { + errorReporter( + RiverpodAnalysisError( + 'Only elements annotated with @riverpod are supported.', + targetNode: that, + code: RiverpodAnalysisErrorCode.providerDependencyListParseError, + ), + ); + return null; + } + + final dependencyElement = that.staticElement; + if (dependencyElement is FunctionElement) { + final dependencyProvider = FunctionalProviderDeclarationElement._parse( + dependencyElement, + ); + + if (dependencyProvider != null) { + return ProviderDependency._(provider: dependencyProvider, node: that); + } + + errorReporter( + RiverpodAnalysisError( + 'The dependency $that is not a function annotated with @riverpod', + targetNode: that, + code: RiverpodAnalysisErrorCode.providerDependencyListParseError, + ), + ); + return null; + } + + if (dependencyElement is ClassElement) { + final dependencyProvider = ClassBasedProviderDeclarationElement._parse( + dependencyElement, + ); + + if (dependencyProvider != null) { + return ProviderDependency._(provider: dependencyProvider, node: that); + } + + errorReporter( + RiverpodAnalysisError( + 'The dependency $that is not a class annotated with @riverpod', + targetNode: that, + code: RiverpodAnalysisErrorCode.providerDependencyListParseError, + ), + ); + return null; + } + + errorReporter( + RiverpodAnalysisError( + 'Only elements annotated with @riverpod are supported.', + targetNode: that, + code: RiverpodAnalysisErrorCode.providerDependencyListParseError, + ), + ); + return null; + }); + } +} + +final class ProviderDependency { + ProviderDependency._({ + required this.node, + required this.provider, + }); + + final CollectionElement node; + final GeneratorProviderDeclarationElement provider; +} + +extension on Expression { + static final _cache = Expando>(); + + ProviderDependencyList? get providerDependencyList { + return _cache.upsert(this, () { + final that = this; + // explicit null, count as valid value (no dependencies) + if (that is NullLiteral) { + return ProviderDependencyList._(node: null, values: null); + } + + if (that is! ListLiteral) { + errorReporter( + RiverpodAnalysisError( + 'Only list literals (using []) as supported.', + targetNode: that, + code: RiverpodAnalysisErrorCode.providerDependencyListParseError, + ), + ); + return null; + } + + return ProviderDependencyList._( + node: that, + values: that.elements + .map((e) => e.providerDependency) + .whereType() + .toList(), + ); + }); + } +} + +final class ProviderDependencyList { + ProviderDependencyList._({ + required this.node, + required this.values, + }); + + final ListLiteral? node; + final List? values; +} + +extension on DartObject { + /// An element in `@Riverpod(dependencies: [a, b])` or equivalent. + GeneratorProviderDeclarationElement? toDependency({ + required Element? from, + }) { + final functionType = toFunctionValue(); + if (functionType != null) { + final provider = FunctionalProviderDeclarationElement._parse( + functionType, + ); + + if (provider != null) return provider; + } + + final type = toTypeValue(); + if (type != null) { + final provider = ClassBasedProviderDeclarationElement._parse( + type.element! as ClassElement, + ); + + if (provider != null) return provider; + } + + errorReporter( + RiverpodAnalysisError( + 'Unsupported dependency "${functionType ?? type ?? this}". ' + 'Only functions and classes annotated by @riverpod are supported.', + targetElement: from, + code: RiverpodAnalysisErrorCode.providerDependencyListParseError, + ), + ); + return null; + } + + /// The list passed to `@Riverpod(dependencies: [a, b])` or equivalent. + List? toDependencyList({ + required Element? from, + }) { + final list = toListValue(); + if (list == null) { + return null; + } + + final values = + list.map((e) => e.toDependency(from: from)).nonNulls.toList(); + + // If any dependency failed to parse, return null. + // Errors should already have been reported + if (values.length != list.length) return null; + + return values; + } +} + +final class AccumulatedDependency { + AccumulatedDependency._({required this.node, required this.provider}); + + final AstNode? node; + final GeneratorProviderDeclarationElement provider; +} + +sealed class AccumulatedDependencyNode {} + +final class AccumulatedDependencyList { + AccumulatedDependencyList._({ + required this.node, + required this.riverpod, + required this.dependencies, + required this.widgetDependencies, + required this.overrides, + }) : parent = node.ancestors + .map((e) => e.accumulatedDependencies) + .nonNulls + .firstOrNull; + + final AstNode node; + final AccumulatedDependencyList? parent; + final GeneratorProviderDeclaration? riverpod; + final DependenciesAnnotation? dependencies; + final List? widgetDependencies; + final ProviderScopeInstanceCreationExpression? overrides; + + Iterable? get allDependencies { + final dependencies = this.dependencies?.dependencies; + final riverpod = this.riverpod?.annotation.dependencyList; + final widgetDependencies = this.widgetDependencies; + + if (dependencies == null && + riverpod == null && + widgetDependencies == null) { + return null; + } + + final dependenciesValues = dependencies?.values?.map( + (e) => AccumulatedDependency._( + node: e.node, + provider: e.provider, + ), + ); + final riverpodValues = riverpod?.values?.map( + (e) => AccumulatedDependency._( + node: e.node, + provider: e.provider, + ), + ); + final dependenciesElementValues = widgetDependencies?.map( + (provider) => AccumulatedDependency._( + node: null, + provider: provider, + ), + ); + + return (dependenciesValues ?? const []) + .followedBy(riverpodValues ?? const []) + .followedBy(dependenciesElementValues ?? const []); + } + + Iterable get _overridesIncludingParents sync* { + if (overrides?.overrides?.overrides case final overrides?) { + yield* overrides; + } + + if (parent case final parent?) yield* parent._overridesIncludingParents; + } + + late final Set _allOverrides = + _overridesIncludingParents + // If we are overriding only one part of a family, + // we can't guarantee that later reads will point to the override. + // So we ignore those overrides when considering if a provider is + // safe to use. + .where((e) => e.familyArguments == null) + .map((e) => e.provider?.providerElement) + .nonNulls + .toSet(); + + bool isSafelyAccessibleAfterOverrides( + GeneratorProviderDeclarationElement provider, + ) { + final dependencies = provider.annotation.dependencies; + if (dependencies == null) return true; + + if (_allOverrides.contains(provider)) return true; + + // If the provider has an empty list of dependencies, and it is not overridden, + // then it is not safe to use. + if (dependencies.isEmpty) return false; + + return dependencies.every(isSafelyAccessibleAfterOverrides); + } +} + +@_ast +extension AccumulatedDependenciesX on AstNode { + AccumulatedDependencyList? get accumulatedDependencies { + final that = this; + switch (that) { + case InstanceCreationExpression(): + return that.accumulatedDependencies; + case AnnotatedNode(): + return that.accumulatedDependencies; + default: + return null; + } + } +} + +extension on InstanceCreationExpression { + static final _cache = Expando>(); + + AccumulatedDependencyList? get accumulatedDependencies { + return _cache.upsert(this, () { + final providerScope = this.providerScope; + if (providerScope == null) return null; + + return AccumulatedDependencyList._( + node: this, + overrides: providerScope, + dependencies: null, + riverpod: null, + widgetDependencies: null, + ); + }); + } +} + +extension on AnnotatedNode { + static final _cache = Expando>(); + + AccumulatedDependencyList? get accumulatedDependencies { + return _cache.upsert(this, () { + final provider = cast()?.provider; + // Have State inherit dependencies from its widget + final state = cast()?.state; + + if (provider == null && + dependencies == null && + state == null && + // Always initialize root declarations, + // to handle cases where a method in a class has @Dependencies + // but the class itself does not. + this is! CompilationUnitMember) { + return null; + } + + return AccumulatedDependencyList._( + node: this, + overrides: null, + dependencies: dependencies, + riverpod: provider, + widgetDependencies: state?.widget?.dependencies?.dependencies, + ); + }); + } +} + +class IdentifierDependencies { + IdentifierDependencies._({required this.node, required this.dependencies}); + + final Identifier node; + final DependenciesAnnotationElement dependencies; +} + +@_ast +extension IdentifierDependenciesX on Identifier { + static final _cache = Expando>(); + + IdentifierDependencies? get identifierDependencies { + return _cache.upsert(this, () { + final staticElement = this.staticElement; + if (staticElement == null) return null; + + final dependencies = DependenciesAnnotationElement._of(staticElement); + if (dependencies == null) return null; + + return IdentifierDependencies._(node: this, dependencies: dependencies); + }); + } +} + +class NamedTypeDependencies { + NamedTypeDependencies._({ + required this.node, + required this.dependencies, + }); + + final NamedType node; + final DependenciesAnnotationElement dependencies; +} + +@_ast +extension NamedTypeDependenciesX on NamedType { + static final _cache = Expando>(); + + NamedTypeDependencies? get typeAnnotationDependencies { + return _cache.upsert(this, () { + final staticElement = type?.element; + if (staticElement == null) return null; + + final dependencies = DependenciesAnnotationElement._of(staticElement); + if (dependencies == null) return null; + + return NamedTypeDependencies._( + node: this, + dependencies: dependencies, + ); + }); + } +} + +extension DependenciesAnnotatedAnnotatedNodeOfX on AnnotatedNode { + static final _cache = Expando>(); + + DependenciesAnnotation? get dependencies { + return _cache.upsert(this, () { + return metadata.map((e) => e.dependencies).nonNulls.firstOrNull; + }); + } +} + +@_ast +extension DependenciesAnnotatedAnnotatedNodeX on Annotation { + static final _cache = Expando>(); + + DependenciesAnnotation? get dependencies { + return _cache.upsert(this, () { + final elementAnnotation = this.elementAnnotation; + final element = this.element; + if (element == null || elementAnnotation == null) return null; + if (element is! ExecutableElement || + !dependenciesType.isExactlyType(element.returnType)) { + // The annotation is not an @Dependencies + return null; + } + + final dependenciesElement = DependenciesAnnotationElement._parse( + elementAnnotation, + ); + if (dependenciesElement == null) return null; + + final dependenciesNode = arguments?.positional(0); + // Required argument missing. There should be a compilation error already. + if (dependenciesNode == null) return null; + + final dependencyList = dependenciesNode.providerDependencyList; + // No valid dependencies arg found. There should already be an error reported. + if (dependencyList == null) return null; + + return DependenciesAnnotation._( + node: this, + dependencies: dependencyList, + dependenciesNode: dependenciesNode, + element: dependenciesElement, + ); + }); + } +} + +final class DependenciesAnnotation { + DependenciesAnnotation._({ + required this.dependencies, + required this.node, + required this.dependenciesNode, + required this.element, + }); + + final Annotation node; + final ProviderDependencyList dependencies; + final Expression dependenciesNode; + final DependenciesAnnotationElement element; +} + +final class DependenciesAnnotationElement { + DependenciesAnnotationElement._({ + required this.dependencies, + required this.element, + }); + + static final _cache = _Cache(); + + static DependenciesAnnotationElement? _parse(ElementAnnotation element) { + return _cache(element, () { + final type = element.element.cast()?.returnType; + if (type == null || !dependenciesType.isExactlyType(type)) return null; + + final dependencies = + element.computeConstantValue()?.getField('dependencies'); + if (dependencies == null) return null; + + final dependencyList = dependencies.toDependencyList( + from: element.element, + ); + + return DependenciesAnnotationElement._( + element: element, + dependencies: dependencyList, + ); + }); + } + + static DependenciesAnnotationElement? _of(Element element) { + return element.metadata.map(_parse).nonNulls.firstOrNull; + } + + final ElementAnnotation element; + final List? dependencies; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/provider_for.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/provider_for.dart new file mode 100644 index 000000000..b1e23b3a5 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/provider_for.dart @@ -0,0 +1,23 @@ +part of '../nodes.dart'; + +@internal +(ProviderDeclarationElement?,)? parseProviderFor( + ElementAnnotation annotation, { + required Element? from, +}) { + final type = annotation.element.cast()?.returnType; + if (type == null || !providerForType.isExactlyType(type)) return null; + + final value = annotation.computeConstantValue()?.getField('value'); + if (value == null) return (null,); + + return (value.toDependency(from: from),); +} + +@internal +(ProviderDeclarationElement?,)? parseFirstProviderFor(Element annotation) { + return annotation.metadata + .map((e) => parseProviderFor(e, from: annotation)) + .nonNulls + .firstOrNull; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/provider_listenable.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/provider_listenable.dart new file mode 100644 index 000000000..2c76d990e --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/provider_listenable.dart @@ -0,0 +1,46 @@ +part of '../nodes.dart'; + +@_ast +extension ProviderListenableExpressionX on Expression { + static final _cache = Expando>(); + + ProviderListenableExpression? get providerListenable { + return _cache.upsert(this, () { + final returnType = staticType; + if (returnType == null) return null; + if (!providerListenableType.isAssignableFromType(returnType)) return null; + + final parseResult = _parsesProviderExpression(this); + if (parseResult == null) return null; + final ( + :provider, + :providerPrefix, + :familyArguments, + ) = parseResult; + + return ProviderListenableExpression._( + node: this, + provider: provider, + providerPrefix: providerPrefix, + familyArguments: familyArguments, + ); + }); + } +} + +final class ProviderListenableExpression { + ProviderListenableExpression._({ + required this.node, + required this.provider, + required this.providerPrefix, + required this.familyArguments, + }); + + final Expression node; + final SimpleIdentifier? providerPrefix; + final ProviderIdentifier? provider; + + /// If [provider] is a provider with arguments (family), represents the arguments + /// passed to the provider. + final ArgumentList? familyArguments; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/provider_or_family.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/provider_or_family.dart new file mode 100644 index 000000000..84c62ee84 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/provider_or_family.dart @@ -0,0 +1,93 @@ +part of '../nodes.dart'; + +({ + ProviderIdentifier? provider, + SimpleIdentifier? providerPrefix, + ArgumentList? familyArguments, +})? _parsesProviderExpression(Expression? expression) { + ProviderIdentifier? provider; + SimpleIdentifier? providerPrefix; + ArgumentList? familyArguments; + + void parseExpression(Expression? expression) { + // Can be reached when the code contains syntax errors + if (expression == null) return; + if (expression is SimpleIdentifier) { + // watch(expression) + provider = expression.provider; + } else if (expression is FunctionExpressionInvocation) { + // watch(expression()) + familyArguments = expression.argumentList; + parseExpression(expression.function); + } else if (expression is MethodInvocation) { + // watch(expression.method()) + parseExpression(expression.target); + } else if (expression is PrefixedIdentifier) { + // watch(expression.modifier) + final element = expression.prefix.staticElement; + if (element is PrefixElement) { + providerPrefix = expression.prefix; + parseExpression(expression.identifier); + } else { + parseExpression(expression.prefix); + } + } else if (expression is IndexExpression) { + // watch(expression[]) + parseExpression(expression.target); + } else if (expression is PropertyAccess) { + // watch(expression.property) + parseExpression(expression.target); + } + } + + parseExpression(expression); + + return ( + provider: provider, + providerPrefix: providerPrefix, + familyArguments: provider != null ? familyArguments : null, + ); +} + +final class ProviderOrFamilyExpression { + ProviderOrFamilyExpression._({ + required this.node, + required this.provider, + required this.providerPrefix, + required this.familyArguments, + }); + + static ProviderOrFamilyExpression? _parse(Expression? expression) { + if (expression == null) return null; + + final returnType = expression.staticType; + if (returnType == null) return null; + if (!providerBaseType.isAssignableFromType(returnType) && + !familyType.isAssignableFromType(returnType)) { + return null; + } + + final parseResult = _parsesProviderExpression(expression); + if (parseResult == null) return null; + final ( + :provider, + :providerPrefix, + :familyArguments, + ) = parseResult; + + return ProviderOrFamilyExpression._( + node: expression, + provider: provider, + providerPrefix: providerPrefix, + familyArguments: familyArguments, + ); + } + + final Expression node; + final SimpleIdentifier? providerPrefix; + final ProviderIdentifier? provider; + + /// If [provider] is a provider with arguments (family), represents the arguments + /// passed to the provider. + final ArgumentList? familyArguments; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/providers/function.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/function.dart new file mode 100644 index 000000000..0c903c5f5 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/function.dart @@ -0,0 +1,110 @@ +part of '../../nodes.dart'; + +@_ast +extension FunctionalProviderDeclarationX on FunctionDeclaration { + static final _cache = Expando>(); + + FunctionalProviderDeclaration? get provider { + return _cache.upsert(this, () { + final element = declaredElement; + if (element == null) return null; + + final riverpod = this.riverpod; + if (riverpod == null) return null; + + final providerElement = FunctionalProviderDeclarationElement._parse( + element, + ); + if (providerElement == null) return null; + + final createdTypeNode = returnType; + final exposedTypeNode = _computeExposedType( + createdTypeNode, + root.cast()!, + ); + if (exposedTypeNode == null) { + // Error already reported + return null; + } + + return FunctionalProviderDeclaration._( + name: name, + node: this, + providerElement: providerElement, + annotation: riverpod, + createdTypeNode: createdTypeNode, + exposedTypeNode: exposedTypeNode, + valueTypeNode: _getValueType(createdTypeNode, element.library), + ); + }); + } +} + +final class FunctionalProviderDeclaration extends GeneratorProviderDeclaration { + FunctionalProviderDeclaration._({ + required this.name, + required this.node, + required this.providerElement, + required this.annotation, + required this.createdTypeNode, + required this.exposedTypeNode, + required this.valueTypeNode, + }); + + @override + final Token name; + + @override + final FunctionDeclaration node; + @override + final FunctionalProviderDeclarationElement providerElement; + @override + final RiverpodAnnotation annotation; + @override + final TypeAnnotation? createdTypeNode; + @override + final TypeAnnotation? valueTypeNode; + @override + final SourcedType exposedTypeNode; +} + +class FunctionalProviderDeclarationElement + extends GeneratorProviderDeclarationElement { + FunctionalProviderDeclarationElement._({ + required this.name, + required this.annotation, + required this.element, + }); + + static final _cache = _Cache(); + + static FunctionalProviderDeclarationElement? _parse( + ExecutableElement element, + ) { + return _cache(element, () { + final riverpodAnnotation = RiverpodAnnotationElement._of(element); + if (riverpodAnnotation == null) return null; + + return FunctionalProviderDeclarationElement._( + name: element.name, + annotation: riverpodAnnotation, + element: element, + ); + }); + } + + @override + bool get isScoped => super.isScoped || element.isExternal; + + @override + bool get isFamily { + return element.parameters.length > 1 || element.typeParameters.isNotEmpty; + } + + @override + final RiverpodAnnotationElement annotation; + @override + final String name; + @override + final ExecutableElement element; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/providers/identifiers.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/identifiers.dart new file mode 100644 index 000000000..c9ce8aa84 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/identifiers.dart @@ -0,0 +1,38 @@ +part of '../../nodes.dart'; + +@_ast +extension ProviderIdentifierX on SimpleIdentifier { + static final _cache = Expando>(); + + ProviderIdentifier? get provider { + return _cache.upsert(this, () { + final element = staticElement; + if (element is! PropertyAccessorElement) return null; + final variable = element.variable2; + if (variable == null) return null; + + final providerFor = parseFirstProviderFor(variable); + + ProviderDeclarationElement? providerElement; + if (providerFor != null) { + providerElement = providerFor.$1; + } else { + providerElement = LegacyProviderDeclarationElement._parse(variable); + } + + if (providerElement == null) return null; + + return ProviderIdentifier._( + node: this, + providerElement: providerElement, + ); + }); + } +} + +final class ProviderIdentifier { + ProviderIdentifier._({required this.node, required this.providerElement}); + + final SimpleIdentifier node; + final ProviderDeclarationElement providerElement; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/providers/legacy.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/legacy.dart new file mode 100644 index 000000000..489e951ca --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/legacy.dart @@ -0,0 +1,299 @@ +part of '../../nodes.dart'; + +final class LegacyProviderDependencies { + LegacyProviderDependencies._({ + required this.dependencies, + required this.node, + }); + + static LegacyProviderDependencies? _parse(NamedExpression? dependenciesNode) { + if (dependenciesNode == null) return null; + + final value = dependenciesNode.expression; + + List? dependencies; + if (value is ListLiteral) { + dependencies = + value.elements.map(LegacyProviderDependency._parse).toList(); + } + + return LegacyProviderDependencies._( + node: dependenciesNode, + dependencies: dependencies, + ); + } + + final List? dependencies; + final NamedExpression node; +} + +final class LegacyProviderDependency { + LegacyProviderDependency._({ + required this.node, + required this.provider, + }); + + factory LegacyProviderDependency._parse(CollectionElement node) { + final provider = + node.cast().let(ProviderOrFamilyExpression._parse); + + return LegacyProviderDependency._( + node: node, + provider: provider, + ); + } + + final CollectionElement node; + final ProviderOrFamilyExpression? provider; +} + +@_ast +extension LegacyProviderDeclarationX on VariableDeclaration { + static final _cache = Expando>(); + + LegacyProviderDeclaration? get provider { + return _cache.upsert(this, () { + final element = declaredElement; + if (element == null) return null; + + final providerElement = LegacyProviderDeclarationElement._parse(element); + if (providerElement == null) return null; + + final initializer = this.initializer; + ArgumentList? arguments; + late SyntacticEntity provider; + SimpleIdentifier? autoDisposeModifier; + SimpleIdentifier? familyModifier; + TypeArgumentList? typeArguments; + if (initializer is InstanceCreationExpression) { + // Provider((ref) => ...) + + arguments = initializer.argumentList; + provider = initializer.constructorName.type.name2; + typeArguments = initializer.constructorName.type.typeArguments; + } else if (initializer is FunctionExpressionInvocation) { + // Provider.modifier() + + void decodeIdentifier(SimpleIdentifier identifier) { + switch (identifier.name) { + case 'autoDispose': + autoDisposeModifier = identifier; + case 'family': + familyModifier = identifier; + default: + provider = identifier; + } + } + + void decodeTarget(Expression? expression) { + if (expression is SimpleIdentifier) { + decodeIdentifier(expression); + } else if (expression is PrefixedIdentifier) { + decodeIdentifier(expression.identifier); + decodeIdentifier(expression.prefix); + } else { + throw UnsupportedError( + 'unknown expression "$expression" (${expression.runtimeType})', + ); + } + } + + final modifier = initializer.function; + if (modifier is! PropertyAccess) return null; + + decodeIdentifier(modifier.propertyName); + decodeTarget(modifier.target); + arguments = initializer.argumentList; + typeArguments = initializer.typeArguments; + } else { + // Invalid provider expression. + // Such as "final provider = variable;" + return null; + } + + final build = arguments.positionalArguments().firstOrNull; + if (build is! FunctionExpression) return null; + + final dependenciesElement = arguments + .namedArguments() + .firstWhereOrNull((e) => e.name.label.name == 'dependencies'); + final dependencies = + LegacyProviderDependencies._parse(dependenciesElement); + + return LegacyProviderDeclaration._( + name: name, + node: this, + build: build, + providerElement: providerElement, + argumentList: arguments, + typeArguments: typeArguments, + provider: provider, + autoDisposeModifier: autoDisposeModifier, + familyModifier: familyModifier, + dependencies: dependencies, + ); + }); + } +} + +final class LegacyProviderDeclaration implements ProviderDeclaration { + LegacyProviderDeclaration._({ + required this.name, + required this.node, + required this.build, + required this.typeArguments, + required this.providerElement, + required this.argumentList, + required this.provider, + required this.autoDisposeModifier, + required this.familyModifier, + required this.dependencies, + }); + + final LegacyProviderDependencies? dependencies; + + final FunctionExpression build; + final ArgumentList argumentList; + final SyntacticEntity provider; + final SimpleIdentifier? autoDisposeModifier; + final SimpleIdentifier? familyModifier; + final TypeArgumentList? typeArguments; + + @override + final LegacyProviderDeclarationElement providerElement; + + @override + final Token name; + + @override + final VariableDeclaration node; +} + +/// The class name for explicitly typed provider. +/// +/// Such as `FutureProvider` for `final provider = FutureProvider(...)`. +/// This is only about the type, and does not include autoDispose/family/... +enum LegacyProviderType { + /// Type for `ChangeNotifierProvider` + changeNotifierProvider, + + /// Type for `FutureProvider` + futureProvider, + + /// Type for `AsyncNotifierProvider` + asyncNotifierProvider, + + /// Type for `StreamProvider` + streamProvider, + + /// Type for `StreamNotifier` + streamNotifier, + + /// Type for `StateNotifierProvider` + stateNotifierProvider, + + /// Type for `StateProvider` + stateProvider, + + /// Type for `Provider` + provider, + + /// Type for `NotifierProvider` + notifierProvider; +} + +@internal +LegacyProviderType? parseLegacyProviderType(DartType type) { + if (!isFromRiverpod.isExactlyType(type) && + !isFromFlutterRiverpod.isExactlyType(type)) { + return null; + } + + final name = type.element?.name; + if (name == 'FutureProvider' || name == 'FutureProviderFamily') { + return LegacyProviderType.futureProvider; + } + if (name == 'StreamProvider' || name == 'StreamProviderFamily') { + return LegacyProviderType.streamProvider; + } + if (name == 'StreamNotifierProvider' || + name == 'StreamNotifierProviderFamily') { + return LegacyProviderType.streamNotifier; + } + if (name == 'StateProvider' || name == 'StateProviderFamily') { + return LegacyProviderType.stateProvider; + } + if (name == 'StateNotifierProvider' || + name == 'StateNotifierProviderFamily') { + return LegacyProviderType.stateNotifierProvider; + } + if (name == 'Provider' || name == 'ProviderFamily') { + return LegacyProviderType.provider; + } + if (name == 'NotifierProvider' || name == 'NotifierProviderFamily') { + return LegacyProviderType.notifierProvider; + } + if (name == 'AsyncNotifierProvider' || + name == 'AsyncNotifierProviderFamily') { + return LegacyProviderType.asyncNotifierProvider; + } + if (name == 'ChangeNotifierProvider' || + name == 'ChangeNotifierProviderFamily') { + return LegacyProviderType.changeNotifierProvider; + } + + return null; +} + +class LegacyProviderDeclarationElement implements ProviderDeclarationElement { + LegacyProviderDeclarationElement._({ + required this.name, + required this.element, + required this.familyElement, + required this.providerType, + }); + + static LegacyProviderDeclarationElement? _parse(VariableElement element) { + return _cache(element, () { + final type = element.type; + final providerType = parseLegacyProviderType(type); + // Not a legacy provider + if (providerType == null) return null; + + LegacyFamilyInvocationElement? familyElement; + if (familyType.isAssignableFromType(element.type)) { + final callFn = (element.type as InterfaceType).lookUpMethod2( + 'call', + element.library!, + )!; + final parameter = callFn.parameters.single; + + familyElement = LegacyFamilyInvocationElement._(parameter.type); + } + + return LegacyProviderDeclarationElement._( + name: element.name, + element: element, + familyElement: familyElement, + providerType: providerType, + ); + }); + } + + static final _cache = _Cache(); + + @override + final VariableElement element; + + @override + final String name; + + final LegacyFamilyInvocationElement? familyElement; + + final LegacyProviderType providerType; +} + +class LegacyFamilyInvocationElement { + LegacyFamilyInvocationElement._(this.parameterType); + final DartType parameterType; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/providers/notifier.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/notifier.dart new file mode 100644 index 000000000..dfe10e6aa --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/notifier.dart @@ -0,0 +1,310 @@ +part of '../../nodes.dart'; + +@_ast +extension ClassBasedProviderDeclarationX on ClassDeclaration { + static final _cache = Expando>(); + + ClassBasedProviderDeclaration? get provider { + return _cache.upsert(this, () { + final element = declaredElement; + if (element == null) return null; + + final riverpod = this.riverpod; + if (riverpod == null) return null; + + if (abstractKeyword != null) { + errorReporter( + RiverpodAnalysisError( + 'Classes annotated with @riverpod cannot be abstract.', + targetNode: this, + targetElement: declaredElement, + code: RiverpodAnalysisErrorCode.abstractNotifier, + ), + ); + } + + final constructors = members.whereType().toList(); + final defaultConstructor = constructors + .firstWhereOrNull((constructor) => constructor.name == null); + if (defaultConstructor == null && constructors.isNotEmpty) { + errorReporter( + RiverpodAnalysisError( + 'Classes annotated with @riverpod must have a default constructor.', + targetNode: this, + targetElement: declaredElement, + code: RiverpodAnalysisErrorCode.missingNotifierDefaultConstructor, + ), + ); + } + if (defaultConstructor != null && + defaultConstructor.parameters.parameters.any((e) => e.isRequired)) { + errorReporter( + RiverpodAnalysisError( + 'The default constructor of classes annotated with @riverpod ' + 'cannot have required parameters.', + targetNode: this, + targetElement: declaredElement, + code: RiverpodAnalysisErrorCode + .notifierDefaultConstructorHasRequiredParameters, + ), + ); + } + + final buildMethod = members + .whereType() + .firstWhereOrNull((method) => method.name.lexeme == 'build'); + if (buildMethod == null) { + errorReporter( + RiverpodAnalysisError( + 'No "build" method found. ' + 'Classes annotated with @riverpod must define a method named "build".', + targetNode: this, + code: RiverpodAnalysisErrorCode.missingNotifierBuild, + ), + ); + return null; + } + + final providerElement = ClassBasedProviderDeclarationElement._parse( + element, + ); + if (providerElement == null) return null; + + final createdTypeNode = buildMethod.returnType; + + final exposedTypeNode = _computeExposedType( + createdTypeNode, + root.cast()!, + ); + if (exposedTypeNode == null) { + // Error already reported + return null; + } + + final valueTypeNode = _getValueType(createdTypeNode, element.library); + final classBasedProviderDeclaration = ClassBasedProviderDeclaration._( + name: name, + node: this, + buildMethod: buildMethod, + providerElement: providerElement, + annotation: riverpod, + createdTypeNode: createdTypeNode, + exposedTypeNode: exposedTypeNode, + valueTypeNode: valueTypeNode, + ); + + return classBasedProviderDeclaration; + }); + } +} + +final class ClassBasedProviderDeclaration extends GeneratorProviderDeclaration { + ClassBasedProviderDeclaration._({ + required this.name, + required this.node, + required this.buildMethod, + required this.providerElement, + required this.annotation, + required this.createdTypeNode, + required this.exposedTypeNode, + required this.valueTypeNode, + }) : mutations = node.members + .whereType() + .map((e) => e.mutation) + .nonNulls + .toList(); + + @override + final Token name; + @override + final ClassDeclaration node; + @override + final ClassBasedProviderDeclarationElement providerElement; + @override + final RiverpodAnnotation annotation; + final MethodDeclaration buildMethod; + @override + final TypeAnnotation? createdTypeNode; + @override + final TypeAnnotation? valueTypeNode; + @override + final SourcedType exposedTypeNode; + + final List mutations; +} + +extension MutationMethodDeclarationX on MethodDeclaration { + static final _cache = _Cache(); + + Mutation? get mutation { + return _cache(this, () { + final element = declaredElement; + if (element == null) return null; + + final mutationElement = MutationElement._parse(element); + if (mutationElement == null) return null; + + if (isStatic) { + errorReporter( + RiverpodAnalysisError( + 'Mutations cannot be static.', + targetNode: this, + targetElement: element, + code: RiverpodAnalysisErrorCode.mutationIsStatic, + ), + ); + return null; + } + if (isAbstract) { + errorReporter( + RiverpodAnalysisError( + 'Mutations cannot be abstract.', + targetNode: this, + targetElement: element, + code: RiverpodAnalysisErrorCode.mutationIsAbstract, + ), + ); + return null; + } + + final expectedReturnType = thisOrAncestorOfType()! + .members + .whereType() + .firstWhereOrNull((e) => e.name.lexeme == 'build') + ?.returnType; + if (expectedReturnType == null) return null; + + final expectedValueType = _getValueType( + expectedReturnType, + element.library, + ); + if (expectedValueType == null) return null; + + final expectedType = + element.library.typeProvider.futureOrElement.instantiate( + typeArguments: [expectedValueType.type!], + nullabilitySuffix: NullabilitySuffix.none, + ); + + final actualType = element.returnType; + + final isAssignable = element.library.typeSystem.isAssignableTo( + actualType, + expectedType, + strictCasts: true, + ); + if (!isAssignable) { + errorReporter( + RiverpodAnalysisError( + 'The return type of mutations must match the type returned by the "build" method.', + targetNode: this, + targetElement: element, + code: RiverpodAnalysisErrorCode.mutationReturnTypeMismatch, + ), + ); + return null; + } + + final mutation = Mutation._( + node: this, + element: mutationElement, + ); + + return mutation; + }); + } +} + +final class Mutation { + Mutation._({ + required this.node, + required this.element, + }); + + String get name => node.name.lexeme; + final MethodDeclaration node; + final MutationElement element; +} + +class ClassBasedProviderDeclarationElement + extends GeneratorProviderDeclarationElement { + ClassBasedProviderDeclarationElement._({ + required this.name, + required this.annotation, + required this.buildMethod, + required this.element, + }); + + static final _cache = _Cache(); + + static ClassBasedProviderDeclarationElement? _parse(ClassElement element) { + return _cache(element, () { + final riverpodAnnotation = RiverpodAnnotationElement._of(element); + if (riverpodAnnotation == null) return null; + + final buildMethod = + element.methods.firstWhereOrNull((method) => method.name == 'build'); + + if (buildMethod == null) { + errorReporter( + RiverpodAnalysisError( + 'No "build" method found. ' + 'Classes annotated with @riverpod must define a method named "build".', + targetElement: element, + code: RiverpodAnalysisErrorCode.missingNotifierBuild, + ), + ); + + return null; + } + + return ClassBasedProviderDeclarationElement._( + name: element.name, + buildMethod: buildMethod, + element: element, + annotation: riverpodAnnotation, + ); + }); + } + + @override + bool get isFamily { + return buildMethod.parameters.isNotEmpty || + element.typeParameters.isNotEmpty; + } + + @override + final ClassElement element; + + @override + final String name; + + @override + final RiverpodAnnotationElement annotation; + + final ExecutableElement buildMethod; +} + +class MutationElement { + MutationElement._({ + required this.name, + required this.method, + }); + + static final _cache = _Cache(); + + static MutationElement? _parse(ExecutableElement element) { + return _cache(element, () { + final annotation = MutationAnnotationElement._of(element); + if (annotation == null) return null; + + return MutationElement._( + name: element.name, + method: element, + ); + }); + } + + final String name; + final ExecutableElement method; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/providers/providers.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/providers.dart new file mode 100644 index 000000000..4ca651d43 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/providers.dart @@ -0,0 +1,156 @@ +part of '../../nodes.dart'; + +sealed class ProviderDeclaration { + Token get name; + Declaration get node; + ProviderDeclarationElement get providerElement; +} + +sealed class ProviderDeclarationElement { + Element get element; + String get name; +} + +@_ast +extension GeneratorProviderDeclarationX on Declaration { + GeneratorProviderDeclaration? get provider { + final that = this; + switch (that) { + case ClassDeclaration(): + return ClassBasedProviderDeclarationX(that).provider; + case FunctionDeclaration(): + return FunctionalProviderDeclarationX(that).provider; + default: + return null; + } + } +} + +sealed class GeneratorProviderDeclaration extends ProviderDeclaration { + @override + GeneratorProviderDeclarationElement get providerElement; + RiverpodAnnotation get annotation; + + String get valueTypeDisplayString => valueTypeNode?.toSource() ?? 'Object?'; + String get exposedTypeDisplayString => exposedTypeNode?.source ?? 'Object?'; + String get createdTypeDisplayString { + final type = createdTypeNode?.type; + + if (type != null && + !type.isRaw && + (type.isDartAsyncFuture || type.isDartAsyncFutureOr)) { + return 'FutureOr<$valueTypeDisplayString>'; + } + + return createdTypeNode?.toSource() ?? 'Object?'; + } + + TypeAnnotation? get valueTypeNode; + SourcedType? get exposedTypeNode; + TypeAnnotation? get createdTypeNode; + + String computeProviderHash() { + final bytes = utf8.encode(node.toSource()); + final digest = sha1.convert(bytes); + return digest.toString(); + } +} + +sealed class GeneratorProviderDeclarationElement + implements ProviderDeclarationElement { + RiverpodAnnotationElement get annotation; + + /// Whether a provider has any form of parameter, be it function parameters + /// or type parameters. + bool get isFamily; + + bool get isScoped { + if (annotation.dependencies != null) return true; + + final that = this; + return that is ClassBasedProviderDeclarationElement && + that.buildMethod.isAbstract; + } + + bool get isAutoDispose => !annotation.keepAlive; +} + +typedef SourcedType = ({String? source, DartType dartType}); + +SourcedType? _computeExposedType( + TypeAnnotation? createdType, + CompilationUnit unit, +) { + final library = unit.declaredElement!.library; + + if (createdType == null) { + return ( + source: null, + dartType: library.typeProvider.dynamicType, + ); + } + + final createdDartType = createdType.type!; + if (createdDartType.isRaw) { + return ( + source: createdType.toSource(), + dartType: createdType.type!, + ); + } + + if (createdDartType.isDartAsyncFuture || + createdDartType.isDartAsyncFutureOr || + createdDartType.isDartAsyncStream) { + createdType as NamedType; + createdDartType as InterfaceType; + + final typeSource = createdType.toSource(); + if (typeSource != 'Future' && + typeSource != 'FutureOr' && + typeSource != 'Stream' && + !typeSource.startsWith('Future<') && + !typeSource.startsWith('FutureOr<') && + !typeSource.startsWith('Stream<')) { + throw UnsupportedError( + 'Returning a typedef of type Future/FutureOr/Stream is not supported\n' + 'The code that triggered this error is: $typeSource', + ); + } + + final valueTypeArg = createdType.typeArguments?.arguments.firstOrNull; + + final exposedDartType = unit.createdTypeToValueType( + createdDartType.typeArguments.first, + ); + if (exposedDartType == null) return null; + + return ( + source: valueTypeArg == null ? 'AsyncValue' : 'AsyncValue<$valueTypeArg>', + dartType: exposedDartType, + ); + } + + return ( + source: createdType.toSource(), + dartType: createdType.type!, + ); +} + +TypeAnnotation? _getValueType( + TypeAnnotation? createdType, + LibraryElement library, +) { + if (createdType == null) return null; + final dartType = createdType.type!; + if (dartType.isRaw) return createdType; + + if (dartType.isDartAsyncFuture || + dartType.isDartAsyncFutureOr || + dartType.isDartAsyncStream) { + createdType as NamedType; + + return createdType.typeArguments?.arguments.firstOrNull; + } + + return createdType; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/ref_invocation.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/ref_invocation.dart new file mode 100644 index 000000000..e69a3f494 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/ref_invocation.dart @@ -0,0 +1,161 @@ +part of '../nodes.dart'; + +@_ast +extension RefInvocationX on MethodInvocation { + static final _cache = Expando>(); + + RefInvocation? get refInvocation { + return _cache.upsert(this, () { + final targetType = realTarget?.staticType; + if (targetType == null) return null; + + if (!isRiverpodRef(targetType)) return null; + + final function = this.function; + if (function is! SimpleIdentifier) return null; + final functionOwner = function.staticElement + .cast() + ?.declaration + .enclosingElement3; + + if (functionOwner == null || + // Since Ref is sealed, checking that the function is from the package:riverpod + // before checking its type skips iterating over the superclasses of an element + // if it's not from Riverpod. + !isFromRiverpod.isExactly(functionOwner) || + !refType.isAssignableFrom(functionOwner)) { + return null; + } + + switch (function.name) { + case 'watch': + return RefWatchInvocation._parse(this, function); + case 'read': + return RefReadInvocation._parse(this, function); + case 'listen': + return RefListenInvocation._parse(this, function); + default: + return null; + } + }); + } +} + +sealed class RefInvocation { + RefInvocation._({ + required this.node, + required this.function, + }); + + final MethodInvocation node; + final SimpleIdentifier function; +} + +/// A [RefInvocation] which interacts with a provider, inducing a dependency. +sealed class RefDependencyInvocation extends RefInvocation { + RefDependencyInvocation._({ + required super.node, + required super.function, + required this.listenable, + }) : super._(); + + /// The provider that is being interacted with. + final ProviderListenableExpression listenable; +} + +final class RefWatchInvocation extends RefDependencyInvocation { + RefWatchInvocation._({ + required super.node, + required super.function, + required super.listenable, + }) : super._(); + + static RefWatchInvocation? _parse( + MethodInvocation node, + SimpleIdentifier function, + ) { + assert( + function.name == 'watch', + 'Argument error, function is not a ref.watch function', + ); + + final providerListenableExpression = node.argumentList + .positionalArguments() + .singleOrNull + ?.providerListenable; + if (providerListenableExpression == null) return null; + + return RefWatchInvocation._( + node: node, + function: function, + listenable: providerListenableExpression, + ); + } +} + +final class RefReadInvocation extends RefDependencyInvocation { + RefReadInvocation._({ + required super.node, + required super.function, + required super.listenable, + }) : super._(); + + static RefReadInvocation? _parse( + MethodInvocation node, + SimpleIdentifier function, + ) { + assert( + function.name == 'read', + 'Argument error, function is not a ref.read function', + ); + + final providerListenableExpression = node.argumentList + .positionalArguments() + .singleOrNull + ?.providerListenable; + if (providerListenableExpression == null) return null; + + return RefReadInvocation._( + node: node, + function: function, + listenable: providerListenableExpression, + ); + } +} + +final class RefListenInvocation extends RefDependencyInvocation { + RefListenInvocation._({ + required super.node, + required super.function, + required this.listener, + required super.listenable, + }) : super._(); + + static RefListenInvocation? _parse( + MethodInvocation node, + SimpleIdentifier function, + ) { + assert( + function.name == 'listen', + 'Argument error, function is not a ref.listen function', + ); + + final positionalArgs = node.argumentList.positionalArguments().toList(); + + final listener = positionalArgs.elementAtOrNull(1); + if (listener == null) return null; + + final providerListenableExpression = + positionalArgs.firstOrNull?.providerListenable; + if (providerListenableExpression == null) return null; + + return RefListenInvocation._( + node: node, + function: function, + listener: listener, + listenable: providerListenableExpression, + ); + } + + final Expression listener; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/scopes/overrides.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/scopes/overrides.dart new file mode 100644 index 000000000..7caad7890 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/scopes/overrides.dart @@ -0,0 +1,82 @@ +part of '../../nodes.dart'; + +@_ast +extension ProviderOverrideExpressionX on CollectionElement { + static final _cache = Expando>(); + + ProviderOverrideExpression? get providerOverride { + return _cache.upsert(this, () { + final expression = this; + if (expression is! Expression) return null; + + final type = expression.staticType; + if (type == null || !overrideType.isAssignableFromType(type)) return null; + + final result = _parsesProviderExpression(expression); + + return ProviderOverrideExpression._( + node: expression, + familyArguments: result?.familyArguments, + provider: result?.provider, + providerPrefix: result?.providerPrefix, + ); + }); + } +} + +final class ProviderOverrideExpression { + ProviderOverrideExpression._({ + required this.node, + required this.provider, + required this.familyArguments, + required this.providerPrefix, + }); + + final CollectionElement node; + final ProviderIdentifier? provider; + final SimpleIdentifier? providerPrefix; + + /// If [provider] is a provider with arguments (family), represents the arguments + /// passed to the provider. + final ArgumentList? familyArguments; +} + +@_ast +extension ProviderOverrideListX on Expression { + static final _cache = Expando>(); + + ProviderOverrideList? get overrides { + return _cache.upsert(this, () { + final expression = this; + final type = staticType; + if (type == null || !type.isDartCoreList) return null; + + type as InterfaceType; + final valueType = type.typeArguments.single; + if (!overrideType.isAssignableFromType(valueType)) return null; + + List? overrides; + if (expression is ListLiteral) { + overrides = expression.elements + .map((e) => e.providerOverride) + .nonNulls + .toList(); + } + + return ProviderOverrideList._( + node: expression, + overrides: overrides, + ); + }); + } +} + +final class ProviderOverrideList { + ProviderOverrideList._({ + required this.node, + required this.overrides, + }); + + final Expression node; + final List? overrides; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/scopes/provider_container.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/scopes/provider_container.dart new file mode 100644 index 000000000..9549ae326 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/scopes/provider_container.dart @@ -0,0 +1,39 @@ +part of '../../nodes.dart'; + +@_ast +extension ProviderContainerInstanceCreationExpressionX + on InstanceCreationExpression { + static final _cache = + Expando>(); + + ProviderContainerInstanceCreationExpression? get providerContainer { + return _cache.upsert(this, () { + final createdType = constructorName.type.type; + if (createdType == null || + !providerContainerType.isExactlyType(createdType)) { + return null; + } + + final overrides = argumentList + .namedArguments() + .firstWhereOrNull((e) => e.name.label.name == 'overrides'); + + return ProviderContainerInstanceCreationExpression._( + node: this, + overrides: overrides?.expression.overrides, + ); + }); + } +} + +final class ProviderContainerInstanceCreationExpression { + ProviderContainerInstanceCreationExpression._({ + required this.node, + required this.overrides, + }); + + final InstanceCreationExpression node; + final ProviderOverrideList? overrides; + + late final NamedExpression? parent = node.argumentList.named('parent'); +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/scopes/provider_scope.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/scopes/provider_scope.dart new file mode 100644 index 000000000..50c7efb2a --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/scopes/provider_scope.dart @@ -0,0 +1,37 @@ +part of '../../nodes.dart'; + +@_ast +extension ProviderScopeInstanceCreationExpressionX + on InstanceCreationExpression { + static final _cache = + Expando>(); + + ProviderScopeInstanceCreationExpression? get providerScope { + return _cache.upsert(this, () { + final createdType = constructorName.type.type; + if (createdType == null || + !providerScopeType.isExactlyType(createdType)) { + return null; + } + + final overrides = argumentList + .namedArguments() + .firstWhereOrNull((e) => e.name.label.name == 'overrides'); + + return ProviderScopeInstanceCreationExpression._( + node: this, + overrides: overrides?.expression.overrides, + ); + }); + } +} + +final class ProviderScopeInstanceCreationExpression { + ProviderScopeInstanceCreationExpression._({ + required this.node, + required this.overrides, + }); + + final InstanceCreationExpression node; + final ProviderOverrideList? overrides; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/widget_ref_invocation.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/widget_ref_invocation.dart new file mode 100644 index 000000000..5bdf93f49 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/widget_ref_invocation.dart @@ -0,0 +1,205 @@ +part of '../nodes.dart'; + +@_ast +extension WidgetRefInvocationX on MethodInvocation { + static final _cache = Expando>(); + + WidgetRefInvocation? get widgetRefInvocation { + return _cache.upsert(this, () { + final targetType = realTarget?.staticType; + if (targetType == null) return null; + + // Since Ref is sealed, checking that the function is from the package:riverpod + // before checking its type skips iterating over the superclasses of an element + // if it's not from Riverpod. + if (!isFromFlutterRiverpod.isExactlyType(targetType) | + !widgetRefType.isAssignableFromType(targetType)) { + return null; + } + final function = this.function; + if (function is! SimpleIdentifier) return null; + final functionOwner = function.staticElement + .cast() + ?.declaration + .enclosingElement3; + + if (functionOwner == null || + // Since Ref is sealed, checking that the function is from the package:riverpod + // before checking its type skips iterating over the superclasses of an element + // if it's not from Riverpod. + !isFromFlutterRiverpod.isExactly(functionOwner) || + !widgetRefType.isAssignableFrom(functionOwner)) { + return null; + } + + switch (function.name) { + case 'watch': + return WidgetRefWatchInvocation._parse(this, function); + case 'read': + return WidgetRefReadInvocation._parse(this, function); + case 'listen': + return WidgetRefListenInvocation._parse(this, function); + case 'listenManual': + return WidgetRefListenManualInvocation._parse(this, function); + + default: + return null; + } + }); + } +} + +sealed class WidgetRefInvocation { + WidgetRefInvocation._({ + required this.node, + required this.function, + }); + + final MethodInvocation node; + final SimpleIdentifier function; +} + +/// A [RefInvocation] which interacts with a provider, inducing a dependency. +sealed class WidgetRefDependencyInvocation extends WidgetRefInvocation { + WidgetRefDependencyInvocation._({ + required super.node, + required super.function, + required this.listenable, + }) : super._(); + + /// The provider that is being interacted with. + final ProviderListenableExpression listenable; +} + +final class WidgetRefWatchInvocation extends WidgetRefDependencyInvocation { + WidgetRefWatchInvocation._({ + required super.node, + required super.function, + required super.listenable, + }) : super._(); + + static WidgetRefWatchInvocation? _parse( + MethodInvocation node, + SimpleIdentifier function, + ) { + assert( + function.name == 'watch', + 'Argument error, function is not a ref.watch function', + ); + + final providerListenableExpression = node.argumentList + .positionalArguments() + .singleOrNull + ?.providerListenable; + if (providerListenableExpression == null) return null; + + return WidgetRefWatchInvocation._( + node: node, + function: function, + listenable: providerListenableExpression, + ); + } +} + +final class WidgetRefReadInvocation extends WidgetRefDependencyInvocation { + WidgetRefReadInvocation._({ + required super.node, + required super.function, + required super.listenable, + }) : super._(); + + static WidgetRefReadInvocation? _parse( + MethodInvocation node, + SimpleIdentifier function, + ) { + assert( + function.name == 'read', + 'Argument error, function is not a ref.read function', + ); + + final providerListenableExpression = node.argumentList + .positionalArguments() + .singleOrNull + ?.providerListenable; + if (providerListenableExpression == null) return null; + + return WidgetRefReadInvocation._( + node: node, + function: function, + listenable: providerListenableExpression, + ); + } +} + +final class WidgetRefListenInvocation extends WidgetRefDependencyInvocation { + WidgetRefListenInvocation._({ + required super.node, + required super.function, + required super.listenable, + required this.listener, + }) : super._(); + + static WidgetRefListenInvocation? _parse( + MethodInvocation node, + SimpleIdentifier function, + ) { + assert( + function.name == 'listen', + 'Argument error, function is not a ref.listen function', + ); + + final positionalArgs = node.argumentList.positionalArguments().toList(); + final listener = positionalArgs.elementAtOrNull(1); + if (listener == null) return null; + + final providerListenableExpression = + positionalArgs.firstOrNull?.providerListenable; + if (providerListenableExpression == null) return null; + + return WidgetRefListenInvocation._( + node: node, + function: function, + listenable: providerListenableExpression, + listener: listener, + ); + } + + final Expression listener; +} + +final class WidgetRefListenManualInvocation + extends WidgetRefDependencyInvocation { + WidgetRefListenManualInvocation._({ + required super.node, + required super.function, + required super.listenable, + required this.listener, + }) : super._(); + + static WidgetRefListenManualInvocation? _parse( + MethodInvocation node, + SimpleIdentifier function, + ) { + assert( + function.name == 'listenManual', + 'Argument error, function is not a ref.listen function', + ); + + final positionalArgs = node.argumentList.positionalArguments().toList(); + final listener = positionalArgs.elementAtOrNull(1); + if (listener == null) return null; + + final providerListenableExpression = + positionalArgs.firstOrNull?.providerListenable; + if (providerListenableExpression == null) return null; + + return WidgetRefListenManualInvocation._( + node: node, + function: function, + listenable: providerListenableExpression, + listener: listener, + ); + } + + final Expression listener; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/widgets/state.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/widgets/state.dart new file mode 100644 index 000000000..e0e9e7082 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/widgets/state.dart @@ -0,0 +1,78 @@ +part of '../../nodes.dart'; + +ClassElement? _findStateWidget(ClassElement node) { + final type = node.supertype?.typeArguments.firstOrNull; + if (type == null) return null; + + // May be typed as `State` or `State`. + // The latter prevents from finding the widget class. + if (isFromFlutter.isExactlyType(type) || + isFromFlutterRiverpod.isExactlyType(type) || + isFromRiverpod.isExactlyType(type) || + isFromHooksRiverpod.isExactlyType(type)) { + return null; + } + + return type.element.cast(); +} + +final class StateDeclaration { + StateDeclaration._({ + required this.widget, + required this.element, + required this.node, + }); + + static StateDeclaration? _parse(ClassDeclaration node) { + final widget = node.declaredElement.let(_findStateWidget); + final element = node.declaredElement.let(StateDeclarationElement._parse); + + if (element == null) return null; + + return StateDeclaration._( + widget: widget.let(StatefulWidgetDeclarationElement._parse), + element: element, + node: node, + ); + } + + final ClassDeclaration node; + final StatefulWidgetDeclarationElement? widget; + final StateDeclarationElement element; + + WidgetDeclaration? findWidgetAst() { + final widgetName = widget?.element.name; + if (widgetName == null) return null; + + final unit = node.thisOrAncestorOfType()!; + + final widgetClass = unit.declarations + .whereType() + .firstWhereOrNull((e) => e.name.lexeme == widgetName); + + return widgetClass?.widget; + } +} + +final class StateDeclarationElement { + StateDeclarationElement._({ + required this.widget, + required this.element, + }); + + static final _cache = _Cache(); + + static StateDeclarationElement? _parse(ClassElement element) { + return _cache(element, () { + final widget = _findStateWidget(element); + + return StateDeclarationElement._( + element: element, + widget: widget.let(StatefulWidgetDeclarationElement._parse), + ); + }); + } + + final ClassElement element; + final StatefulWidgetDeclarationElement? widget; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/widgets/stateful_widget.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/widgets/stateful_widget.dart new file mode 100644 index 000000000..6bd15f065 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/widgets/stateful_widget.dart @@ -0,0 +1,101 @@ +part of '../../nodes.dart'; + +bool _isCoreType(DartType type) { + return isFromFlutter.isExactlyType(type) || + isFromFlutterRiverpod.isExactlyType(type) || + isFromRiverpod.isExactlyType(type) || + isFromHooksRiverpod.isExactlyType(type); +} + +ClassElement? _findStateFromReturnType(ClassElement node) { + final type = + node.methods.firstWhereOrNull((e) => e.name == 'createState')?.returnType; + + if (type == null) return null; + + // May be typed as `MyState createState()` or `State createState()`. + // The latter prevents from finding the state class. + if (_isCoreType(type)) return null; + + return type.element.cast(); +} + +ClassElement? _findStateWithMatchingGeneric(ClassElement node) { + for (final clazz in node.enclosingElement3.classes) { + final type = clazz.supertype; + if (type != null && isState(type) && _findStateWidget(clazz) == node) { + return clazz; + } + } + + return null; +} + +ClassElement? _findState(ClassElement node) { + return _findStateFromReturnType(node) ?? _findStateWithMatchingGeneric(node); +} + +final class StatefulWidgetDeclaration extends WidgetDeclaration { + StatefulWidgetDeclaration({ + required this.node, + required this.state, + required this.element, + }); + + static StatefulWidgetDeclaration? _parse(ClassDeclaration node) { + final stateClass = node.declaredElement.let(_findState); + final element = node.declaredElement.let( + StatefulWidgetDeclarationElement._parse, + ); + if (element == null) return null; + + return StatefulWidgetDeclaration( + node: node, + element: element, + state: stateClass.let(StateDeclarationElement._parse), + ); + } + + final StateDeclarationElement? state; + @override + final StatefulWidgetDeclarationElement element; + @override + final ClassDeclaration node; + + StateDeclaration? findStateAst() { + final stateName = state?.element.name; + if (stateName == null) return null; + + final unit = node.thisOrAncestorOfType()!; + + final stateClass = unit.declarations + .whereType() + .firstWhereOrNull((e) => e.name.lexeme == stateName); + + return stateClass?.state; + } +} + +final class StatefulWidgetDeclarationElement extends WidgetDeclarationElement { + StatefulWidgetDeclarationElement({ + required this.element, + required this.dependencies, + }); + + static final _cache = _Cache(); + + static StatefulWidgetDeclarationElement? _parse(ClassElement node) { + return _cache(node, () { + final dependencies = DependenciesAnnotationElement._of(node); + + return StatefulWidgetDeclarationElement( + element: node, + dependencies: dependencies, + ); + }); + } + + final ClassElement element; + @override + final DependenciesAnnotationElement? dependencies; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/widgets/stateless_widget.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/widgets/stateless_widget.dart new file mode 100644 index 000000000..956e56c98 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/widgets/stateless_widget.dart @@ -0,0 +1,45 @@ +part of '../../nodes.dart'; + +final class StatelessWidgetDeclaration extends WidgetDeclaration { + StatelessWidgetDeclaration._({ + required this.element, + required this.node, + }); + + static StatelessWidgetDeclaration? _parse(ClassDeclaration node) { + final element = node.declaredElement.let( + StatelessWidgetDeclarationElement._parse, + ); + if (element == null) return null; + + return StatelessWidgetDeclaration._( + element: element, + node: node, + ); + } + + @override + final StatelessWidgetDeclarationElement element; + + @override + final ClassDeclaration node; +} + +final class StatelessWidgetDeclarationElement extends WidgetDeclarationElement { + StatelessWidgetDeclarationElement._({required this.dependencies}); + + static final _cache = _Cache(); + + static StatelessWidgetDeclarationElement? _parse(ClassElement node) { + return _cache(node, () { + final dependencies = DependenciesAnnotationElement._of(node); + + return StatelessWidgetDeclarationElement._( + dependencies: dependencies, + ); + }); + } + + @override + final DependenciesAnnotationElement? dependencies; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/widgets/widget.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/widgets/widget.dart new file mode 100644 index 000000000..0e505a0f8 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/widgets/widget.dart @@ -0,0 +1,45 @@ +part of '../../nodes.dart'; + +@_ast +extension WidgetX on ClassDeclaration { + static final _cache1 = Expando>(); + + WidgetDeclaration? get widget { + return _cache1.upsert(this, () { + final type = extendsClause?.superclass.type; + if (type == null) return null; + + if (isStatelessWidget(type)) { + return StatelessWidgetDeclaration._parse(this); + } + + if (isStatefulWidget(type)) { + return StatefulWidgetDeclaration._parse(this); + } + + return null; + }); + } + + static final _cache2 = Expando>(); + + StateDeclaration? get state { + return _cache2.upsert(this, () { + final type = extendsClause?.superclass.type; + if (type == null) return null; + + if (isState(type)) return StateDeclaration._parse(this); + + return null; + }); + } +} + +abstract class WidgetDeclaration { + ClassDeclaration get node; + WidgetDeclarationElement get element; +} + +abstract class WidgetDeclarationElement { + DependenciesAnnotationElement? get dependencies; +} diff --git a/packages/riverpod_analyzer_utils/lib/src/object_extensions.dart b/packages/riverpod_analyzer_utils/lib/src/object_extensions.dart new file mode 100644 index 000000000..d55d885e5 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/object_extensions.dart @@ -0,0 +1,17 @@ +import 'package:meta/meta.dart'; + +@internal +extension ObjectX on T? { + R? cast() { + final that = this; + if (that is R) return that; + return null; + } + + R? let(R? Function(T value)? cb) { + if (cb == null) return null; + final that = this; + if (that != null) return cb(that); + return null; + } +} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_ast.dart deleted file mode 100644 index 5b0699daf..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/ast/syntactic_entity.dart'; -import 'package:analyzer/dart/ast/token.dart'; -import 'package:analyzer/dart/ast/visitor.dart'; -import 'package:analyzer/dart/constant/value.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:collection/collection.dart'; -import 'package:crypto/crypto.dart'; -import 'package:meta/meta.dart'; - -import '../riverpod_analyzer_utils.dart'; -import 'argument_list_utils.dart'; -import 'errors.dart'; - -part 'riverpod_ast/consumer.dart'; -part 'riverpod_ast/generator_provider_declaration.dart'; -part 'riverpod_ast/legacy_provider_declaration.dart'; -part 'riverpod_ast/provider_container_instance_creation_expression.dart'; -part 'riverpod_ast/provider_declaration.dart'; -part 'riverpod_ast/provider_listenable_expression.dart'; -part 'riverpod_ast/ref_invocation.dart'; -part 'riverpod_ast/resolve_riverpod.dart'; -part 'riverpod_ast/riverpod_annotation.dart'; -part 'riverpod_ast/visitor.dart'; -part 'riverpod_ast/widget_ref_invocation.dart'; -part 'riverpod_ast/provider_scope.dart'; -part 'riverpod_ast/provider_override.dart'; - -@sealed -abstract class RiverpodAst { - RiverpodAst? _parent; - RiverpodAst? get parent => _parent; - - void accept(RiverpodAstVisitor visitor); - - @mustCallSuper - void visitChildren(RiverpodAstVisitor visitor); -} - -@internal -extension ObjectUtils on T? { - R? cast() { - final that = this; - if (that is R) return that; - return null; - } - - R? let(R? Function(T value)? cb) { - if (cb == null) return null; - final that = this; - if (that != null) return cb(that); - return null; - } -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/consumer.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/consumer.dart deleted file mode 100644 index d5eafca21..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/consumer.dart +++ /dev/null @@ -1,263 +0,0 @@ -part of '../riverpod_ast.dart'; - -abstract class ConsumerDeclaration extends RiverpodAst { - static ConsumerDeclaration? _parse( - ClassDeclaration node, - _ParseRefInvocationMixin parent, - ) { - final extendsClause = node.extendsClause; - if (extendsClause == null) return null; - final extendsType = extendsClause.superclass.type; - if (extendsType == null) return null; - - if (consumerWidgetType.isExactlyType(extendsType)) { - return ConsumerWidgetDeclaration._parse(node, parent); - } else if (hookConsumerWidgetType.isExactlyType(extendsType)) { - return HookConsumerWidgetDeclaration._parse(node, parent); - } else if (consumerStatefulWidgetType.isExactlyType(extendsType)) { - return ConsumerStatefulWidgetDeclaration.parse(node); - } else if (statefulHookConsumerStateType.isExactlyType(extendsType)) { - return StatefulHookConsumerWidgetDeclaration.parse(node); - } else if (consumerStateType.isExactlyType(extendsType)) { - return ConsumerStateDeclaration._parse(node, parent); - } - - return null; - } - - ClassDeclaration get node; -} - -class ConsumerWidgetDeclaration extends ConsumerDeclaration { - ConsumerWidgetDeclaration._({ - required this.buildMethod, - required this.node, - }); - - static ConsumerWidgetDeclaration? _parse( - ClassDeclaration node, - _ParseRefInvocationMixin parent, - ) { - final buildMethod = node.members - .whereType() - .firstWhereOrNull((e) => e.name.lexeme == 'build'); - - final consumerWidgetDeclaration = ConsumerWidgetDeclaration._( - buildMethod: buildMethod, - node: node, - ); - final visitor = _ParseConsumerRefInvocationVisitor( - consumerWidgetDeclaration, - consumerWidgetDeclaration.widgetRefInvocations, - consumerWidgetDeclaration.providerScopeInstanceCreateExpressions, - parent, - ); - - buildMethod?.accept(visitor); - - return consumerWidgetDeclaration; - } - - final MethodDeclaration? buildMethod; - final List widgetRefInvocations = []; - final List - providerScopeInstanceCreateExpressions = []; - - @override - final ClassDeclaration node; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitConsumerWidgetDeclaration(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - for (final invocation in widgetRefInvocations) { - invocation.accept(visitor); - } - for (final expression in providerScopeInstanceCreateExpressions) { - expression.accept(visitor); - } - } -} - -class _ParseConsumerRefInvocationVisitor extends RecursiveAstVisitor - with _ParseRefInvocationMixin { - _ParseConsumerRefInvocationVisitor( - this.parent, - this.widgetRefInvocations, - this.providerScopeInstanceCreateExpressions, - this.parentVisitor, - ); - - final RiverpodAst parent; - final List widgetRefInvocations; - final List - providerScopeInstanceCreateExpressions; - - final _ParseRefInvocationMixin parentVisitor; - - @override - void visitRefInvocation(RefInvocation invocation) { - parentVisitor.visitRefInvocation(invocation); - } - - @override - void visitWidgetRefInvocation(WidgetRefInvocation invocation) { - widgetRefInvocations.add(invocation); - invocation._parent = parent; - } - - @override - void visitProviderContainerInstanceCreationExpression( - ProviderContainerInstanceCreationExpression expression, - ) { - parentVisitor.visitProviderContainerInstanceCreationExpression(expression); - } - - @override - void visitProviderScopeInstanceCreationExpression( - ProviderScopeInstanceCreationExpression expression, - ) { - providerScopeInstanceCreateExpressions.add(expression); - expression._parent = parent; - } -} - -class HookConsumerWidgetDeclaration extends ConsumerDeclaration { - HookConsumerWidgetDeclaration({ - required this.buildMethod, - required this.node, - }); - - static HookConsumerWidgetDeclaration? _parse( - ClassDeclaration node, - _ParseRefInvocationMixin parent, - ) { - final buildMethod = node.members - .whereType() - .firstWhereOrNull((e) => e.name.lexeme == 'build'); - - final consumerWidgetDeclaration = HookConsumerWidgetDeclaration( - buildMethod: buildMethod, - node: node, - ); - final visitor = _ParseConsumerRefInvocationVisitor( - consumerWidgetDeclaration, - consumerWidgetDeclaration.widgetRefInvocations, - consumerWidgetDeclaration.providerScopeInstanceCreateExpressions, - parent, - ); - - buildMethod?.accept(visitor); - - return consumerWidgetDeclaration; - } - - final MethodDeclaration? buildMethod; - final List widgetRefInvocations = []; - final List - providerScopeInstanceCreateExpressions = []; - - @override - final ClassDeclaration node; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitHookConsumerWidgetDeclaration(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - for (final invocation in widgetRefInvocations) { - invocation.accept(visitor); - } - for (final expression in providerScopeInstanceCreateExpressions) { - expression.accept(visitor); - } - } -} - -class ConsumerStatefulWidgetDeclaration extends ConsumerDeclaration { - ConsumerStatefulWidgetDeclaration._({required this.node}); - - ConsumerStatefulWidgetDeclaration.parse(ClassDeclaration node) - : this._(node: node); - - @override - final ClassDeclaration node; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitConsumerStatefulWidgetDeclaration(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) {} -} - -class StatefulHookConsumerWidgetDeclaration extends ConsumerDeclaration { - StatefulHookConsumerWidgetDeclaration._({required this.node}); - - StatefulHookConsumerWidgetDeclaration.parse(ClassDeclaration node) - : this._(node: node); - - @override - final ClassDeclaration node; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitStatefulHookConsumerWidgetDeclaration(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) {} -} - -class ConsumerStateDeclaration extends ConsumerDeclaration { - ConsumerStateDeclaration._({ - required this.node, - }); - - static ConsumerStateDeclaration? _parse( - ClassDeclaration node, - _ParseRefInvocationMixin parent, - ) { - final consumerWidgetDeclaration = ConsumerStateDeclaration._( - node: node, - ); - final visitor = _ParseConsumerRefInvocationVisitor( - consumerWidgetDeclaration, - consumerWidgetDeclaration.widgetRefInvocations, - consumerWidgetDeclaration.providerScopeInstanceCreateExpressions, - parent, - ); - - node.accept(visitor); - - return consumerWidgetDeclaration; - } - - final List widgetRefInvocations = []; - final List - providerScopeInstanceCreateExpressions = []; - - @override - final ClassDeclaration node; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitConsumerStateDeclaration(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - for (final invocation in widgetRefInvocations) { - invocation.accept(visitor); - } - for (final expression in providerScopeInstanceCreateExpressions) { - expression.accept(visitor); - } - } -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/generator_provider_declaration.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/generator_provider_declaration.dart deleted file mode 100644 index 09d84544f..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/generator_provider_declaration.dart +++ /dev/null @@ -1,394 +0,0 @@ -part of '../riverpod_ast.dart'; - -extension RawTypeX on DartType { - /// Returns whether this type is a `Raw` typedef from `package:riverpod_annotation`. - bool get isRaw { - final alias = this.alias; - if (alias == null) return false; - return alias.element.name == 'Raw' && - isFromRiverpodAnnotation.isExactly(alias.element); - } -} - -extension on LibraryElement { - static final _asyncValueCache = Expando(); - - Element? findElementWithNameFromPackage( - String name, { - required String packageName, - }) { - return library.importedLibraries - .map((e) => e.exportNamespace.get(name)) - .firstWhereOrNull( - // TODO find a way to test this - (element) => element != null && isFromRiverpod.isExactly(element), - ); - } - - ClassElement? findAsyncValue() { - final cache = _asyncValueCache[this]; - if (cache != null) return cache; - - final result = findElementWithNameFromPackage( - 'AsyncValue', - packageName: 'riverpod', - ); - if (result == null) { - errorReporter?.call( - RiverpodAnalysisError( - 'No AsyncValue accessible in the library. ' - 'Did you forget to import Riverpod?', - targetElement: this, - ), - ); - return null; - } - - return _asyncValueCache[this] = result as ClassElement?; - } - - DartType? createdTypeToValueType(DartType? typeArg) { - final asyncValue = findAsyncValue(); - - return asyncValue?.instantiate( - typeArguments: [if (typeArg != null) typeArg], - nullabilitySuffix: NullabilitySuffix.none, - ); - } -} - -abstract class GeneratorProviderDeclaration extends ProviderDeclaration { - @override - GeneratorProviderDeclarationElement get providerElement; - RiverpodAnnotation get annotation; - - String get valueTypeDisplayString => valueTypeNode?.toSource() ?? 'Object?'; - String get exposedTypeDisplayString => exposedTypeNode?.source ?? 'Object?'; - String get createdTypeDisplayString { - final type = createdTypeNode?.type; - - if (type != null && - !type.isRaw && - (type.isDartAsyncFuture || type.isDartAsyncFutureOr)) { - return 'FutureOr<$valueTypeDisplayString>'; - } - - return createdTypeNode?.toSource() ?? 'Object?'; - } - - TypeAnnotation? get valueTypeNode; - SourcedType? get exposedTypeNode; - TypeAnnotation? get createdTypeNode; - - final List refInvocations = []; - - String computeProviderHash() { - // TODO improve hash function to inspect the body of the create fn - // such that the hash changes if one of the element defined outside of the - // fn changes. - final bytes = utf8.encode(node.toSource()); - final digest = sha1.convert(bytes); - return digest.toString(); - } - - @mustCallSuper - @override - void visitChildren(RiverpodAstVisitor visitor) { - for (final refInvocation in refInvocations) { - refInvocation.accept(visitor); - } - } -} - -SourcedType? _computeExposedType( - TypeAnnotation? createdType, - LibraryElement library, -) { - if (createdType == null) { - return ( - source: null, - dartType: library.typeProvider.dynamicType, - ); - } - - final createdDartType = createdType.type!; - if (createdDartType.isRaw) { - return ( - source: createdType.toSource(), - dartType: createdType.type!, - ); - } - - if (createdDartType.isDartAsyncFuture || - createdDartType.isDartAsyncFutureOr || - createdDartType.isDartAsyncStream) { - createdType as NamedType; - createdDartType as InterfaceType; - - final typeSource = createdType.toSource(); - if (typeSource != 'Future' && - typeSource != 'FutureOr' && - typeSource != 'Stream' && - !typeSource.startsWith('Future<') && - !typeSource.startsWith('FutureOr<') && - !typeSource.startsWith('Stream<')) { - throw UnsupportedError( - 'Returning a typedef of type Future/FutureOr/Stream is not supported\n' - 'The code that triggered this error is: $typeSource', - ); - } - - final valueTypeArg = createdType.typeArguments?.arguments.firstOrNull; - - final exposedDartType = - library.createdTypeToValueType(createdDartType.typeArguments.first); - if (exposedDartType == null) return null; - - return ( - source: valueTypeArg == null ? 'AsyncValue' : 'AsyncValue<$valueTypeArg>', - dartType: exposedDartType, - ); - } - - return ( - source: createdType.toSource(), - dartType: createdType.type!, - ); -} - -TypeAnnotation? _getValueType( - TypeAnnotation? createdType, - LibraryElement library, -) { - if (createdType == null) return null; - final dartType = createdType.type!; - if (dartType.isRaw) return createdType; - - if (dartType.isDartAsyncFuture || - dartType.isDartAsyncFutureOr || - dartType.isDartAsyncStream) { - createdType as NamedType; - - return createdType.typeArguments?.arguments.firstOrNull; - } - - return createdType; -} - -typedef SourcedType = ({String? source, DartType dartType}); - -class ClassBasedProviderDeclaration extends GeneratorProviderDeclaration { - ClassBasedProviderDeclaration._({ - required this.name, - required this.node, - required this.buildMethod, - required this.providerElement, - required this.annotation, - required this.createdTypeNode, - required this.exposedTypeNode, - required this.valueTypeNode, - }); - - static ClassBasedProviderDeclaration? _parse( - ClassDeclaration node, - _ParseRefInvocationMixin parent, - ) { - final element = node.declaredElement; - if (element == null) return null; - final riverpodAnnotation = RiverpodAnnotation._parse(node); - if (riverpodAnnotation == null) return null; - - final buildMethod = node.members - .whereType() - .firstWhereOrNull((method) => method.name.lexeme == 'build'); - if (buildMethod == null) { - errorReporter?.call( - RiverpodAnalysisError( - 'No "build" method found. ' - 'Classes annotated with @riverpod must define a method named "build".', - targetNode: node, - ), - ); - return null; - } - - final providerElement = ClassBasedProviderDeclarationElement.parse( - element, - annotation: riverpodAnnotation.element, - ); - if (providerElement == null) return null; - - final createdTypeNode = buildMethod.returnType; - - final exposedTypeNode = - _computeExposedType(createdTypeNode, element.library); - if (exposedTypeNode == null) { - // Error already reported - return null; - } - - final valueTypeNode = _getValueType(createdTypeNode, element.library); - final classBasedProviderDeclaration = ClassBasedProviderDeclaration._( - name: node.name, - node: node, - buildMethod: buildMethod, - providerElement: providerElement, - annotation: riverpodAnnotation, - createdTypeNode: createdTypeNode, - exposedTypeNode: exposedTypeNode, - valueTypeNode: valueTypeNode, - ); - riverpodAnnotation._parent = classBasedProviderDeclaration; - node.accept( - _GeneratorRefInvocationVisitor(classBasedProviderDeclaration, parent), - ); - - return classBasedProviderDeclaration; - } - - @override - final Token name; - @override - final ClassDeclaration node; - @override - final ClassBasedProviderDeclarationElement providerElement; - @override - final RiverpodAnnotation annotation; - final MethodDeclaration buildMethod; - @override - final TypeAnnotation? createdTypeNode; - @override - final TypeAnnotation? valueTypeNode; - @override - final SourcedType exposedTypeNode; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitClassBasedProviderDeclaration(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - super.visitChildren(visitor); - annotation.accept(visitor); - } -} - -class _GeneratorRefInvocationVisitor extends RecursiveAstVisitor - with _ParseRefInvocationMixin { - _GeneratorRefInvocationVisitor(this.declaration, this.parent); - - final GeneratorProviderDeclaration declaration; - final _ParseRefInvocationMixin parent; - - @override - void visitRefInvocation(RefInvocation invocation) { - declaration.refInvocations.add(invocation); - invocation._parent = declaration; - } - - @override - void visitWidgetRefInvocation(WidgetRefInvocation invocation) { - parent.visitWidgetRefInvocation(invocation); - } - - @override - void visitProviderContainerInstanceCreationExpression( - ProviderContainerInstanceCreationExpression expression, - ) { - parent.visitProviderContainerInstanceCreationExpression(expression); - } - - @override - void visitProviderScopeInstanceCreationExpression( - ProviderScopeInstanceCreationExpression expression, - ) { - parent.visitProviderScopeInstanceCreationExpression(expression); - } -} - -class FunctionalProviderDeclaration extends GeneratorProviderDeclaration { - FunctionalProviderDeclaration._({ - required this.name, - required this.node, - required this.providerElement, - required this.annotation, - required this.createdTypeNode, - required this.exposedTypeNode, - required this.valueTypeNode, - }); - - static FunctionalProviderDeclaration? _parse( - FunctionDeclaration node, - _ParseRefInvocationMixin parent, - ) { - final element = node.declaredElement; - if (element == null) return null; - final riverpodAnnotation = RiverpodAnnotation._parse(node); - if (riverpodAnnotation == null) return null; - - final providerElement = FunctionalProviderDeclarationElement.parse( - element, - annotation: riverpodAnnotation.element, - ); - if (providerElement == null) return null; - - final createdTypeNode = node.returnType; - final exposedTypeNode = - _computeExposedType(createdTypeNode, element.library); - if (exposedTypeNode == null) { - // Error already reported - return null; - } - - final functionalProviderDeclaration = FunctionalProviderDeclaration._( - name: node.name, - node: node, - providerElement: providerElement, - annotation: riverpodAnnotation, - createdTypeNode: createdTypeNode, - exposedTypeNode: exposedTypeNode, - valueTypeNode: _getValueType(createdTypeNode, element.library), - ); - riverpodAnnotation._parent = functionalProviderDeclaration; - node.accept( - _GeneratorRefInvocationVisitor(functionalProviderDeclaration, parent), - ); - return functionalProviderDeclaration; - } - - @override - final Token name; - - @override - final FunctionDeclaration node; - @override - final FunctionalProviderDeclarationElement providerElement; - @override - final RiverpodAnnotation annotation; - @override - final TypeAnnotation? createdTypeNode; - @override - final TypeAnnotation? valueTypeNode; - @override - final SourcedType exposedTypeNode; - - /// Whether the provider uses the syntax sugar for scoped providers: - /// - /// ```dart - /// @riverpod - /// external int count(); - /// ``` - bool get needsOverride => node.externalKeyword != null; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitFunctionalProviderDeclaration(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - super.visitChildren(visitor); - annotation.accept(visitor); - } -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/legacy_provider_declaration.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/legacy_provider_declaration.dart deleted file mode 100644 index 65b71b06a..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/legacy_provider_declaration.dart +++ /dev/null @@ -1,257 +0,0 @@ -part of '../riverpod_ast.dart'; - -class LegacyProviderDependencies extends RiverpodAst { - LegacyProviderDependencies._({ - required this.dependencies, - required this.dependenciesNode, - }); - - static LegacyProviderDependencies? _parse(NamedExpression? dependenciesNode) { - if (dependenciesNode == null) return null; - - final value = dependenciesNode.expression; - - List? dependencies; - if (value is ListLiteral) { - dependencies = - value.elements.map(LegacyProviderDependency._parse).toList(); - } - - final legacyProviderDependencies = LegacyProviderDependencies._( - dependenciesNode: dependenciesNode, - dependencies: dependencies, - ); - legacyProviderDependencies.dependencies?.forEach((element) { - element._parent = legacyProviderDependencies; - }); - - return legacyProviderDependencies; - } - - final List? dependencies; - final NamedExpression dependenciesNode; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitLegacyProviderDependencies(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - final dependencies = this.dependencies; - if (dependencies != null) { - for (final dependency in dependencies) { - dependency.accept(visitor); - } - } - } -} - -class LegacyProviderDependency extends RiverpodAst - implements ProviderListenableExpressionParent { - LegacyProviderDependency._({ - required this.node, - required this.provider, - }); - - factory LegacyProviderDependency._parse(CollectionElement node) { - final provider = - node.cast().let(ProviderListenableExpression._parse); - - final legacyProviderDependency = LegacyProviderDependency._( - node: node, - provider: provider, - ); - provider?._parent = legacyProviderDependency; - return legacyProviderDependency; - } - - final CollectionElement node; - final ProviderListenableExpression? provider; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitLegacyProviderDependency(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - provider?.accept(visitor); - } -} - -class LegacyProviderDeclaration extends RiverpodAst - implements ProviderDeclaration { - LegacyProviderDeclaration._({ - required this.name, - required this.node, - required this.build, - required this.typeArguments, - required this.providerElement, - required this.argumentList, - required this.provider, - required this.autoDisposeModifier, - required this.familyModifier, - required this.dependencies, - }); - - static LegacyProviderDeclaration? _parse( - VariableDeclaration node, - _ParseRefInvocationMixin parent, - ) { - final element = node.declaredElement; - if (element == null) return null; - - final providerElement = LegacyProviderDeclarationElement.parse(element); - if (providerElement == null) return null; - - final initializer = node.initializer; - ArgumentList? arguments; - late SyntacticEntity provider; - SimpleIdentifier? autoDisposeModifier; - SimpleIdentifier? familyModifier; - TypeArgumentList? typeArguments; - if (initializer is InstanceCreationExpression) { - // Provider((ref) => ...) - - arguments = initializer.argumentList; - provider = initializer.constructorName.type.name2; - typeArguments = initializer.constructorName.type.typeArguments; - } else if (initializer is FunctionExpressionInvocation) { - // Provider.modifier() - - void decodeIdentifier(SimpleIdentifier identifier) { - switch (identifier.name) { - case 'autoDispose': - autoDisposeModifier = identifier; - case 'family': - familyModifier = identifier; - default: - provider = identifier; - } - } - - void decodeTarget(Expression? expression) { - if (expression is SimpleIdentifier) { - decodeIdentifier(expression); - } else if (expression is PrefixedIdentifier) { - decodeIdentifier(expression.identifier); - decodeIdentifier(expression.prefix); - } else { - throw UnsupportedError( - 'unknown expression "$expression" (${expression.runtimeType})', - ); - } - } - - final modifier = initializer.function; - if (modifier is! PropertyAccess) return null; - - decodeIdentifier(modifier.propertyName); - decodeTarget(modifier.target); - arguments = initializer.argumentList; - typeArguments = initializer.typeArguments; - } else { - // Invalid provider expression. - // Such as "final provider = variable;" - return null; - } - - final build = arguments.positionalArguments().firstOrNull; - if (build is! FunctionExpression) return null; - - final dependenciesElement = arguments - .namedArguments() - .firstWhereOrNull((e) => e.name.label.name == 'dependencies'); - final dependencies = LegacyProviderDependencies._parse(dependenciesElement); - - final legacyProviderDeclaration = LegacyProviderDeclaration._( - name: node.name, - node: node, - build: build, - providerElement: providerElement, - argumentList: arguments, - typeArguments: typeArguments, - provider: provider, - autoDisposeModifier: autoDisposeModifier, - familyModifier: familyModifier, - dependencies: dependencies, - ); - - dependencies?._parent = legacyProviderDeclaration; - build.accept( - _LegacyRefInvocationVisitor( - legacyProviderDeclaration, - parent, - ), - ); - - return legacyProviderDeclaration; - } - - final LegacyProviderDependencies? dependencies; - - final FunctionExpression build; - final ArgumentList argumentList; - final SyntacticEntity provider; - final SimpleIdentifier? autoDisposeModifier; - final SimpleIdentifier? familyModifier; - final TypeArgumentList? typeArguments; - - @override - final LegacyProviderDeclarationElement providerElement; - - final List refInvocations = []; - - @override - final Token name; - - @override - final VariableDeclaration node; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitLegacyProviderDeclaration(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - dependencies?.accept(visitor); - for (final refInvocation in refInvocations) { - refInvocation.accept(visitor); - } - } -} - -class _LegacyRefInvocationVisitor extends RecursiveAstVisitor - with _ParseRefInvocationMixin { - _LegacyRefInvocationVisitor(this.declaration, this.parent); - - final LegacyProviderDeclaration declaration; - final _ParseRefInvocationMixin parent; - - @override - void visitRefInvocation(RefInvocation invocation) { - declaration.refInvocations.add(invocation); - invocation._parent = declaration; - } - - @override - void visitWidgetRefInvocation(WidgetRefInvocation invocation) { - parent.visitWidgetRefInvocation(invocation); - } - - @override - void visitProviderContainerInstanceCreationExpression( - ProviderContainerInstanceCreationExpression expression, - ) { - parent.visitProviderContainerInstanceCreationExpression(expression); - } - - @override - void visitProviderScopeInstanceCreationExpression( - ProviderScopeInstanceCreationExpression expression, - ) { - parent.visitProviderScopeInstanceCreationExpression(expression); - } -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_container_instance_creation_expression.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_container_instance_creation_expression.dart deleted file mode 100644 index 034f26c59..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_container_instance_creation_expression.dart +++ /dev/null @@ -1,40 +0,0 @@ -part of '../riverpod_ast.dart'; - -class ProviderContainerInstanceCreationExpression extends RiverpodAst { - ProviderContainerInstanceCreationExpression._({ - required this.node, - required this.overrides, - }); - - static ProviderContainerInstanceCreationExpression? _parse( - InstanceCreationExpression node, - ) { - final createdType = node.constructorName.type.type; - if (createdType == null || - !providerContainerType.isExactlyType(createdType)) { - return null; - } - - final overrides = node.argumentList - .namedArguments() - .firstWhereOrNull((e) => e.name.label.name == 'overrides'); - - return ProviderContainerInstanceCreationExpression._( - node: node, - overrides: ProviderOverrideList._parse(overrides), - ); - } - - final InstanceCreationExpression node; - final ProviderOverrideList? overrides; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitProviderContainerInstanceCreationExpression(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - overrides?.accept(visitor); - } -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_declaration.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_declaration.dart deleted file mode 100644 index 8c0de4207..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_declaration.dart +++ /dev/null @@ -1,7 +0,0 @@ -part of '../riverpod_ast.dart'; - -abstract class ProviderDeclaration extends RiverpodAst { - Token get name; - AnnotatedNode get node; - ProviderDeclarationElement get providerElement; -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_listenable_expression.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_listenable_expression.dart deleted file mode 100644 index d76a791ed..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_listenable_expression.dart +++ /dev/null @@ -1,123 +0,0 @@ -part of '../riverpod_ast.dart'; - -abstract class ProviderListenableExpressionParent implements RiverpodAst {} - -class ProviderListenableExpression extends RiverpodAst { - ProviderListenableExpression._({ - required this.node, - required this.provider, - required this.providerPrefix, - required this.providerElement, - required this.familyArguments, - }); - - static ProviderListenableExpression? _parse(Expression? expression) { - if (expression == null) return null; - - SimpleIdentifier? provider; - SimpleIdentifier? providerPrefix; - ProviderDeclarationElement? providerElement; - ArgumentList? familyArguments; - - void parseExpression(Expression? expression) { - // Can be reached when the code contains syntax errors - if (expression == null) return; - if (expression is SimpleIdentifier) { - // watch(expression) - provider = expression; - final element = expression.staticElement; - if (element is PropertyAccessorElement) { - // watch(provider) - DartObject? annotation; - try { - annotation = - providerForType.firstAnnotationOfExact(element.variable2!); - } catch (_) { - return; - } - - if (annotation == null) { - providerElement = - LegacyProviderDeclarationElement.parse(element.variable2!); - } else { - providerElement = _parseGeneratedProviderFromAnnotation(annotation); - } - } - } else if (expression is FunctionExpressionInvocation) { - // watch(expression()) - familyArguments = expression.argumentList; - parseExpression(expression.function); - } else if (expression is MethodInvocation) { - // watch(expression.method()) - parseExpression(expression.target); - } else if (expression is PrefixedIdentifier) { - // watch(expression.modifier) - final element = expression.prefix.staticElement; - if (element is PrefixElement) { - providerPrefix = expression.prefix; - parseExpression(expression.identifier); - } else { - parseExpression(expression.prefix); - } - } else if (expression is IndexExpression) { - // watch(expression[]) - parseExpression(expression.target); - } else if (expression is PropertyAccess) { - // watch(expression.property) - parseExpression(expression.target); - } - } - - parseExpression(expression); - - return ProviderListenableExpression._( - node: expression, - provider: provider, - providerPrefix: providerPrefix, - providerElement: providerElement, - // Make sure `(){}()` doesn't report an argument list even though it's not a provider - familyArguments: provider == null ? null : familyArguments, - ); - } - - static GeneratorProviderDeclarationElement? - _parseGeneratedProviderFromAnnotation( - DartObject annotation, - ) { - final generatedProviderDefinition = annotation.getField('value')!; - - final function = generatedProviderDefinition.toFunctionValue(); - if (function != null) { - return FunctionalProviderDeclarationElement.parse( - function, - annotation: null, - ); - } - late final type = generatedProviderDefinition.toTypeValue()?.element; - if (type != null && type is ClassElement) { - return ClassBasedProviderDeclarationElement.parse( - type, - annotation: null, - ); - } else { - throw StateError('Unknown value $generatedProviderDefinition'); - } - } - - final Expression node; - final SimpleIdentifier? providerPrefix; - final SimpleIdentifier? provider; - final ProviderDeclarationElement? providerElement; - - /// If [provider] is a provider with arguments (family), represents the arguments - /// passed to the provider. - final ArgumentList? familyArguments; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitProviderListenableExpression(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) {} -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_override.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_override.dart deleted file mode 100644 index 9b6ff4bcd..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_override.dart +++ /dev/null @@ -1,87 +0,0 @@ -part of '../riverpod_ast.dart'; - -class ProviderOverrideExpression extends RiverpodAst { - ProviderOverrideExpression._({ - required this.expression, - required this.providerElement, - required this.provider, - required this.familyArguments, - }); - - // ignore: prefer_constructors_over_static_methods - static ProviderOverrideExpression _parse(CollectionElement expression) { - SimpleIdentifier? provider; - ProviderDeclarationElement? providerElement; - ArgumentList? familyArguments; - if (expression is Expression) { - final listenable = ProviderListenableExpression._parse(expression); - provider = listenable?.provider; - providerElement = listenable?.providerElement; - familyArguments = listenable?.familyArguments; - } - - return ProviderOverrideExpression._( - expression: expression, - providerElement: providerElement, - familyArguments: familyArguments, - provider: provider, - ); - } - - final CollectionElement expression; - final ProviderDeclarationElement? providerElement; - final SimpleIdentifier? provider; - - /// If [provider] is a provider with arguments (family), represents the arguments - /// passed to the provider. - final ArgumentList? familyArguments; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitProviderOverrideExpression(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) {} -} - -class ProviderOverrideList extends RiverpodAst { - ProviderOverrideList._({ - required this.node, - required this.overrides, - }); - - static ProviderOverrideList? _parse(NamedExpression? expression) { - if (expression == null) return null; - final expressionValue = expression.expression; - - List? overrides; - if (expressionValue is ListLiteral) { - overrides = expressionValue.elements - .map(ProviderOverrideExpression._parse) - .toList(); - } - - final providerOverrideList = ProviderOverrideList._( - node: expression, - overrides: overrides, - ); - - overrides?.forEach((e) => e._parent = providerOverrideList); - - return providerOverrideList; - } - - final NamedExpression node; - final List? overrides; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitProviderOverrideList(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - overrides?.forEach((e) => e.accept(visitor)); - } -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_scope.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_scope.dart deleted file mode 100644 index c0bc19ae1..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/provider_scope.dart +++ /dev/null @@ -1,39 +0,0 @@ -part of '../riverpod_ast.dart'; - -class ProviderScopeInstanceCreationExpression extends RiverpodAst { - ProviderScopeInstanceCreationExpression._({ - required this.node, - required this.overrides, - }); - - static ProviderScopeInstanceCreationExpression? _parse( - InstanceCreationExpression node, - ) { - final createdType = node.constructorName.type.type; - if (createdType == null || !providerScopeType.isExactlyType(createdType)) { - return null; - } - - final overrides = node.argumentList - .namedArguments() - .firstWhereOrNull((e) => e.name.label.name == 'overrides'); - - return ProviderScopeInstanceCreationExpression._( - node: node, - overrides: ProviderOverrideList._parse(overrides), - ); - } - - final InstanceCreationExpression node; - final ProviderOverrideList? overrides; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitProviderScopeInstanceCreationExpression(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - overrides?.accept(visitor); - } -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/ref_invocation.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/ref_invocation.dart deleted file mode 100644 index 44adc4c45..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/ref_invocation.dart +++ /dev/null @@ -1,208 +0,0 @@ -part of '../riverpod_ast.dart'; - -abstract class RefInvocation extends RiverpodAst - implements ProviderListenableExpressionParent { - RefInvocation._({ - required this.node, - required this.function, - }); - - static RefInvocation? _parse( - MethodInvocation node, { - required void Function() superCall, - }) { - final targetType = node.realTarget?.staticType; - if (targetType == null) return null; - - if (!isRiverpodRef(targetType)) return null; - - final function = node.function; - if (function is! SimpleIdentifier) return null; - final functionOwner = function.staticElement - .cast() - ?.declaration - .enclosingElement3; - - if (functionOwner == null || - // Since Ref is sealed, checking that the function is from the package:riverpod - // before checking its type skips iterating over the superclasses of an element - // if it's not from Riverpod. - !isFromRiverpod.isExactly(functionOwner) || - !refType.isAssignableFrom(functionOwner)) { - return null; - } - - switch (function.name) { - case 'watch': - return RefWatchInvocation._parse( - node, - function, - superCall: superCall, - ); - case 'read': - return RefReadInvocation._parse( - node, - function, - superCall: superCall, - ); - case 'listen': - return RefListenInvocation._parse( - node, - function, - superCall: superCall, - ); - default: - return null; - } - } - - final MethodInvocation node; - final SimpleIdentifier function; -} - -/// A [RefInvocation] which interacts with a provider, inducing a dependency. -abstract class RefDependencyInvocation extends RefInvocation { - RefDependencyInvocation._({ - required super.node, - required super.function, - required this.provider, - }) : super._(); - - /// The provider that is being interacted with. - final ProviderListenableExpression provider; -} - -class RefWatchInvocation extends RefDependencyInvocation { - RefWatchInvocation._({ - required super.node, - required super.function, - required super.provider, - }) : super._(); - - static RefWatchInvocation? _parse( - MethodInvocation node, - SimpleIdentifier function, { - required void Function() superCall, - }) { - assert( - function.name == 'watch', - 'Argument error, function is not a ref.watch function', - ); - - final providerListenableExpression = ProviderListenableExpression._parse( - node.argumentList.positionalArguments().singleOrNull, - ); - if (providerListenableExpression == null) return null; - - final refWatchInvocation = RefWatchInvocation._( - node: node, - function: function, - provider: providerListenableExpression, - ); - providerListenableExpression._parent = refWatchInvocation; - return refWatchInvocation; - } - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitRefWatchInvocation(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - provider.accept(visitor); - } -} - -class RefReadInvocation extends RefDependencyInvocation { - RefReadInvocation._({ - required super.node, - required super.function, - required super.provider, - }) : super._(); - - static RefReadInvocation? _parse( - MethodInvocation node, - SimpleIdentifier function, { - required void Function() superCall, - }) { - assert( - function.name == 'read', - 'Argument error, function is not a ref.read function', - ); - - final providerListenableExpression = ProviderListenableExpression._parse( - node.argumentList.positionalArguments().singleOrNull, - ); - if (providerListenableExpression == null) return null; - - final refReadInvocation = RefReadInvocation._( - node: node, - function: function, - provider: providerListenableExpression, - ); - providerListenableExpression._parent = refReadInvocation; - return refReadInvocation; - } - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitRefReadInvocation(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - provider.accept(visitor); - } -} - -class RefListenInvocation extends RefDependencyInvocation { - RefListenInvocation._({ - required super.node, - required super.function, - required this.listener, - required super.provider, - }) : super._(); - - static RefListenInvocation? _parse( - MethodInvocation node, - SimpleIdentifier function, { - required void Function() superCall, - }) { - assert( - function.name == 'listen', - 'Argument error, function is not a ref.listen function', - ); - - final positionalArgs = node.argumentList.positionalArguments().toList(); - - final listener = positionalArgs.elementAtOrNull(1); - if (listener == null) return null; - - final providerListenableExpression = ProviderListenableExpression._parse( - positionalArgs.firstOrNull, - ); - if (providerListenableExpression == null) return null; - - final refListenInvocation = RefListenInvocation._( - node: node, - function: function, - listener: listener, - provider: providerListenableExpression, - ); - providerListenableExpression._parent = refListenInvocation; - return refListenInvocation; - } - - final Expression listener; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitRefListenInvocation(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - provider.accept(visitor); - } -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/resolve_riverpod.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/resolve_riverpod.dart deleted file mode 100644 index bfa95da54..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/resolve_riverpod.dart +++ /dev/null @@ -1,295 +0,0 @@ -part of '../riverpod_ast.dart'; - -class ResolvedRiverpodLibraryResult extends RiverpodAst { - ResolvedRiverpodLibraryResult._(); - - factory ResolvedRiverpodLibraryResult.from( - List units, - ) { - final result = ResolvedRiverpodLibraryResult._(); - final visitor = _ParseRiverpodUnitVisitor(result); - - try { - errorReporter = result.errors.add; - - for (final unit in units) { - // Let's not parse generated files - const generatedExtensions = {'.freezed.dart', '.g.dart'}; - final shortName = unit.declaredElement?.source.shortName ?? ''; - if (generatedExtensions.any(shortName.endsWith)) { - continue; - } - unit.accept(visitor); - } - } finally { - errorReporter = null; - } - - return result; - } - - final errors = []; - - final providerScopeInstanceCreationExpressions = - []; - final providerContainerInstanceCreationExpressions = - []; - - final functionalProviderDeclarations = []; - final classBasedProviderDeclarations = []; - - final legacyProviderDeclarations = []; - - final consumerWidgetDeclarations = []; - final consumerStatefulWidgetDeclarations = - []; - final consumerStateDeclaration = []; - final statefulHookConsumerWidgetDeclarations = - []; - final hookConsumerWidgetDeclarations = []; - - final unknownRefInvocations = []; - final unknownWidgetRefInvocations = []; - - @override - Null get parent => null; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitResolvedRiverpodUnit(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - for (final declaration in providerScopeInstanceCreationExpressions) { - declaration.accept(visitor); - } - for (final declaration in providerContainerInstanceCreationExpressions) { - declaration.accept(visitor); - } - - for (final declaration in functionalProviderDeclarations) { - declaration.accept(visitor); - } - for (final declaration in classBasedProviderDeclarations) { - declaration.accept(visitor); - } - for (final declaration in legacyProviderDeclarations) { - declaration.accept(visitor); - } - for (final declaration in consumerWidgetDeclarations) { - declaration.accept(visitor); - } - for (final declaration in consumerStatefulWidgetDeclarations) { - declaration.accept(visitor); - } - for (final declaration in consumerStateDeclaration) { - declaration.accept(visitor); - } - for (final declaration in statefulHookConsumerWidgetDeclarations) { - declaration.accept(visitor); - } - for (final declaration in hookConsumerWidgetDeclarations) { - declaration.accept(visitor); - } - - for (final invocation in unknownRefInvocations) { - invocation.accept(visitor); - } - for (final invocation in unknownWidgetRefInvocations) { - invocation.accept(visitor); - } - } -} - -mixin _ParseRefInvocationMixin on RecursiveAstVisitor { - @override - void visitMethodInvocation(MethodInvocation node) { - void superCall() => super.visitMethodInvocation(node); - - final refInvocation = RefInvocation._parse(node, superCall: superCall); - if (refInvocation != null) { - visitRefInvocation(refInvocation); - - for (final argument in refInvocation.node.argumentList.arguments - .whereType()) { - argument.accept(this); - } - - // Don't call super as RefInvocation should already be recursive - return; - } - - final widgetRefInvocation = - WidgetRefInvocation._parse(node, superCall: superCall); - if (widgetRefInvocation != null) { - visitWidgetRefInvocation(widgetRefInvocation); - - for (final argument in widgetRefInvocation.node.argumentList.arguments - .whereType()) { - argument.accept(this); - } - // Don't call super as WidgetRefInvocation should already be recursive - return; - } - - super.visitMethodInvocation(node); - } - - @override - void visitInstanceCreationExpression(InstanceCreationExpression node) { - final providerScopeInstanceCreationExpression = - ProviderScopeInstanceCreationExpression._parse(node); - if (providerScopeInstanceCreationExpression != null) { - visitProviderScopeInstanceCreationExpression( - providerScopeInstanceCreationExpression, - ); - // Don't call super as ProviderScopeInstanceCreationExpression should - // already be recursive - return; - } - - final providerContainerInstanceCreationExpression = - ProviderContainerInstanceCreationExpression._parse(node); - if (providerContainerInstanceCreationExpression != null) { - visitProviderContainerInstanceCreationExpression( - providerContainerInstanceCreationExpression, - ); - // Don't call super as ProviderContainerInstanceCreationExpression should - // already be recursive - return; - } - - super.visitInstanceCreationExpression(node); - } - - void visitProviderScopeInstanceCreationExpression( - ProviderScopeInstanceCreationExpression expression, - ); - void visitProviderContainerInstanceCreationExpression( - ProviderContainerInstanceCreationExpression expression, - ); - - void visitRefInvocation(RefInvocation invocation); - - void visitWidgetRefInvocation(WidgetRefInvocation invocation); -} - -class _AddConsumerDeclarationVisitor extends UnimplementedRiverpodAstVisitor { - _AddConsumerDeclarationVisitor(this.result); - - final ResolvedRiverpodLibraryResult result; - - @override - void visitConsumerStatefulWidgetDeclaration( - ConsumerStatefulWidgetDeclaration declaration, - ) { - result.consumerStatefulWidgetDeclarations.add(declaration); - } - - @override - void visitConsumerStateDeclaration(ConsumerStateDeclaration declaration) { - result.consumerStateDeclaration.add(declaration); - } - - @override - void visitStatefulHookConsumerWidgetDeclaration( - StatefulHookConsumerWidgetDeclaration declaration, - ) { - result.statefulHookConsumerWidgetDeclarations.add(declaration); - } - - @override - void visitHookConsumerWidgetDeclaration( - HookConsumerWidgetDeclaration declaration, - ) { - result.hookConsumerWidgetDeclarations.add(declaration); - } - - @override - void visitConsumerWidgetDeclaration(ConsumerWidgetDeclaration declaration) { - result.consumerWidgetDeclarations.add(declaration); - } -} - -class _ParseRiverpodUnitVisitor extends RecursiveAstVisitor - with _ParseRefInvocationMixin { - _ParseRiverpodUnitVisitor(this.result); - - final ResolvedRiverpodLibraryResult result; - - @override - void visitClassDeclaration(ClassDeclaration node) { - final declaration = ClassBasedProviderDeclaration._parse(node, this); - if (declaration != null) { - result.classBasedProviderDeclarations.add(declaration); - declaration._parent = result; - // Don't call super as ClassBasedProviderDeclaration should already be recursive - return; - } - - final consumerDeclaration = ConsumerDeclaration._parse(node, this); - if (consumerDeclaration != null) { - consumerDeclaration._parent = result; - consumerDeclaration.accept(_AddConsumerDeclarationVisitor(result)); - // Don't call super as ClassBasedProviderDeclaration should already be recursive - return; - } - - super.visitClassDeclaration(node); - } - - @override - void visitFunctionDeclaration(FunctionDeclaration node) { - final declaration = FunctionalProviderDeclaration._parse(node, this); - if (declaration != null) { - result.functionalProviderDeclarations.add(declaration); - declaration._parent = result; - // Don't call super as FunctionalProviderDeclaration should already be recursive - return; - } - - super.visitFunctionDeclaration(node); - } - - @override - void visitVariableDeclaration(VariableDeclaration node) { - final declaration = LegacyProviderDeclaration._parse(node, this); - if (declaration != null) { - result.legacyProviderDeclarations.add(declaration); - declaration._parent = result; - // Don't call super as LegacyProviderDeclaration should already be recursive - return; - } - - super.visitVariableDeclaration(node); - } - - @override - void visitRefInvocation(RefInvocation invocation) { - result.unknownRefInvocations.add(invocation); - invocation._parent = result; - } - - @override - void visitWidgetRefInvocation(WidgetRefInvocation invocation) { - result.unknownWidgetRefInvocations.add(invocation); - invocation._parent = result; - } - - @override - void visitProviderContainerInstanceCreationExpression( - ProviderContainerInstanceCreationExpression expression, - ) { - result.providerContainerInstanceCreationExpressions.add(expression); - expression._parent = result; - } - - @override - void visitProviderScopeInstanceCreationExpression( - ProviderScopeInstanceCreationExpression expression, - ) { - result.providerScopeInstanceCreationExpressions.add(expression); - expression._parent = result; - } -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/riverpod_annotation.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/riverpod_annotation.dart deleted file mode 100644 index 7a00e22a8..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/riverpod_annotation.dart +++ /dev/null @@ -1,226 +0,0 @@ -part of '../riverpod_ast.dart'; - -class RiverpodAnnotationDependency extends RiverpodAst { - RiverpodAnnotationDependency._({ - required this.node, - required this.provider, - }); - - final Expression node; - final GeneratorProviderDeclarationElement provider; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitRiverpodAnnotationDependency(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) {} -} - -class RiverpodAnnotationDependencies extends RiverpodAst { - RiverpodAnnotationDependencies._({ - required this.node, - required this.dependencies, - }); - - final NamedExpression node; - final List? dependencies; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitRiverpodAnnotationDependencies(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - final dependencies = this.dependencies; - if (dependencies != null) { - for (final dependency in dependencies) { - dependency.accept(visitor); - } - } - } -} - -class RiverpodAnnotation extends RiverpodAst { - RiverpodAnnotation._({ - required this.annotation, - required this.element, - required this.keepAliveNode, - required this.dependencies, - }); - - static RiverpodAnnotation? _parse( - Declaration node, - ) { - final annotatedElement = node.declaredElement; - if (annotatedElement == null) return null; - - for (final annotation in node.metadata) { - final elementAnnotation = annotation.elementAnnotation; - final annotationElement = annotation.element; - if (elementAnnotation == null || annotationElement == null) continue; - if (annotationElement is! ExecutableElement || - !riverpodType.isExactlyType(annotationElement.returnType)) { - // The annotation is not an @Riverpod - continue; - } - - final dartObject = elementAnnotation.computeConstantValue(); - if (dartObject == null) return null; - - NamedExpression? keepAliveNode; - NamedExpression? dependenciesNode; - final argumentList = annotation.arguments; - if (argumentList != null) { - for (final argument - in argumentList.arguments.whereType()) { - switch (argument.name.label.name) { - case 'keepAlive': - keepAliveNode = argument; - case 'dependencies': - dependenciesNode = argument; - } - } - } - - final riverpodAnnotationElement = - RiverpodAnnotationElement.parse(annotatedElement); - if (riverpodAnnotationElement == null) return null; - - final dependencies = _parseDependencies(dependenciesNode); - - final riverpodAnnotation = RiverpodAnnotation._( - annotation: annotation, - element: riverpodAnnotationElement, - keepAliveNode: keepAliveNode, - dependencies: dependencies, - ); - dependencies?._parent = riverpodAnnotation; - - return riverpodAnnotation; - } - - return null; - } - - static RiverpodAnnotationDependencies? _parseDependencies( - NamedExpression? dependenciesNode, - ) { - if (dependenciesNode == null) return null; - final dependenciesNodeValue = dependenciesNode.expression; - // TODO handle Riverpod(dependencies:null) - - final dependencies = []; - - if (dependenciesNodeValue is! ListLiteral) { - errorReporter?.call( - RiverpodAnalysisError( - '@Riverpod(dependencies: <...>) only support list literals (using []).', - targetNode: dependenciesNodeValue, - ), - ); - } else { - for (final dependency in dependenciesNodeValue.elements) { - if (dependency is! Expression) { - errorReporter?.call( - RiverpodAnalysisError( - '@Riverpod(dependencies: [...]) does not support if/for/spread operators.', - targetNode: dependency, - ), - ); - continue; - } - - if (dependency is! SimpleIdentifier) { - errorReporter?.call( - RiverpodAnalysisError( - 'Only elements annotated with @riverpod are supported as "dependencies".', - targetNode: dependency, - ), - ); - continue; - } - - final dependencyElement = dependency.staticElement; - if (dependencyElement is FunctionElement) { - final dependencyProvider = FunctionalProviderDeclarationElement.parse( - dependencyElement, - annotation: null, - ); - if (dependencyProvider == null) { - errorReporter?.call( - RiverpodAnalysisError( - 'The dependency $dependency is not a class annotated with @riverpod', - targetNode: dependency, - ), - ); - continue; - } - - dependencies.add( - RiverpodAnnotationDependency._( - node: dependency, - provider: dependencyProvider, - ), - ); - } else if (dependencyElement is ClassElement) { - final dependencyProvider = ClassBasedProviderDeclarationElement.parse( - dependencyElement, - annotation: null, - ); - if (dependencyProvider == null) { - errorReporter?.call( - RiverpodAnalysisError( - 'The dependency $dependency is not a class annotated with @riverpod', - targetNode: dependency, - ), - ); - continue; - } - - dependencies.add( - RiverpodAnnotationDependency._( - node: dependency, - provider: dependencyProvider, - ), - ); - } else { - errorReporter?.call( - RiverpodAnalysisError( - '@Riverpod(dependencies: [...]) only supports elements annotated with @riverpod as values.', - targetNode: dependency, - ), - ); - } - } - } - - final riverpodAnnotationDependencies = RiverpodAnnotationDependencies._( - node: dependenciesNode, - dependencies: dependencies, - ); - - for (final dependency in dependencies) { - dependency._parent = riverpodAnnotationDependencies; - } - - return riverpodAnnotationDependencies; - } - - final Annotation annotation; - final RiverpodAnnotationElement element; - final NamedExpression? keepAliveNode; - final RiverpodAnnotationDependencies? dependencies; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitRiverpodAnnotation(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - dependencies?.accept(visitor); - } -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/visitor.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/visitor.dart deleted file mode 100644 index 9d35c490f..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/visitor.dart +++ /dev/null @@ -1,986 +0,0 @@ -part of '../riverpod_ast.dart'; - -abstract class RiverpodAstVisitor { - void visitProviderOverrideList( - ProviderOverrideList overrideList, - ); - void visitProviderOverrideExpression( - ProviderOverrideExpression expression, - ); - - void visitResolvedRiverpodUnit(ResolvedRiverpodLibraryResult result); - - void visitProviderScopeInstanceCreationExpression( - ProviderScopeInstanceCreationExpression container, - ); - void visitProviderContainerInstanceCreationExpression( - ProviderContainerInstanceCreationExpression expression, - ); - - void visitRiverpodAnnotation( - RiverpodAnnotation annotation, - ); - void visitRiverpodAnnotationDependency( - RiverpodAnnotationDependency dependency, - ); - void visitRiverpodAnnotationDependencies( - RiverpodAnnotationDependencies dependencies, - ); - - void visitLegacyProviderDeclaration( - LegacyProviderDeclaration declaration, - ); - void visitLegacyProviderDependencies( - LegacyProviderDependencies dependencies, - ); - void visitLegacyProviderDependency( - LegacyProviderDependency dependency, - ); - - void visitClassBasedProviderDeclaration( - ClassBasedProviderDeclaration declaration, - ); - void visitFunctionalProviderDeclaration( - FunctionalProviderDeclaration declaration, - ); - - void visitProviderListenableExpression( - ProviderListenableExpression expression, - ); - - void visitRefWatchInvocation(RefWatchInvocation invocation); - void visitRefListenInvocation(RefListenInvocation invocation); - void visitRefReadInvocation(RefReadInvocation invocation); - - void visitWidgetRefReadInvocation(WidgetRefReadInvocation invocation); - void visitWidgetRefWatchInvocation(WidgetRefWatchInvocation invocation); - void visitWidgetRefListenInvocation(WidgetRefListenInvocation invocation); - void visitWidgetRefListenManualInvocation( - WidgetRefListenManualInvocation invocation, - ); - - void visitConsumerWidgetDeclaration(ConsumerWidgetDeclaration declaration); - void visitHookConsumerWidgetDeclaration( - HookConsumerWidgetDeclaration declaration, - ); - void visitConsumerStatefulWidgetDeclaration( - ConsumerStatefulWidgetDeclaration declaration, - ); - void visitStatefulHookConsumerWidgetDeclaration( - StatefulHookConsumerWidgetDeclaration declaration, - ); - void visitConsumerStateDeclaration(ConsumerStateDeclaration declaration); -} - -class RecursiveRiverpodAstVisitor extends RiverpodAstVisitor { - @override - void visitProviderOverrideList( - ProviderOverrideList overrideList, - ) { - overrideList.visitChildren(this); - } - - @override - void visitProviderOverrideExpression( - ProviderOverrideExpression expression, - ) { - expression.visitChildren(this); - } - - @override - void visitProviderScopeInstanceCreationExpression( - ProviderScopeInstanceCreationExpression container, - ) { - container.visitChildren(this); - } - - @override - void visitProviderContainerInstanceCreationExpression( - ProviderContainerInstanceCreationExpression expression, - ) { - expression.visitChildren(this); - } - - @override - void visitConsumerStateDeclaration(ConsumerStateDeclaration declaration) { - declaration.visitChildren(this); - } - - @override - void visitConsumerWidgetDeclaration(ConsumerWidgetDeclaration declaration) { - declaration.visitChildren(this); - } - - @override - void visitLegacyProviderDeclaration(LegacyProviderDeclaration declaration) { - declaration.visitChildren(this); - } - - @override - void visitLegacyProviderDependencies( - LegacyProviderDependencies dependencies, - ) { - dependencies.visitChildren(this); - } - - @override - void visitLegacyProviderDependency(LegacyProviderDependency dependency) { - dependency.visitChildren(this); - } - - @override - void visitProviderListenableExpression( - ProviderListenableExpression expression, - ) { - expression.visitChildren(this); - } - - @override - void visitRefListenInvocation(RefListenInvocation invocation) { - invocation.visitChildren(this); - } - - @override - void visitRefReadInvocation(RefReadInvocation invocation) { - invocation.visitChildren(this); - } - - @override - void visitRefWatchInvocation(RefWatchInvocation invocation) { - invocation.visitChildren(this); - } - - @override - void visitResolvedRiverpodUnit(ResolvedRiverpodLibraryResult result) { - result.visitChildren(this); - } - - @override - void visitRiverpodAnnotation(RiverpodAnnotation annotation) { - annotation.visitChildren(this); - } - - @override - void visitRiverpodAnnotationDependency( - RiverpodAnnotationDependency dependency, - ) { - dependency.visitChildren(this); - } - - @override - void visitRiverpodAnnotationDependencies( - RiverpodAnnotationDependencies dependencies, - ) { - dependencies.visitChildren(this); - } - - @override - void visitConsumerStatefulWidgetDeclaration( - ConsumerStatefulWidgetDeclaration declaration, - ) { - declaration.visitChildren(this); - } - - @override - void visitClassBasedProviderDeclaration( - ClassBasedProviderDeclaration declaration, - ) { - declaration.visitChildren(this); - } - - @override - void visitFunctionalProviderDeclaration( - FunctionalProviderDeclaration declaration, - ) { - declaration.visitChildren(this); - } - - @override - void visitWidgetRefListenInvocation(WidgetRefListenInvocation invocation) { - invocation.visitChildren(this); - } - - @override - void visitWidgetRefListenManualInvocation( - WidgetRefListenManualInvocation invocation, - ) { - invocation.visitChildren(this); - } - - @override - void visitWidgetRefReadInvocation(WidgetRefReadInvocation invocation) { - invocation.visitChildren(this); - } - - @override - void visitWidgetRefWatchInvocation(WidgetRefWatchInvocation invocation) { - invocation.visitChildren(this); - } - - @override - void visitHookConsumerWidgetDeclaration( - HookConsumerWidgetDeclaration declaration, - ) { - declaration.visitChildren(this); - } - - @override - void visitStatefulHookConsumerWidgetDeclaration( - StatefulHookConsumerWidgetDeclaration declaration, - ) { - declaration.visitChildren(this); - } -} - -class SimpleRiverpodAstVisitor extends RiverpodAstVisitor { - @override - void visitProviderOverrideList( - ProviderOverrideList overrideList, - ) {} - - @override - void visitProviderOverrideExpression( - ProviderOverrideExpression expression, - ) {} - - @override - void visitProviderScopeInstanceCreationExpression( - ProviderScopeInstanceCreationExpression container, - ) {} - - @override - void visitProviderContainerInstanceCreationExpression( - ProviderContainerInstanceCreationExpression expression, - ) {} - - @override - void visitConsumerStateDeclaration(ConsumerStateDeclaration declaration) {} - - @override - void visitConsumerStatefulWidgetDeclaration( - ConsumerStatefulWidgetDeclaration declaration, - ) {} - - @override - void visitConsumerWidgetDeclaration(ConsumerWidgetDeclaration declaration) {} - - @override - void visitHookConsumerWidgetDeclaration( - HookConsumerWidgetDeclaration declaration, - ) {} - - @override - void visitLegacyProviderDeclaration(LegacyProviderDeclaration declaration) {} - - @override - void visitLegacyProviderDependencies( - LegacyProviderDependencies dependencies, - ) {} - - @override - void visitLegacyProviderDependency(LegacyProviderDependency dependency) {} - - @override - void visitProviderListenableExpression( - ProviderListenableExpression expression, - ) {} - - @override - void visitRefListenInvocation(RefListenInvocation invocation) {} - - @override - void visitRefReadInvocation(RefReadInvocation invocation) {} - - @override - void visitRefWatchInvocation(RefWatchInvocation invocation) {} - - @override - void visitResolvedRiverpodUnit(ResolvedRiverpodLibraryResult result) {} - - @override - void visitRiverpodAnnotation(RiverpodAnnotation annotation) {} - - @override - void visitRiverpodAnnotationDependency( - RiverpodAnnotationDependency dependency, - ) {} - - @override - void visitRiverpodAnnotationDependencies( - RiverpodAnnotationDependencies dependencies, - ) {} - - @override - void visitStatefulHookConsumerWidgetDeclaration( - StatefulHookConsumerWidgetDeclaration declaration, - ) {} - - @override - void visitClassBasedProviderDeclaration( - ClassBasedProviderDeclaration declaration, - ) {} - - @override - void visitFunctionalProviderDeclaration( - FunctionalProviderDeclaration declaration, - ) {} - - @override - void visitWidgetRefListenInvocation(WidgetRefListenInvocation invocation) {} - - @override - void visitWidgetRefListenManualInvocation( - WidgetRefListenManualInvocation invocation, - ) {} - - @override - void visitWidgetRefReadInvocation(WidgetRefReadInvocation invocation) {} - - @override - void visitWidgetRefWatchInvocation(WidgetRefWatchInvocation invocation) {} -} - -class UnimplementedRiverpodAstVisitor extends RiverpodAstVisitor { - @override - void visitProviderOverrideList( - ProviderOverrideList overrideList, - ) { - throw UnimplementedError( - 'implement visitProviderOverrideList', - ); - } - - @override - void visitProviderOverrideExpression( - ProviderOverrideExpression expression, - ) { - throw UnimplementedError( - 'implement visitProviderOverrideExpression', - ); - } - - @override - void visitProviderScopeInstanceCreationExpression( - ProviderScopeInstanceCreationExpression container, - ) { - throw UnimplementedError( - 'implement visitProviderScopeInstanceCreationExpression', - ); - } - - @override - void visitProviderContainerInstanceCreationExpression( - ProviderContainerInstanceCreationExpression expression, - ) { - throw UnimplementedError( - 'implement visitProviderContainerInstanceCreationExpression', - ); - } - - @override - void visitConsumerStateDeclaration(ConsumerStateDeclaration declaration) { - throw UnimplementedError('implement visitConsumerStateDeclaration'); - } - - @override - void visitConsumerStatefulWidgetDeclaration( - ConsumerStatefulWidgetDeclaration declaration, - ) { - throw UnimplementedError( - 'implement visitConsumerStatefulWidgetDeclaration', - ); - } - - @override - void visitConsumerWidgetDeclaration(ConsumerWidgetDeclaration declaration) { - throw UnimplementedError('implement visitConsumerWidgetDeclaration'); - } - - @override - void visitHookConsumerWidgetDeclaration( - HookConsumerWidgetDeclaration declaration, - ) { - throw UnimplementedError('implement visitHookConsumerWidgetDeclaration'); - } - - @override - void visitLegacyProviderDeclaration(LegacyProviderDeclaration declaration) { - throw UnimplementedError('implement visitLegacyProviderDeclaration'); - } - - @override - void visitLegacyProviderDependencies( - LegacyProviderDependencies dependencies, - ) { - throw UnimplementedError('implement visitLegacyProviderDependencies'); - } - - @override - void visitLegacyProviderDependency(LegacyProviderDependency dependency) { - throw UnimplementedError('implement visitLegacyProviderDependency'); - } - - @override - void visitProviderListenableExpression( - ProviderListenableExpression expression, - ) { - throw UnimplementedError('implement visitProviderListenableExpression'); - } - - @override - void visitRefListenInvocation(RefListenInvocation invocation) { - throw UnimplementedError('implement visitRefListenInvocation'); - } - - @override - void visitRefReadInvocation(RefReadInvocation invocation) { - throw UnimplementedError('implement visitRefReadInvocation'); - } - - @override - void visitRefWatchInvocation(RefWatchInvocation invocation) { - throw UnimplementedError('implement visitRefWatchInvocation'); - } - - @override - void visitResolvedRiverpodUnit(ResolvedRiverpodLibraryResult result) { - throw UnimplementedError('implement visitResolvedRiverpodUnit'); - } - - @override - void visitRiverpodAnnotation(RiverpodAnnotation annotation) { - throw UnimplementedError('implement visitRiverpodAnnotation'); - } - - @override - void visitRiverpodAnnotationDependency( - RiverpodAnnotationDependency dependency, - ) { - throw UnimplementedError('implement visitRiverpodAnnotationDependency'); - } - - @override - void visitRiverpodAnnotationDependencies( - RiverpodAnnotationDependencies dependencies, - ) { - throw UnimplementedError('implement visitRiverpodAnnotationDependencies'); - } - - @override - void visitStatefulHookConsumerWidgetDeclaration( - StatefulHookConsumerWidgetDeclaration declaration, - ) { - throw UnimplementedError( - 'implement visitStatefulHookConsumerWidgetDeclaration', - ); - } - - @override - void visitClassBasedProviderDeclaration( - ClassBasedProviderDeclaration declaration, - ) { - throw UnimplementedError('implement visitClassBasedProviderDeclaration'); - } - - @override - void visitFunctionalProviderDeclaration( - FunctionalProviderDeclaration declaration, - ) { - throw UnimplementedError('implement visitFunctionalProviderDeclaration'); - } - - @override - void visitWidgetRefListenInvocation(WidgetRefListenInvocation invocation) { - throw UnimplementedError('implement visitWidgetRefListenInvocation'); - } - - @override - void visitWidgetRefListenManualInvocation( - WidgetRefListenManualInvocation invocation, - ) { - throw UnimplementedError('implement visitWidgetRefListenManualInvocation'); - } - - @override - void visitWidgetRefReadInvocation(WidgetRefReadInvocation invocation) { - throw UnimplementedError('implement visitWidgetRefReadInvocation'); - } - - @override - void visitWidgetRefWatchInvocation(WidgetRefWatchInvocation invocation) { - throw UnimplementedError('implement visitWidgetRefWatchInvocation'); - } -} - -class RiverpodAstRegistry { - void run(RiverpodAst node) { - node.accept(_RiverpodAstRegistryVisitor(this)); - } - - // misc - final _onResolvedRiverpodUnit = - []; - void addRiverpodUnit(void Function(ResolvedRiverpodLibraryResult) cb) { - _onResolvedRiverpodUnit.add(cb); - } - - // Both generator and legacy visitors - void addProviderDeclaration(void Function(ProviderDeclaration) cb) { - addGeneratorProviderDeclaration(cb); - addLegacyProviderDeclaration(cb); - } - - // Generator-specific visitors - - void addGeneratorProviderDeclaration( - void Function(GeneratorProviderDeclaration) cb, - ) { - addClassBasedProviderDeclaration(cb); - addFunctionalProviderDeclaration(cb); - } - - final _onClassBasedProviderDeclaration = - []; - void addClassBasedProviderDeclaration( - void Function(ClassBasedProviderDeclaration) cb, - ) { - _onClassBasedProviderDeclaration.add(cb); - } - - final _onFunctionalProviderDeclaration = - []; - void addFunctionalProviderDeclaration( - void Function(FunctionalProviderDeclaration) cb, - ) { - _onFunctionalProviderDeclaration.add(cb); - } - - final _onRiverpodAnnotation = []; - void addRiverpodAnnotation( - void Function(RiverpodAnnotation) cb, - ) { - _onRiverpodAnnotation.add(cb); - } - - final _onRiverpodAnnotationDependency = - []; - void addRiverpodAnnotationDependency( - void Function(RiverpodAnnotationDependency) cb, - ) { - _onRiverpodAnnotationDependency.add(cb); - } - - final _onRiverpodAnnotationDependencies = - []; - void addRiverpodAnnotationDependencies( - void Function(RiverpodAnnotationDependencies) cb, - ) { - _onRiverpodAnnotationDependencies.add(cb); - } - - // Legacy-specific visitors - - final _onLegacyProviderDeclaration = - []; - void addLegacyProviderDeclaration( - void Function(LegacyProviderDeclaration) cb, - ) { - _onLegacyProviderDeclaration.add(cb); - } - - final _onLegacyProviderDependencies = - []; - void addLegacyProviderDependencies( - void Function(LegacyProviderDependencies) cb, - ) { - _onLegacyProviderDependencies.add(cb); - } - - final _onLegacyProviderDependency = - []; - void addLegacyProviderDependency( - void Function(LegacyProviderDependency) cb, - ) { - _onLegacyProviderDependency.add(cb); - } - - // Ref life-cycle visitors - - final _onRefInvocation = []; - void addRefInvocation( - void Function(RefInvocation) cb, - ) { - _onRefInvocation.add(cb); - } - - final _onRefWatchInvocation = []; - void addRefWatchInvocation( - void Function(RefWatchInvocation) cb, - ) { - _onRefWatchInvocation.add(cb); - } - - final _onRefListenInvocation = []; - void addRefListenInvocation( - void Function(RefListenInvocation) cb, - ) { - _onRefListenInvocation.add(cb); - } - - final _onRefReadInvocation = []; - void addRefReadInvocation( - void Function(RefReadInvocation) cb, - ) { - _onRefReadInvocation.add(cb); - } - - // WidgetRef life-cycle visitors - - final _onWidgetRefInvocation = []; - void addWidgetRefInvocation( - void Function(WidgetRefInvocation) cb, - ) { - _onWidgetRefInvocation.add(cb); - } - - final _onWidgetRefWatchInvocation = - []; - void addWidgetRefWatchInvocation( - void Function(WidgetRefWatchInvocation) cb, - ) { - _onWidgetRefWatchInvocation.add(cb); - } - - final _onWidgetRefReadInvocation = []; - void addWidgetRefReadInvocation( - void Function(WidgetRefReadInvocation) cb, - ) { - _onWidgetRefReadInvocation.add(cb); - } - - final _onWidgetRefListenInvocation = - []; - void addWidgetRefListenInvocation( - void Function(WidgetRefListenInvocation) cb, - ) { - _onWidgetRefListenInvocation.add(cb); - } - - final _onWidgetRefListenManualInvocation = - []; - void addWidgetRefListenManualInvocation( - void Function(WidgetRefListenManualInvocation) cb, - ) { - _onWidgetRefListenManualInvocation.add(cb); - } - - // Ref misc - - final _onProviderListenableExpression = - []; - void addProviderListenableExpression( - void Function(ProviderListenableExpression) cb, - ) { - _onProviderListenableExpression.add(cb); - } - - // Consumers - - final _onConsumerWidgetDeclaration = - []; - void addConsumerWidgetDeclaration( - void Function(ConsumerWidgetDeclaration) cb, - ) { - _onConsumerWidgetDeclaration.add(cb); - } - - final _onHookConsumerWidgetDeclaration = - []; - void addHookConsumerWidgetDeclaration( - void Function(HookConsumerWidgetDeclaration) cb, - ) { - _onHookConsumerWidgetDeclaration.add(cb); - } - - final _onStatefulHookConsumerWidgetDeclaration = - []; - void addStatefulHookConsumerWidgetDeclaration( - void Function(StatefulHookConsumerWidgetDeclaration) cb, - ) { - _onStatefulHookConsumerWidgetDeclaration.add(cb); - } - - final _onConsumerStatefulWidgetDeclaration = - []; - void addConsumerStatefulWidgetDeclaration( - void Function(ConsumerStatefulWidgetDeclaration) cb, - ) { - _onConsumerStatefulWidgetDeclaration.add(cb); - } - - final _onConsumerStateDeclaration = - []; - void addConsumerStateDeclaration( - void Function(ConsumerStateDeclaration) cb, - ) { - _onConsumerStateDeclaration.add(cb); - } - - final _onProviderScopeInstanceCreationExpression = - []; - void addProviderScopeInstanceCreationExpression( - void Function(ProviderScopeInstanceCreationExpression) cb, - ) { - _onProviderScopeInstanceCreationExpression.add(cb); - } - - final _onProviderContainerInstanceCreationExpression = - []; - void addProviderContainerInstanceCreationExpression( - void Function(ProviderContainerInstanceCreationExpression) cb, - ) { - _onProviderContainerInstanceCreationExpression.add(cb); - } - - final _onProviderOverrideExpression = - []; - void addProviderOverrideExpression( - void Function(ProviderOverrideExpression) cb, - ) { - _onProviderOverrideExpression.add(cb); - } - - final _onProviderOverrideList = []; - void addProviderOverrideList(void Function(ProviderOverrideList) cb) { - _onProviderOverrideList.add(cb); - } -} - -// Voluntarily not extenting RiverpodAstVisitor to trigger a compilation error -// when new nodes are added. -class _RiverpodAstRegistryVisitor extends RiverpodAstVisitor { - _RiverpodAstRegistryVisitor(this._registry); - - final RiverpodAstRegistry _registry; - - @override - void visitProviderOverrideList( - ProviderOverrideList overrideList, - ) { - overrideList.visitChildren(this); - _runSubscriptions( - overrideList, - _registry._onProviderOverrideList, - ); - } - - @override - void visitProviderOverrideExpression( - ProviderOverrideExpression expression, - ) { - expression.visitChildren(this); - _runSubscriptions( - expression, - _registry._onProviderOverrideExpression, - ); - } - - @override - void visitProviderScopeInstanceCreationExpression( - ProviderScopeInstanceCreationExpression container, - ) { - container.visitChildren(this); - _runSubscriptions( - container, - _registry._onProviderScopeInstanceCreationExpression, - ); - } - - @override - void visitProviderContainerInstanceCreationExpression( - ProviderContainerInstanceCreationExpression expression, - ) { - expression.visitChildren(this); - _runSubscriptions( - expression, - _registry._onProviderContainerInstanceCreationExpression, - ); - } - - @override - void visitConsumerStateDeclaration(ConsumerStateDeclaration declaration) { - declaration.visitChildren(this); - _runSubscriptions(declaration, _registry._onConsumerStateDeclaration); - } - - @override - void visitConsumerWidgetDeclaration(ConsumerWidgetDeclaration declaration) { - declaration.visitChildren(this); - _runSubscriptions(declaration, _registry._onConsumerWidgetDeclaration); - } - - @override - void visitLegacyProviderDeclaration(LegacyProviderDeclaration declaration) { - declaration.visitChildren(this); - _runSubscriptions(declaration, _registry._onLegacyProviderDeclaration); - } - - @override - void visitLegacyProviderDependencies( - LegacyProviderDependencies dependencies, - ) { - dependencies.visitChildren(this); - _runSubscriptions(dependencies, _registry._onLegacyProviderDependencies); - } - - @override - void visitLegacyProviderDependency(LegacyProviderDependency dependency) { - dependency.visitChildren(this); - _runSubscriptions(dependency, _registry._onLegacyProviderDependency); - } - - @override - void visitProviderListenableExpression( - ProviderListenableExpression expression, - ) { - expression.visitChildren(this); - _runSubscriptions(expression, _registry._onProviderListenableExpression); - } - - @override - void visitRefListenInvocation(RefListenInvocation invocation) { - invocation.visitChildren(this); - _runSubscriptions(invocation, _registry._onRefListenInvocation); - } - - @override - void visitRefReadInvocation(RefReadInvocation invocation) { - invocation.visitChildren(this); - _runSubscriptions(invocation, _registry._onRefReadInvocation); - } - - @override - void visitRefWatchInvocation(RefWatchInvocation invocation) { - invocation.visitChildren(this); - _runSubscriptions(invocation, _registry._onRefWatchInvocation); - } - - @override - void visitResolvedRiverpodUnit(ResolvedRiverpodLibraryResult result) { - result.visitChildren(this); - _runSubscriptions(result, _registry._onResolvedRiverpodUnit); - } - - @override - void visitRiverpodAnnotation(RiverpodAnnotation annotation) { - annotation.visitChildren(this); - _runSubscriptions(annotation, _registry._onRiverpodAnnotation); - } - - @override - void visitRiverpodAnnotationDependency( - RiverpodAnnotationDependency dependency, - ) { - dependency.visitChildren(this); - _runSubscriptions(dependency, _registry._onRiverpodAnnotationDependency); - } - - @override - void visitRiverpodAnnotationDependencies( - RiverpodAnnotationDependencies dependencies, - ) { - dependencies.visitChildren(this); - _runSubscriptions( - dependencies, - _registry._onRiverpodAnnotationDependencies, - ); - } - - @override - void visitConsumerStatefulWidgetDeclaration( - ConsumerStatefulWidgetDeclaration declaration, - ) { - declaration.visitChildren(this); - _runSubscriptions( - declaration, - _registry._onConsumerStatefulWidgetDeclaration, - ); - } - - @override - void visitClassBasedProviderDeclaration( - ClassBasedProviderDeclaration declaration, - ) { - declaration.visitChildren(this); - _runSubscriptions(declaration, _registry._onClassBasedProviderDeclaration); - } - - @override - void visitFunctionalProviderDeclaration( - FunctionalProviderDeclaration declaration, - ) { - declaration.visitChildren(this); - _runSubscriptions( - declaration, - _registry._onFunctionalProviderDeclaration, - ); - } - - @override - void visitWidgetRefListenInvocation(WidgetRefListenInvocation invocation) { - invocation.visitChildren(this); - _runSubscriptions(invocation, _registry._onWidgetRefListenInvocation); - } - - @override - void visitWidgetRefListenManualInvocation( - WidgetRefListenManualInvocation invocation, - ) { - invocation.visitChildren(this); - _runSubscriptions(invocation, _registry._onWidgetRefListenManualInvocation); - } - - @override - void visitWidgetRefReadInvocation(WidgetRefReadInvocation invocation) { - invocation.visitChildren(this); - _runSubscriptions(invocation, _registry._onWidgetRefReadInvocation); - } - - @override - void visitWidgetRefWatchInvocation(WidgetRefWatchInvocation invocation) { - invocation.visitChildren(this); - _runSubscriptions(invocation, _registry._onWidgetRefWatchInvocation); - } - - void _runSubscriptions( - R value, - List subscriptions, - ) { - for (final sub in subscriptions) { - try { - sub(value); - } catch (e, stack) { - Zone.current.handleUncaughtError(e, stack); - } - } - } - - @override - void visitHookConsumerWidgetDeclaration( - HookConsumerWidgetDeclaration declaration, - ) { - declaration.visitChildren(this); - _runSubscriptions(declaration, _registry._onHookConsumerWidgetDeclaration); - } - - @override - void visitStatefulHookConsumerWidgetDeclaration( - StatefulHookConsumerWidgetDeclaration declaration, - ) { - declaration.visitChildren(this); - _runSubscriptions( - declaration, - _registry._onStatefulHookConsumerWidgetDeclaration, - ); - } -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/widget_ref_invocation.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/widget_ref_invocation.dart deleted file mode 100644 index 79b3ce5d8..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_ast/widget_ref_invocation.dart +++ /dev/null @@ -1,263 +0,0 @@ -part of '../riverpod_ast.dart'; - -abstract class WidgetRefInvocation extends RiverpodAst - implements ProviderListenableExpressionParent { - WidgetRefInvocation._({ - required this.node, - required this.function, - }); - - static WidgetRefInvocation? _parse( - MethodInvocation node, { - required void Function() superCall, - }) { - final targetType = node.realTarget?.staticType; - if (targetType == null) return null; - - // Since Ref is sealed, checking that the function is from the package:riverpod - // before checking its type skips iterating over the superclasses of an element - // if it's not from Riverpod. - if (!isFromFlutterRiverpod.isExactlyType(targetType) | - !widgetRefType.isAssignableFromType(targetType)) { - return null; - } - final function = node.function; - if (function is! SimpleIdentifier) return null; - final functionOwner = function.staticElement - .cast() - ?.declaration - .enclosingElement3; - - if (functionOwner == null || - // Since Ref is sealed, checking that the function is from the package:riverpod - // before checking its type skips iterating over the superclasses of an element - // if it's not from Riverpod. - !isFromFlutterRiverpod.isExactly(functionOwner) || - !widgetRefType.isAssignableFrom(functionOwner)) { - return null; - } - - switch (function.name) { - case 'watch': - return WidgetRefWatchInvocation._parse( - node, - function, - superCall: superCall, - ); - case 'read': - return WidgetRefReadInvocation._parse( - node, - function, - superCall: superCall, - ); - case 'listen': - return WidgetRefListenInvocation._parse( - node, - function, - superCall: superCall, - ); - case 'listenManual': - return WidgetRefListenManualInvocation._parse( - node, - function, - superCall: superCall, - ); - - default: - return null; - } - } - - final MethodInvocation node; - final SimpleIdentifier function; -} - -class WidgetRefWatchInvocation extends WidgetRefInvocation { - WidgetRefWatchInvocation._({ - required super.node, - required super.function, - required this.provider, - }) : super._(); - - static WidgetRefWatchInvocation? _parse( - MethodInvocation node, - SimpleIdentifier function, { - required void Function() superCall, - }) { - assert( - function.name == 'watch', - 'Argument error, function is not a ref.watch function', - ); - - final providerListenableExpression = ProviderListenableExpression._parse( - node.argumentList.positionalArguments().singleOrNull, - ); - if (providerListenableExpression == null) return null; - - final widgetRefWatchInvocation = WidgetRefWatchInvocation._( - node: node, - function: function, - provider: providerListenableExpression, - ); - providerListenableExpression._parent = widgetRefWatchInvocation; - return widgetRefWatchInvocation; - } - - final ProviderListenableExpression provider; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitWidgetRefWatchInvocation(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - provider.accept(visitor); - } -} - -class WidgetRefReadInvocation extends WidgetRefInvocation { - WidgetRefReadInvocation._({ - required super.node, - required super.function, - required this.provider, - }) : super._(); - - static WidgetRefReadInvocation? _parse( - MethodInvocation node, - SimpleIdentifier function, { - required void Function() superCall, - }) { - assert( - function.name == 'read', - 'Argument error, function is not a ref.read function', - ); - - final providerListenableExpression = ProviderListenableExpression._parse( - node.argumentList.positionalArguments().singleOrNull, - ); - if (providerListenableExpression == null) return null; - - final widgetRefReadInvocation = WidgetRefReadInvocation._( - node: node, - function: function, - provider: providerListenableExpression, - ); - providerListenableExpression._parent = widgetRefReadInvocation; - return widgetRefReadInvocation; - } - - final ProviderListenableExpression provider; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitWidgetRefReadInvocation(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - provider.accept(visitor); - } -} - -class WidgetRefListenInvocation extends WidgetRefInvocation { - WidgetRefListenInvocation._({ - required super.node, - required super.function, - required this.provider, - required this.listener, - }) : super._(); - - static WidgetRefListenInvocation? _parse( - MethodInvocation node, - SimpleIdentifier function, { - required void Function() superCall, - }) { - assert( - function.name == 'listen', - 'Argument error, function is not a ref.listen function', - ); - - final positionalArgs = node.argumentList.positionalArguments().toList(); - final listener = positionalArgs.elementAtOrNull(1); - if (listener == null) return null; - - final providerListenableExpression = ProviderListenableExpression._parse( - positionalArgs.firstOrNull, - ); - if (providerListenableExpression == null) return null; - - final widgetRefListenInvocation = WidgetRefListenInvocation._( - node: node, - function: function, - provider: providerListenableExpression, - listener: listener, - ); - providerListenableExpression._parent = widgetRefListenInvocation; - return widgetRefListenInvocation; - } - - final ProviderListenableExpression provider; - final Expression listener; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitWidgetRefListenInvocation(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - provider.accept(visitor); - } -} - -class WidgetRefListenManualInvocation extends WidgetRefInvocation { - WidgetRefListenManualInvocation._({ - required super.node, - required super.function, - required this.provider, - required this.listener, - }) : super._(); - - static WidgetRefListenManualInvocation? _parse( - MethodInvocation node, - SimpleIdentifier function, { - required void Function() superCall, - }) { - assert( - function.name == 'listenManual', - 'Argument error, function is not a ref.listen function', - ); - - final positionalArgs = node.argumentList.positionalArguments().toList(); - final listener = positionalArgs.elementAtOrNull(1); - if (listener == null) return null; - - final providerListenableExpression = ProviderListenableExpression._parse( - positionalArgs.firstOrNull, - ); - if (providerListenableExpression == null) return null; - - final widgetRefListenManualInvocation = WidgetRefListenManualInvocation._( - node: node, - function: function, - provider: providerListenableExpression, - listener: listener, - ); - providerListenableExpression._parent = widgetRefListenManualInvocation; - return widgetRefListenManualInvocation; - } - - final ProviderListenableExpression provider; - final Expression listener; - - @override - void accept(RiverpodAstVisitor visitor) { - visitor.visitWidgetRefListenManualInvocation(this); - } - - @override - void visitChildren(RiverpodAstVisitor visitor) { - provider.accept(visitor); - } -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_element.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_element.dart deleted file mode 100644 index 6c1bdefae..000000000 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_element.dart +++ /dev/null @@ -1,380 +0,0 @@ -import 'package:analyzer/dart/constant/value.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:collection/collection.dart'; -import 'package:meta/meta.dart'; - -import '../riverpod_analyzer_utils.dart'; -import 'errors.dart'; - -class RiverpodAnnotationElement { - @internal - RiverpodAnnotationElement({ - required this.keepAlive, - required this.dependencies, - }) : allTransitiveDependencies = - _computeAllTransitiveDependencies(dependencies); - - @internal - static RiverpodAnnotationElement? parse(Element element) { - DartObject? annotation; - try { - annotation = riverpodType.firstAnnotationOfExact(element); - } catch (_) { - return RiverpodAnnotationElement( - keepAlive: false, - dependencies: null, - ); - } - if (annotation == null) return null; - - final dependencies = readDependencies(annotation)?.map((dep) { - final result = _parseDependency( - dep, - targetElement: element, - ); - if (result == null) { - errorReporter?.call( - RiverpodAnalysisError( - 'Failed to parse dependency $dep', - targetElement: element, - ), - ); - return null; - } - return result; - }).toSet(); - - if (dependencies?.any((e) => e == null) ?? false) { - // One of the dependencies failed to parse - return null; - } - - return RiverpodAnnotationElement( - keepAlive: readKeepAlive(annotation), - dependencies: dependencies?.cast(), - ); - } - - static GeneratorProviderDeclarationElement? _parseDependency( - DartObject object, { - required Element targetElement, - }) { - final functionType = object.toFunctionValue(); - if (functionType != null) { - final provider = FunctionalProviderDeclarationElement.parse( - functionType, - annotation: null, - ); - if (provider != null) return provider; - } - final valueType = object.toTypeValue(); - if (valueType != null) { - final provider = ClassBasedProviderDeclarationElement.parse( - valueType.element! as ClassElement, - annotation: null, - ); - if (provider != null) return provider; - } - errorReporter?.call( - RiverpodAnalysisError( - 'Unsupported dependency. ' - 'Only functions and classes annotated by @riverpod are supported.', - targetElement: targetElement, - ), - ); - return null; - } - - static Set? - _computeAllTransitiveDependencies( - Set? dependencies, - ) { - if (dependencies == null) return null; - - return { - ...dependencies, - for (final dependency in dependencies) - ...?dependency.annotation.allTransitiveDependencies, - }; - } - - @internal - static bool readKeepAlive(DartObject object) { - return object.getField('keepAlive')?.toBoolValue() ?? false; - } - - @internal - static List? readDependencies(DartObject object) { - return object.getField('dependencies')?.toListValue(); - } - - final bool keepAlive; - final Set? dependencies; - final Set? allTransitiveDependencies; -} - -abstract class ProviderDeclarationElement { - bool get isAutoDispose; - Element get element; - String get name; -} - -/// The class name for explicitly typed provider. -/// -/// Such as `FutureProvider` for `final provider = FutureProvider(...)`. -/// This is only about the type, and does not include autoDispose/family/... -enum LegacyProviderType { - /// Type for `ChangeNotifierProvider` - changeNotifierProvider, - - /// Type for `FutureProvider` - futureProvider, - - /// Type for `StreamProvider` - streamProvider, - - /// Type for `StateNotifierProvider` - stateNotifierProvider, - - /// Type for `StateProvider` - stateProvider, - - /// Type for `NotifierProvider` - notifierProvider, - - /// Type for `AsyncNotifierProvider` - asyncNotifierProvider, - - /// Type for `Provider` - provider; - - static LegacyProviderType? _parse(DartType providerType) { - if (anyFutureProviderType.isAssignableFromType(providerType)) { - return LegacyProviderType.futureProvider; - } - if (anyStreamProviderType.isAssignableFromType(providerType)) { - return LegacyProviderType.streamProvider; - } - if (anyStateProviderType.isAssignableFromType(providerType)) { - return LegacyProviderType.stateProvider; - } - if (anyStateNotifierProviderType.isAssignableFromType(providerType)) { - return LegacyProviderType.stateNotifierProvider; - } - if (anyProviderType.isAssignableFromType(providerType)) { - return LegacyProviderType.provider; - } - if (anyNotifierProviderType.isAssignableFromType(providerType)) { - return LegacyProviderType.notifierProvider; - } - if (anyAsyncNotifierProviderType.isAssignableFromType(providerType)) { - return LegacyProviderType.asyncNotifierProvider; - } - if (anyChangeNotifierProviderType.isAssignableFromType(providerType)) { - return LegacyProviderType.changeNotifierProvider; - } - - return null; - } -} - -class LegacyProviderDeclarationElement implements ProviderDeclarationElement { - LegacyProviderDeclarationElement._({ - required this.name, - required this.element, - required this.isAutoDispose, - required this.familyElement, - required this.providerType, - }); - - @internal - static LegacyProviderDeclarationElement? parse( - VariableElement element, - ) { - return _cache.putIfAbsent(element, () { - // Search for @ProviderFor annotation. If present, then this is a generated provider - if (providerForType.hasAnnotationOfExact( - element, - throwOnUnresolved: false, - )) { - return null; - } - - bool isAutoDispose; - LegacyFamilyInvocationElement? familyElement; - LegacyProviderType? providerType; - if (providerBaseType.isAssignableFromType(element.type)) { - isAutoDispose = !alwaysAliveProviderListenableType - .isAssignableFromType(element.type); - - providerType = LegacyProviderType._parse(element.type); - } else if (familyType.isAssignableFromType(element.type)) { - final callFn = (element.type as InterfaceType).lookUpMethod2( - 'call', - element.library!, - )!; - final parameter = callFn.parameters.single; - - isAutoDispose = !alwaysAliveProviderListenableType - .isAssignableFromType(callFn.returnType); - providerType = LegacyProviderType._parse(callFn.returnType); - familyElement = LegacyFamilyInvocationElement._(parameter.type); - } else { - // Not a provider - return null; - } - - return LegacyProviderDeclarationElement._( - name: element.name, - element: element, - isAutoDispose: isAutoDispose, - familyElement: familyElement, - providerType: providerType, - ); - }); - } - - static final _cache = Expando<_Box>(); - - @override - final VariableElement element; - - @override - final String name; - - @override - final bool isAutoDispose; - - final LegacyFamilyInvocationElement? familyElement; - - final LegacyProviderType? providerType; -} - -class LegacyFamilyInvocationElement { - LegacyFamilyInvocationElement._(this.parameterType); - final DartType parameterType; -} - -abstract class GeneratorProviderDeclarationElement - implements ProviderDeclarationElement { - RiverpodAnnotationElement get annotation; - - bool get isScoped => annotation.dependencies != null; - - @override - bool get isAutoDispose => !annotation.keepAlive; -} - -class ClassBasedProviderDeclarationElement - extends GeneratorProviderDeclarationElement { - ClassBasedProviderDeclarationElement._({ - required this.name, - required this.annotation, - required this.buildMethod, - required this.element, - }); - - @internal - static ClassBasedProviderDeclarationElement? parse( - ClassElement element, { - required RiverpodAnnotationElement? annotation, - }) { - return _cache.putIfAbsent(element, () { - final riverpodAnnotation = - annotation ?? RiverpodAnnotationElement.parse(element); - if (riverpodAnnotation == null) return null; - - final buildMethod = - element.methods.firstWhereOrNull((method) => method.name == 'build'); - - if (buildMethod == null) { - errorReporter?.call( - RiverpodAnalysisError( - 'No "build" method found. ' - 'Classes annotated with @riverpod must define a method named "build".', - targetElement: element, - ), - ); - - return null; - } - - return ClassBasedProviderDeclarationElement._( - name: element.name, - buildMethod: buildMethod, - element: element, - annotation: riverpodAnnotation, - ); - }); - } - - static final _cache = Expando<_Box>(); - - @override - final ClassElement element; - - @override - final String name; - - @override - final RiverpodAnnotationElement annotation; - - final ExecutableElement buildMethod; -} - -class FunctionalProviderDeclarationElement - extends GeneratorProviderDeclarationElement { - FunctionalProviderDeclarationElement._({ - required this.name, - required this.annotation, - required this.element, - }); - - @internal - static FunctionalProviderDeclarationElement? parse( - ExecutableElement element, { - required RiverpodAnnotationElement? annotation, - }) { - return _cache.putIfAbsent(element, () { - final riverpodAnnotation = RiverpodAnnotationElement.parse(element); - if (riverpodAnnotation == null) return null; - - return FunctionalProviderDeclarationElement._( - name: element.name, - annotation: riverpodAnnotation, - element: element, - ); - }); - } - - static final _cache = Expando<_Box>(); - - @override - final ExecutableElement element; - - @override - final String name; - - @override - final RiverpodAnnotationElement annotation; - - @override - bool get isScoped => super.isScoped || element.isExternal; -} - -/// An object for differentiating "no cache" from "cache but value is null". -class _Box { - _Box(this.value); - final T? value; -} - -extension on Expando<_Box> { - T? putIfAbsent(Object key, T? Function() value) { - final cache = this[key]; - if (cache != null) return cache.value; - - final box = this[key] = _Box(value()); - return box.value; - } -} diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_types.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_types.dart index 8d95da378..ffb01e333 100644 --- a/packages/riverpod_analyzer_utils/lib/src/riverpod_types.dart +++ b/packages/riverpod_analyzer_utils/lib/src/riverpod_types.dart @@ -2,138 +2,11 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:custom_lint_core/custom_lint_core.dart'; -/// TypeChecker for the `ProviderFor` annotation -const providerForType = TypeChecker.fromName( - 'ProviderFor', - packageName: 'riverpod_annotation', -); - -/// Matches with the `Riverpod` annotation from riverpod_annotation. -const riverpodType = - TypeChecker.fromName('Riverpod', packageName: 'riverpod_annotation'); - -/// [TypeChecker] for `ProviderBase` -const providerBaseType = TypeChecker.fromName( - 'ProviderBase', - packageName: 'riverpod', -); - -/// [TypeChecker] from `ProviderContainer` -const providerContainerType = TypeChecker.fromName( - 'ProviderContainer', - packageName: 'riverpod', -); - -/// [TypeChecker] from `ProviderScope` -const providerScopeType = TypeChecker.fromName( - 'ProviderScope', - packageName: 'flutter_riverpod', -); - -/// [TypeChecker] from `ProviderScope` -const uncontrolledProviderScopeType = TypeChecker.fromName( - 'UncontrolledProviderScope', - packageName: 'flutter_riverpod', -); - -/// [TypeChecker] for `AlwaysAliveProviderListenable` -const alwaysAliveProviderListenableType = TypeChecker.any([ - TypeChecker.fromName( - 'AlwaysAliveProviderListenable', - packageName: 'riverpod', - ), -]); - -/// Either FutureProvider or AutoDisposeFutureProvider -const anyFutureProviderType = TypeChecker.any([ - TypeChecker.fromName('FutureProvider', packageName: 'riverpod'), - TypeChecker.fromName('AutoDisposeFutureProvider', packageName: 'riverpod'), -]); - -/// Either StreamProvider or AutoDisposeStreamProvider -const anyStreamProviderType = TypeChecker.any([ - TypeChecker.fromName('StreamProvider', packageName: 'riverpod'), - TypeChecker.fromName('AutoDisposeStreamProvider', packageName: 'riverpod'), -]); - -/// Either NotifierProvider or AutoDisposeNotifierProvider or their family form -const anyNotifierProviderType = TypeChecker.any([ - TypeChecker.fromName('NotifierProviderImpl', packageName: 'riverpod'), - TypeChecker.fromName('FamilyNotifierProviderImpl', packageName: 'riverpod'), - TypeChecker.fromName( - 'AutoDisposeNotifierProviderImpl', - packageName: 'riverpod', - ), - TypeChecker.fromName( - 'AutoDisposeFamilyNotifierProviderImpl', - packageName: 'riverpod', - ), -]); - -/// Either AsyncNotifierProvider or AutoDisposeAsyncNotifierProvider or their family form -const anyAsyncNotifierProviderType = TypeChecker.any([ - TypeChecker.fromName('AsyncNotifierProviderImpl', packageName: 'riverpod'), - TypeChecker.fromName( - 'FamilyAsyncNotifierProviderImpl', - packageName: 'riverpod', - ), - TypeChecker.fromName( - 'AutoDisposeAsyncNotifierProviderImpl', - packageName: 'riverpod', - ), - TypeChecker.fromName( - 'AutoDisposeFamilyAsyncNotifierProviderImpl', - packageName: 'riverpod', - ), -]); - -/// Either ChangeNotifierProvider or AutoDisposeChangeNotifierProvider -const anyChangeNotifierProviderType = TypeChecker.any([ - TypeChecker.fromName( - 'ChangeNotifierProvider', - packageName: 'flutter_riverpod', - ), - TypeChecker.fromName( - 'AutoDisposeChangeNotifierProvider', - packageName: 'flutter_riverpod', - ), -]); - -/// Either StateNotifierProvider or AutoDisposeStateNotifierProvider -const anyStateNotifierProviderType = TypeChecker.any([ - TypeChecker.fromName('StateNotifierProvider', packageName: 'riverpod'), - TypeChecker.fromName( - 'AutoDisposeStateNotifierProvider', - packageName: 'riverpod', - ), -]); - -/// [TypeChecker] for `StateNotifier` -const stateNotifierType = TypeChecker.fromName( - 'StateNotifier', - packageName: 'state_notifier', -); - -/// [TypeChecker] for `ChangeNotifier` -const changeNotifierType = TypeChecker.fromName( - 'ChangeNotifier', - packageName: 'flutter', -); - -/// Either StateProvider or AutoDisposeStateProvider -const anyStateProviderType = TypeChecker.any([ - TypeChecker.fromName('StateProvider', packageName: 'riverpod'), - TypeChecker.fromName('AutoDisposeStateProvider', packageName: 'riverpod'), -]); - -/// Either Provider or AutoDisposeProvider -const anyProviderType = TypeChecker.any([ - TypeChecker.fromName('Provider', packageName: 'riverpod'), - TypeChecker.fromName('AutoDisposeProvider', packageName: 'riverpod'), -]); - -/// [TypeChecker] for `Family` -const familyType = TypeChecker.fromName('Family', packageName: 'riverpod'); +part 'riverpod_types/core.dart'; +part 'riverpod_types/generator.dart'; +part 'riverpod_types/legacy_providers.dart'; +part 'riverpod_types/providers.dart'; +part 'riverpod_types/widgets.dart'; /// [TypeChecker] for `Future` const futureType = TypeChecker.fromUrl('dart:async#Future'); @@ -141,130 +14,11 @@ const futureType = TypeChecker.fromUrl('dart:async#Future'); /// [TypeChecker] for `Stream` const streamType = TypeChecker.fromUrl('dart:async#Stream'); -/// [TypeChecker] for `ProviderContainer` -const containerType = - TypeChecker.fromName('ProviderContainer', packageName: 'riverpod'); - -/// [TypeChecker] for `AsyncNotifierBase` -const asyncNotifierBaseType = - TypeChecker.fromName('AsyncNotifierBase', packageName: 'riverpod'); - -/// [TypeChecker] for `NotifierBase` -const notifierBaseType = - TypeChecker.fromName('NotifierBase', packageName: 'riverpod'); - -/// Either `NotifierBase` or `AsyncNotifierBase` -const anyNotifierType = TypeChecker.any([ - asyncNotifierBaseType, - notifierBaseType, -]); - -/// Either `ProviderBase` or `Family` -const providerOrFamilyType = TypeChecker.any([providerBaseType, familyType]); - /// Either `FutureOr` or `Stream` const futureOrStreamType = TypeChecker.any([futureType, streamType]); -/// [TypeChecker] from `Widget` from Flutter -const widgetType = TypeChecker.fromName('Widget', packageName: 'flutter'); - -/// [TypeChecker] from `State` from Flutter -const widgetStateType = TypeChecker.fromName('State', packageName: 'flutter'); - -/// [TypeChecker] from `WidgetRef` -const widgetRefType = - TypeChecker.fromName('WidgetRef', packageName: 'flutter_riverpod'); - -/// Checks that the value is coming from a `package:riverpod` package -const isFromRiverpod = TypeChecker.fromPackage('riverpod'); - -/// Checks that the value is coming from a `package:riverpod_annotation` package -const isFromRiverpodAnnotation = TypeChecker.fromPackage('riverpod_annotation'); - -/// Checks that the value is coming from a `package:riverpod` package -const isFromFlutterRiverpod = TypeChecker.fromPackage('flutter_riverpod'); - -/// [TypeChecker for `Ref` -const refType = TypeChecker.fromName('Ref', packageName: 'riverpod'); - -bool _isBuiltInRef(DartType targetType) { - // Since Ref is sealed, checking that the function is from the package:riverpod - // before checking its type skips iterating over the superclasses of an element - // if it's not from Riverpod. - return isFromRiverpod.isExactlyType(targetType) && - refType.isAssignableFromType(targetType); -} - -bool isRiverpodRef(DartType targetType) { - final isBuiltInRef = _isBuiltInRef(targetType); - if (isBuiltInRef) return true; - - final targetElement = targetType.element; - - // Not a built-in ref. Might be a generated ref, let's check that. - if (targetElement is! MixinElement) return false; - final constraints = targetElement.superclassConstraints.singleOrNull; - if (constraints == null) return false; - - return _isBuiltInRef(constraints); -} - -/// Either `WidgetRef` or `Ref` -const anyRefType = TypeChecker.any([widgetRefType, refType]); - -/// [TypeChecker for `ConsumerWidget`` -const consumerWidgetType = TypeChecker.fromName( - 'ConsumerWidget', - packageName: 'flutter_riverpod', -); - -/// [TypeChecker for `HookConsumerWidget` -const hookConsumerWidgetType = TypeChecker.fromName( - 'HookConsumerWidget', - packageName: 'hooks_riverpod', -); - -/// [TypeChecker for `ConsumerStatefulWidget` -const consumerStatefulWidgetType = TypeChecker.fromName( - 'ConsumerStatefulWidget', - packageName: 'flutter_riverpod', -); - -/// [TypeChecker for `ConsumerState` -const consumerStateType = TypeChecker.fromName( - 'ConsumerState', - packageName: 'flutter_riverpod', -); - -/// [TypeChecker for `StatefulHookConsumerWidget` -const statefulHookConsumerStateType = TypeChecker.fromName( - 'StatefulHookConsumerWidget', - packageName: 'hooks_riverpod', -); - /// `Ref` methods that can make a provider depend on another provider. const refBinders = {'read', 'watch', 'listen'}; -/// [TypeChecker for `AsyncValue` -const asyncValueType = TypeChecker.fromName( - 'AsyncValue', - packageName: 'riverpod', -); - -/// [TypeChecker for `AsyncData` -const asyncDataType = TypeChecker.fromName( - 'AsyncData', - packageName: 'riverpod', -); - -/// [TypeChecker for `AsyncError` -const asyncErrorType = TypeChecker.fromName( - 'AsyncError', - packageName: 'riverpod', -); - -/// [TypeChecker for `AsyncLoading` -const asyncLoadingType = TypeChecker.fromName( - 'AsyncLoading', - packageName: 'riverpod', -); +/// Checks that the value is coming from a `package:flutter` package +const isFromFlutter = TypeChecker.fromPackage('flutter'); diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_types/core.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_types/core.dart new file mode 100644 index 000000000..01fdfe30e --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/riverpod_types/core.dart @@ -0,0 +1,119 @@ +part of '../riverpod_types.dart'; + +/// [TypeChecker] for `Override` +const overrideType = TypeChecker.fromName( + 'Override', + packageName: 'riverpod', +); + +/// [TypeChecker] for `ProviderBase` +const providerBaseType = TypeChecker.fromName( + 'ProviderBase', + packageName: 'riverpod', +); + +/// [TypeChecker] for `ProviderListenable` +const providerListenableType = TypeChecker.fromName( + 'ProviderListenable', + packageName: 'riverpod', +); + +/// [TypeChecker] from `ProviderContainer` +const providerContainerType = TypeChecker.fromName( + 'ProviderContainer', + packageName: 'riverpod', +); + +/// [TypeChecker] from `ProviderScope` +const providerScopeType = TypeChecker.fromName( + 'ProviderScope', + packageName: 'flutter_riverpod', +); + +/// [TypeChecker] from `ProviderScope` +const uncontrolledProviderScopeType = TypeChecker.fromName( + 'UncontrolledProviderScope', + packageName: 'flutter_riverpod', +); + +/// [TypeChecker] from `WidgetRef` +const widgetRefType = TypeChecker.fromName( + 'WidgetRef', + packageName: 'flutter_riverpod', +); + +/// Checks that the value is coming from a `package:riverpod` package +const isFromRiverpod = TypeChecker.fromPackage('riverpod'); + +/// Checks that the value is coming from a `package:riverpod_annotation` package +const isFromRiverpodAnnotation = TypeChecker.fromPackage('riverpod_annotation'); + +/// Checks that the value is coming from a `package:riverpod` package +const isFromFlutterRiverpod = TypeChecker.fromPackage('flutter_riverpod'); + +/// Checks that the value is coming from a `package:riverpod` package +const isFromHooksRiverpod = TypeChecker.fromPackage('hooks_riverpod'); + +/// [TypeChecker for `Ref` +const refType = TypeChecker.fromName('Ref', packageName: 'riverpod'); + +bool _isBuiltInRef(DartType targetType) { + // Since Ref is sealed, checking that the function is from the package:riverpod + // before checking its type skips iterating over the superclasses of an element + // if it's not from Riverpod. + return isFromRiverpod.isExactlyType(targetType) && + refType.isAssignableFromType(targetType); +} + +bool isRiverpodRef(DartType targetType) { + final isBuiltInRef = _isBuiltInRef(targetType); + if (isBuiltInRef) return true; + + final targetElement = targetType.element; + + // Not a built-in ref. Might be a generated ref, let's check that. + if (targetElement is! MixinElement) return false; + final constraints = targetElement.superclassConstraints.singleOrNull; + if (constraints == null) return false; + + return _isBuiltInRef(constraints); +} + +/// Either `WidgetRef` or `Ref` +const anyRefType = TypeChecker.any([widgetRefType, refType]); + +/// [TypeChecker for `AsyncData` +const asyncDataType = TypeChecker.fromName( + 'AsyncData', + packageName: 'riverpod', +); + +/// [TypeChecker for `AsyncError` +const asyncErrorType = TypeChecker.fromName( + 'AsyncError', + packageName: 'riverpod', +); + +/// [TypeChecker for `AsyncLoading` +const asyncLoadingType = TypeChecker.fromName( + 'AsyncLoading', + packageName: 'riverpod', +); + +/// [TypeChecker for `AsyncValue` +const asyncValueType = TypeChecker.fromName( + 'AsyncValue', + packageName: 'riverpod', +); + +/// [TypeChecker] for `ProviderContainer` +const containerType = TypeChecker.fromName( + 'ProviderContainer', + packageName: 'riverpod', +); + +/// Either `ProviderBase` or `Family` +const providerOrFamilyType = TypeChecker.any([providerBaseType, familyType]); + +/// [TypeChecker] for `Family` +const familyType = TypeChecker.fromName('Family', packageName: 'riverpod'); diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_types/generator.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_types/generator.dart new file mode 100644 index 000000000..eeefbba62 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/riverpod_types/generator.dart @@ -0,0 +1,25 @@ +part of '../riverpod_types.dart'; + +/// TypeChecker for the `ProviderFor` annotation +const providerForType = TypeChecker.fromName( + 'ProviderFor', + packageName: 'riverpod_annotation', +); + +/// Matches with the `Dependencies` annotation from riverpod_annotation. +const dependenciesType = TypeChecker.fromName( + 'Dependencies', + packageName: 'riverpod_annotation', +); + +/// Matches with the `Riverpod` annotation from riverpod_annotation. +const riverpodType = TypeChecker.fromName( + 'Riverpod', + packageName: 'riverpod_annotation', +); + +/// Matches with the `Mutation` annotation from riverpod_annotation. +const mutationType = TypeChecker.fromName( + 'Mutation', + packageName: 'riverpod', +); diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_types/legacy_providers.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_types/legacy_providers.dart new file mode 100644 index 000000000..0f8aad674 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/riverpod_types/legacy_providers.dart @@ -0,0 +1,56 @@ +part of '../riverpod_types.dart'; + +/// Either ChangeNotifierProvider or AutoDisposeChangeNotifierProvider +const changeNotifierProviderType = TypeChecker.fromName( + 'ChangeNotifierProvider', + packageName: 'flutter_riverpod', +); + +/// Either StateNotifierProvider or AutoDisposeStateNotifierProvider +const stateNotifierProviderType = TypeChecker.fromName( + 'StateNotifierProvider', + packageName: 'riverpod', +); + +/// [TypeChecker] for `StateNotifier` +const stateNotifierType = TypeChecker.fromName( + 'StateNotifier', + packageName: 'state_notifier', +); + +/// [TypeChecker] for `ChangeNotifier` +const changeNotifierType = TypeChecker.fromName( + 'ChangeNotifier', + packageName: 'flutter', +); + +/// Either StateProvider or AutoDisposeStateProvider +const stateProviderType = TypeChecker.fromName( + 'StateProvider', + packageName: 'riverpod', +); + +/// [TypeChecker] for `AsyncNotifierBase` +const asyncNotifierBaseType = TypeChecker.fromName( + r'$AsyncNotifier', + packageName: 'riverpod', +); + +/// [TypeChecker] for `AsyncNotifierBase` +const streamNotifierBaseType = TypeChecker.fromName( + r'$StreamNotifier', + packageName: 'riverpod', +); + +/// [TypeChecker] for `NotifierBase` +const notifierBaseType = TypeChecker.fromName( + r'$Notifier', + packageName: 'riverpod', +); + +/// Either `NotifierBase` or `AsyncNotifierBase` +const anyNotifierType = TypeChecker.any([ + asyncNotifierBaseType, + streamNotifierBaseType, + notifierBaseType, +]); diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_types/providers.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_types/providers.dart new file mode 100644 index 000000000..2bf9aa31d --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/riverpod_types/providers.dart @@ -0,0 +1,31 @@ +part of '../riverpod_types.dart'; + +/// FutureProvider +const futureProviderType = TypeChecker.fromName( + 'FutureProvider', + packageName: 'riverpod', +); + +/// StreamProvider +const streamProviderType = TypeChecker.fromName( + 'StreamProvider', + packageName: 'riverpod', +); + +/// NotifierProvider +const anyNotifierProviderType = TypeChecker.fromName( + r'$NotifierProvider', + packageName: 'riverpod', +); + +/// Either AsyncNotifierProvider or AutoDisposeAsyncNotifierProvider or their family form +const anyAsyncNotifierProviderType = TypeChecker.any([ + TypeChecker.fromName(r'$AsyncNotifierProvider', packageName: 'riverpod'), + TypeChecker.fromName(r'$StreamNotifierProvider', packageName: 'riverpod'), +]); + +/// Either Provider or AutoDisposeProvider +const providerType = TypeChecker.fromName( + 'Provider', + packageName: 'riverpod', +); diff --git a/packages/riverpod_analyzer_utils/lib/src/riverpod_types/widgets.dart b/packages/riverpod_analyzer_utils/lib/src/riverpod_types/widgets.dart new file mode 100644 index 000000000..5712eb266 --- /dev/null +++ b/packages/riverpod_analyzer_utils/lib/src/riverpod_types/widgets.dart @@ -0,0 +1,85 @@ +part of '../riverpod_types.dart'; + +/// [TypeChecker] from `Widget` from Flutter +const widgetType = TypeChecker.fromName('Widget', packageName: 'flutter'); + +/// [TypeChecker] from `State` from Flutter +const widgetStateType = TypeChecker.fromName('State', packageName: 'flutter'); + +/// [TypeChecker for `ConsumerWidget`` +const statelessWidgetType = TypeChecker.fromName( + 'StatelessWidget', + packageName: 'flutter', +); + +/// [TypeChecker for `ConsumerWidget`` +const statefulWidgetType = TypeChecker.fromName( + 'StatefulWidget', + packageName: 'flutter', +); + +/// [TypeChecker for `ConsumerWidget`` +const stateType = TypeChecker.fromName( + 'State', + packageName: 'flutter', +); + +/// [TypeChecker for `ConsumerWidget`` +const consumerWidgetType = TypeChecker.fromName( + 'ConsumerWidget', + packageName: 'flutter_riverpod', +); + +/// [TypeChecker for `ConsumerStatefulWidget` +const consumerStatefulWidgetType = TypeChecker.fromName( + 'ConsumerStatefulWidget', + packageName: 'flutter_riverpod', +); + +/// [TypeChecker for `ConsumerState` +const consumerStateType = TypeChecker.fromName( + 'ConsumerState', + packageName: 'flutter_riverpod', +); + +/// [TypeChecker for `HookConsumerWidget` +const hookConsumerWidgetType = TypeChecker.fromName( + 'HookConsumerWidget', + packageName: 'hooks_riverpod', +); + +/// [TypeChecker for `StatefulHookConsumerWidget` +const statefulHookConsumerType = TypeChecker.fromName( + 'StatefulHookConsumerWidget', + packageName: 'hooks_riverpod', +); + +/// [TypeChecker for `HookConsumerWidget` +const hookWidgetType = TypeChecker.fromName( + 'HookWidget', + packageName: 'hooks_riverpod', +); + +/// [TypeChecker for `StatefulHookConsumerWidget` +const statefulHookType = TypeChecker.fromName( + 'StatefulHookWidget', + packageName: 'hooks_riverpod', +); + +bool isState(DartType type) { + return stateType.isExactlyType(type) || consumerStateType.isExactlyType(type); +} + +bool isStatelessWidget(DartType type) { + return statelessWidgetType.isExactlyType(type) || + consumerWidgetType.isExactlyType(type) || + hookConsumerWidgetType.isExactlyType(type) || + hookWidgetType.isExactlyType(type); +} + +bool isStatefulWidget(DartType type) { + return statefulWidgetType.isExactlyType(type) || + consumerStatefulWidgetType.isExactlyType(type) || + statefulHookConsumerType.isExactlyType(type) || + statefulHookType.isExactlyType(type); +} diff --git a/packages/riverpod_analyzer_utils/pubspec.yaml b/packages/riverpod_analyzer_utils/pubspec.yaml index 1b6b25573..c102d1eb8 100644 --- a/packages/riverpod_analyzer_utils/pubspec.yaml +++ b/packages/riverpod_analyzer_utils/pubspec.yaml @@ -4,16 +4,16 @@ repository: https://github.com/rrousselGit/river_pod issue_tracker: https://github.com/rrousselGit/riverpod/issues funding: - https://github.com/sponsors/rrousselGit/ -version: 0.5.9 +version: 1.0.0-dev.1 environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.0.0<4.0.0" dependencies: analyzer: ^7.0.0 collection: ^1.16.0 crypto: ^3.0.2 - custom_lint_core: ^0.7.0 + custom_lint_core: ^0.7.1 freezed_annotation: ^2.2.0 meta: ^1.7.0 path: ^1.8.0 @@ -24,5 +24,7 @@ dev_dependencies: build_runner: ^2.3.2 build_test: ^2.1.5 freezed: ^2.2.0 + lint_visitor_generator: + path: ../lint_visitor_generator lints: ^2.0.0 test: ^1.21.7 diff --git a/packages/riverpod_analyzer_utils/pubspec_overrides.yaml b/packages/riverpod_analyzer_utils/pubspec_overrides.yaml index ac6b59295..efa318369 100644 --- a/packages/riverpod_analyzer_utils/pubspec_overrides.yaml +++ b/packages/riverpod_analyzer_utils/pubspec_overrides.yaml @@ -1,9 +1,12 @@ +# melos_managed_dependency_overrides: lint_visitor_generator dependency_overrides: flutter_riverpod: path: ../flutter_riverpod hooks_riverpod: path: ../hooks_riverpod + lint_visitor_generator: + path: ../lint_visitor_generator riverpod: path: ../riverpod riverpod_annotation: - path: ../riverpod_annotation \ No newline at end of file + path: ../riverpod_annotation diff --git a/packages/riverpod_analyzer_utils_tests/pubspec.yaml b/packages/riverpod_analyzer_utils_tests/pubspec.yaml index abbcdc592..f138cd07e 100644 --- a/packages/riverpod_analyzer_utils_tests/pubspec.yaml +++ b/packages/riverpod_analyzer_utils_tests/pubspec.yaml @@ -3,7 +3,7 @@ description: A sample command-line application. publish_to: none environment: - sdk: ">=2.18.0 <4.0.0" + sdk: ">=3.0.0<4.0.0" dependencies: analyzer: ^7.0.0 diff --git a/packages/riverpod_analyzer_utils_tests/test/analyzer_test_utils.dart b/packages/riverpod_analyzer_utils_tests/test/analyzer_test_utils.dart index 724b7a7d9..a7bb9965f 100644 --- a/packages/riverpod_analyzer_utils_tests/test/analyzer_test_utils.dart +++ b/packages/riverpod_analyzer_utils_tests/test/analyzer_test_utils.dart @@ -1,15 +1,48 @@ import 'dart:async'; import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/diagnostic/diagnostic.dart'; import 'package:build/build.dart'; import 'package:build_test/build_test.dart'; +import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; +import 'package:riverpod_analyzer_utils/src/nodes.dart'; import 'package:riverpod_generator/src/riverpod_generator.dart'; import 'package:test/test.dart'; +@internal +extension ObjectX on T? { + R? cast() { + final that = this; + if (that is R) return that; + return null; + } + + R? let(R? Function(T value)? cb) { + if (cb == null) return null; + final that = this; + if (that != null) return cb(that); + return null; + } +} + +List collectErrors(void Function() cb) { + final errors = []; + final previousErrorReporter = errorReporter; + + try { + errorReporter = errors.add; + cb(); + return errors; + } finally { + errorReporter = previousErrorReporter; + } +} + int _testNumber = 0; /// Due to [resolveSource] throwing if trying to interact with the resolver @@ -18,15 +51,21 @@ int _testNumber = 0; @isTest void testSource( String description, - Future Function(Resolver resolver) run, { + Future Function( + Resolver resolver, + CompilationUnit unit, + List units, + ) run, { required String source, Map files = const {}, bool runGenerator = false, Timeout? timeout, + Object? skip, }) { final testId = _testNumber++; test( description, + skip: skip, timeout: timeout, () async { // Giving a unique name to the package to avoid the analyzer cache @@ -42,20 +81,33 @@ void testSource( 'library "${entry.key}"; ${entry.value}', }; + Future<(List, CompilationUnit)> getUnits( + Resolver resolver, + ) async { + final lib = await resolver.findLibraryByName('foo'); + + final ast = await lib!.session.getResolvedLibrary(lib.source.fullName); + ast as ResolvedLibraryResult; + + return ( + ast.units, + ast.units.firstWhere((e) => e.path.endsWith('foo.dart')).unit, + ); + } + String? generated; if (runGenerator) { - final analysisResult = await resolveSources( + generated = await resolveSources( { '$packageName|lib/foo.dart': sourceWithLibrary, ...otherSources, }, - (resolver) { - return resolver.resolveRiverpodLibraryResult( - ignoreErrors: true, - ); + (resolver) async { + final (_, unit) = await getUnits(resolver); + + return RiverpodGenerator(const {}).generateForUnit([unit]); }, ); - generated = RiverpodGenerator(const {}).runGenerator(analysisResult); } await resolveSources({ @@ -67,7 +119,19 @@ void testSource( try { final originalZone = Zone.current; return runZoned( - () => run(resolver), + () async { + final (units, unit) = await getUnits(resolver); + + try { + return await run(resolver, unit, units); + } finally { + collectErrors(() { + for (final unit in units) { + expectRiverpodAstOnlyHasASingleOptionPerNode(unit.unit); + } + }); + } + }, zoneSpecification: ZoneSpecification( // Somehow prints are captured inside the callback. Let's restore them print: (self, parent, zone, line) => enclosingZone.print(line), @@ -85,6 +149,33 @@ void testSource( ); } +/// Asserts that no [AstNode] has to Riverpod ast. +void expectRiverpodAstOnlyHasASingleOptionPerNode(AstNode node) { + final result = CollectionRiverpodAst(); + node.accept(result); + + for (final entry in result.riverpodAst.entries) { + expect( + entry.value, + anyOf( + hasLength(0), + hasLength(1), + ), + reason: entry.key, + ); + } + + node.visitChildren(_VisitNode(expectRiverpodAstOnlyHasASingleOptionPerNode)); +} + +class _VisitNode extends GeneralizingAstVisitor { + _VisitNode(this.cb); + + final void Function(AstNode node) cb; + @override + void visitNode(AstNode node) => cb(node); +} + extension MapTake on Map { Map take(List keys) { return { @@ -97,36 +188,57 @@ extension MapTake on Map { } } -extension ResolverX on Resolver { - Future resolveRiverpodAnalysisResult({ - String libraryName = 'foo', - bool ignoreErrors = false, - }) async { - final riverpodAst = await resolveRiverpodLibraryResult( - libraryName: libraryName, - ignoreErrors: ignoreErrors, - ); +extension TakeList on List { + Map takeAll(List names) { + final result = Map.fromEntries(map((e) => MapEntry(e.name.lexeme, e))); + return result.take(names); + } - final result = RiverpodAnalysisResult(); - riverpodAst.accept(result); + T findByName(String name) { + return singleWhere((element) => element.name.lexeme == name); + } +} - if (!ignoreErrors) { - final errors = result.resolvedRiverpodLibraryResults - .expand((e) => e.errors) - .toList(); - if (errors.isNotEmpty) { - throw StateError(errors.map((e) => '- $e\n').join()); +extension FindAst on List { + T findByName(String name) { + for (final node in this) { + switch (node) { + case TopLevelVariableDeclaration(): + final variableWithName = node.variables.variables.firstWhereOrNull( + (element) => element.name.lexeme == name, + ); + + if (variableWithName != null) return variableWithName as T; + + case MethodDeclaration(): + if (node.name.lexeme == name) return node as T; + + case FieldDeclaration(): + final variableWithName = node.fields.variables.firstWhereOrNull( + (element) => element.name.lexeme == name, + ); + + if (variableWithName != null) return variableWithName as T; + + case NamedCompilationUnitMember(): + if (node.name.lexeme == name) return node as T; + + default: + throw UnsupportedError('Unsupported node ${node.runtimeType}'); } } - return result; + throw StateError('No node found with name "$name"'); } +} - Future resolveRiverpodLibraryResult({ +extension ResolverX on Resolver { + // ignore: invalid_use_of_internal_member + Future resolveRiverpodAnalysisResult({ String libraryName = 'foo', bool ignoreErrors = false, }) async { - final library = await _requireFindLibraryByName( + final library = await requireFindLibraryByName( libraryName, ignoreErrors: ignoreErrors, ); @@ -134,16 +246,38 @@ extension ResolverX on Resolver { await library.session.getResolvedLibraryByElement(library); libraryAst as ResolvedLibraryResult; - final result = ResolvedRiverpodLibraryResult.from( - libraryAst.units.map((e) => e.unit).toList(), - ); + final result = RiverpodAnalysisResult(); + + final errors = []; + final previousErrorReporter = errorReporter; + try { + if (ignoreErrors) { + errorReporter = errors.add; + } else { + errorReporter = (error) { + throw StateError('Unexpected error: $error'); + }; + } - expectValidParentChildrenRelationship(result); + for (final unit in libraryAst.units) { + unit.unit.accept(result); + } + } finally { + errorReporter = previousErrorReporter; + } + + result.errors.addAll(errors); + + if (!ignoreErrors) { + if (errors.isNotEmpty) { + throw StateError(errors.map((e) => '- $e\n').join()); + } + } return result; } - Future _requireFindLibraryByName( + Future requireFindLibraryByName( String libraryName, { required bool ignoreErrors, }) async { @@ -173,535 +307,3 @@ ${errors.map((e) => '- $e\n').join()} return library; } } - -/// Visit all the nodes of the AST and ensure that that all children -/// have the correct parent. -void expectValidParentChildrenRelationship( - ResolvedRiverpodLibraryResult result, -) { - result.accept(_ParentRiverpodVisitor(null)); -} - -class _ParentRiverpodVisitor extends RecursiveRiverpodAstVisitor { - _ParentRiverpodVisitor(this.expectedParent); - - final RiverpodAst? expectedParent; - - @override - void visitProviderScopeInstanceCreationExpression( - ProviderScopeInstanceCreationExpression declaration, - ) { - expect( - declaration.parent, - expectedParent, - reason: - 'Node ${declaration.runtimeType} should have $expectedParent as parent', - ); - declaration.visitChildren(_ParentRiverpodVisitor(declaration)); - } - - @override - void visitProviderContainerInstanceCreationExpression( - ProviderContainerInstanceCreationExpression declaration, - ) { - expect( - declaration.parent, - expectedParent, - reason: - 'Node ${declaration.runtimeType} should have $expectedParent as parent', - ); - declaration.visitChildren(_ParentRiverpodVisitor(declaration)); - } - - @override - void visitConsumerStateDeclaration(ConsumerStateDeclaration declaration) { - expect( - declaration.parent, - expectedParent, - reason: - 'Node ${declaration.runtimeType} should have $expectedParent as parent', - ); - declaration.visitChildren(_ParentRiverpodVisitor(declaration)); - } - - @override - void visitConsumerWidgetDeclaration(ConsumerWidgetDeclaration declaration) { - expect( - declaration.parent, - expectedParent, - reason: - 'Node ${declaration.runtimeType} should have $expectedParent as parent', - ); - declaration.visitChildren(_ParentRiverpodVisitor(declaration)); - } - - @override - void visitLegacyProviderDeclaration(LegacyProviderDeclaration declaration) { - expect( - declaration.parent, - expectedParent, - reason: - 'Node ${declaration.runtimeType} should have $expectedParent as parent', - ); - declaration.visitChildren(_ParentRiverpodVisitor(declaration)); - } - - @override - void visitLegacyProviderDependencies( - LegacyProviderDependencies dependencies, - ) { - expect( - dependencies.parent, - expectedParent, - reason: - 'Node ${dependencies.runtimeType} should have $expectedParent as parent', - ); - dependencies.visitChildren(_ParentRiverpodVisitor(dependencies)); - } - - @override - void visitLegacyProviderDependency(LegacyProviderDependency dependency) { - expect( - dependency.parent, - expectedParent, - reason: - 'Node ${dependency.runtimeType} should have $expectedParent as parent', - ); - dependency.visitChildren(_ParentRiverpodVisitor(dependency)); - } - - @override - void visitProviderListenableExpression( - ProviderListenableExpression expression, - ) { - expect( - expression.parent, - expectedParent, - reason: - 'Node ${expression.runtimeType} should have $expectedParent as parent', - ); - expression.visitChildren(_ParentRiverpodVisitor(expression)); - } - - @override - void visitRefListenInvocation(RefListenInvocation invocation) { - expect( - invocation.parent, - expectedParent, - reason: - 'Node ${invocation.runtimeType} should have $expectedParent as parent', - ); - invocation.visitChildren(_ParentRiverpodVisitor(invocation)); - } - - @override - void visitRefReadInvocation(RefReadInvocation invocation) { - expect( - invocation.parent, - expectedParent, - reason: - 'Node ${invocation.runtimeType} should have $expectedParent as parent', - ); - invocation.visitChildren(_ParentRiverpodVisitor(invocation)); - } - - @override - void visitRefWatchInvocation(RefWatchInvocation invocation) { - expect( - invocation.parent, - expectedParent, - reason: - 'Node ${invocation.runtimeType} should have $expectedParent as parent', - ); - invocation.visitChildren(_ParentRiverpodVisitor(invocation)); - } - - @override - void visitResolvedRiverpodUnit(ResolvedRiverpodLibraryResult result) { - expect( - result.parent, - expectedParent, - reason: - 'Node ${result.runtimeType} should have $expectedParent as parent', - ); - result.visitChildren(_ParentRiverpodVisitor(result)); - } - - @override - void visitRiverpodAnnotation(RiverpodAnnotation annotation) { - expect( - annotation.parent, - expectedParent, - reason: - 'Node ${annotation.runtimeType} should have $expectedParent as parent', - ); - annotation.visitChildren(_ParentRiverpodVisitor(annotation)); - } - - @override - void visitRiverpodAnnotationDependency( - RiverpodAnnotationDependency dependency, - ) { - expect( - dependency.parent, - expectedParent, - reason: - 'Node ${dependency.runtimeType} should have $expectedParent as parent', - ); - dependency.visitChildren(_ParentRiverpodVisitor(dependency)); - } - - @override - void visitRiverpodAnnotationDependencies( - RiverpodAnnotationDependencies dependencies, - ) { - expect( - dependencies.parent, - expectedParent, - reason: - 'Node ${dependencies.runtimeType} should have $expectedParent as parent', - ); - dependencies.visitChildren(_ParentRiverpodVisitor(dependencies)); - } - - @override - void visitConsumerStatefulWidgetDeclaration( - ConsumerStatefulWidgetDeclaration declaration, - ) { - expect( - declaration.parent, - expectedParent, - reason: - 'Node ${declaration.runtimeType} should have $expectedParent as parent', - ); - declaration.visitChildren(_ParentRiverpodVisitor(declaration)); - } - - @override - void visitClassBasedProviderDeclaration( - ClassBasedProviderDeclaration declaration, - ) { - expect( - declaration.parent, - expectedParent, - reason: - 'Node ${declaration.runtimeType} should have $expectedParent as parent', - ); - declaration.visitChildren(_ParentRiverpodVisitor(declaration)); - } - - @override - void visitFunctionalProviderDeclaration( - FunctionalProviderDeclaration declaration, - ) { - expect( - declaration.parent, - expectedParent, - reason: - 'Node ${declaration.runtimeType} should have $expectedParent as parent', - ); - declaration.visitChildren(_ParentRiverpodVisitor(declaration)); - } - - @override - void visitWidgetRefListenInvocation(WidgetRefListenInvocation invocation) { - expect( - invocation.parent, - expectedParent, - reason: - 'Node ${invocation.runtimeType} should have $expectedParent as parent', - ); - invocation.visitChildren(_ParentRiverpodVisitor(invocation)); - } - - @override - void visitWidgetRefListenManualInvocation( - WidgetRefListenManualInvocation invocation, - ) { - expect( - invocation.parent, - expectedParent, - reason: - 'Node ${invocation.runtimeType} should have $expectedParent as parent', - ); - invocation.visitChildren(_ParentRiverpodVisitor(invocation)); - } - - @override - void visitWidgetRefReadInvocation(WidgetRefReadInvocation invocation) { - expect( - invocation.parent, - expectedParent, - reason: - 'Node ${invocation.runtimeType} should have $expectedParent as parent', - ); - invocation.visitChildren(_ParentRiverpodVisitor(invocation)); - } - - @override - void visitWidgetRefWatchInvocation(WidgetRefWatchInvocation invocation) { - expect( - invocation.parent, - expectedParent, - reason: - 'Node ${invocation.runtimeType} should have $expectedParent as parent', - ); - invocation.visitChildren(_ParentRiverpodVisitor(invocation)); - } - - @override - void visitHookConsumerWidgetDeclaration( - HookConsumerWidgetDeclaration declaration, - ) { - expect( - declaration.parent, - expectedParent, - reason: - 'Node ${declaration.runtimeType} should have $expectedParent as parent', - ); - declaration.visitChildren(_ParentRiverpodVisitor(declaration)); - } - - @override - void visitStatefulHookConsumerWidgetDeclaration( - StatefulHookConsumerWidgetDeclaration declaration, - ) { - expect( - declaration.parent, - expectedParent, - reason: - 'Node ${declaration.runtimeType} should have $expectedParent as parent', - ); - declaration.visitChildren(_ParentRiverpodVisitor(declaration)); - } -} - -class RiverpodAnalysisResult extends RecursiveRiverpodAstVisitor { - final providerContainerInstanceCreationExpressions = - []; - @override - void visitProviderContainerInstanceCreationExpression( - ProviderContainerInstanceCreationExpression expression, - ) { - super.visitProviderContainerInstanceCreationExpression(expression); - providerContainerInstanceCreationExpressions.add(expression); - } - - final providerScopeInstanceCreationExpressions = - []; - @override - void visitProviderScopeInstanceCreationExpression( - ProviderScopeInstanceCreationExpression expression, - ) { - super.visitProviderScopeInstanceCreationExpression(expression); - providerScopeInstanceCreationExpressions.add(expression); - } - - final consumerStateDeclarations = []; - @override - void visitConsumerStateDeclaration(ConsumerStateDeclaration declaration) { - super.visitConsumerStateDeclaration(declaration); - consumerStateDeclarations.add(declaration); - } - - final consumerWidgetDeclarations = []; - @override - void visitConsumerWidgetDeclaration(ConsumerWidgetDeclaration declaration) { - super.visitConsumerWidgetDeclaration(declaration); - consumerWidgetDeclarations.add(declaration); - } - - final hookConsumerWidgetDeclaration = []; - @override - void visitHookConsumerWidgetDeclaration( - HookConsumerWidgetDeclaration declaration, - ) { - super.visitHookConsumerWidgetDeclaration(declaration); - hookConsumerWidgetDeclaration.add(declaration); - } - - final statefulHookConsumerWidgetDeclaration = - []; - @override - void visitStatefulHookConsumerWidgetDeclaration( - StatefulHookConsumerWidgetDeclaration declaration, - ) { - super.visitStatefulHookConsumerWidgetDeclaration(declaration); - statefulHookConsumerWidgetDeclaration.add(declaration); - } - - final legacyProviderDeclarations = []; - @override - void visitLegacyProviderDeclaration(LegacyProviderDeclaration declaration) { - super.visitLegacyProviderDeclaration(declaration); - legacyProviderDeclarations.add(declaration); - } - - final legacyProviderDependencies = []; - @override - void visitLegacyProviderDependencies( - LegacyProviderDependencies dependencies, - ) { - super.visitLegacyProviderDependencies(dependencies); - legacyProviderDependencies.add(dependencies); - } - - final legacyProviderDependencyList = []; - @override - void visitLegacyProviderDependency(LegacyProviderDependency dependency) { - super.visitLegacyProviderDependency(dependency); - legacyProviderDependencyList.add(dependency); - } - - final providerListenableExpressions = []; - @override - void visitProviderListenableExpression( - ProviderListenableExpression expression, - ) { - super.visitProviderListenableExpression(expression); - providerListenableExpressions.add(expression); - } - - final refInvocations = []; - final refListenInvocations = []; - @override - void visitRefListenInvocation(RefListenInvocation invocation) { - super.visitRefListenInvocation(invocation); - refInvocations.add(invocation); - refListenInvocations.add(invocation); - } - - final refReadInvocations = []; - @override - void visitRefReadInvocation(RefReadInvocation invocation) { - super.visitRefReadInvocation(invocation); - refInvocations.add(invocation); - refReadInvocations.add(invocation); - } - - final refWatchInvocations = []; - @override - void visitRefWatchInvocation(RefWatchInvocation invocation) { - super.visitRefWatchInvocation(invocation); - refInvocations.add(invocation); - refWatchInvocations.add(invocation); - } - - final resolvedRiverpodLibraryResults = []; - @override - void visitResolvedRiverpodUnit(ResolvedRiverpodLibraryResult result) { - super.visitResolvedRiverpodUnit(result); - resolvedRiverpodLibraryResults.add(result); - } - - final riverpodAnnotations = []; - @override - void visitRiverpodAnnotation(RiverpodAnnotation annotation) { - super.visitRiverpodAnnotation(annotation); - riverpodAnnotations.add(annotation); - } - - final riverpodAnnotationDependencyList = []; - @override - void visitRiverpodAnnotationDependency( - RiverpodAnnotationDependency dependency, - ) { - super.visitRiverpodAnnotationDependency(dependency); - riverpodAnnotationDependencyList.add(dependency); - } - - final riverpodAnnotationDependencies = []; - @override - void visitRiverpodAnnotationDependencies( - RiverpodAnnotationDependencies dependencies, - ) { - super.visitRiverpodAnnotationDependencies(dependencies); - riverpodAnnotationDependencies.add(dependencies); - } - - final consumerStatefulWidgetDeclarations = - []; - @override - void visitConsumerStatefulWidgetDeclaration( - ConsumerStatefulWidgetDeclaration declaration, - ) { - super.visitConsumerStatefulWidgetDeclaration(declaration); - consumerStatefulWidgetDeclarations.add(declaration); - } - - final generatorProviderDeclarations = []; - final classBasedProviderDeclarations = []; - @override - void visitClassBasedProviderDeclaration( - ClassBasedProviderDeclaration declaration, - ) { - super.visitClassBasedProviderDeclaration(declaration); - generatorProviderDeclarations.add(declaration); - classBasedProviderDeclarations.add(declaration); - } - - final functionalProviderDeclarations = []; - @override - void visitFunctionalProviderDeclaration( - FunctionalProviderDeclaration declaration, - ) { - super.visitFunctionalProviderDeclaration(declaration); - generatorProviderDeclarations.add(declaration); - functionalProviderDeclarations.add(declaration); - } - - final widgetRefInvocations = []; - final widgetRefListenInvocations = []; - @override - void visitWidgetRefListenInvocation(WidgetRefListenInvocation invocation) { - super.visitWidgetRefListenInvocation(invocation); - widgetRefInvocations.add(invocation); - widgetRefListenInvocations.add(invocation); - } - - final widgetRefListenManualInvocations = []; - @override - void visitWidgetRefListenManualInvocation( - WidgetRefListenManualInvocation invocation, - ) { - super.visitWidgetRefListenManualInvocation(invocation); - widgetRefInvocations.add(invocation); - widgetRefListenManualInvocations.add(invocation); - } - - final widgetRefReadInvocations = []; - @override - void visitWidgetRefReadInvocation(WidgetRefReadInvocation invocation) { - super.visitWidgetRefReadInvocation(invocation); - widgetRefInvocations.add(invocation); - widgetRefReadInvocations.add(invocation); - } - - final widgetRefWatchInvocations = []; - @override - void visitWidgetRefWatchInvocation(WidgetRefWatchInvocation invocation) { - super.visitWidgetRefWatchInvocation(invocation); - widgetRefInvocations.add(invocation); - widgetRefWatchInvocations.add(invocation); - } -} - -extension TakeList on List { - Map takeAll(List names) { - final result = Map.fromEntries(map((e) => MapEntry(e.name.lexeme, e))); - return result.take(names); - } - - T findByName(String name) { - return singleWhere((element) => element.name.lexeme == name); - } -} - -extension LibraryElementX on LibraryElement { - Element findElementWithName(String name) { - return topLevelElements.singleWhere( - (element) => !element.isSynthetic && element.name == name, - orElse: () => throw StateError('No element found with name "$name"'), - ); - } -} diff --git a/packages/riverpod_analyzer_utils_tests/test/consumer_test.dart b/packages/riverpod_analyzer_utils_tests/test/consumer_test.dart index b23769ef2..046c16767 100644 --- a/packages/riverpod_analyzer_utils_tests/test/consumer_test.dart +++ b/packages/riverpod_analyzer_utils_tests/test/consumer_test.dart @@ -19,38 +19,12 @@ class ProviderWidget extends ConsumerWidget { return Container(); } } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); - final consumerWidget = result.consumerWidgetDeclarations.single; - expect(consumerWidget, isA()); + final consumerWidget = result.widgetDeclarations.single; + expect(consumerWidget, isA()); expect(consumerWidget.node.name.toString(), 'ProviderWidget'); - expect( - consumerWidget.buildMethod!.toSource(), - '@override Widget build(BuildContext context, WidgetRef ref) {ref.watch(provider); return Container();}', - ); - - expect(consumerWidget.widgetRefInvocations, [ - isA() - .having((e) => e.node.toSource(), 'node', 'ref.watch(provider)') - .having( - (e) => e.provider, - 'provider', - isA() - .having((e) => e.node.toSource(), 'node', 'provider') - .having( - (e) => e.providerElement, - 'providerElement', - isA() - .having((e) => e.providerType, 'providerType', null), - ), - ), - ]); - - expect( - result.resolvedRiverpodLibraryResults.single.unknownWidgetRefInvocations, - isEmpty, - ); }); testSource('Decode ConsumerWidget declarations', source: ''' @@ -68,27 +42,12 @@ class MyConsumerWidget extends ConsumerWidget { return Container(); } } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); - final consumerWidget = result.consumerWidgetDeclarations.single; - expect(consumerWidget, isA()); + final consumerWidget = result.widgetDeclarations.single; + expect(consumerWidget, isA()); expect(consumerWidget.node.name.toString(), 'MyConsumerWidget'); - expect( - consumerWidget.buildMethod!.toSource(), - '@override Widget build(BuildContext context, WidgetRef ref) {ref.watch(provider); return Container();}', - ); - - expect( - result.resolvedRiverpodLibraryResults.single.unknownWidgetRefInvocations, - isEmpty, - ); - - expect(consumerWidget.widgetRefInvocations, hasLength(1)); - expect( - consumerWidget.widgetRefInvocations.single, - isA(), - ); }); testSource('Decode HookConsumerWidgetDeclaration declarations', source: ''' @@ -106,27 +65,12 @@ class MyConsumerWidget extends HookConsumerWidget { return Container(); } } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); - final consumerWidget = result.hookConsumerWidgetDeclaration.single; - expect(consumerWidget, isA()); + final consumerWidget = result.widgetDeclarations.single; + expect(consumerWidget, isA()); expect(consumerWidget.node.name.toString(), 'MyConsumerWidget'); - expect( - consumerWidget.buildMethod!.toSource(), - '@override Widget build(BuildContext context, WidgetRef ref) {ref.watch(provider); return Container();}', - ); - - expect( - result.resolvedRiverpodLibraryResults.single.unknownWidgetRefInvocations, - isEmpty, - ); - - expect(consumerWidget.widgetRefInvocations, hasLength(1)); - expect( - consumerWidget.widgetRefInvocations.single, - isA(), - ); }); testSource('Decode ConsumerStatefulWidgetDeclarations declarations', @@ -157,34 +101,19 @@ class MyConsumerState extends ConsumerState { return Container(); } } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); - final consumerWidget = result.consumerStatefulWidgetDeclarations.single; - final consumerState = result.consumerStateDeclarations.single; + final consumerWidget = + result.widgetDeclarations.single as StatefulWidgetDeclaration; + final consumerState = result.stateDeclarations.single; - expect(consumerWidget, isA()); expect(consumerWidget.node.name.toString(), 'MyConsumerWidget'); + expect(consumerWidget.state, consumerState.element); - expect(consumerState, isA()); expect(consumerState.node.name.toString(), 'MyConsumerState'); - - expect( - result.resolvedRiverpodLibraryResults.single.unknownWidgetRefInvocations, - isEmpty, - ); - - expect(consumerState.widgetRefInvocations, hasLength(2)); - expect( - consumerState.widgetRefInvocations[0], - isA() - .having((e) => e.node.toSource(), 'node', 'ref.watch(provider)'), - ); - expect( - consumerState.widgetRefInvocations[1], - isA() - .having((e) => e.node.toSource(), 'node', 'ref.watch(provider2)'), - ); + expect(consumerState.widget, consumerWidget.element); + expect(consumerState.element.widget, consumerWidget.element); }); testSource('Decode StatefulHookConsumerWidgetDeclaration declarations', @@ -211,37 +140,21 @@ class MyConsumerState extends ConsumerState { @override Widget build(BuildContext context) { - ref.watch(provider2); return Container(); } } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); - final consumerWidget = result.statefulHookConsumerWidgetDeclaration.single; - final consumerState = result.consumerStateDeclarations.single; + final consumerWidget = + result.widgetDeclarations.single as StatefulWidgetDeclaration; + final consumerState = result.stateDeclarations.single; - expect(consumerWidget, isA()); expect(consumerWidget.node.name.toString(), 'MyConsumerWidget'); + expect(consumerWidget.state, consumerState.element); - expect(consumerState, isA()); expect(consumerState.node.name.toString(), 'MyConsumerState'); - - expect( - result.resolvedRiverpodLibraryResults.single.unknownWidgetRefInvocations, - isEmpty, - ); - - expect(consumerState.widgetRefInvocations, hasLength(2)); - expect( - consumerState.widgetRefInvocations[0], - isA() - .having((e) => e.node.toSource(), 'node', 'ref.watch(provider)'), - ); - expect( - consumerState.widgetRefInvocations[1], - isA() - .having((e) => e.node.toSource(), 'node', 'ref.watch(provider2)'), - ); + expect(consumerState.widget, consumerWidget.element); + expect(consumerState.element.widget, consumerWidget.element); }); } diff --git a/packages/riverpod_analyzer_utils_tests/test/legacy_provider_declaration_test.dart b/packages/riverpod_analyzer_utils_tests/test/legacy_provider_declaration_test.dart index 032ce4fb5..079668e09 100644 --- a/packages/riverpod_analyzer_utils_tests/test/legacy_provider_declaration_test.dart +++ b/packages/riverpod_analyzer_utils_tests/test/legacy_provider_declaration_test.dart @@ -1,6 +1,5 @@ import 'package:analyzer/dart/element/type.dart'; -import 'package:riverpod_analyzer_utils/src/riverpod_ast.dart'; -import 'package:riverpod_analyzer_utils/src/riverpod_element.dart'; +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; import 'package:test/test.dart'; import 'analyzer_test_utils.dart'; @@ -34,7 +33,7 @@ Provider Function() get getter => () => Provider((ref) => 0); final unknown5 = getter(); ''', - (resolver) async { + (resolver, unit, units) async { // Regression test for https://github.com/rrousselGit/riverpod/issues/2313 final result = await resolver.resolveRiverpodAnalysisResult(); @@ -57,12 +56,12 @@ part 'foo.g.dart'; final legacy = Provider((ref) => 0); @riverpod -int simple(SimpleRef ref) => 0; +int simple(Ref ref) => 0; // Regression test for https://github.com/rrousselGit/riverpod/issues/2194 @riverpod -int complex(ComplexRef ref, {int? id, String? another}) => 0; -''', (resolver) async { +int complex(Ref ref, {int? id, String? another}) => 0; +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.legacyProviderDeclarations, hasLength(1)); @@ -74,7 +73,7 @@ import 'package:riverpod/riverpod.dart'; final first = Provider((ref) => 0); final second = Provider((ref) => 0); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); final providers = result.legacyProviderDeclarations.takeAll(['first', 'second']); @@ -129,10 +128,6 @@ final autoDisposeProvider = Provider.autoDispose( (ref) => 0, dependencies: [dep, family, family(42), ...getDeps()], ); -final explicitAutoDisposeProvider = AutoDisposeProvider( - (ref) => 0, - dependencies: [dep, family, family(42), ...getDeps()], -); final autoDisposeFamily = Provider.autoDispose.family( (ref, id) => 0, dependencies: [dep, family, family(42), ...getDeps()], @@ -141,11 +136,7 @@ final autoDisposeFamily2 = Provider.family.autoDispose( (ref, id) => 0, dependencies: [dep, family, family(42), ...getDeps()], ); -final explicitAutoDisposeFamily = AutoDisposeProviderFamily( - (ref, id) => 0, - dependencies: [dep, family, family(42), ...getDeps()], -); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); final deps = result.legacyProviderDeclarations.takeAll([ 'dep', @@ -165,10 +156,8 @@ final explicitAutoDisposeFamily = AutoDisposeProviderFamily( 'alwaysAliveFamily', 'explicitAlwaysAliveFamily', 'autoDisposeProvider', - 'explicitAutoDisposeProvider', 'autoDisposeFamily', 'autoDisposeFamily2', - 'explicitAutoDisposeFamily', ]); for (final provider in deps.entries) { @@ -182,7 +171,7 @@ final explicitAutoDisposeFamily = AutoDisposeProviderFamily( expect(unknownDependencies.dependencies, isNotNull); expect(unknownDependencies.dependencies?.dependencies, isNull); expect( - unknownDependencies.dependencies?.dependenciesNode.toSource(), + unknownDependencies.dependencies?.node.toSource(), 'dependencies: getDeps()', ); @@ -193,7 +182,7 @@ final explicitAutoDisposeFamily = AutoDisposeProviderFamily( reason: '${provider.key} has no dependency', ); expect( - provider.value.dependencies?.dependenciesNode.toSource(), + provider.value.dependencies?.node.toSource(), 'dependencies: []', ); } @@ -206,7 +195,7 @@ final explicitAutoDisposeFamily = AutoDisposeProviderFamily( ); expect( - provider.value.dependencies?.dependenciesNode.toSource(), + provider.value.dependencies?.node.toSource(), 'dependencies: [dep, family, family(42), ...getDeps()]', ); @@ -215,7 +204,7 @@ final explicitAutoDisposeFamily = AutoDisposeProviderFamily( isA() .having((e) => e.node.toSource(), 'node', 'dep') .having( - (e) => e.provider?.providerElement, + (e) => e.provider?.provider?.providerElement, 'provider', same(deps['dep']?.providerElement), ), @@ -226,7 +215,7 @@ final explicitAutoDisposeFamily = AutoDisposeProviderFamily( isA() .having((e) => e.node.toSource(), 'node', 'family') .having( - (e) => e.provider?.providerElement, + (e) => e.provider?.provider?.providerElement, 'provider', same(deps['family']?.providerElement), ), @@ -237,7 +226,7 @@ final explicitAutoDisposeFamily = AutoDisposeProviderFamily( isA() .having((e) => e.node.toSource(), 'node', 'family(42)') .having( - (e) => e.provider?.providerElement, + (e) => e.provider?.provider?.providerElement, 'provider', same(deps['family']?.providerElement), ), @@ -247,7 +236,11 @@ final explicitAutoDisposeFamily = AutoDisposeProviderFamily( provider.value.dependencies?.dependencies?[3], isA() .having((e) => e.node.toSource(), 'node', '...getDeps()') - .having((e) => e.provider?.providerElement, 'provider', null), + .having( + (e) => e.provider?.provider?.providerElement, + 'provider', + null, + ), reason: '${provider.key} has an unknown expression as fourth dependency', ); @@ -258,7 +251,7 @@ final explicitAutoDisposeFamily = AutoDisposeProviderFamily( hasLength(2), ); expect( - generatorDependencies.dependencies?.dependenciesNode.toSource(), + generatorDependencies.dependencies?.node.toSource(), 'dependencies: [dep2Provider, family2Provider]', ); expect( @@ -266,7 +259,7 @@ final explicitAutoDisposeFamily = AutoDisposeProviderFamily( isA() .having((e) => e.node.toSource(), 'node', 'dep2Provider') .having( - (e) => e.provider?.providerElement, + (e) => e.provider?.provider?.providerElement, 'provider', same( result.generatorProviderDeclarations @@ -280,7 +273,7 @@ final explicitAutoDisposeFamily = AutoDisposeProviderFamily( isA() .having((e) => e.node.toSource(), 'node', 'family2Provider') .having( - (e) => e.provider?.providerElement, + (e) => e.provider?.provider?.providerElement, 'provider', same( result.generatorProviderDeclarations @@ -298,21 +291,17 @@ final alwaysAliveProvider = Provider((ref) => 0); final alwaysAliveFamily = Provider.family((ref, id) => 0); final explicitAlwaysAliveFamily = ProviderFamily((ref, id) => 0); final autoDisposeProvider = Provider.autoDispose((ref) => 0); -final explicitAutoDisposeProvider = AutoDisposeProvider((ref) => 0); final autoDisposeFamily = Provider.autoDispose.family((ref, id) => 0); final autoDisposeFamily2 = Provider.family.autoDispose((ref, id) => 0); -final explicitAutoDisposeFamily = AutoDisposeProviderFamily((ref, id) => 0); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); final providers = result.legacyProviderDeclarations.takeAll([ 'alwaysAliveProvider', 'alwaysAliveFamily', 'explicitAlwaysAliveFamily', 'autoDisposeProvider', - 'explicitAutoDisposeProvider', 'autoDisposeFamily', 'autoDisposeFamily2', - 'explicitAutoDisposeFamily', ]); for (final provider in providers.entries) { @@ -334,10 +323,9 @@ final provider = Provider((ref) => 0, name: 'foo'); final family = Provider.family((ref, id) => 0, name: 'bar'); final autoDisposeFamily = Provider.autoDispose.family((ref, id) => 0); -final explicitAutoDisposeFamily = AutoDisposeProviderFamily((ref, id) => 0); final weird = Provider(name: 'foo', dependencies: [], (ref) => 0); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); final providers = result.legacyProviderDeclarations.takeAll([ 'inferred', @@ -345,7 +333,6 @@ final weird = Provider(name: 'foo', dependencies: [], (ref) => 0); 'provider', 'family', 'autoDisposeFamily', - 'explicitAutoDisposeFamily', 'weird', ]); @@ -359,22 +346,16 @@ final weird = Provider(name: 'foo', dependencies: [], (ref) => 0); ); expect(providers['autoDisposeFamily']?.provider.toString(), 'Provider'); - expect( - providers['explicitAutoDisposeFamily']?.provider.toString(), - 'AutoDisposeProviderFamily', - ); expect( providers['autoDisposeFamily']?.familyModifier?.toSource(), 'family', ); - expect(providers['explicitAutoDisposeFamily']?.familyModifier, null); expect( providers['autoDisposeFamily']?.autoDisposeModifier?.toSource(), 'autoDispose', ); - expect(providers['explicitAutoDisposeFamily']?.autoDisposeModifier, null); expect( providers['autoDisposeFamily']?.build.toSource(), @@ -401,7 +382,7 @@ final weird = Provider(name: 'foo', dependencies: [], (ref) => 0); "(name: 'foo', dependencies: [], (ref) => 0)", ); expect( - providers['weird']?.dependencies?.dependenciesNode.toSource(), + providers['weird']?.dependencies?.node.toSource(), 'dependencies: []', ); expect(providers['weird']?.build.toSource(), '(ref) => 0'); @@ -414,21 +395,17 @@ final alwaysAliveProvider = FutureProvider((ref) => 0); final alwaysAliveFamily = FutureProvider.family((ref, id) => 0); final explicitAlwaysAliveFamily = FutureProviderFamily((ref, id) => 0); final autoDisposeProvider = FutureProvider.autoDispose((ref) => 0); -final explicitAutoDisposeProvider = AutoDisposeFutureProvider((ref) => 0); final autoDisposeFamily = FutureProvider.autoDispose.family((ref, id) => 0); final autoDisposeFamily2 = FutureProvider.family.autoDispose((ref, id) => 0); -final explicitAutoDisposeFamily = AutoDisposeFutureProviderFamily((ref, id) => 0); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); final providers = result.legacyProviderDeclarations.takeAll([ 'alwaysAliveProvider', 'alwaysAliveFamily', 'explicitAlwaysAliveFamily', 'autoDisposeProvider', - 'explicitAutoDisposeProvider', 'autoDisposeFamily', 'autoDisposeFamily2', - 'explicitAutoDisposeFamily', ]); for (final provider in providers.entries) { @@ -441,27 +418,23 @@ final explicitAutoDisposeFamily = AutoDisposeFutureProviderFamily((ref }); testSource('Decode LegacyProviderType.stateProvider', source: ''' -import 'package:riverpod/riverpod.dart'; +import 'package:riverpod/legacy.dart'; final alwaysAliveProvider = StateProvider((ref) => 0); final alwaysAliveFamily = StateProvider.family((ref, id) => 0); final explicitAlwaysAliveFamily = StateProviderFamily((ref, id) => 0); final autoDisposeProvider = StateProvider.autoDispose((ref) => 0); -final explicitAutoDisposeProvider = AutoDisposeStateProvider((ref) => 0); final autoDisposeFamily = StateProvider.autoDispose.family((ref, id) => 0); final autoDisposeFamily2 = StateProvider.family.autoDispose((ref, id) => 0); -final explicitAutoDisposeFamily = AutoDisposeStateProviderFamily((ref, id) => 0); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); final providers = result.legacyProviderDeclarations.takeAll([ 'alwaysAliveProvider', 'alwaysAliveFamily', 'explicitAlwaysAliveFamily', 'autoDisposeProvider', - 'explicitAutoDisposeProvider', 'autoDisposeFamily', 'autoDisposeFamily2', - 'explicitAutoDisposeFamily', ]); for (final provider in providers.entries) { @@ -480,21 +453,17 @@ final alwaysAliveProvider = StreamProvider((ref) => Stream.empty()); final alwaysAliveFamily = StreamProvider.family((ref, id) => Stream.empty()); final explicitAlwaysAliveFamily = StreamProviderFamily((ref, id) => Stream.empty()); final autoDisposeProvider = StreamProvider.autoDispose((ref) => Stream.empty()); -final explicitAutoDisposeProvider = AutoDisposeStreamProvider((ref) => Stream.empty()); final autoDisposeFamily = StreamProvider.autoDispose.family((ref, id) => Stream.empty()); final autoDisposeFamily2 = StreamProvider.family.autoDispose((ref, id) => Stream.empty()); -final explicitAutoDisposeFamily = AutoDisposeStreamProviderFamily((ref, id) => Stream.empty()); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); final providers = result.legacyProviderDeclarations.takeAll([ 'alwaysAliveProvider', 'alwaysAliveFamily', 'explicitAlwaysAliveFamily', 'autoDisposeProvider', - 'explicitAutoDisposeProvider', 'autoDisposeFamily', 'autoDisposeFamily2', - 'explicitAutoDisposeFamily', ]); for (final provider in providers.entries) { @@ -513,21 +482,17 @@ final alwaysAliveProvider = NotifierProvider, int>(() => throw Uni final alwaysAliveFamily = NotifierProvider.family, int, int>(() => throw UnimplementedError()); final explicitAlwaysAliveFamily = NotifierProviderFamily, int, int>(() => throw UnimplementedError()); final autoDisposeProvider = NotifierProvider.autoDispose, int>(() => throw UnimplementedError()); -final explicitAutoDisposeProvider = AutoDisposeNotifierProvider, int>(() => throw UnimplementedError()); final autoDisposeFamily = NotifierProvider.autoDispose.family, int, int>(() => throw UnimplementedError()); final autoDisposeFamily2 = NotifierProvider.family.autoDispose, int, int>(() => throw UnimplementedError()); -final explicitAutoDisposeFamily = AutoDisposeNotifierProviderFamily, int, int>(() => throw UnimplementedError()); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); final providers = result.legacyProviderDeclarations.takeAll([ 'alwaysAliveProvider', 'alwaysAliveFamily', 'explicitAlwaysAliveFamily', 'autoDisposeProvider', - 'explicitAutoDisposeProvider', 'autoDisposeFamily', 'autoDisposeFamily2', - 'explicitAutoDisposeFamily', ]); for (final provider in providers.entries) { @@ -546,21 +511,17 @@ final alwaysAliveProvider = AsyncNotifierProvider, int>(() => final alwaysAliveFamily = AsyncNotifierProvider.family, int, int>(() => throw UnimplementedError()); final explicitAlwaysAliveFamily = AsyncNotifierProviderFamily, int, int>(() => throw UnimplementedError()); final autoDisposeProvider = AsyncNotifierProvider.autoDispose, int>(() => throw UnimplementedError()); -final explicitAutoDisposeProvider = AutoDisposeAsyncNotifierProvider, int>(() => throw UnimplementedError()); final autoDisposeFamily = AsyncNotifierProvider.autoDispose.family, int, int>(() => throw UnimplementedError()); final autoDisposeFamily2 = AsyncNotifierProvider.family.autoDispose, int, int>(() => throw UnimplementedError()); -final explicitAutoDisposeFamily = AutoDisposeAsyncNotifierProviderFamily, int, int>(() => throw UnimplementedError()); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); final providers = result.legacyProviderDeclarations.takeAll([ 'alwaysAliveProvider', 'alwaysAliveFamily', 'explicitAlwaysAliveFamily', 'autoDisposeProvider', - 'explicitAutoDisposeProvider', 'autoDisposeFamily', 'autoDisposeFamily2', - 'explicitAutoDisposeFamily', ]); for (final provider in providers.entries) { @@ -573,28 +534,24 @@ final explicitAutoDisposeFamily = AutoDisposeAsyncNotifierProviderFamily>((ref) => ValueNotifier(0)); final alwaysAliveFamily = ChangeNotifierProvider.family, int>((ref, id) => ValueNotifier(0)); final explicitAlwaysAliveFamily = ChangeNotifierProviderFamily, int>((ref, id) => ValueNotifier(0)); final autoDisposeProvider = ChangeNotifierProvider.autoDispose>((ref) => ValueNotifier(0)); -final explicitAutoDisposeProvider = AutoDisposeChangeNotifierProvider>((ref) => ValueNotifier(0)); final autoDisposeFamily = ChangeNotifierProvider.autoDispose.family, int>((ref, id) => ValueNotifier(0)); final autoDisposeFamily2 = ChangeNotifierProvider.family.autoDispose, int>((ref, id) => ValueNotifier(0)); -final explicitAutoDisposeFamily = AutoDisposeChangeNotifierProviderFamily, int>((ref, id) => ValueNotifier(0)); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); final providers = result.legacyProviderDeclarations.takeAll([ 'alwaysAliveProvider', 'alwaysAliveFamily', 'explicitAlwaysAliveFamily', 'autoDisposeProvider', - 'explicitAutoDisposeProvider', 'autoDisposeFamily', 'autoDisposeFamily2', - 'explicitAutoDisposeFamily', ]); for (final provider in providers.entries) { @@ -607,27 +564,23 @@ final explicitAutoDisposeFamily = AutoDisposeChangeNotifierProviderFamily, int>((ref) => StateController(0)); final alwaysAliveFamily = StateNotifierProvider.family, int, int>((ref, id) => StateController(0)); final explicitAlwaysAliveFamily = StateNotifierProviderFamily, int, int>((ref, id) => StateController(0)); final autoDisposeProvider = StateNotifierProvider.autoDispose, int>((ref) => StateController(0)); -final explicitAutoDisposeProvider = AutoDisposeStateNotifierProvider, int>((ref) => StateController(0)); final autoDisposeFamily = StateNotifierProvider.autoDispose.family, int, int>((ref, id) => StateController(0)); final autoDisposeFamily2 = StateNotifierProvider.family.autoDispose, int, int>((ref, id) => StateController(0)); -final explicitAutoDisposeFamily = AutoDisposeStateNotifierProviderFamily, int, int>((ref, id) => StateController(0)); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); final providers = result.legacyProviderDeclarations.takeAll([ 'alwaysAliveProvider', 'alwaysAliveFamily', 'explicitAlwaysAliveFamily', 'autoDisposeProvider', - 'explicitAutoDisposeProvider', 'autoDisposeFamily', 'autoDisposeFamily2', - 'explicitAutoDisposeFamily', ]); for (final provider in providers.entries) { @@ -639,68 +592,21 @@ final explicitAutoDisposeFamily = AutoDisposeStateNotifierProviderFamily((ref) => 0); -final alwaysAliveFamily = Provider.family((ref, id) => 0); -final explicitAlwaysAliveFamily = ProviderFamily((ref, id) => 0); - -final autoDisposeProvider = Provider.autoDispose((ref) => 0); -final explicitAutoDisposeProvider = AutoDisposeProvider((ref) => 0); -final autoDisposeFamily = Provider.autoDispose.family((ref, id) => 0); -final autoDisposeFamily2 = Provider.family.autoDispose((ref, id) => 0); -final explicitAutoDisposeFamily = AutoDisposeProviderFamily((ref, id) => 0); -''', (resolver) async { - final result = await resolver.resolveRiverpodAnalysisResult(); - final autoDisposeProviders = result.legacyProviderDeclarations.takeAll([ - 'autoDisposeProvider', - 'explicitAutoDisposeProvider', - 'autoDisposeFamily', - 'autoDisposeFamily2', - 'explicitAutoDisposeFamily', - ]); - - final alwaysAliveProviders = result.legacyProviderDeclarations.takeAll([ - 'alwaysAliveProvider', - 'alwaysAliveFamily', - 'explicitAlwaysAliveFamily', - ]); - - for (final provider in autoDisposeProviders.entries) { - expect( - provider.value.providerElement.isAutoDispose, - true, - reason: '${provider.key} is autoDispose', - ); - } - for (final provider in alwaysAliveProviders.entries) { - expect( - provider.value.providerElement.isAutoDispose, - false, - reason: '${provider.key} is not autoDispose', - ); - } - }); - testSource('Decode families', source: ''' import 'package:riverpod/riverpod.dart'; final alwaysAliveProvider = Provider((ref) => 0); final autoDisposeProvider = Provider.autoDispose((ref) => 0); -final explicitAutoDisposeProvider = AutoDisposeProvider((ref) => 0); final alwaysAliveFamily = Provider.family((ref, id) => 0); final explicitAlwaysAliveFamily = ProviderFamily((ref, id) => 0); final autoDisposeFamily = Provider.autoDispose.family((ref, id) => 0); final autoDisposeFamily2 = Provider.family.autoDispose((ref, id) => 0); -final explicitAutoDisposeFamily = AutoDisposeProviderFamily((ref, id) => 0); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); final providers = result.legacyProviderDeclarations.takeAll([ 'alwaysAliveProvider', 'autoDisposeProvider', - 'explicitAutoDisposeProvider', ]); final families = result.legacyProviderDeclarations.takeAll([ @@ -708,7 +614,6 @@ final explicitAutoDisposeFamily = AutoDisposeProviderFamily((ref, id) 'explicitAlwaysAliveFamily', 'autoDisposeFamily', 'autoDisposeFamily2', - 'explicitAutoDisposeFamily', ]); for (final provider in providers.entries) { diff --git a/packages/riverpod_analyzer_utils_tests/test/matchers.dart b/packages/riverpod_analyzer_utils_tests/test/matchers.dart new file mode 100644 index 000000000..fa13158dd --- /dev/null +++ b/packages/riverpod_analyzer_utils_tests/test/matchers.dart @@ -0,0 +1,344 @@ +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; +import 'package:test/test.dart'; + +TypeMatcher + isLegacyProviderDeclarationElement({ + Object? element = const Object(), + Object? name = const Object(), + Object? familyElement = const Object(), + Object? providerType = const Object(), +}) { + var matcher = isA(); + + if (element != const Object()) { + matcher = matcher.having( + (e) => e.element, + 'element', + element, + ); + } + + if (name != const Object()) { + matcher = matcher.having( + (e) => e.name, + 'name', + name, + ); + } + + if (familyElement != const Object()) { + matcher = matcher.having( + (e) => e.familyElement, + 'familyElement', + familyElement, + ); + } + + if (providerType != const Object()) { + matcher = matcher.having( + (e) => e.providerType, + 'providerType', + providerType, + ); + } + + return matcher; +} + +TypeMatcher isProviderIdentifier({ + Object? node = const Object(), + Object? providerElement = const Object(), + Object? annotation = const Object(), +}) { + var matcher = isA(); + + if (node != const Object()) { + matcher = matcher.having( + (e) => e.node, + 'node', + node, + ); + } + + if (providerElement != const Object()) { + matcher = matcher.having( + (e) => e.providerElement, + 'providerElement', + providerElement, + ); + } + + return matcher; +} + +TypeMatcher + isFunctionalProviderDeclarationElement({ + Object? name = const Object(), + Object? element = const Object(), + Object? annotation = const Object(), +}) { + var matcher = isA(); + + if (name != const Object()) { + matcher = matcher.having( + (e) => e.name, + 'name', + name, + ); + } + + if (element != const Object()) { + matcher = matcher.having( + (e) => e.element, + 'element', + element, + ); + } + + if (annotation != const Object()) { + matcher = matcher.having( + (e) => e.annotation, + 'annotation', + annotation, + ); + } + + return matcher; +} + +TypeMatcher + isClassBasedProviderDeclarationElement({ + Object? name = const Object(), + Object? element = const Object(), + Object? annotation = const Object(), +}) { + var matcher = isA(); + + if (name != const Object()) { + matcher = matcher.having( + (e) => e.name, + 'name', + name, + ); + } + + if (element != const Object()) { + matcher = matcher.having( + (e) => e.element, + 'element', + element, + ); + } + + if (annotation != const Object()) { + matcher = matcher.having( + (e) => e.annotation, + 'annotation', + annotation, + ); + } + + return matcher; +} + +TypeMatcher isDependencies({ + Object? node = const Object(), + Object? element = const Object(), + Object? dependenciesNode = const Object(), + Object? dependencies = const Object(), +}) { + var matcher = isA(); + + if (node != const Object()) { + matcher = matcher.having( + (e) => e.node, + 'node', + node, + ); + } + + if (element != const Object()) { + matcher = matcher.having( + (e) => e.element, + 'element', + element, + ); + } + + if (dependencies != const Object()) { + matcher = matcher.having( + (e) => e.dependencies, + 'dependencies', + dependencies, + ); + } + + if (dependenciesNode != const Object()) { + matcher = matcher.having( + (e) => e.dependenciesNode, + 'dependenciesNode', + dependenciesNode, + ); + } + + return matcher; +} + +TypeMatcher isDependenciesElement({ + Object? element = const Object(), + Object? dependencies = const Object(), +}) { + var matcher = isA(); + + if (element != const Object()) { + matcher = matcher.having( + (e) => e.element, + 'element', + element, + ); + } + + if (dependencies != const Object()) { + matcher = matcher.having( + (e) => e.dependencies, + 'dependencies', + dependencies, + ); + } + + return matcher; +} + +TypeMatcher isRiverpod({ + Object? node = const Object(), + Object? element = const Object(), + Object? keepAlive = const Object(), + Object? dependenciesNode = const Object(), + Object? dependencyList = const Object(), +}) { + var matcher = isA(); + + if (node != const Object()) { + matcher = matcher.having( + (e) => e.node, + 'node', + node, + ); + } + + if (element != const Object()) { + matcher = matcher.having( + (e) => e.element, + 'element', + element, + ); + } + + if (keepAlive != const Object()) { + matcher = matcher.having( + (e) => e.keepAliveNode, + 'keepAliveNode', + keepAlive, + ); + } + + if (dependencyList != const Object()) { + matcher = matcher.having( + (e) => e.dependencyList, + 'dependencyList', + dependencyList, + ); + } + + if (dependenciesNode != const Object()) { + matcher = matcher.having( + (e) => e.dependenciesNode, + 'dependenciesNode', + dependenciesNode, + ); + } + + return matcher; +} + +TypeMatcher isProviderDependency({ + Object? node = const Object(), + Object? provider = const Object(), +}) { + var matcher = isA(); + + if (node != const Object()) { + matcher = matcher.having( + (e) => e.node, + 'node', + node, + ); + } + + if (provider != const Object()) { + matcher = matcher.having( + (e) => e.provider, + 'provider', + provider, + ); + } + + return matcher; +} + +TypeMatcher isProviderDependencyList({ + Object? node = const Object(), + Object? values = const Object(), +}) { + var matcher = isA(); + + if (node != const Object()) { + matcher = matcher.having( + (e) => e.node, + 'node', + node, + ); + } + + if (values != const Object()) { + matcher = matcher.having( + (e) => e.values, + 'values', + values, + ); + } + + return matcher; +} + +TypeMatcher isRiverpodAnnotationElement({ + Object? keepAlive = const Object(), + Object? dependencies = const Object(), +}) { + var matcher = isA(); + + if (keepAlive != const Object()) { + matcher = matcher.having( + (e) => e.keepAlive, + 'keepAlive', + keepAlive, + ); + } + + if (dependencies != const Object()) { + matcher = matcher.having( + (e) => e.dependencies, + 'dependencies', + dependencies, + ); + } + + return matcher; +} + +Matcher hasToString(Object? expected) { + return predicate( + (e) => e.toString() == expected.toString(), + 'toString', + ); +} diff --git a/packages/riverpod_analyzer_utils_tests/test/mutation_test.dart b/packages/riverpod_analyzer_utils_tests/test/mutation_test.dart new file mode 100644 index 000000000..d9dccd451 --- /dev/null +++ b/packages/riverpod_analyzer_utils_tests/test/mutation_test.dart @@ -0,0 +1,98 @@ +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; +import 'package:test/test.dart'; + +import 'analyzer_test_utils.dart'; + +void main() { + testSource( + 'Rejects mutations with a return value non-matching the build value', + source: r''' +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +@riverpod +class SyncNotifier extends _$SyncNotifier { + @override + T build() => throw 42; + + @mutation + Stream a() => throw 42; + + @mutation + T b() => throw 42; + + @mutation + FutureOr c() => throw 42; + + @mutation + Future d() => throw 42; + + @mutation + Future e() => throw 42; + + @mutation + int e() => throw 42; +} +''', (resolver, unit, units) async { + final result = + await resolver.resolveRiverpodAnalysisResult(ignoreErrors: true); + + expect(result.errors, hasLength(3)); + + expect( + result.errors, + everyElement( + isA() + .having((e) => e.targetNode, 'node', isNotNull) + .having((e) => e.targetElement, 'element', isNotNull) + .having( + (e) => e.code, + 'code', + RiverpodAnalysisErrorCode.mutationReturnTypeMismatch, + ) + .having( + (e) => e.message, + 'message', + 'The return type of mutations must match the type returned by the "build" method.', + ), + ), + ); + }); + + testSource('rejects abstract/static mutations', source: r''' +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +@riverpod +class Abstract extends _$Abstract { + @override + int build() => 0; + + @mutation + Future a(); + + @mutation + static Future b() async => 42; +} +''', (resolver, unit, units) async { + final result = + await resolver.resolveRiverpodAnalysisResult(ignoreErrors: true); + + expect(result.errors, hasLength(2)); + + expect( + result.errors, + everyElement( + isA() + .having((e) => e.targetNode, 'node', isNotNull) + .having((e) => e.targetElement, 'element', isNotNull) + .having( + (e) => e.code, + 'code', + anyOf( + RiverpodAnalysisErrorCode.mutationIsAbstract, + RiverpodAnalysisErrorCode.mutationIsStatic, + ), + ), + ), + ); + }); +} diff --git a/packages/riverpod_analyzer_utils_tests/test/nodes/dependencies_test.dart b/packages/riverpod_analyzer_utils_tests/test/nodes/dependencies_test.dart new file mode 100644 index 000000000..5585672bb --- /dev/null +++ b/packages/riverpod_analyzer_utils_tests/test/nodes/dependencies_test.dart @@ -0,0 +1,97 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; +import 'package:test/test.dart'; + +import '../analyzer_test_utils.dart'; +import '../matchers.dart'; + +void main() { + testSource('Decodes Dependencies', runGenerator: true, source: r''' +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter/material.dart'; + +@riverpod +int a(Ref ref) => 0; + +@riverpod +class B extends _$B { + @override + int build() => 0; +} + +@riverpod +int c(Ref ref, int arg) => 0; + +@riverpod +class D extends _$D { + @override + int build(int arg) => 0; +} + +@Dependencies([a, B, c, D]) +class Class {} + +@Dependencies([a, B, c, D]) +void function() {} + +void main() { + @Dependencies([a, B, c, D]) + var value = 0; +} +''', (resolver, unit, units) async { + final clazz = unit.declarations.findByName('Class'); + final function = unit.declarations.findByName('function'); + + final value = unit.declarations + .findByName('main') + .functionExpression + .body + .cast()! + .block + .statements + .whereType() + .single; + + expect( + clazz.dependencies, + isDependencies( + node: hasToString('@Dependencies([a, B, c, D])'), + dependenciesNode: hasToString('[a, B, c, D]'), + element: isDependenciesElement( + element: isA().having( + (e) => e.toSource(), + 'toSource', + '@Dependencies([a, B, c, D])', + ), + dependencies: [ + isFunctionalProviderDeclarationElement(name: 'a'), + isClassBasedProviderDeclarationElement(name: 'B'), + isFunctionalProviderDeclarationElement(name: 'c'), + isClassBasedProviderDeclarationElement(name: 'D'), + ], + ), + dependencies: isProviderDependencyList( + values: [ + isProviderDependency( + provider: isFunctionalProviderDeclarationElement(name: 'a'), + ), + isProviderDependency( + provider: isClassBasedProviderDeclarationElement(name: 'B'), + ), + isProviderDependency( + provider: isFunctionalProviderDeclarationElement(name: 'c'), + ), + isProviderDependency( + provider: isClassBasedProviderDeclarationElement(name: 'D'), + ), + ], + ), + ), + ); + + expect(function.dependencies, isDependencies()); + expect(value.variables.dependencies, isDependencies()); + }); +} diff --git a/packages/riverpod_analyzer_utils_tests/test/generator_provider_declaration_test.dart b/packages/riverpod_analyzer_utils_tests/test/nodes/providers/generated_providers_test.dart similarity index 65% rename from packages/riverpod_analyzer_utils_tests/test/generator_provider_declaration_test.dart rename to packages/riverpod_analyzer_utils_tests/test/nodes/providers/generated_providers_test.dart index bf66ba6dd..eed80d7fd 100644 --- a/packages/riverpod_analyzer_utils_tests/test/generator_provider_declaration_test.dart +++ b/packages/riverpod_analyzer_utils_tests/test/nodes/providers/generated_providers_test.dart @@ -1,34 +1,76 @@ -import 'package:riverpod_analyzer_utils/src/riverpod_ast.dart'; +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; import 'package:test/test.dart'; -import 'analyzer_test_utils.dart'; +import '../../analyzer_test_utils.dart'; void main() { + testSource('Parses element.isFamily', source: r''' +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +@riverpod +T generic(Ref ref) => throw UnimplementedError(); + +@riverpod +class GenericClass extends _$GenericClass { + @override + T build() => throw UnimplementedError(); +} + +@riverpod +int value(Ref ref) => throw UnimplementedError(); + +@riverpod +class ValueClass extends _$ValueClass { + @override + int build() => throw UnimplementedError(); +} + +@riverpod +int parametrized(Ref ref, int id) => throw UnimplementedError(); + +@riverpod +class ParametrizedClass extends _$ParametrizedClass { + @override + int build(int id) => throw UnimplementedError(); +} +''', (resolver, unit, units) async { + final generic = unit.declarations.findByName('generic').provider!; + final genericClass = unit.declarations.findByName('GenericClass').provider!; + final value = unit.declarations.findByName('value').provider!; + final valueClass = unit.declarations.findByName('ValueClass').provider!; + final parametrized = unit.declarations.findByName('parametrized').provider!; + final parametrizedClass = + unit.declarations.findByName('ParametrizedClass').provider!; + + expect(generic.providerElement.isFamily, true); + expect(genericClass.providerElement.isFamily, true); + + expect(value.providerElement.isFamily, false); + expect(valueClass.providerElement.isFamily, false); + + expect(parametrized.providerElement.isFamily, true); + expect(parametrizedClass.providerElement.isFamily, true); + }); + testSource('Parses Raw types', source: ''' import 'package:riverpod_annotation/riverpod_annotation.dart'; @riverpod -Raw> value(ValueRef ref) async => 0; +Raw> value(Ref ref) async => 0; @riverpod -Future value2(Value2Ref ref) async => 0; +Future value2(Ref ref) async => 0; @riverpod -Future> value3(Value3Ref ref) async => 0; -''', (resolver) async { - final result = await resolver.resolveRiverpodAnalysisResult( - ignoreErrors: true, - ); - - final value = result.functionalProviderDeclarations.singleWhere( - (e) => e.name.toString() == 'value', - ); - final value2 = result.functionalProviderDeclarations.singleWhere( - (e) => e.name.toString() == 'value2', - ); - final value3 = result.functionalProviderDeclarations.singleWhere( - (e) => e.name.toString() == 'value3', - ); +Future> value3(Ref ref) async => 0; +''', (resolver, unit, units) async { + final value = + unit.declarations.findByName('value').provider!; + final value2 = + unit.declarations.findByName('value2').provider!; + final value3 = + unit.declarations.findByName('value3').provider!; expect(value.createdTypeNode.toString(), 'Raw>'); expect(value.createdTypeDisplayString, 'Raw>'); expect(value.exposedTypeNode.source, 'Raw>'); @@ -61,126 +103,42 @@ Future> value3(Value3Ref ref) async => 0; expect(value3.valueTypeNode!.type!.isRaw, true); }); - testSource('Decode needsOverride/isScoped', source: ''' + testSource('Decode isScoped', source: ''' import 'package:riverpod_annotation/riverpod_annotation.dart'; -@riverpod -external int needsOverride(); - @Riverpod(dependencies: []) int scoped() => 0; @riverpod -int plain(PlainRef ref) => 0; -''', (resolver) async { - final result = await resolver.resolveRiverpodAnalysisResult( - ignoreErrors: true, - ); - - final needsOverride = result.functionalProviderDeclarations.singleWhere( - (e) => e.name.toString() == 'needsOverride', - ); - final scoped = result.functionalProviderDeclarations.singleWhere( - (e) => e.name.toString() == 'scoped', - ); - final plain = result.functionalProviderDeclarations.singleWhere( - (e) => e.name.toString() == 'plain', - ); - - expect(needsOverride.needsOverride, true); - expect(scoped.needsOverride, false); - expect(plain.needsOverride, false); - - expect(needsOverride.providerElement.isScoped, true); +int plain(Ref ref) => 0; +''', (resolver, unit, units) async { + final scoped = + unit.declarations.findByName('scoped').provider!; + final plain = + unit.declarations.findByName('plain').provider!; + expect(scoped.providerElement.isScoped, true); expect(plain.providerElement.isScoped, false); }); - testSource('Decode dependencies with syntax errors', source: ''' -import 'package:riverpod_annotation/riverpod_annotation.dart'; - -const deps = []; - -@Riverpod(dependencies: deps) -int first(FirstRef ref) => 0; - -@Riverpod(dependencies: ) -int second(SecondRef ref) => 0; - -@Riverpod(dependencies: [gibberish]) -int forth(ForthRef ref) => 0; - -@Riverpod(dependencies: [if (true) forth]) -int fifth(FifthRef ref) => 0; - -@Riverpod(dependencies: [int]) -int sixth(SixthRef ref) => 0; -''', (resolver) async { - final result = await resolver.resolveRiverpodAnalysisResult( - ignoreErrors: true, - ); - - final errors = - result.resolvedRiverpodLibraryResults.expand((e) => e.errors).toList(); - - expect(errors, hasLength(6)); - - expect( - errors[0].message, - '@Riverpod(dependencies: <...>) only support list literals (using []).', - ); - expect(errors[0].targetNode?.toSource(), 'deps'); - - expect( - errors[1].message, - '@Riverpod(dependencies: <...>) only support list literals (using []).', - ); - expect(errors[1].targetNode?.toSource(), ''); - - expect( - errors[2].message, - '@Riverpod(dependencies: [...]) only supports elements annotated with @riverpod as values.', - ); - expect(errors[2].targetNode?.toSource(), 'gibberish'); - - expect( - errors[3].message, - '@Riverpod(dependencies: [...]) does not support if/for/spread operators.', - ); - expect(errors[3].targetNode?.toSource(), 'if (true) forth'); - - expect( - errors[4].message, - 'Unsupported dependency. Only functions and classes annotated by @riverpod are supported.', - ); - expect(errors[4].targetElement.toString(), 'int sixth(InvalidType ref)'); - - expect( - errors[5].message, - 'Failed to parse dependency Type (int)', - ); - expect(errors[5].targetElement?.toString(), 'int sixth(InvalidType ref)'); - }); - testSource('Decode name', runGenerator: true, source: r''' import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'foo.g.dart'; @riverpod -int first(FirstRef ref) => 0; +int first(Ref ref) => 0; @Riverpod() -int second(SecondRef ref) => 0; +int second(Ref ref) => 0; @riverpod class Counter extends _$Counter { @override int build() => 0; } -''', (resolver) async { - final result = await resolver.resolveRiverpodAnalysisResult(); - final providers = result.generatorProviderDeclarations; +''', (resolver, unit, units) async { + final providers = unit.declarations.map((e) => e.provider).toList(); expect(providers, [ isA() @@ -201,13 +159,13 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'foo.g.dart'; @riverpod -int autoDispose(AutoDisposeRef ref) => 0; +int autoDispose(Ref ref) => 0; @Riverpod(keepAlive: false) -int autoDispose2(AutoDisposeRef ref) => 0; +int autoDispose2(Ref ref) => 0; @Riverpod(keepAlive: true) -int keepAlive(KeepAliveRef ref) => 0; +int keepAlive(Ref ref) => 0; @riverpod class AutoDisposeNotifierTest extends _$AutoDisposeNotifierTest { @@ -226,17 +184,19 @@ class KeepAliveNotifier extends _$KeepAliveNotifier { @override int build() => 0; } -''', (resolver) async { - final result = await resolver.resolveRiverpodAnalysisResult(); - final autoDispose = result.generatorProviderDeclarations.takeAll([ +''', (resolver, unit, units) async { + final providers = + unit.declarations.map((e) => e.provider).nonNulls.toList(); + + final autoDispose = providers.takeAll([ 'autoDispose', 'AutoDisposeNotifierTest', ]); - final explicitAutoDispose = result.generatorProviderDeclarations.takeAll([ + final explicitAutoDispose = providers.takeAll([ 'autoDispose2', 'AutoDisposeNotifier2', ]); - final keepAlive = result.generatorProviderDeclarations.takeAll([ + final keepAlive = providers.takeAll([ 'keepAlive', 'KeepAliveNotifier', ]); @@ -285,7 +245,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'foo.g.dart'; @riverpod -int root(RootRef ref) => 0; +int root(Ref ref) => 0; @riverpod class RootNotifier extends _$RootNotifier { @@ -294,7 +254,7 @@ class RootNotifier extends _$RootNotifier { } @Riverpod(dependencies: []) -int empty(EmptyRef ref) => 0; +int empty(Ref ref) => 0; @Riverpod(dependencies: []) class EmptyNotifier extends _$EmptyNotifier { @@ -303,7 +263,7 @@ class EmptyNotifier extends _$EmptyNotifier { } @Riverpod(dependencies: [empty, EmptyNotifier]) -int providerDependency(ProviderDependencyRef ref) => 0; +int providerDependency(Ref ref) => 0; @Riverpod(dependencies: [empty, EmptyNotifier]) class ProviderDependencyNotifier extends _$ProviderDependencyNotifier { @@ -312,7 +272,7 @@ class ProviderDependencyNotifier extends _$ProviderDependencyNotifier { } @Riverpod(dependencies: [providerDependency, ProviderDependencyNotifier]) -int nestedDependency(NestedDependencyRef ref) => 0; +int nestedDependency(Ref ref) => 0; @Riverpod(dependencies: [providerDependency, ProviderDependencyNotifier]) class NestedDependencyNotifier extends _$NestedDependencyNotifier { @@ -321,37 +281,39 @@ class NestedDependencyNotifier extends _$NestedDependencyNotifier { } @Riverpod(dependencies: [empty, EmptyNotifier]) -int family(NestedDependencyRef ref) => 0; +int family(Ref ref) => 0; @Riverpod(dependencies: [empty, EmptyNotifier]) class FamilyClass extends _$FamilyClass { @override int build() => 0; } -''', (resolver) async { - final result = await resolver.resolveRiverpodAnalysisResult(); - final roots = result.generatorProviderDeclarations.takeAll([ +''', (resolver, unit, units) async { + final providers = + unit.declarations.map((e) => e.provider).nonNulls.toList(); + + final roots = providers.takeAll([ 'root', 'RootNotifier', ]); - final empty = result.generatorProviderDeclarations.takeAll([ + final empty = providers.takeAll([ 'empty', 'EmptyNotifier', ]); - final providers = result.generatorProviderDeclarations.takeAll([ + final direct = providers.takeAll([ 'providerDependency', 'ProviderDependencyNotifier', 'family', 'FamilyClass', ]); - final nesteds = result.generatorProviderDeclarations.takeAll([ + final nesteds = providers.takeAll([ 'nestedDependency', 'NestedDependencyNotifier', ]); for (final provider in roots.entries) { expect( - provider.value.annotation.dependencies, + provider.value.annotation.dependencyList, null, reason: '${provider.key} has no dependency', ); @@ -368,7 +330,7 @@ class FamilyClass extends _$FamilyClass { } for (final provider in empty.entries) { expect( - provider.value.annotation.dependencies?.dependencies, + provider.value.annotation.dependencyList?.values, isEmpty, reason: '${provider.key} has an empty list of dependencies', ); @@ -383,14 +345,20 @@ class FamilyClass extends _$FamilyClass { reason: '${provider.key} has an empty list of dependencies', ); expect( - provider.value.annotation.dependencies?.node.toSource(), + provider.value.annotation.dependenciesNode?.toSource(), 'dependencies: []', reason: '${provider.key} has an empty list of dependencies', ); + expect( + provider.value.annotation.dependencyList?.node?.toSource(), + '[]', + reason: '${provider.key} has an empty list of dependencies', + ); } - for (final provider in providers.entries) { + + for (final provider in direct.entries) { expect( - provider.value.annotation.dependencies?.dependencies, + provider.value.annotation.dependencyList?.values, hasLength(2), reason: '${provider.key} has two explicit dependencies', ); @@ -405,8 +373,8 @@ class FamilyClass extends _$FamilyClass { reason: '${provider.key} has two explicit dependencies', ); expect( - provider.value.annotation.dependencies?.dependencies?[0], - isA() + provider.value.annotation.dependencyList?.values?[0], + isA() .having( (e) => e.provider, 'provider', @@ -416,8 +384,8 @@ class FamilyClass extends _$FamilyClass { reason: '${provider.key} has `empty` as first dependency', ); expect( - provider.value.annotation.dependencies?.dependencies?[1], - isA() + provider.value.annotation.dependencyList?.values?[1], + isA() .having( (e) => e.provider, 'provider', @@ -444,15 +412,20 @@ class FamilyClass extends _$FamilyClass { ); expect( - provider.value.annotation.dependencies?.node.toSource(), + provider.value.annotation.dependenciesNode?.toSource(), 'dependencies: [empty, EmptyNotifier]', reason: '${provider.key} has two dependencies', ); + expect( + provider.value.annotation.dependencyList?.node?.toSource(), + '[empty, EmptyNotifier]', + reason: '${provider.key} has two dependencies', + ); } for (final provider in nesteds.entries) { expect( - provider.value.annotation.dependencies?.dependencies, + provider.value.annotation.dependencyList?.values, hasLength(2), reason: '${provider.key} has two explicit dependencies', ); @@ -464,31 +437,31 @@ class FamilyClass extends _$FamilyClass { expect( provider.value.annotation.element.allTransitiveDependencies, unorderedEquals([ - providers['providerDependency']!.providerElement, - providers['ProviderDependencyNotifier']!.providerElement, + direct['providerDependency']!.providerElement, + direct['ProviderDependencyNotifier']!.providerElement, empty['empty']!.providerElement, empty['EmptyNotifier']!.providerElement, ]), reason: '${provider.key} has two explicit dependencies', ); expect( - provider.value.annotation.dependencies?.dependencies?[0], - isA() + provider.value.annotation.dependencyList?.values?[0], + isA() .having( (e) => e.provider, 'provider', - same(providers['providerDependency']!.providerElement), + same(direct['providerDependency']!.providerElement), ) .having((e) => e.node.toString(), 'node', 'providerDependency'), reason: '${provider.key} has `providerDependency` as first dependency', ); expect( - provider.value.annotation.dependencies?.dependencies?[1], - isA() + provider.value.annotation.dependencyList?.values?[1], + isA() .having( (e) => e.provider, 'provider', - same(providers['ProviderDependencyNotifier']!.providerElement), + same(direct['ProviderDependencyNotifier']!.providerElement), ) .having( (e) => e.node.toString(), @@ -506,21 +479,26 @@ class FamilyClass extends _$FamilyClass { ); expect( provider.value.annotation.element.dependencies?.elementAt(0), - same(providers['providerDependency']!.providerElement), + same(direct['providerDependency']!.providerElement), reason: '${provider.key} has `providerDependency` as first dependency', ); expect( provider.value.annotation.element.dependencies?.elementAt(1), - same(providers['ProviderDependencyNotifier']!.providerElement), + same(direct['ProviderDependencyNotifier']!.providerElement), reason: '${provider.key} has `ProviderDependencyNotifier` as second dependency', ); expect( - provider.value.annotation.dependencies?.node.toSource(), + provider.value.annotation.dependenciesNode?.toSource(), 'dependencies: [providerDependency, ProviderDependencyNotifier]', reason: '${provider.key} has two dependencies', ); + expect( + provider.value.annotation.dependencyList?.node?.toSource(), + '[providerDependency, ProviderDependencyNotifier]', + reason: '${provider.key} has two dependencies', + ); } }); } diff --git a/packages/riverpod_analyzer_utils_tests/test/nodes/providers/identifiers_test.dart b/packages/riverpod_analyzer_utils_tests/test/nodes/providers/identifiers_test.dart new file mode 100644 index 000000000..93c46fb2e --- /dev/null +++ b/packages/riverpod_analyzer_utils_tests/test/nodes/providers/identifiers_test.dart @@ -0,0 +1,88 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; +import 'package:test/expect.dart'; +import 'package:test/test.dart'; + +import '../../analyzer_test_utils.dart'; +import '../../matchers.dart'; + +void main() { + testSource('Decode generated provider identifiers', + runGenerator: true, source: r''' +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'foo.g.dart'; + +// Let's define some providers +@riverpod +int a(Ref ref) => 0; + +@riverpod +class B extends _$B { + @override + int build() => 0; +} + +// Using those providers in random places +void main() { + aProvider; + bProvider; +} +''', (resolver, unit, units) async { + final visitor = _FindIdentifiersVisitor(); + unit.accept(visitor); + + expect(visitor.identifiers, hasLength(2)); + + expect( + visitor.identifiers[0], + isProviderIdentifier( + node: hasToString('aProvider'), + providerElement: isFunctionalProviderDeclarationElement(name: 'a'), + ), + ); + expect( + visitor.identifiers[1], + isProviderIdentifier( + node: hasToString('bProvider'), + providerElement: isClassBasedProviderDeclarationElement(name: 'B'), + ), + ); + }); + + testSource('Decode legacy provider identifiers', source: ''' +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final provider = Provider((ref) => 0); + +void main() { + provider; +} +''', (resolver, unit, units) async { + final visitor = _FindIdentifiersVisitor(); + unit.accept(visitor); + + expect(visitor.identifiers, hasLength(1)); + + expect( + visitor.identifiers[0], + isProviderIdentifier( + node: hasToString('provider'), + providerElement: isLegacyProviderDeclarationElement(name: 'provider'), + ), + ); + }); +} + +class _FindIdentifiersVisitor extends RecursiveAstVisitor { + final List identifiers = []; + + @override + void visitSimpleIdentifier(SimpleIdentifier node) { + super.visitSimpleIdentifier(node); + if (node.provider case final provider?) { + identifiers.add(provider); + } + } +} diff --git a/packages/riverpod_analyzer_utils_tests/test/nodes/riverpod_test.dart b/packages/riverpod_analyzer_utils_tests/test/nodes/riverpod_test.dart new file mode 100644 index 000000000..5335ae89a --- /dev/null +++ b/packages/riverpod_analyzer_utils_tests/test/nodes/riverpod_test.dart @@ -0,0 +1,276 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; +import 'package:test/expect.dart'; +import 'package:test/test.dart'; + +import '../analyzer_test_utils.dart'; +import '../matchers.dart'; + +void main() { + testSource('Decode dependencies with syntax errors', source: ''' +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +const deps = []; + +@Riverpod(dependencies: deps) +int first(Ref ref) => 0; + +@Riverpod(dependencies: ) +int second(Ref ref) => 0; + +@Riverpod(dependencies: [gibberish]) +int forth(Ref ref) => 0; + +@Riverpod(dependencies: [if (true) forth]) +int fifth(Ref ref) => 0; + +@Riverpod(dependencies: [int]) +int sixth(Ref ref) => 0; + +void fn() {} + +@Riverpod(dependencies: [fn]) +int seven(Ref ref) => 0; + +@Riverpod(dependencies: ['foo']) +int eight(Ref ref) => 0; +''', (resolver, unit, units) async { + final errors = collectErrors(() { + for (final d in unit.declarations) { + d.provider; + } + }); + + expect(errors, hasLength(10)); + + expect( + errors[0].message, + 'Only list literals (using []) as supported.', + ); + expect(errors[0].targetNode?.toSource(), 'deps'); + + expect( + errors[1].message, + 'Only list literals (using []) as supported.', + ); + expect(errors[1].targetNode?.toSource(), ''); + + expect( + errors[2].message, + 'Only elements annotated with @riverpod are supported.', + ); + expect(errors[2].targetNode?.toSource(), 'gibberish'); + + expect( + errors[3].message, + 'if/for/spread operators as not supported.', + ); + expect(errors[3].targetNode?.toSource(), 'if (true) forth'); + + expect( + errors[4].message, + 'Unsupported dependency "int". Only functions and classes annotated by @riverpod are supported.', + ); + expect( + errors[4].targetElement.toString(), + 'Riverpod Riverpod({bool keepAlive = false, List? dependencies, Duration? Function(int, Object)? retry})', + ); + + expect( + errors[5].message, + 'The dependency int is not a class annotated with @riverpod', + ); + expect(errors[5].targetNode.toString(), 'int'); + + expect( + errors[6].message, + 'Unsupported dependency "void fn()". Only functions and classes annotated by @riverpod are supported.', + ); + expect( + errors[6].targetElement.toString(), + 'Riverpod Riverpod({bool keepAlive = false, List? dependencies, Duration? Function(int, Object)? retry})', + ); + + expect( + errors[7].message, + 'The dependency fn is not a function annotated with @riverpod', + ); + expect(errors[7].targetNode.toString(), 'fn'); + + expect( + errors[8].message, + 'Unsupported dependency "String (\'foo\')". Only functions and classes annotated by @riverpod are supported.', + ); + expect( + errors[8].targetElement.toString(), + 'Riverpod Riverpod({bool keepAlive = false, List? dependencies, Duration? Function(int, Object)? retry})', + ); + + expect( + errors[9].message, + 'Only elements annotated with @riverpod are supported.', + ); + expect(errors[9].targetNode.toString(), "'foo'"); + }); + + testSource('Reuses elements', source: ''' +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +@riverpod +int dependency(Ref ref) => 0; + +@Riverpod(dependencies: [dependency]) +int fn(Ref ref) => 0; + +@Riverpod(dependencies: [dependency]) +int fn2(Ref ref) => 0; +''', (resolver, unit, units) async { + final dependency = unit.declarations.findByName('dependency').riverpod; + + final fn = unit.declarations.findByName('fn').riverpod; + + final fn2 = unit.declarations.findByName('fn2').riverpod; + + expect( + dependency!.element, + same(fn!.dependencyList!.values!.single.provider.annotation), + ); + expect( + fn.dependencyList!.values!.single.provider, + same(fn2!.dependencyList!.values!.single.provider), + ); + }); + + testSource('Decodes @riverpod', source: ''' +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +@deprecated +int unrelated() => 0; + +@riverpod +int variable(Ref ref) => 0; + +@Riverpod() +int constructor(Ref ref) => 0; + +@Riverpod(keepAlive: false, dependencies: null) +int explicit(Ref ref) => 0; + +@Riverpod(keepAlive: true, dependencies: [variable]) +int constructor2(Constructor2Ref ref) => 0; + +class NestedClass { + void method() { + @riverpod + int value = 0; + } +} +''', (resolver, unit, units) async { + expect( + unit.declarations.findByName('unrelated').riverpod, + isNull, + ); + + final variable = unit.declarations.findByName('variable').riverpod; + expect( + variable, + isRiverpod( + node: hasToString('@riverpod'), + element: isRiverpodAnnotationElement( + keepAlive: false, + dependencies: isNull, + ), + keepAlive: isNull, + dependenciesNode: isNull, + dependencyList: isNull, + ), + ); + + expect( + unit.declarations.findByName('constructor').riverpod, + isRiverpod( + node: hasToString('@Riverpod()'), + element: isRiverpodAnnotationElement( + keepAlive: false, + dependencies: isNull, + ), + keepAlive: isNull, + dependenciesNode: isNull, + dependencyList: isNull, + ), + ); + + final explicit = unit.declarations.findByName('explicit').riverpod; + expect( + explicit, + isRiverpod( + node: hasToString('@Riverpod(keepAlive: false, dependencies: null)'), + element: isRiverpodAnnotationElement( + keepAlive: false, + dependencies: isNull, + ), + keepAlive: hasToString('keepAlive: false'), + dependenciesNode: hasToString('dependencies: null'), + dependencyList: isProviderDependencyList( + node: hasToString('null'), + values: isNull, + ), + ), + ); + + final constructor2 = unit.declarations.findByName('constructor2').riverpod; + expect( + constructor2, + isRiverpod( + node: hasToString( + '@Riverpod(keepAlive: true, dependencies: [variable])', + ), + element: isRiverpodAnnotationElement( + keepAlive: true, + dependencies: [ + isFunctionalProviderDeclarationElement(name: 'variable'), + ], + ), + keepAlive: hasToString('keepAlive: true'), + dependenciesNode: hasToString('dependencies: [variable]'), + dependencyList: isProviderDependencyList( + node: hasToString('[variable]'), + values: [ + isProviderDependency( + node: hasToString('variable'), + provider: isFunctionalProviderDeclarationElement( + name: 'variable', + ), + ), + ], + ), + ), + ); + + final nestedVariable = unit.declarations + .findByName('NestedClass') + .members + .findByName('method') + .body + .cast()! + .block + .statements + .first + .cast()! + .variables; + + expect( + nestedVariable.riverpod, + isRiverpod( + node: hasToString('@riverpod'), + element: isRiverpodAnnotationElement( + keepAlive: false, + dependencies: isNull, + ), + keepAlive: isNull, + dependenciesNode: isNull, + dependencyList: isNull, + ), + ); + }); +} diff --git a/packages/riverpod_analyzer_utils_tests/test/provider_container_test.dart b/packages/riverpod_analyzer_utils_tests/test/provider_container_test.dart index 02957512b..73e98f69e 100644 --- a/packages/riverpod_analyzer_utils_tests/test/provider_container_test.dart +++ b/packages/riverpod_analyzer_utils_tests/test/provider_container_test.dart @@ -33,7 +33,7 @@ void main() { ], ); } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); final provider = @@ -54,49 +54,49 @@ void main() { expect(containers[1].overrides!.overrides, hasLength(6)); expect( containers[1].overrides!.node.toSource(), - 'overrides: [provider.overrideWith((ref) => 0), provider.overrideWithValue(42), provider, family(42), family.overrideWith((ref, id) => 0), family(42).overrideWith((ref) => 0)]', + '[provider.overrideWith((ref) => 0), provider.overrideWithValue(42), provider, family(42), family.overrideWith((ref, id) => 0), family(42).overrideWith((ref) => 0)]', ); { expect( - containers[1].overrides!.overrides![0].providerElement, + containers[1].overrides!.overrides![0].provider?.providerElement, same(provider.providerElement), ); expect( - containers[1].overrides!.overrides![0].expression.toSource(), + containers[1].overrides!.overrides![0].node.toSource(), 'provider.overrideWith((ref) => 0)', ); expect( - containers[1].overrides!.overrides![0].provider!.toSource(), + containers[1].overrides!.overrides![0].provider!.node.toSource(), 'provider', ); expect(containers[1].overrides!.overrides![0].familyArguments, null); } { expect( - containers[1].overrides!.overrides![1].providerElement, + containers[1].overrides!.overrides![1].provider?.providerElement, same(provider.providerElement), ); expect( - containers[1].overrides!.overrides![1].expression.toSource(), + containers[1].overrides!.overrides![1].node.toSource(), 'provider.overrideWithValue(42)', ); expect( - containers[1].overrides!.overrides![1].provider!.toSource(), + containers[1].overrides!.overrides![1].provider!.node.toSource(), 'provider', ); expect(containers[1].overrides!.overrides![1].familyArguments, null); } { expect( - containers[1].overrides!.overrides![2].providerElement, + containers[1].overrides!.overrides![2].provider?.providerElement, same(provider.providerElement), ); expect( - containers[1].overrides!.overrides![2].expression.toSource(), + containers[1].overrides!.overrides![2].node.toSource(), 'provider', ); expect( - containers[1].overrides!.overrides![2].provider!.toSource(), + containers[1].overrides!.overrides![2].provider!.node.toSource(), 'provider', ); expect(containers[1].overrides!.overrides![2].familyArguments, null); @@ -104,15 +104,15 @@ void main() { { expect( - containers[1].overrides!.overrides![3].providerElement, + containers[1].overrides!.overrides![3].provider?.providerElement, same(family.providerElement), ); expect( - containers[1].overrides!.overrides![3].expression.toSource(), + containers[1].overrides!.overrides![3].node.toSource(), 'family(42)', ); expect( - containers[1].overrides!.overrides![3].provider!.toSource(), + containers[1].overrides!.overrides![3].provider!.node.toSource(), 'family', ); expect( @@ -122,30 +122,30 @@ void main() { } { expect( - containers[1].overrides!.overrides![4].providerElement, + containers[1].overrides!.overrides![4].provider?.providerElement, same(family.providerElement), ); expect( - containers[1].overrides!.overrides![4].expression.toSource(), + containers[1].overrides!.overrides![4].node.toSource(), 'family.overrideWith((ref, id) => 0)', ); expect( - containers[1].overrides!.overrides![4].provider!.toSource(), + containers[1].overrides!.overrides![4].provider!.node.toSource(), 'family', ); expect(containers[1].overrides!.overrides![4].familyArguments, null); } { expect( - containers[1].overrides!.overrides![5].providerElement, + containers[1].overrides!.overrides![5].provider?.providerElement, same(family.providerElement), ); expect( - containers[1].overrides!.overrides![5].expression.toSource(), + containers[1].overrides!.overrides![5].node.toSource(), 'family(42).overrideWith((ref) => 0)', ); expect( - containers[1].overrides!.overrides![5].provider!.toSource(), + containers[1].overrides!.overrides![5].provider!.node.toSource(), 'family', ); expect( @@ -159,7 +159,7 @@ void main() { 'ProviderContainer(overrides: fn())', ); expect(containers[2].overrides!.overrides, null); - expect(containers[2].overrides!.node.toSource(), 'overrides: fn()'); + expect(containers[2].overrides!.node.toSource(), 'fn()'); expect( containers[3].node.toSource(), @@ -168,13 +168,16 @@ void main() { expect(containers[3].overrides?.overrides, hasLength(1)); expect( containers[3].overrides!.node.toSource(), - 'overrides: [() {return provider;}()]', + '[() {return provider;}()]', ); expect( - containers[3].overrides?.overrides?.single.expression.toSource(), + containers[3].overrides?.overrides?.single.node.toSource(), '() {return provider;}()', ); - expect(containers[3].overrides?.overrides?.single.providerElement, null); + expect( + containers[3].overrides?.overrides?.single.provider?.providerElement, + null, + ); expect(containers[3].overrides?.overrides?.single.provider, null); expect(containers[3].overrides?.overrides?.single.familyArguments, null); }); diff --git a/packages/riverpod_analyzer_utils_tests/test/provider_scope_test.dart b/packages/riverpod_analyzer_utils_tests/test/provider_scope_test.dart index 966ee83f2..9f8907767 100644 --- a/packages/riverpod_analyzer_utils_tests/test/provider_scope_test.dart +++ b/packages/riverpod_analyzer_utils_tests/test/provider_scope_test.dart @@ -43,11 +43,10 @@ class Example extends ConsumerWidget { return ProviderScope(child: Text('foo')); } } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); final scopes = result.providerScopeInstanceCreationExpressions; - final consumer = result.consumerWidgetDeclarations.single; final provider = result.legacyProviderDeclarations.takeAll(['provider']).values.single; @@ -66,49 +65,49 @@ class Example extends ConsumerWidget { expect(scopes[1].overrides!.overrides, hasLength(6)); expect( scopes[1].overrides!.node.toSource(), - 'overrides: [provider.overrideWith((ref) => 0), provider.overrideWithValue(42), provider, family(42), family.overrideWith((ref, id) => 0), family(42).overrideWith((ref) => 0)]', + '[provider.overrideWith((ref) => 0), provider.overrideWithValue(42), provider, family(42), family.overrideWith((ref, id) => 0), family(42).overrideWith((ref) => 0)]', ); { expect( - scopes[1].overrides!.overrides![0].providerElement, + scopes[1].overrides!.overrides![0].provider?.providerElement, same(provider.providerElement), ); expect( - scopes[1].overrides!.overrides![0].expression.toSource(), + scopes[1].overrides!.overrides![0].node.toSource(), 'provider.overrideWith((ref) => 0)', ); expect( - scopes[1].overrides!.overrides![0].provider!.toSource(), + scopes[1].overrides!.overrides![0].provider!.node.toSource(), 'provider', ); expect(scopes[1].overrides!.overrides![0].familyArguments, null); } { expect( - scopes[1].overrides!.overrides![1].providerElement, + scopes[1].overrides!.overrides![1].provider?.providerElement, same(provider.providerElement), ); expect( - scopes[1].overrides!.overrides![1].expression.toSource(), + scopes[1].overrides!.overrides![1].node.toSource(), 'provider.overrideWithValue(42)', ); expect( - scopes[1].overrides!.overrides![1].provider!.toSource(), + scopes[1].overrides!.overrides![1].provider!.node.toSource(), 'provider', ); expect(scopes[1].overrides!.overrides![1].familyArguments, null); } { expect( - scopes[1].overrides!.overrides![2].providerElement, + scopes[1].overrides!.overrides![2].provider?.providerElement, same(provider.providerElement), ); expect( - scopes[1].overrides!.overrides![2].expression.toSource(), + scopes[1].overrides!.overrides![2].node.toSource(), 'provider', ); expect( - scopes[1].overrides!.overrides![2].provider!.toSource(), + scopes[1].overrides!.overrides![2].provider!.node.toSource(), 'provider', ); expect(scopes[1].overrides!.overrides![2].familyArguments, null); @@ -116,15 +115,15 @@ class Example extends ConsumerWidget { { expect( - scopes[1].overrides!.overrides![3].providerElement, + scopes[1].overrides!.overrides![3].provider?.providerElement, same(family.providerElement), ); expect( - scopes[1].overrides!.overrides![3].expression.toSource(), + scopes[1].overrides!.overrides![3].node.toSource(), 'family(42)', ); expect( - scopes[1].overrides!.overrides![3].provider!.toSource(), + scopes[1].overrides!.overrides![3].provider!.node.toSource(), 'family', ); expect( @@ -134,30 +133,30 @@ class Example extends ConsumerWidget { } { expect( - scopes[1].overrides!.overrides![4].providerElement, + scopes[1].overrides!.overrides![4].provider?.providerElement, same(family.providerElement), ); expect( - scopes[1].overrides!.overrides![4].expression.toSource(), + scopes[1].overrides!.overrides![4].node.toSource(), 'family.overrideWith((ref, id) => 0)', ); expect( - scopes[1].overrides!.overrides![4].provider!.toSource(), + scopes[1].overrides!.overrides![4].provider!.node.toSource(), 'family', ); expect(scopes[1].overrides!.overrides![4].familyArguments, null); } { expect( - scopes[1].overrides!.overrides![5].providerElement, + scopes[1].overrides!.overrides![5].provider?.providerElement, same(family.providerElement), ); expect( - scopes[1].overrides!.overrides![5].expression.toSource(), + scopes[1].overrides!.overrides![5].node.toSource(), 'family(42).overrideWith((ref) => 0)', ); expect( - scopes[1].overrides!.overrides![5].provider!.toSource(), + scopes[1].overrides!.overrides![5].provider!.node.toSource(), 'family', ); expect( @@ -171,7 +170,7 @@ class Example extends ConsumerWidget { 'ProviderScope(overrides: fn(), child: Container())', ); expect(scopes[2].overrides!.overrides, null); - expect(scopes[2].overrides!.node.toSource(), 'overrides: fn()'); + expect(scopes[2].overrides!.node.toSource(), 'fn()'); expect( scopes[3].node.toSource(), @@ -180,22 +179,20 @@ class Example extends ConsumerWidget { expect(scopes[3].overrides?.overrides, hasLength(1)); expect( scopes[3].overrides!.node.toSource(), - 'overrides: [() {return provider;}()]', + '[() {return provider;}()]', ); expect( - scopes[3].overrides?.overrides?.single.expression.toSource(), + scopes[3].overrides?.overrides?.single.node.toSource(), '() {return provider;}()', ); - expect(scopes[3].overrides?.overrides?.single.providerElement, null); - expect(scopes[3].overrides?.overrides?.single.provider, null); - expect(scopes[3].overrides?.overrides?.single.familyArguments, null); + expect( + scopes[3].overrides?.overrides?.single.provider?.providerElement, + null, + ); + expect(scopes[3].overrides!.overrides!.single.provider, null); + expect(scopes[3].overrides!.overrides!.single.familyArguments, null); expect(scopes[4].node.toSource(), "ProviderScope(child: Text('foo'))"); expect(scopes[4].overrides, null); - expect(consumer.providerScopeInstanceCreateExpressions, hasLength(1)); - expect( - consumer.providerScopeInstanceCreateExpressions.single, - same(scopes[4]), - ); }); } diff --git a/packages/riverpod_analyzer_utils_tests/test/ref_invocation_test.dart b/packages/riverpod_analyzer_utils_tests/test/ref_invocation_test.dart index f6fa98ed3..17c6f930e 100644 --- a/packages/riverpod_analyzer_utils_tests/test/ref_invocation_test.dart +++ b/packages/riverpod_analyzer_utils_tests/test/ref_invocation_test.dart @@ -1,15 +1,32 @@ -import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; +import 'package:riverpod_analyzer_utils/src/nodes.dart'; import 'package:test/test.dart'; import 'analyzer_test_utils.dart'; +// ignore: invalid_use_of_internal_member +extension on RiverpodAnalysisResult { + List get refWatchInvocations { + return refInvocations.whereType().toList(); + } + + List get refListenInvocations { + return refInvocations.whereType().toList(); + } + + List get refReadInvocations { + return refInvocations.whereType().toList(); + } +} + void main() { testSource( 'Parses import aliases', + timeout: const Timeout.factor(4), runGenerator: true, files: { 'file.dart': ''' -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; + final aProvider = Provider((ref) => 0); ''', }, @@ -19,12 +36,12 @@ import 'file.dart' as alias; part 'foo.g.dart'; @Riverpod(keepAlive: true) -int aliased(AliasedRef ref) { +int aliased(Ref ref) { ref.watch(alias.aProvider); return 0; } ''', - (resolver) async { + (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.refWatchInvocations, hasLength(1)); @@ -35,26 +52,27 @@ int aliased(AliasedRef ref) { ); expect( - result.refWatchInvocations.single.provider.provider?.toSource(), + result.refWatchInvocations.single.listenable.provider?.node.toSource(), 'aProvider', ); expect( - result.refWatchInvocations.single.provider.providerPrefix?.toSource(), + result.refWatchInvocations.single.listenable.providerPrefix?.toSource(), 'alias', ); }, ); - testSource('Decode watch expressions with syntax errors', source: ''' -import 'package:riverpod_annotation/riverpod_annotation.dart'; + testSource('Decode watch expressions with syntax errors', + timeout: const Timeout.factor(4), source: ''' +import 'package:riverpod/riverpod.dart'; @ProviderFor(gibberish) -final gibberishProvider = Provider((ref) => 0); +final gibberishProvider = Provider((ref) => 0).select((p) => p); final dependency = Provider((ref) { ref.watch(gibberishProvider); }); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult( ignoreErrors: true, ); @@ -65,57 +83,55 @@ final dependency = Provider((ref) { result.refInvocations.single.node.toSource(), 'ref.watch(gibberishProvider)', ); - expect(result.refWatchInvocations.single.provider.familyArguments, null); + expect(result.refWatchInvocations.single.listenable.familyArguments, null); expect( - result.refWatchInvocations.single.provider.node.toSource(), + result.refWatchInvocations.single.listenable.node.toSource(), 'gibberishProvider', ); - expect( - result.refWatchInvocations.single.provider.provider?.toSource(), - 'gibberishProvider', - ); - expect(result.refWatchInvocations.single.provider.providerElement, null); + expect(result.refWatchInvocations.single.listenable.provider, isNull); }); testSource('Decodes ref expressions in Notifier methods', - runGenerator: true, source: r''' + timeout: const Timeout.factor(4), runGenerator: true, source: r''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'foo.g.dart'; +@riverpod +int generated(Ref ref) => 0; + @riverpod class MyNotifier extends _$MyNotifier { @override int build() => 0; void method() { - ref.watch(generatedScopedProvider); + ref.watch(generatedProvider); } } -''', (resolver) async { +''', (resolver, unit, units) async { // Regression test for https://github.com/rrousselGit/riverpod/issues/2417 final result = await resolver.resolveRiverpodAnalysisResult(); - final notifier = result.classBasedProviderDeclarations.single; - expect(result.refInvocations, hasLength(1)); expect( - result.refWatchInvocations.single.provider.node.toSource(), - 'generatedScopedProvider', + result.refWatchInvocations.single.listenable.node.toSource(), + 'generatedProvider', ); expect( - notifier.refInvocations.single, + result.refInvocations.single, isA().having( - (e) => e.provider.node.toSource(), + (e) => e.listenable.node.toSource(), 'provider', - 'generatedScopedProvider', + 'generatedProvider', ), ); }); - testSource('Decodes ..watch', runGenerator: true, source: r''' + testSource('Decodes ..watch', + timeout: const Timeout.factor(4), runGenerator: true, source: r''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -141,7 +157,7 @@ final provider = Provider((ref) { return 0; }); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.refWatchInvocations, hasLength(3)); @@ -152,11 +168,14 @@ final provider = Provider((ref) { '..watch(dep)', ); expect(result.refWatchInvocations[0].function.toSource(), 'watch'); - expect(result.refWatchInvocations[0].provider.node.toSource(), 'dep'); - expect(result.refWatchInvocations[0].provider.familyArguments, null); - expect(result.refWatchInvocations[0].provider.provider?.toSource(), 'dep'); + expect(result.refWatchInvocations[0].listenable.node.toSource(), 'dep'); + expect(result.refWatchInvocations[0].listenable.familyArguments, null); expect( - result.refWatchInvocations[0].provider.providerElement, + result.refWatchInvocations[0].listenable.provider?.node.toSource(), + 'dep', + ); + expect( + result.refWatchInvocations[0].listenable.provider?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); @@ -166,22 +185,22 @@ final provider = Provider((ref) { ); expect(result.refWatchInvocations[1].function.toSource(), 'watch'); expect( - result.refWatchInvocations[1].provider.node.toSource(), + result.refWatchInvocations[1].listenable.node.toSource(), 'dep2Provider', ); expect( - result.refWatchInvocations[1].provider.provider?.toSource(), + result.refWatchInvocations[1].listenable.provider?.node.toSource(), 'dep2Provider', ); expect( - result.refWatchInvocations[1].provider.providerElement, + result.refWatchInvocations[1].listenable.provider?.providerElement, same( result.functionalProviderDeclarations .findByName('dep2') .providerElement, ), ); - expect(result.refWatchInvocations[1].provider.familyArguments, null); + expect(result.refWatchInvocations[1].listenable.familyArguments, null); expect( result.refWatchInvocations[2].node.toSource(), @@ -189,25 +208,26 @@ final provider = Provider((ref) { ); expect(result.refWatchInvocations[2].function.toSource(), 'watch'); expect( - result.refWatchInvocations[2].provider.node.toSource(), + result.refWatchInvocations[2].listenable.node.toSource(), 'dep3Provider', ); expect( - result.refWatchInvocations[2].provider.provider?.toSource(), + result.refWatchInvocations[2].listenable.provider?.node.toSource(), 'dep3Provider', ); expect( - result.refWatchInvocations[2].provider.providerElement, + result.refWatchInvocations[2].listenable.provider?.providerElement, same( result.classBasedProviderDeclarations .findByName('Dep3') .providerElement, ), ); - expect(result.refWatchInvocations[2].provider.familyArguments, null); + expect(result.refWatchInvocations[2].listenable.familyArguments, null); }); - testSource('Decodes simple ref.watch usages', runGenerator: true, source: r''' + testSource('Decodes simple ref.watch usages', + timeout: const Timeout.factor(4), runGenerator: true, source: r''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -243,7 +263,7 @@ class _Ref { void fn(_Ref ref) { ref.watch(dep); } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.refWatchInvocations, hasLength(3)); @@ -254,11 +274,14 @@ void fn(_Ref ref) { 'ref.watch(dep)', ); expect(result.refWatchInvocations[0].function.toSource(), 'watch'); - expect(result.refWatchInvocations[0].provider.node.toSource(), 'dep'); - expect(result.refWatchInvocations[0].provider.familyArguments, null); - expect(result.refWatchInvocations[0].provider.provider?.toSource(), 'dep'); + expect(result.refWatchInvocations[0].listenable.node.toSource(), 'dep'); + expect(result.refWatchInvocations[0].listenable.familyArguments, null); + expect( + result.refWatchInvocations[0].listenable.provider?.node.toSource(), + 'dep', + ); expect( - result.refWatchInvocations[0].provider.providerElement, + result.refWatchInvocations[0].listenable.provider?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); @@ -268,22 +291,22 @@ void fn(_Ref ref) { ); expect(result.refWatchInvocations[1].function.toSource(), 'watch'); expect( - result.refWatchInvocations[1].provider.node.toSource(), + result.refWatchInvocations[1].listenable.node.toSource(), 'dep2Provider', ); expect( - result.refWatchInvocations[1].provider.provider?.toSource(), + result.refWatchInvocations[1].listenable.provider?.node.toSource(), 'dep2Provider', ); expect( - result.refWatchInvocations[1].provider.providerElement, + result.refWatchInvocations[1].listenable.provider?.providerElement, same( result.functionalProviderDeclarations .findByName('dep2') .providerElement, ), ); - expect(result.refWatchInvocations[1].provider.familyArguments, null); + expect(result.refWatchInvocations[1].listenable.familyArguments, null); expect( result.refWatchInvocations[2].node.toSource(), @@ -291,25 +314,26 @@ void fn(_Ref ref) { ); expect(result.refWatchInvocations[2].function.toSource(), 'watch'); expect( - result.refWatchInvocations[2].provider.node.toSource(), + result.refWatchInvocations[2].listenable.node.toSource(), 'dep3Provider', ); expect( - result.refWatchInvocations[2].provider.provider?.toSource(), + result.refWatchInvocations[2].listenable.provider?.node.toSource(), 'dep3Provider', ); expect( - result.refWatchInvocations[2].provider.providerElement, + result.refWatchInvocations[2].listenable.provider?.providerElement, same( result.classBasedProviderDeclarations .findByName('Dep3') .providerElement, ), ); - expect(result.refWatchInvocations[2].provider.familyArguments, null); + expect(result.refWatchInvocations[2].listenable.familyArguments, null); }); - testSource('Decodes ref.listen usages', runGenerator: true, source: ''' + testSource('Decodes ref.listen usages', + timeout: const Timeout.factor(4), runGenerator: true, source: ''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -324,7 +348,7 @@ final provider = Provider((ref) { return 0; }); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.refListenInvocations, hasLength(3)); @@ -340,7 +364,7 @@ final provider = Provider((ref) { '(prev, next) {}', ); expect( - result.refListenInvocations[0].provider.providerElement, + result.refListenInvocations[0].listenable.provider?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); @@ -354,7 +378,7 @@ final provider = Provider((ref) { '(prev, next) {}', ); expect( - result.refListenInvocations[1].provider.providerElement, + result.refListenInvocations[1].listenable.provider?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); @@ -368,12 +392,13 @@ final provider = Provider((ref) { '(prev, next) {}', ); expect( - result.refListenInvocations[2].provider.providerElement, + result.refListenInvocations[2].listenable.provider?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); }); - testSource('Decodes ref.read usages', runGenerator: true, source: ''' + testSource('Decodes ref.read usages', + timeout: const Timeout.factor(4), runGenerator: true, source: ''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -388,7 +413,7 @@ final provider = Provider((ref) { return 0; }); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.refReadInvocations, hasLength(2)); @@ -397,14 +422,14 @@ final provider = Provider((ref) { expect(result.refReadInvocations[0].node.toSource(), 'ref.read(dep)'); expect(result.refReadInvocations[0].function.toSource(), 'read'); expect( - result.refReadInvocations[0].provider.providerElement, + result.refReadInvocations[0].listenable.provider?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); expect(result.refReadInvocations[1].node.toSource(), 'ref.read(dep2)'); expect(result.refReadInvocations[1].function.toSource(), 'read'); expect( - result.refReadInvocations[1].provider.providerElement, + result.refReadInvocations[1].listenable.provider?.providerElement, same( result.legacyProviderDeclarations.findByName('dep2').providerElement, ), @@ -443,58 +468,58 @@ final provider3 = Provider((ref) { return 0; }); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.refReadInvocations, hasLength(7)); expect(result.refInvocations, result.refReadInvocations); // provider - expect( - result.refReadInvocations[0].node.toSource(), - 'ref.read(dep2(ref.read(dep)))', - ); + expect(result.refReadInvocations[0].node.toSource(), 'ref.read(dep)'); expect(result.refReadInvocations[0].function.toSource(), 'read'); expect( - result.refReadInvocations[0].provider.providerElement, + result.refReadInvocations[0].listenable.provider?.providerElement, same( - result.legacyProviderDeclarations.findByName('dep2').providerElement, + result.legacyProviderDeclarations.findByName('dep').providerElement, ), ); - expect(result.refReadInvocations[1].node.toSource(), 'ref.read(dep)'); + expect( + result.refReadInvocations[1].node.toSource(), + 'ref.read(dep2(ref.read(dep)))', + ); expect(result.refReadInvocations[1].function.toSource(), 'read'); expect( - result.refReadInvocations[1].provider.providerElement, + result.refReadInvocations[1].listenable.provider?.providerElement, same( - result.legacyProviderDeclarations.findByName('dep').providerElement, + result.legacyProviderDeclarations.findByName('dep2').providerElement, ), ); // provider2 + expect(result.refReadInvocations[2].node.toSource(), 'ref.read(dep)'); expect( - result.refReadInvocations[2].node.toSource(), + result.refReadInvocations[3].node.toSource(), + 'ref.read(dep2(ref.read(dep)))', + ); + expect( + result.refReadInvocations[4].node.toSource(), 'ref.read(dep3(ref.read(dep2(ref.read(dep)))))', ); - expect(result.refReadInvocations[2].function.toSource(), 'read'); + expect(result.refReadInvocations[4].function.toSource(), 'read'); expect( - result.refReadInvocations[2].provider.providerElement, + result.refReadInvocations[4].listenable.provider?.providerElement, same( result.legacyProviderDeclarations.findByName('dep3').providerElement, ), ); + // provider3 + expect(result.refReadInvocations[5].node.toSource(), 'ref.read(dep)'); expect( - result.refReadInvocations[3].node.toSource(), - 'ref.read(dep2(ref.read(dep)))', - ); - expect(result.refReadInvocations[4].node.toSource(), 'ref.read(dep)'); - - expect( - result.refReadInvocations[5].node.toSource(), + result.refReadInvocations[6].node.toSource(), 'ref.read(dep3(transformArg(ref.read(dep))))', ); - expect(result.refReadInvocations[6].node.toSource(), 'ref.read(dep)'); }); testSource('Decodes unknown ref usages', source: ''' @@ -504,37 +529,36 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; final dep = FutureProvider((ref) => 0); final dep2 = FutureProvider((ref) => 0); -void fn(Ref ref) { +void fn(Ref ref) { ref.read(dep); ref.read(dep2); } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); - final libraryResult = result.resolvedRiverpodLibraryResults.single; - - expect(libraryResult.unknownRefInvocations, hasLength(2)); - expect(result.refReadInvocations, libraryResult.unknownRefInvocations); + expect(result.refInvocations, hasLength(2)); + expect(result.refReadInvocations, result.refInvocations); expect(result.refInvocations, result.refReadInvocations); expect(result.refReadInvocations[0].node.toSource(), 'ref.read(dep)'); expect(result.refReadInvocations[0].function.toSource(), 'read'); expect( - result.refReadInvocations[0].provider.providerElement, + result.refReadInvocations[0].listenable.provider?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); expect(result.refReadInvocations[1].node.toSource(), 'ref.read(dep2)'); expect(result.refReadInvocations[1].function.toSource(), 'read'); expect( - result.refReadInvocations[1].provider.providerElement, + result.refReadInvocations[1].listenable.provider?.providerElement, same( result.legacyProviderDeclarations.findByName('dep2').providerElement, ), ); }); - testSource('Decodes family ref.watch usages', runGenerator: true, source: r''' + testSource('Decodes family ref.watch usages', + timeout: const Timeout.factor(4), runGenerator: true, source: r''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -570,41 +594,33 @@ class _Ref { void fn(_Ref ref) { ref.watch(family(0)); } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); - final libraryResult = result.resolvedRiverpodLibraryResults.single; - - expect(libraryResult.unknownRefInvocations, isEmpty); - expect(libraryResult.unknownWidgetRefInvocations, isEmpty); - - final providerRefInvocations = libraryResult.legacyProviderDeclarations - .where((e) => e.providerElement.name == 'provider') - .single - .refInvocations - .cast(); + final providerRefInvocations = + result.refInvocations.cast(); expect(providerRefInvocations, hasLength(3)); - expect(result.refInvocations, providerRefInvocations); + expect(result.refWatchInvocations, providerRefInvocations); expect( providerRefInvocations[0].node.toSource(), 'ref.watch(family(0))', ); expect(providerRefInvocations[0].function.toSource(), 'watch'); - expect(providerRefInvocations[0].provider.node.toSource(), 'family(0)'); + expect(providerRefInvocations[0].listenable.node.toSource(), 'family(0)'); expect( - providerRefInvocations[0].provider.provider?.toSource(), + providerRefInvocations[0].listenable.provider?.node.toSource(), 'family', ); expect( - providerRefInvocations[0].provider.providerElement, + providerRefInvocations[0].listenable.provider?.providerElement, same( result.legacyProviderDeclarations.findByName('family').providerElement, ), ); expect( - providerRefInvocations[0].provider.familyArguments?.toSource(), + providerRefInvocations[0].listenable.familyArguments?.toSource(), '(0)', ); @@ -614,15 +630,15 @@ void fn(_Ref ref) { ); expect(providerRefInvocations[1].function.toSource(), 'watch'); expect( - providerRefInvocations[1].provider.node.toSource(), + providerRefInvocations[1].listenable.node.toSource(), 'family2Provider(id: 0)', ); expect( - providerRefInvocations[1].provider.provider?.toSource(), + providerRefInvocations[1].listenable.provider?.node.toSource(), 'family2Provider', ); expect( - providerRefInvocations[1].provider.providerElement, + providerRefInvocations[1].listenable.provider?.providerElement, same( result.functionalProviderDeclarations .findByName('family2') @@ -630,7 +646,7 @@ void fn(_Ref ref) { ), ); expect( - providerRefInvocations[1].provider.familyArguments?.toSource(), + providerRefInvocations[1].listenable.familyArguments?.toSource(), '(id: 0)', ); @@ -640,15 +656,15 @@ void fn(_Ref ref) { ); expect(result.refWatchInvocations[2].function.toSource(), 'watch'); expect( - result.refWatchInvocations[2].provider.node.toSource(), + result.refWatchInvocations[2].listenable.node.toSource(), 'family3Provider(id: 0)', ); expect( - result.refWatchInvocations[2].provider.provider?.toSource(), + result.refWatchInvocations[2].listenable.provider?.node.toSource(), 'family3Provider', ); expect( - result.refWatchInvocations[2].provider.providerElement, + result.refWatchInvocations[2].listenable.provider?.providerElement, same( result.classBasedProviderDeclarations .findByName('Family3') @@ -656,7 +672,7 @@ void fn(_Ref ref) { ), ); expect( - result.refWatchInvocations[2].provider.familyArguments?.toSource(), + result.refWatchInvocations[2].listenable.familyArguments?.toSource(), '(id: 0)', ); }); @@ -676,14 +692,14 @@ final provider = Provider((ref) { return 0; }); -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.refWatchInvocations, hasLength(1)); expect(result.refReadInvocations, hasLength(1)); expect( result.refInvocations, - [...result.refWatchInvocations, ...result.refReadInvocations], + [...result.refReadInvocations, ...result.refWatchInvocations], ); expect( @@ -692,7 +708,7 @@ final provider = Provider((ref) { ); expect(result.refWatchInvocations[0].function.toSource(), 'watch'); expect( - result.refWatchInvocations[0].provider.providerElement, + result.refWatchInvocations[0].listenable.provider?.providerElement, same( result.legacyProviderDeclarations.findByName('dep2').providerElement, ), @@ -701,7 +717,7 @@ final provider = Provider((ref) { expect(result.refReadInvocations[0].node.toSource(), 'ref.read(dep)'); expect(result.refReadInvocations[0].function.toSource(), 'read'); expect( - result.refReadInvocations[0].provider.providerElement, + result.refReadInvocations[0].listenable.provider?.providerElement, same( result.legacyProviderDeclarations.findByName('dep').providerElement, ), @@ -709,7 +725,7 @@ final provider = Provider((ref) { }); testSource('Decodes provider.query ref.watch usages', - runGenerator: true, source: r''' + timeout: const Timeout.factor(4), runGenerator: true, source: r''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -761,7 +777,7 @@ class _Ref { void fn(_Ref ref) { ref.watch(dep); } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.refWatchInvocations, hasLength(4)); @@ -773,13 +789,16 @@ void fn(_Ref ref) { ); expect(result.refWatchInvocations[0].function.toSource(), 'watch'); expect( - result.refWatchInvocations[0].provider.node.toSource(), + result.refWatchInvocations[0].listenable.node.toSource(), 'dep.select((e) => e)', ); - expect(result.refWatchInvocations[0].provider.familyArguments, null); - expect(result.refWatchInvocations[0].provider.provider?.toSource(), 'dep'); + expect(result.refWatchInvocations[0].listenable.familyArguments, null); + expect( + result.refWatchInvocations[0].listenable.provider?.node.toSource(), + 'dep', + ); expect( - result.refWatchInvocations[0].provider.providerElement, + result.refWatchInvocations[0].listenable.provider?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); @@ -789,16 +808,16 @@ void fn(_Ref ref) { ); expect(result.refWatchInvocations[1].function.toSource(), 'watch'); expect( - result.refWatchInvocations[1].provider.node.toSource(), + result.refWatchInvocations[1].listenable.node.toSource(), 'dep2Provider.select((e) => e)', ); - expect(result.refWatchInvocations[1].provider.familyArguments, null); + expect(result.refWatchInvocations[1].listenable.familyArguments, null); expect( - result.refWatchInvocations[1].provider.provider?.toSource(), + result.refWatchInvocations[1].listenable.provider?.node.toSource(), 'dep2Provider', ); expect( - result.refWatchInvocations[1].provider.providerElement, + result.refWatchInvocations[1].listenable.provider?.providerElement, same( result.functionalProviderDeclarations .findByName('dep2') @@ -812,16 +831,16 @@ void fn(_Ref ref) { ); expect(result.refWatchInvocations[2].function.toSource(), 'watch'); expect( - result.refWatchInvocations[2].provider.node.toSource(), + result.refWatchInvocations[2].listenable.node.toSource(), 'dep3Provider.select((e) => e)', ); - expect(result.refWatchInvocations[2].provider.familyArguments, null); + expect(result.refWatchInvocations[2].listenable.familyArguments, null); expect( - result.refWatchInvocations[2].provider.provider?.toSource(), + result.refWatchInvocations[2].listenable.provider?.node.toSource(), 'dep3Provider', ); expect( - result.refWatchInvocations[2].provider.providerElement, + result.refWatchInvocations[2].listenable.provider?.providerElement, same( result.classBasedProviderDeclarations .findByName('Dep3') @@ -835,19 +854,19 @@ void fn(_Ref ref) { ); expect(result.refWatchInvocations[3].function.toSource(), 'watch'); expect( - result.refWatchInvocations[3].provider.node.toSource(), + result.refWatchInvocations[3].listenable.node.toSource(), 'familyProvider(id: 42).notifier.select((e) => e).getter.method()[0]', ); expect( - result.refWatchInvocations[3].provider.familyArguments?.toSource(), + result.refWatchInvocations[3].listenable.familyArguments?.toSource(), '(id: 42)', ); expect( - result.refWatchInvocations[3].provider.provider?.toSource(), + result.refWatchInvocations[3].listenable.provider?.node.toSource(), 'familyProvider', ); expect( - result.refWatchInvocations[3].provider.providerElement, + result.refWatchInvocations[3].listenable.provider?.providerElement, same( result.classBasedProviderDeclarations .findByName('Family') diff --git a/packages/riverpod_analyzer_utils_tests/test/type_utils_test.dart b/packages/riverpod_analyzer_utils_tests/test/type_utils_test.dart new file mode 100644 index 000000000..ddc0205ca --- /dev/null +++ b/packages/riverpod_analyzer_utils_tests/test/type_utils_test.dart @@ -0,0 +1,184 @@ +// ignore_for_file: invalid_use_of_internal_member + +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:riverpod_analyzer_utils/src/nodes.dart'; +import 'package:test/test.dart'; + +import 'analyzer_test_utils.dart'; + +void main() { + testSource('Rejects unrelated types', source: ''' +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +const random = 42; +ProviderBase? fromRiverpod = null; +Consumer? fromFlutterRiverpod = null; +''', (resolver, unit, units) async { + final variables = + unit.declarations.whereType(); + + expect(variables, hasLength(3)); + + for (final variable in variables) { + expect( + parseLegacyProviderType( + variable.variables.variables.single.declaredElement!.type, + ), + isNull, + reason: variable.toString(), + ); + expect( + parseFirstProviderFor( + variable.variables.variables.single.declaredElement!, + ), + isNull, + reason: variable.toString(), + ); + } + }); + + testSource('Parses all generated providers', runGenerator: true, source: r''' +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'foo.g.dart'; + +Never throws() => throw UnimplementedError(); + +@riverpod +int a(Ref ref) => throws(); + +@riverpod +class B extends _$B { + @override + int build() => throws();; +} + +@riverpod +int c(Ref ref, int arg) => throws(); + +@riverpod +class D extends _$D { + @override + int build(int arg) => throws(); +} + + +@riverpod +Future a2(A2Ref ref) => throws(); + +@riverpod +class B2 extends _$B2 { + @override + Future build() => throws();; +} + +@riverpod +Future c2(C2Ref ref, int arg) => throws(); + +@riverpod +class D2 extends _$D2 { + @override + Future build(int arg) => throws(); +} + + +@riverpod +Stream a3(A3Ref ref) => throws(); + +@riverpod +class B3 extends _$B3 { + @override + Stream build() => throws();; +} + +@riverpod +Stream c3(C3Ref ref, int arg) => throws(); + +@riverpod +class D3 extends _$D3 { + @override + Stream build(int arg) => throws(); +} +''', (resolver, unit, units) async { + final generated = units.singleWhere((e) => e.path.endsWith('.g.dart')).unit; + + final variables = generated.declarations + .whereType() + .toList(); + + expect(variables, hasLength(12)); + + for (final variable in variables) { + expect( + parseFirstProviderFor( + variable.variables.variables.single.declaredElement!, + )?.$1, + isNotNull, + reason: variable.toString(), + ); + expect( + parseLegacyProviderType( + variable.variables.variables.single.declaredElement!.type, + ), + isNull, + reason: variable.toString(), + ); + } + }); + + testSource('Parses all legacy providers', source: ''' +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; + +Never throws() => throw UnimplementedError(); + +final provider = Provider((ref) => throws()); +final providerFamily = Provider.family((ref, id) => throws()); + +final stateProvider = StateProvider((ref) => throws()); +final stateProviderFamily = StateProvider.family((ref, id) => throws()); + +final futureProvider = FutureProvider((ref) async => throws()); +final futureProviderFamily = FutureProvider.family((ref, id) => throws()); + +final streamProvider = StreamProvider((ref) => throws()); +final streamProviderFamily = StreamProvider.family((ref, id) => throws()); + +final changeNotifierProvider = ChangeNotifierProvider((ref) => throws()); +final changeNotifierProviderFamily = ChangeNotifierProvider.family((ref, id) => throws()); + +final stateNotifierProvider = StateNotifierProvider((ref) => throws()); +final stateNotifierProviderFamily = StateNotifierProvider.family((ref, id) => throws()); + +final notifierProvider = NotifierProvider(() => throws()); +final notifierProviderFamily = NotifierProvider.family((ref, id) => throws()); + +final asyncNotifierProvider = AsyncNotifierProvider((ref) => throws()); +final asyncNotifierProviderFamily = AsyncNotifierProvider.family((ref, id) => throws()); + +final streamNotifierProvider = StreamNotifierProvider((ref) => throws()); +final streamNotifierProviderFamily = StreamNotifierProvider.family((ref, id) => throws()); +''', (resolver, unit, units) async { + final variables = + unit.declarations.whereType(); + + expect(variables, hasLength(18)); + + for (final variable in variables) { + expect( + parseLegacyProviderType( + variable.variables.variables.single.declaredElement!.type, + ), + isNotNull, + reason: variable.toString(), + ); + expect( + parseFirstProviderFor( + variable.variables.variables.single.declaredElement!, + ), + isNull, + reason: variable.toString(), + ); + } + }); +} diff --git a/packages/riverpod_analyzer_utils_tests/test/widget_ref_invocation_test.dart b/packages/riverpod_analyzer_utils_tests/test/widget_ref_invocation_test.dart index d3d54fe8c..1aab5b165 100644 --- a/packages/riverpod_analyzer_utils_tests/test/widget_ref_invocation_test.dart +++ b/packages/riverpod_analyzer_utils_tests/test/widget_ref_invocation_test.dart @@ -1,9 +1,32 @@ +import 'package:riverpod_analyzer_utils/src/nodes.dart'; import 'package:test/test.dart'; import 'analyzer_test_utils.dart'; +// ignore: invalid_use_of_internal_member +extension on RiverpodAnalysisResult { + List get widgetRefWatchInvocations { + return widgetRefInvocations.whereType().toList(); + } + + List get widgetRefReadInvocations { + return widgetRefInvocations.whereType().toList(); + } + + List get widgetRefListenInvocations { + return widgetRefInvocations.whereType().toList(); + } + + List get widgetRefListenManualInvocations { + return widgetRefInvocations + .whereType() + .toList(); + } +} + void main() { - testSource('Decode watch expressions with syntax errors', source: ''' + testSource('Decode watch expressions with syntax errors', + timeout: const Timeout.factor(4), source: ''' import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter/material.dart'; @@ -18,7 +41,7 @@ class Example extends ConsumerWidget { return Container(); } } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult( ignoreErrors: true, ); @@ -30,24 +53,18 @@ class Example extends ConsumerWidget { 'ref.watch(gibberishProvider)', ); expect( - result.widgetRefWatchInvocations.single.provider.familyArguments, + result.widgetRefWatchInvocations.single.listenable.familyArguments, null, ); expect( - result.widgetRefWatchInvocations.single.provider.node.toSource(), - 'gibberishProvider', - ); - expect( - result.widgetRefWatchInvocations.single.provider.provider?.toSource(), + result.widgetRefWatchInvocations.single.listenable.node.toSource(), 'gibberishProvider', ); - expect( - result.widgetRefWatchInvocations.single.provider.providerElement, - null, - ); + expect(result.widgetRefWatchInvocations.single.listenable.provider, isNull); }); - testSource('Decodes ..watch', runGenerator: true, source: r''' + testSource('Decodes ..watch', + timeout: const Timeout.factor(4), runGenerator: true, source: r''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -79,7 +96,7 @@ class MyWidget extends ConsumerWidget { return Container(); } } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.widgetRefWatchInvocations, hasLength(3)); @@ -90,14 +107,20 @@ class MyWidget extends ConsumerWidget { '..watch(dep)', ); expect(result.widgetRefWatchInvocations[0].function.toSource(), 'watch'); - expect(result.widgetRefWatchInvocations[0].provider.node.toSource(), 'dep'); - expect(result.widgetRefWatchInvocations[0].provider.familyArguments, null); expect( - result.widgetRefWatchInvocations[0].provider.provider?.toSource(), + result.widgetRefWatchInvocations[0].listenable.node.toSource(), 'dep', ); expect( - result.widgetRefWatchInvocations[0].provider.providerElement, + result.widgetRefWatchInvocations[0].listenable.familyArguments, + null, + ); + expect( + result.widgetRefWatchInvocations[0].listenable.provider?.node.toSource(), + 'dep', + ); + expect( + result.widgetRefWatchInvocations[0].listenable.provider?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); @@ -107,22 +130,25 @@ class MyWidget extends ConsumerWidget { ); expect(result.widgetRefWatchInvocations[1].function.toSource(), 'watch'); expect( - result.widgetRefWatchInvocations[1].provider.node.toSource(), + result.widgetRefWatchInvocations[1].listenable.node.toSource(), 'dep2Provider', ); expect( - result.widgetRefWatchInvocations[1].provider.provider?.toSource(), + result.widgetRefWatchInvocations[1].listenable.provider?.node.toSource(), 'dep2Provider', ); expect( - result.widgetRefWatchInvocations[1].provider.providerElement, + result.widgetRefWatchInvocations[1].listenable.provider?.providerElement, same( result.functionalProviderDeclarations .findByName('dep2') .providerElement, ), ); - expect(result.widgetRefWatchInvocations[1].provider.familyArguments, null); + expect( + result.widgetRefWatchInvocations[1].listenable.familyArguments, + null, + ); expect( result.widgetRefWatchInvocations[2].node.toSource(), @@ -130,25 +156,29 @@ class MyWidget extends ConsumerWidget { ); expect(result.widgetRefWatchInvocations[2].function.toSource(), 'watch'); expect( - result.widgetRefWatchInvocations[2].provider.node.toSource(), + result.widgetRefWatchInvocations[2].listenable.node.toSource(), 'dep3Provider', ); expect( - result.widgetRefWatchInvocations[2].provider.provider?.toSource(), + result.widgetRefWatchInvocations[2].listenable.provider?.node.toSource(), 'dep3Provider', ); expect( - result.widgetRefWatchInvocations[2].provider.providerElement, + result.widgetRefWatchInvocations[2].listenable.provider?.providerElement, same( result.classBasedProviderDeclarations .findByName('Dep3') .providerElement, ), ); - expect(result.widgetRefWatchInvocations[2].provider.familyArguments, null); + expect( + result.widgetRefWatchInvocations[2].listenable.familyArguments, + null, + ); }); - testSource('Decodes simple ref.watch usages', runGenerator: true, source: r''' + testSource('Decodes simple ref.watch usages', + timeout: const Timeout.factor(4), runGenerator: true, source: r''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -191,7 +221,7 @@ class _Ref { void fn(_Ref ref) { ref.watch(dep); } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.widgetRefWatchInvocations, hasLength(3)); @@ -202,14 +232,20 @@ void fn(_Ref ref) { 'ref.watch(dep)', ); expect(result.widgetRefWatchInvocations[0].function.toSource(), 'watch'); - expect(result.widgetRefWatchInvocations[0].provider.node.toSource(), 'dep'); - expect(result.widgetRefWatchInvocations[0].provider.familyArguments, null); expect( - result.widgetRefWatchInvocations[0].provider.provider?.toSource(), + result.widgetRefWatchInvocations[0].listenable.node.toSource(), 'dep', ); expect( - result.widgetRefWatchInvocations[0].provider.providerElement, + result.widgetRefWatchInvocations[0].listenable.familyArguments, + null, + ); + expect( + result.widgetRefWatchInvocations[0].listenable.provider?.node.toSource(), + 'dep', + ); + expect( + result.widgetRefWatchInvocations[0].listenable.provider?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); @@ -219,22 +255,25 @@ void fn(_Ref ref) { ); expect(result.widgetRefWatchInvocations[1].function.toSource(), 'watch'); expect( - result.widgetRefWatchInvocations[1].provider.node.toSource(), + result.widgetRefWatchInvocations[1].listenable.node.toSource(), 'dep2Provider', ); expect( - result.widgetRefWatchInvocations[1].provider.provider?.toSource(), + result.widgetRefWatchInvocations[1].listenable.provider?.node.toSource(), 'dep2Provider', ); expect( - result.widgetRefWatchInvocations[1].provider.providerElement, + result.widgetRefWatchInvocations[1].listenable.provider?.providerElement, same( result.functionalProviderDeclarations .findByName('dep2') .providerElement, ), ); - expect(result.widgetRefWatchInvocations[1].provider.familyArguments, null); + expect( + result.widgetRefWatchInvocations[1].listenable.familyArguments, + null, + ); expect( result.widgetRefWatchInvocations[2].node.toSource(), @@ -242,25 +281,29 @@ void fn(_Ref ref) { ); expect(result.widgetRefWatchInvocations[2].function.toSource(), 'watch'); expect( - result.widgetRefWatchInvocations[2].provider.node.toSource(), + result.widgetRefWatchInvocations[2].listenable.node.toSource(), 'dep3Provider', ); expect( - result.widgetRefWatchInvocations[2].provider.provider?.toSource(), + result.widgetRefWatchInvocations[2].listenable.provider?.node.toSource(), 'dep3Provider', ); expect( - result.widgetRefWatchInvocations[2].provider.providerElement, + result.widgetRefWatchInvocations[2].listenable.provider?.providerElement, same( result.classBasedProviderDeclarations .findByName('Dep3') .providerElement, ), ); - expect(result.widgetRefWatchInvocations[2].provider.familyArguments, null); + expect( + result.widgetRefWatchInvocations[2].listenable.familyArguments, + null, + ); }); - testSource('Decodes unknown ref usages', source: ''' + testSource('Decodes unknown ref usages', + timeout: const Timeout.factor(4), source: ''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -272,22 +315,20 @@ void fn(WidgetRef ref) { ref.read(dep); ref.read(dep2); } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); - final libraryResult = result.resolvedRiverpodLibraryResults.single; - - expect(libraryResult.unknownWidgetRefInvocations, hasLength(2)); + expect(result.widgetRefInvocations, hasLength(2)); expect( result.widgetRefReadInvocations, - libraryResult.unknownWidgetRefInvocations, + result.widgetRefInvocations, ); expect(result.widgetRefInvocations, result.widgetRefReadInvocations); expect(result.widgetRefReadInvocations[0].node.toSource(), 'ref.read(dep)'); expect(result.widgetRefReadInvocations[0].function.toSource(), 'read'); expect( - result.widgetRefReadInvocations[0].provider.providerElement, + result.widgetRefReadInvocations[0].listenable.provider?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); @@ -297,14 +338,15 @@ void fn(WidgetRef ref) { ); expect(result.widgetRefReadInvocations[1].function.toSource(), 'read'); expect( - result.widgetRefReadInvocations[1].provider.providerElement, + result.widgetRefReadInvocations[1].listenable.provider?.providerElement, same( result.legacyProviderDeclarations.findByName('dep2').providerElement, ), ); }); - testSource('Decodes ref.listen usages', runGenerator: true, source: ''' + testSource('Decodes ref.listen usages', + timeout: const Timeout.factor(4), runGenerator: true, source: ''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -323,7 +365,7 @@ class MyWidget extends ConsumerWidget { return Container(); } } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.widgetRefListenInvocations, hasLength(1)); @@ -339,12 +381,13 @@ class MyWidget extends ConsumerWidget { '(prev, next) {}', ); expect( - result.widgetRefListenInvocations[0].provider.providerElement, + result.widgetRefListenInvocations[0].listenable.provider?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); }); - testSource('Decodes ref.listenManual usages', runGenerator: true, source: ''' + testSource('Decodes ref.listenManual usages', + timeout: const Timeout.factor(4), runGenerator: true, source: ''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -365,7 +408,7 @@ class MyWidget extends ConsumerWidget { return Container(); } } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.widgetRefListenManualInvocations, hasLength(3)); @@ -387,7 +430,8 @@ class MyWidget extends ConsumerWidget { '(prev, next) {}', ); expect( - result.widgetRefListenManualInvocations[0].provider.providerElement, + result.widgetRefListenManualInvocations[0].listenable.provider + ?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); @@ -404,7 +448,8 @@ class MyWidget extends ConsumerWidget { '(prev, next) {}', ); expect( - result.widgetRefListenManualInvocations[1].provider.providerElement, + result.widgetRefListenManualInvocations[1].listenable.provider + ?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); @@ -421,12 +466,14 @@ class MyWidget extends ConsumerWidget { '(prev, next) {}', ); expect( - result.widgetRefListenManualInvocations[2].provider.providerElement, + result.widgetRefListenManualInvocations[2].listenable.provider + ?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); }); - testSource('Decodes ref.read usages', runGenerator: true, source: ''' + testSource('Decodes ref.read usages', + timeout: const Timeout.factor(4), runGenerator: true, source: ''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -449,7 +496,7 @@ class MyWidget extends ConsumerWidget { return Container(); } } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.widgetRefReadInvocations, hasLength(2)); @@ -458,7 +505,7 @@ class MyWidget extends ConsumerWidget { expect(result.widgetRefReadInvocations[0].node.toSource(), 'ref.read(dep)'); expect(result.widgetRefReadInvocations[0].function.toSource(), 'read'); expect( - result.widgetRefReadInvocations[0].provider.providerElement, + result.widgetRefReadInvocations[0].listenable.provider?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); @@ -468,14 +515,15 @@ class MyWidget extends ConsumerWidget { ); expect(result.widgetRefReadInvocations[1].function.toSource(), 'read'); expect( - result.widgetRefReadInvocations[1].provider.providerElement, + result.widgetRefReadInvocations[1].listenable.provider?.providerElement, same( result.legacyProviderDeclarations.findByName('dep2').providerElement, ), ); }); - testSource('Decodes family ref.watch usages', runGenerator: true, source: r''' + testSource('Decodes family ref.watch usages', + timeout: const Timeout.factor(4), runGenerator: true, source: r''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -517,20 +565,14 @@ class _Ref { void fn(_Ref ref) { ref.watch(family(0)); } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); - final libraryResult = result.resolvedRiverpodLibraryResults.single; - - expect(libraryResult.unknownRefInvocations, isEmpty); - expect(libraryResult.unknownWidgetRefInvocations, isEmpty); - - final providerRefInvocations = - libraryResult.consumerWidgetDeclarations.single.widgetRefInvocations; + final providerRefInvocations = result.widgetRefInvocations; expect(result.widgetRefWatchInvocations, hasLength(3)); expect(result.widgetRefInvocations, result.widgetRefWatchInvocations); - expect(result.widgetRefInvocations, providerRefInvocations); + expect(result.widgetRefWatchInvocations, providerRefInvocations); expect( result.widgetRefWatchInvocations[0].node.toSource(), @@ -538,21 +580,22 @@ void fn(_Ref ref) { ); expect(result.widgetRefWatchInvocations[0].function.toSource(), 'watch'); expect( - result.widgetRefWatchInvocations[0].provider.node.toSource(), + result.widgetRefWatchInvocations[0].listenable.node.toSource(), 'family(0)', ); expect( - result.widgetRefWatchInvocations[0].provider.provider?.toSource(), + result.widgetRefWatchInvocations[0].listenable.provider?.node.toSource(), 'family', ); expect( - result.widgetRefWatchInvocations[0].provider.providerElement, + result.widgetRefWatchInvocations[0].listenable.provider?.providerElement, same( result.legacyProviderDeclarations.findByName('family').providerElement, ), ); expect( - result.widgetRefWatchInvocations[0].provider.familyArguments?.toSource(), + result.widgetRefWatchInvocations[0].listenable.familyArguments + ?.toSource(), '(0)', ); @@ -562,15 +605,15 @@ void fn(_Ref ref) { ); expect(result.widgetRefWatchInvocations[1].function.toSource(), 'watch'); expect( - result.widgetRefWatchInvocations[1].provider.node.toSource(), + result.widgetRefWatchInvocations[1].listenable.node.toSource(), 'family2Provider(id: 0)', ); expect( - result.widgetRefWatchInvocations[1].provider.provider?.toSource(), + result.widgetRefWatchInvocations[1].listenable.provider?.node.toSource(), 'family2Provider', ); expect( - result.widgetRefWatchInvocations[1].provider.providerElement, + result.widgetRefWatchInvocations[1].listenable.provider?.providerElement, same( result.functionalProviderDeclarations .findByName('family2') @@ -578,7 +621,8 @@ void fn(_Ref ref) { ), ); expect( - result.widgetRefWatchInvocations[1].provider.familyArguments?.toSource(), + result.widgetRefWatchInvocations[1].listenable.familyArguments + ?.toSource(), '(id: 0)', ); @@ -588,15 +632,15 @@ void fn(_Ref ref) { ); expect(result.widgetRefWatchInvocations[2].function.toSource(), 'watch'); expect( - result.widgetRefWatchInvocations[2].provider.node.toSource(), + result.widgetRefWatchInvocations[2].listenable.node.toSource(), 'family3Provider(id: 0)', ); expect( - result.widgetRefWatchInvocations[2].provider.provider?.toSource(), + result.widgetRefWatchInvocations[2].listenable.provider?.node.toSource(), 'family3Provider', ); expect( - result.widgetRefWatchInvocations[2].provider.providerElement, + result.widgetRefWatchInvocations[2].listenable.provider?.providerElement, same( result.classBasedProviderDeclarations .findByName('Family3') @@ -604,7 +648,8 @@ void fn(_Ref ref) { ), ); expect( - result.widgetRefWatchInvocations[2].provider.familyArguments?.toSource(), + result.widgetRefWatchInvocations[2].listenable.familyArguments + ?.toSource(), '(id: 0)', ); }); @@ -633,19 +678,10 @@ class MyWidget extends ConsumerWidget { return Container(); } } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); - final libraryResult = result.resolvedRiverpodLibraryResults.single; - - expect(libraryResult.unknownRefInvocations, isEmpty); - expect(libraryResult.unknownWidgetRefInvocations, isEmpty); - - final providerRefInvocations = - libraryResult.consumerWidgetDeclarations.single.widgetRefInvocations; - expect(result.widgetRefWatchInvocations, hasLength(3)); - expect(result.widgetRefInvocations, providerRefInvocations); expect( result.widgetRefWatchInvocations[0].node.toSource(), @@ -653,40 +689,41 @@ class MyWidget extends ConsumerWidget { ); expect(result.widgetRefWatchInvocations[0].function.toSource(), 'watch'); expect( - result.widgetRefWatchInvocations[0].provider.node.toSource(), + result.widgetRefWatchInvocations[0].listenable.node.toSource(), 'family(ref.read(family2Provider(id: 0)))', ); expect( - result.widgetRefWatchInvocations[0].provider.provider?.toSource(), + result.widgetRefWatchInvocations[0].listenable.provider?.node.toSource(), 'family', ); expect( - result.widgetRefWatchInvocations[0].provider.providerElement, + result.widgetRefWatchInvocations[0].listenable.provider?.providerElement, same( result.legacyProviderDeclarations.findByName('family').providerElement, ), ); expect( - result.widgetRefWatchInvocations[0].provider.familyArguments?.toSource(), + result.widgetRefWatchInvocations[0].listenable.familyArguments + ?.toSource(), '(ref.read(family2Provider(id: 0)))', ); // ref.watch(family2Provider(ref.watch(family(id: 0))); expect( - result.widgetRefWatchInvocations[1].node.toSource(), + result.widgetRefWatchInvocations[2].node.toSource(), 'ref.watch(family2Provider(ref.watch(family(id: 0))))', ); - expect(result.widgetRefWatchInvocations[1].function.toSource(), 'watch'); + expect(result.widgetRefWatchInvocations[2].function.toSource(), 'watch'); expect( - result.widgetRefWatchInvocations[1].provider.node.toSource(), + result.widgetRefWatchInvocations[2].listenable.node.toSource(), 'family2Provider(ref.watch(family(id: 0)))', ); expect( - result.widgetRefWatchInvocations[1].provider.provider?.toSource(), + result.widgetRefWatchInvocations[2].listenable.provider?.node.toSource(), 'family2Provider', ); expect( - result.widgetRefWatchInvocations[1].provider.providerElement, + result.widgetRefWatchInvocations[2].listenable.provider?.providerElement, same( result.functionalProviderDeclarations .findByName('family2') @@ -694,13 +731,14 @@ class MyWidget extends ConsumerWidget { ), ); expect( - result.widgetRefWatchInvocations[1].provider.familyArguments?.toSource(), + result.widgetRefWatchInvocations[2].listenable.familyArguments + ?.toSource(), '(ref.watch(family(id: 0)))', ); }); testSource('Decodes provider.query ref.watch usages', - runGenerator: true, source: r''' + timeout: const Timeout.factor(4), runGenerator: true, source: r''' import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -759,7 +797,7 @@ class _Ref { void fn(_Ref ref) { ref.watch(dep); } -''', (resolver) async { +''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(); expect(result.widgetRefWatchInvocations, hasLength(4)); @@ -771,16 +809,19 @@ void fn(_Ref ref) { ); expect(result.widgetRefWatchInvocations[0].function.toSource(), 'watch'); expect( - result.widgetRefWatchInvocations[0].provider.node.toSource(), + result.widgetRefWatchInvocations[0].listenable.node.toSource(), 'dep.select((e) => e)', ); - expect(result.widgetRefWatchInvocations[0].provider.familyArguments, null); expect( - result.widgetRefWatchInvocations[0].provider.provider?.toSource(), + result.widgetRefWatchInvocations[0].listenable.familyArguments, + null, + ); + expect( + result.widgetRefWatchInvocations[0].listenable.provider?.node.toSource(), 'dep', ); expect( - result.widgetRefWatchInvocations[0].provider.providerElement, + result.widgetRefWatchInvocations[0].listenable.provider?.providerElement, same(result.legacyProviderDeclarations.findByName('dep').providerElement), ); @@ -790,16 +831,19 @@ void fn(_Ref ref) { ); expect(result.widgetRefWatchInvocations[1].function.toSource(), 'watch'); expect( - result.widgetRefWatchInvocations[1].provider.node.toSource(), + result.widgetRefWatchInvocations[1].listenable.node.toSource(), 'dep2Provider.select((e) => e)', ); - expect(result.widgetRefWatchInvocations[1].provider.familyArguments, null); expect( - result.widgetRefWatchInvocations[1].provider.provider?.toSource(), + result.widgetRefWatchInvocations[1].listenable.familyArguments, + null, + ); + expect( + result.widgetRefWatchInvocations[1].listenable.provider?.node.toSource(), 'dep2Provider', ); expect( - result.widgetRefWatchInvocations[1].provider.providerElement, + result.widgetRefWatchInvocations[1].listenable.provider?.providerElement, same( result.functionalProviderDeclarations .findByName('dep2') @@ -813,16 +857,19 @@ void fn(_Ref ref) { ); expect(result.widgetRefWatchInvocations[2].function.toSource(), 'watch'); expect( - result.widgetRefWatchInvocations[2].provider.node.toSource(), + result.widgetRefWatchInvocations[2].listenable.node.toSource(), 'dep3Provider.select((e) => e)', ); - expect(result.widgetRefWatchInvocations[2].provider.familyArguments, null); expect( - result.widgetRefWatchInvocations[2].provider.provider?.toSource(), + result.widgetRefWatchInvocations[2].listenable.familyArguments, + null, + ); + expect( + result.widgetRefWatchInvocations[2].listenable.provider?.node.toSource(), 'dep3Provider', ); expect( - result.widgetRefWatchInvocations[2].provider.providerElement, + result.widgetRefWatchInvocations[2].listenable.provider?.providerElement, same( result.classBasedProviderDeclarations .findByName('Dep3') @@ -836,19 +883,20 @@ void fn(_Ref ref) { ); expect(result.widgetRefWatchInvocations[3].function.toSource(), 'watch'); expect( - result.widgetRefWatchInvocations[3].provider.node.toSource(), + result.widgetRefWatchInvocations[3].listenable.node.toSource(), 'familyProvider(id: 42).notifier.select((e) => e).getter.method()[0]', ); expect( - result.widgetRefWatchInvocations[3].provider.familyArguments?.toSource(), + result.widgetRefWatchInvocations[3].listenable.familyArguments + ?.toSource(), '(id: 42)', ); expect( - result.widgetRefWatchInvocations[3].provider.provider?.toSource(), + result.widgetRefWatchInvocations[3].listenable.provider?.node.toSource(), 'familyProvider', ); expect( - result.widgetRefWatchInvocations[3].provider.providerElement, + result.widgetRefWatchInvocations[3].listenable.provider?.providerElement, same( result.classBasedProviderDeclarations .findByName('Family') diff --git a/packages/riverpod_annotation/CHANGELOG.md b/packages/riverpod_annotation/CHANGELOG.md index d5fe74f76..d8f6544fc 100644 --- a/packages/riverpod_annotation/CHANGELOG.md +++ b/packages/riverpod_annotation/CHANGELOG.md @@ -1,3 +1,33 @@ +## Unreleased build + +- **Breaking** various `package:riverpod` objects are no-longer exported. + If you wish to use providers by hand, you will have to separately import + `package:riverpod/riverpod.dart`. +- Added `@mutation` support. + Mutations are a way to enable your UI to easily listen to the status of side-effects. + See the documentation of `@mutation` for further information. +- Made `@Riverpod` final +- Added `@Dependencies([...])`, for lint purposes. + This is similar to `@Riverpod(dependencies: [...])`, but is applied on + non-provider objects that may use a scoped provider. +- Added support for `@Riverpod(retry: ...)` + +## 3.0.0-dev.3 - 2023-11-27 + +- `riverpod` upgraded to `3.0.0-dev.3` + +## 3.0.0-dev.2 - 2023-11-20 + +- `riverpod` upgraded to `3.0.0-dev.2` + +## 3.0.0-dev.1 - 2023-11-20 + +- `riverpod` upgraded to `3.0.0-dev.1` + +## 3.0.0-dev.0 - 2023-10-29 + +- `riverpod` upgraded to `3.0.0-dev.0` + ## 2.6.1 - 2024-10-22 - `riverpod` upgraded to `2.6.1` diff --git a/packages/riverpod_annotation/analysis_options.yaml b/packages/riverpod_annotation/analysis_options.yaml new file mode 100644 index 000000000..a9a268589 --- /dev/null +++ b/packages/riverpod_annotation/analysis_options.yaml @@ -0,0 +1,6 @@ +include: ../../analysis_options.yaml +analyzer: + errors: + # We have a tight constraint on Riverpod to use its internal APIs + invalid_use_of_internal_member: ignore + implementation_imports: ignore diff --git a/packages/riverpod_annotation/lib/riverpod_annotation.dart b/packages/riverpod_annotation/lib/riverpod_annotation.dart index 27cc49197..e449dd69d 100644 --- a/packages/riverpod_annotation/lib/riverpod_annotation.dart +++ b/packages/riverpod_annotation/lib/riverpod_annotation.dart @@ -1,121 +1,79 @@ -export 'dart:async' show FutureOr; - // Annotations used by code-generators -export 'package:meta/meta.dart' show visibleForOverriding; +// ignore_for_file: invalid_use_of_internal_member + +import 'package:meta/meta.dart' as meta; + +export 'dart:async' show FutureOr; // ignore: invalid_export_of_internal_element export 'package:riverpod/src/internals.dart' show // General stuff + ProviderContainer, Family, ProviderOrFamily, Override, - // ignore: invalid_use_of_internal_member, Used by notifiers for overriding overrideWith - ProviderOverride, - // ignore: invalid_use_of_internal_member, used by families for overrideWith - FamilyOverride, + $FamilyOverride, + $FunctionalProvider, + $FutureModifier, + Ref, + NotifierBase, + $AsyncClassModifier, + $ClassProvider, + $ValueProvider, + $ProviderOverride, + $RefArg, + $ProviderPointer, - // Provider - Provider, - ProviderFamily, - // ignore: deprecated_member_use - ProviderRef, - AutoDisposeProvider, - AutoDisposeProviderFamily, - // ignore: deprecated_member_use - AutoDisposeProviderRef, + // Mutation/Listenables + ProviderListenable, + $LazyProxyListenable, ProviderElement, - AutoDisposeProviderElement, + $ElementLense, + $Result, + + // Provider + $Provider, + $ProviderElement, // FutureProvider - FutureProvider, - FutureProviderFamily, - // ignore: deprecated_member_use - FutureProviderRef, - AutoDisposeFutureProvider, - AutoDisposeFutureProviderFamily, - // ignore: deprecated_member_use - AutoDisposeFutureProviderRef, - FutureProviderElement, - AutoDisposeFutureProviderElement, + $FutureProvider, + $FutureProviderElement, // StreamProvider - StreamProvider, - StreamProviderFamily, - // ignore: deprecated_member_use - StreamProviderRef, - AutoDisposeStreamProvider, - AutoDisposeStreamProviderFamily, - // ignore: deprecated_member_use - AutoDisposeStreamProviderRef, - StreamProviderElement, - AutoDisposeStreamProviderElement, + $StreamProvider, + $StreamProviderElement, // AsyncValue AsyncValue, AsyncLoading, AsyncData, AsyncError, - AsyncValueX, - - // Notifier - Notifier, - AutoDisposeNotifier, - NotifierProviderElement, - AutoDisposeNotifierProviderElement, - // ignore: invalid_use_of_internal_member - NotifierProviderImpl, - // ignore: invalid_use_of_internal_member - AutoDisposeNotifierProviderImpl, - // ignore: invalid_use_of_internal_member - BuildlessNotifier, - // ignore: invalid_use_of_internal_member - BuildlessAutoDisposeNotifier, - NotifierProvider, - // ignore: deprecated_member_use - NotifierProviderRef, - AutoDisposeNotifierProvider, - // ignore: deprecated_member_use - AutoDisposeNotifierProviderRef, // AsyncNotifier - AsyncNotifier, - AutoDisposeAsyncNotifier, - AsyncNotifierProviderElement, - AutoDisposeAsyncNotifierProviderElement, - // ignore: invalid_use_of_internal_member - AsyncNotifierProviderImpl, - // ignore: invalid_use_of_internal_member - AutoDisposeAsyncNotifierProviderImpl, - // ignore: invalid_use_of_internal_member - BuildlessAsyncNotifier, - // ignore: invalid_use_of_internal_member - BuildlessAutoDisposeAsyncNotifier, - AsyncNotifierProvider, - // ignore: deprecated_member_use - AsyncNotifierProviderRef, - AutoDisposeAsyncNotifierProvider, - // ignore: deprecated_member_use - AutoDisposeAsyncNotifierProviderRef, + $AsyncNotifierProvider, + $AsyncNotifier, + $AsyncNotifierProviderElement, // StreamNotifier - StreamNotifier, - AutoDisposeStreamNotifier, - StreamNotifierProviderElement, - AutoDisposeStreamNotifierProviderElement, - // ignore: invalid_use_of_internal_member - StreamNotifierProviderImpl, - // ignore: invalid_use_of_internal_member - AutoDisposeStreamNotifierProviderImpl, - // ignore: invalid_use_of_internal_member - BuildlessStreamNotifier, - // ignore: invalid_use_of_internal_member - BuildlessAutoDisposeStreamNotifier, - StreamNotifierProvider, - // ignore: deprecated_member_use - StreamNotifierProviderRef, - AutoDisposeStreamNotifierProvider, - // ignore: deprecated_member_use - AutoDisposeStreamNotifierProviderRef; + $StreamNotifierProvider, + $StreamNotifierProviderElement, + $StreamNotifier, + + // Notifier + $NotifierProvider, + $NotifierProviderElement, + $Notifier; + +// ignore: invalid_export_of_internal_element, used by the generator. +export 'package:riverpod/src/mutation.dart' + show $SyncMutationBase, $AsyncMutationBase; +// Separate export to avoid silencing valid @internal issues +export 'package:riverpod/src/mutation.dart' + hide $SyncMutationBase, $AsyncMutationBase; export 'src/riverpod_annotation.dart'; + +/// An implementation detail of `riverpod_generator`. +/// Do not use. +const $internal = meta.internal; diff --git a/packages/riverpod_annotation/lib/src/riverpod_annotation.dart b/packages/riverpod_annotation/lib/src/riverpod_annotation.dart index 6d94a6938..b2a7f0886 100644 --- a/packages/riverpod_annotation/lib/src/riverpod_annotation.dart +++ b/packages/riverpod_annotation/lib/src/riverpod_annotation.dart @@ -17,13 +17,23 @@ import '../riverpod_annotation.dart'; /// {@endtemplate} @Target({TargetKind.classType, TargetKind.function}) @sealed -class Riverpod { +final class Riverpod { /// {@macro riverpod_annotation.provider} const Riverpod({ this.keepAlive = false, this.dependencies, + this.retry, }); + /// The default retry logic used by providers associated to this container. + /// + /// The default implementation: + /// - has unlimited retries + /// - starts with a delay of 200ms + /// - doubles the delay on each retry up to 6.4 seconds + /// - retries all failures + final Duration? Function(int retryCount, Object error)? retry; + /// Whether the state of the provider should be maintained if it is no-longer used. /// /// Defaults to false. @@ -45,11 +55,19 @@ class Riverpod { /// ```dart /// // By not specifying "dependencies", we are saying that this provider is never scoped /// @riverpod - /// Foo root(RootRef ref) => Foo(); + /// Foo root(Ref ref) => Foo(); + /// /// // By specifying "dependencies" (even if the list is empty), /// // we are saying that this provider is potentially scoped /// @Riverpod(dependencies: []) - /// Foo scoped(ScopedRef ref) => Foo(); + /// Foo scoped(Ref ref) => Foo(); + /// + /// // Alternatively, notifiers with an abstract build method are also considered scoped + /// @riverpod + /// class MyNotifier extends _$MyNotifier { + /// @override + /// int build(); + /// } /// ``` /// /// Then if we were to depend on `rootProvider` in a scoped provider, we @@ -57,14 +75,14 @@ class Riverpod { /// /// ```dart /// @riverpod - /// Object? dependent(DependentRef ref) { + /// Object? dependent(Ref ref) { /// ref.watch(rootProvider); /// // This provider does not depend on any scoped provider, /// // as such "dependencies" is optional /// } /// /// @Riverpod(dependencies: []) - /// Object? dependent(DependentRef ref) { + /// Object? dependent(Ref ref) { /// ref.watch(rootProvider); /// // This provider decided to specify "dependencies" anyway, marking /// // "dependentProvider" as possibly scoped. @@ -73,7 +91,7 @@ class Riverpod { /// } /// /// @Riverpod(dependencies: [root]) - /// Object? dependent(DependentRef ref) { + /// Object? dependent(Ref ref) { /// ref.watch(rootProvider); /// // Including "rootProvider" in "dependencies" is fine too, even though /// // it is not required. It is a no-op. @@ -84,7 +102,7 @@ class Riverpod { /// /// ```dart /// @Riverpod(dependencies: [scoped]) - /// Object? dependent(DependentRef ref) { + /// Object? dependent(Ref ref) { /// ref.watch(scopedProvider); /// // Since "scopedProvider" specifies "dependencies", any provider that /// // depends on it must also specify "dependencies" and include "scopedProvider". @@ -94,7 +112,19 @@ class Riverpod { /// In that scenario, the `dependencies` parameter is required and it must /// include `scopedProvider`. /// + /// **Note**: + /// It is not necessary to specify an empty "dependencies" on notifiers with + /// an abstract build method: + /// ```dart + /// @riverpod + /// class MyNotifier extends _$MyNotifier { + /// @override + /// int build(); // Valid, marks this notifier as scoped + /// } + /// ``` + /// /// See also: + /// - [Dependencies], for specifying dependencies on non-providers. /// - [provider_dependencies](https://github.com/rrousselGit/riverpod/tree/master/packages/riverpod_lint#provider_dependencies-riverpod_generator-only) /// and [scoped_providers_should_specify_dependencies](https://github.com/rrousselGit/riverpod/tree/master/packages/riverpod_lint#scoped_providers_should_specify_dependencies-generator-only).\ /// These are lint rules that will warn about incorrect `dependencies` usages. @@ -166,3 +196,92 @@ class ProviderFor { /// /// {@endtemplate} typedef Raw = T; + +/// An exception thrown when a scoped provider is accessed when not yet overridden. +class MissingScopeException implements Exception { + /// An exception thrown when a scoped provider is accessed when not yet overridden. + MissingScopeException(this.ref); + + /// The [Ref] that threw the exception + final Ref ref; + + @override + String toString() { + final element = ref.$element; + + return 'MissingScopeException: The provider ${element.origin} is scoped, ' + 'but was accessed in a place where it is not overridden. ' + 'Either you forgot to override the provider, or you tried to read it outside of where it is defined'; + } +} + +/// {@template riverpod_annotation.dependencies} +/// An annotation to be specified on non-provider objects that use scoped providers. +/// +/// This is equivalent to `@Riverpod(dependencies: [])`, but for non-provider objects. +/// This is most commonly used on `Consumer`s, but can be used on anything, +/// including functions. +/// +/// The sole purpose of this annotation is to notify the linter +/// that an object uses a scoped provider. +/// It then enables the linter to warn in case this object is used in a place +/// where the scoped provider is not overridden. +/// +/// ## Usage example: +/// +/// Consider the following scoped provider: +/// ```dart +/// @Riverpod(dependencies: []) +/// String selectedBookID(Ref ref) => throw UnimplementedError(); +/// ``` +/// +/// Since this provider is scoped, we should specify `@Dependencies` on any object +/// that uses it. +/// For instance, a `Consumer`: +/// +/// ```dart +/// @Dependencies([selectedBookID]) +/// class BookView extends ConsumerWidget { +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// final selectedBookID = ref.watch(selectedBookIDProvider); +/// return Text(selectedBookID); +/// } +/// } +/// ``` +/// +/// By doing so, using `BooKView` now requires either: +/// - overriding `selectedBookIDProvider` in a `ProviderScope` that is an ancestor +/// of `BookView`: +/// ```dart +/// ProviderScope( +/// overrides: [ +/// selectedBookIDProvider.overrideWithValue('myBookID'), +/// ], +/// child: BookView(), +/// ), +/// ``` +/// - or using `BookView` in a widget that also specifies `@Dependencies([selectedBookID])`: +/// ```dart +/// @Dependencies([selectedBookID]) +/// class MyWidget extends StatelessWidget { +/// @override +/// Widget build(BuildContext context) { +/// return BookView(); +/// } +/// } +/// ``` +/// +/// Failing to do so will result in a linter warning. +/// +/// **Note**: When using a `StatefulWidget` (or variant), +/// there is no need to specify `@Dependencies` on the `State` class. +/// Specifying it on the `StatefulWidget` is enough. +/// {@endtemplate} +class Dependencies { + /// {@macro riverpod_annotation.dependencies} + const Dependencies(this.dependencies); + + /// {@macro riverpod_annotation.dependencies} + final List dependencies; +} diff --git a/packages/riverpod_annotation/pubspec.yaml b/packages/riverpod_annotation/pubspec.yaml index 3738553e5..857e89e84 100644 --- a/packages/riverpod_annotation/pubspec.yaml +++ b/packages/riverpod_annotation/pubspec.yaml @@ -1,17 +1,17 @@ name: riverpod_annotation description: A package exposing annotations for riverpod_generator -version: 2.6.1 +version: 3.0.0-dev.3 repository: https://github.com/rrousselGit/riverpod issue_tracker: https://github.com/rrousselGit/riverpod/issues funding: - https://github.com/sponsors/rrousselGit/ environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0<4.0.0" dependencies: meta: ^1.7.0 - riverpod: 2.6.1 + riverpod: 3.0.0-dev.3 dev_dependencies: test: ^1.21.0 diff --git a/packages/riverpod_generator/CHANGELOG.md b/packages/riverpod_generator/CHANGELOG.md index 1e7d05149..9f942e98f 100644 --- a/packages/riverpod_generator/CHANGELOG.md +++ b/packages/riverpod_generator/CHANGELOG.md @@ -1,3 +1,23 @@ +## Unreleased build + +- **Breaking** Removed support for `@riverpod external int fn();`. +- **Breaking** Family arguments are no-longer available on the `Ref` object. + The various override methods now take two parameters: + + ```dart + @riverpod + String example(Ref ref, int arg, {required int anotherArg}) {...} + // ... + exampleProvider.overrideWith( + (ref, ({int arg, int anotherArg}) args) { + + } + ) + ``` + +- Added support for mutations. See also `@mutation` for further information. +- Added support for `@Riverpod(retry: ...)` + ## 2.6.4 - 2025-01-08 Support latest analyzer @@ -48,6 +68,113 @@ Support latest analyzer ## 2.4.0 - 2024-03-10 - Adds `provider_name_prefix` and `provider_family_name_prefix` to `build.yaml`. (thanks to @ValentinVignal) +- Generated providers are now always `const`. +- Added support for abstract `build` method on Notifiers: + ```dart + @riverpod + class Example extends _$Example { + @override + int build(); + } + ``` + This is equivalent to writing: + ```dart + @Riverpod(dependencies: []) + class Example extends _$Example { + @override + int build() => throw UnimplementedError(); + } + ``` +- Added support for documentation and annotations on providers/parameters. + Comments on providers and family parameters will be + injected in the generated code, for IDE documentation + in the relevant places. + Annotations will be pasted over, such as to mark parameters + as `@deprecated` everywhere. +- Updated to support latest `riverpod_analyzer_utils` + +## 3.0.0-dev.11 - 2023-11-27 + +- `riverpod_annotation` upgraded to `3.0.0-dev.3` +- `riverpod` upgraded to `3.0.0-dev.3` + +## 3.0.0-dev.10 - 2023-11-20 + +- `riverpod_annotation` upgraded to `3.0.0-dev.2` +- `riverpod` upgraded to `3.0.0-dev.2` + +## 3.0.0-dev.9 - 2023-11-20 + +- Fix crash when encountering classes with a `ProviderBase` field. + +## 3.0.0-dev.8 - 2023-10-30 + +- `riverpod_analyzer_utils` upgraded to `1.0.0-dev.0` + +## 3.0.0-dev.7 - 2023-10-29 + +- Providers can now be generic: + + ```dart + @riverpod + List example(ExampleRef ref) { + return []; + } + + @riverpod + class ClassExample extends _$ClassExample { + @override + List build() => []; + } + ``` + + Specifying type parameters works the same as specifying arguments, and + make the generated provider a "function": + + ```dart + ref.watch(example()); + ``` + +- Upgraded to use Riverpod 3.0 +- Fixed `family.overrideWith` missing + +## 3.0.0-dev.5 - 2023-10-21 + +- `riverpod_analyzer_utils` upgraded to `0.4.2` + +## 3.0.0-dev.4 - 2023-10-15 + +- Annotating a provider with `@deprecated` and a few other annotations + also annotate the generated code accordingly (thanks to @SunlightBro) +- `provider.argument` is now a record of all arguments in a provider. + +## 3.0.0-dev.3 - 2023-10-06 + +- `riverpod_analyzer_utils` upgraded to `0.4.1` +- `riverpod_annotation` upgraded to `2.2.0` + +## 3.0.0-dev.2 - 2023-10-02 + +- `riverpod_analyzer_utils` upgraded to `0.4.0` + +## 3.0.0-dev.1 - 2023-10-02 + +The code generator now supports import aliases, generated types and typedefs +as input of providers!. + +This comes with a few minor restrictions: + +- **Breaking**: Returning a Typedef or type Future/FutureOr/Stream is no-longer supported: + + ```dart + typedef Example = Future; + + @riverpod + Example foo(FooRef ref) async => 0; + ``` + +- **Breaking**: Arguments of the form `fn(void myParameter())` + are no-longer supported. Instead use `fn(void Function() myParameter)`. ## 2.3.11 - 2024-02-04 @@ -76,7 +203,7 @@ Support latest analyzer ## 2.3.6 - 2023-11-13 -- Fix typos and internal changes +Fix typos and internal changes ## 2.3.5 - 2023-10-21 @@ -205,7 +332,7 @@ Upgrade Riverpod to latest ## 1.1.0 -- The generated hash function of providers is now correctluy private (thanks to @westito) +- The generated hash function of providers is now correctly private (thanks to @westito) - Allow customizing the name of the generated providers (thanks to @trejdych) - Update dependencies. @@ -236,3 +363,5 @@ Fix version conflict with Riverpod ## 1.0.0 Initial release + + diff --git a/packages/riverpod_generator/README.md b/packages/riverpod_generator/README.md index e7110241e..d3566d46d 100644 --- a/packages/riverpod_generator/README.md +++ b/packages/riverpod_generator/README.md @@ -86,9 +86,10 @@ over the default Riverpod syntax. - [Riverpod generator](#riverpod-generator) - [Getting started](#getting-started) - - [Installing riverpod_generator](#installing-riverpod_generator) + - [Installing riverpod\_generator](#installing-riverpod_generator) - [Starting the code generator](#starting-the-code-generator) - [Defining our first "provider"](#defining-our-first-provider) + - [Global configuration](#global-configuration) # Getting started @@ -98,7 +99,7 @@ To install riverpod_generator, edit your `pubspec.yaml` and add the following: ```yaml dependencies: - # or flutter_riverpod/hooks_riverpod as per https://riverpod.dev/docs/getting_started + # or flutter_riverpod/hooks_riverpod as per https://riverpod.dev/docs/introduction/getting_started riverpod: # the annotation package containing @riverpod riverpod_annotation: diff --git a/packages/riverpod_generator/integration/build_yaml/README.md b/packages/riverpod_generator/integration/build_yaml/README.md index ad4d3b1bd..50f850fb4 100644 --- a/packages/riverpod_generator/integration/build_yaml/README.md +++ b/packages/riverpod_generator/integration/build_yaml/README.md @@ -10,7 +10,7 @@ dart pub global activate melos Build the riverpod packages ```bash -fluttter pub get +flutter pub get ``` ## Start the tests diff --git a/packages/riverpod_generator/integration/build_yaml/lib/dependencies.dart b/packages/riverpod_generator/integration/build_yaml/lib/dependencies.dart index 1f59cd52f..4b3fc48b4 100644 --- a/packages/riverpod_generator/integration/build_yaml/lib/dependencies.dart +++ b/packages/riverpod_generator/integration/build_yaml/lib/dependencies.dart @@ -1,4 +1,3 @@ -import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'main.dart'; diff --git a/packages/riverpod_generator/integration/build_yaml/lib/dependencies.g.dart b/packages/riverpod_generator/integration/build_yaml/lib/dependencies.g.dart index eab1c9eda..dfcc891f4 100644 --- a/packages/riverpod_generator/integration/build_yaml/lib/dependencies.g.dart +++ b/packages/riverpod_generator/integration/build_yaml/lib/dependencies.g.dart @@ -6,193 +6,177 @@ part of 'dependencies.dart'; // RiverpodGenerator // ************************************************************************** -String _$calc2Hash() => r'ae1d601ff7cdda569255e8014bd5d8d1c178b3eb'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// See also [calc2]. @ProviderFor(calc2) -const myFamilyCalc2ProviderFamily = Calc2Family(); - -/// See also [calc2]. -class Calc2Family extends Family { - /// See also [calc2]. - const Calc2Family(); +const myFamilyCalc2ProviderFamily = Calc2Family._(); + +final class Calc2Provider extends $FunctionalProvider + with $Provider { + const Calc2Provider._( + {required Calc2Family super.from, + required String super.argument, + int Function( + Ref ref, + String id, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'myFamilyCalc2ProviderFamily', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// See also [calc2]. - Calc2Provider call( + static const $allTransitiveDependencies0 = myCountPod; + static const $allTransitiveDependencies1 = myCountFuturePod; + static const $allTransitiveDependencies2 = myCountStreamPod; + static const $allTransitiveDependencies3 = myCountNotifierPod; + static const $allTransitiveDependencies4 = myCountAsyncNotifierPod; + static const $allTransitiveDependencies5 = myCountStreamNotifierPod; + static const $allTransitiveDependencies6 = myFamilyCount2ProviderFamily; + static const $allTransitiveDependencies7 = myFamilyCountFuture2ProviderFamily; + static const $allTransitiveDependencies8 = myFamilyCountStream2ProviderFamily; + static const $allTransitiveDependencies9 = + myFamilyCountNotifier2ProviderFamily; + static const $allTransitiveDependencies10 = + myFamilyCountAsyncNotifier2ProviderFamily; + static const $allTransitiveDependencies11 = + myFamilyCountStreamNotifier2ProviderFamily; + + final int Function( + Ref ref, String id, - ) { - return Calc2Provider( - id, - ); - } + )? _createCb; @override - Calc2Provider getProviderOverride( - covariant Calc2Provider provider, - ) { - return call( - provider.id, - ); - } - - static final Iterable _dependencies = { - myCountPod, - myCountFuturePod, - myCountStreamPod, - myCountNotifierPod, - myCountAsyncNotifierPod, - myCountStreamNotifierPod, - myFamilyCount2ProviderFamily, - myFamilyCountFuture2ProviderFamily, - myFamilyCountStream2ProviderFamily, - myFamilyCountNotifier2ProviderFamily, - myFamilyCountAsyncNotifier2ProviderFamily, - myFamilyCountStreamNotifier2ProviderFamily - }; + String debugGetCreateSourceHash() => _$calc2Hash(); @override - Iterable? get dependencies => _dependencies; - - static final Iterable _allTransitiveDependencies = - { - myCountPod, - ...?myCountPod.allTransitiveDependencies, - myCountFuturePod, - ...?myCountFuturePod.allTransitiveDependencies, - myCountStreamPod, - ...?myCountStreamPod.allTransitiveDependencies, - myCountNotifierPod, - ...?myCountNotifierPod.allTransitiveDependencies, - myCountAsyncNotifierPod, - ...?myCountAsyncNotifierPod.allTransitiveDependencies, - myCountStreamNotifierPod, - ...?myCountStreamNotifierPod.allTransitiveDependencies, - myFamilyCount2ProviderFamily, - ...?myFamilyCount2ProviderFamily.allTransitiveDependencies, - myFamilyCountFuture2ProviderFamily, - ...?myFamilyCountFuture2ProviderFamily.allTransitiveDependencies, - myFamilyCountStream2ProviderFamily, - ...?myFamilyCountStream2ProviderFamily.allTransitiveDependencies, - myFamilyCountNotifier2ProviderFamily, - ...?myFamilyCountNotifier2ProviderFamily.allTransitiveDependencies, - myFamilyCountAsyncNotifier2ProviderFamily, - ...?myFamilyCountAsyncNotifier2ProviderFamily.allTransitiveDependencies, - myFamilyCountStreamNotifier2ProviderFamily, - ...?myFamilyCountStreamNotifier2ProviderFamily.allTransitiveDependencies - }; + String toString() { + return r'myFamilyCalc2ProviderFamily' + '' + '($argument)'; + } - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + @$internal @override - String? get name => r'myFamilyCalc2ProviderFamily'; -} - -/// See also [calc2]. -class Calc2Provider extends AutoDisposeProvider { - /// See also [calc2]. - Calc2Provider( - String id, - ) : this._internal( - (ref) => calc2( - ref as Calc2Ref, - id, - ), - from: myFamilyCalc2ProviderFamily, - name: r'myFamilyCalc2ProviderFamily', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$calc2Hash, - dependencies: Calc2Family._dependencies, - allTransitiveDependencies: Calc2Family._allTransitiveDependencies, - id: id, - ); - - Calc2Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.id, - }) : super.internal(); - - final String id; + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - Override overrideWith( - int Function(Calc2Ref provider) create, + Calc2Provider $copyWithCreate( + int Function( + Ref ref, + ) create, ) { - return ProviderOverride( - origin: this, - override: Calc2Provider._internal( - (ref) => create(ref as Calc2Ref), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - id: id, - ), - ); + return Calc2Provider._( + argument: argument as String, + from: from! as Calc2Family, + create: ( + ref, + String id, + ) => + create(ref)); } @override - AutoDisposeProviderElement createElement() { - return _Calc2ProviderElement(this); + int create(Ref ref) { + final _$cb = _createCb ?? calc2; + final argument = this.argument as String; + return _$cb( + ref, + argument, + ); } @override bool operator ==(Object other) { - return other is Calc2Provider && other.id == id; + return other is Calc2Provider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, id.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin Calc2Ref on AutoDisposeProviderRef { - /// The parameter `id` of this provider. - String get id; -} +String _$calc2Hash() => r'ae1d601ff7cdda569255e8014bd5d8d1c178b3eb'; + +final class Calc2Family extends Family { + const Calc2Family._() + : super( + retry: null, + name: r'myFamilyCalc2ProviderFamily', + dependencies: const [ + myCountPod, + myCountFuturePod, + myCountStreamPod, + myCountNotifierPod, + myCountAsyncNotifierPod, + myCountStreamNotifierPod, + myFamilyCount2ProviderFamily, + myFamilyCountFuture2ProviderFamily, + myFamilyCountStream2ProviderFamily, + myFamilyCountNotifier2ProviderFamily, + myFamilyCountAsyncNotifier2ProviderFamily, + myFamilyCountStreamNotifier2ProviderFamily + ], + allTransitiveDependencies: const { + Calc2Provider.$allTransitiveDependencies0, + Calc2Provider.$allTransitiveDependencies1, + Calc2Provider.$allTransitiveDependencies2, + Calc2Provider.$allTransitiveDependencies3, + Calc2Provider.$allTransitiveDependencies4, + Calc2Provider.$allTransitiveDependencies5, + Calc2Provider.$allTransitiveDependencies6, + Calc2Provider.$allTransitiveDependencies7, + Calc2Provider.$allTransitiveDependencies8, + Calc2Provider.$allTransitiveDependencies9, + Calc2Provider.$allTransitiveDependencies10, + Calc2Provider.$allTransitiveDependencies11, + }, + isAutoDispose: true, + ); -class _Calc2ProviderElement extends AutoDisposeProviderElement - with Calc2Ref { - _Calc2ProviderElement(super.provider); + Calc2Provider call( + String id, + ) => + Calc2Provider._(argument: id, from: this); @override - String get id => (origin as Calc2Provider).id; + String debugGetCreateSourceHash() => _$calc2Hash(); + + @override + String toString() => r'myFamilyCalc2ProviderFamily'; + + /// {@macro riverpod.override_with} + Override overrideWith( + int Function( + Ref ref, + String args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as Calc2Provider; + + final argument = provider.argument as String; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/integration/build_yaml/lib/main.dart b/packages/riverpod_generator/integration/build_yaml/lib/main.dart index bb6a2201f..df64e3942 100644 --- a/packages/riverpod_generator/integration/build_yaml/lib/main.dart +++ b/packages/riverpod_generator/integration/build_yaml/lib/main.dart @@ -1,4 +1,3 @@ -import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'main.g.dart'; diff --git a/packages/riverpod_generator/integration/build_yaml/lib/main.g.dart b/packages/riverpod_generator/integration/build_yaml/lib/main.g.dart index ae0865d1b..2f67f0f14 100644 --- a/packages/riverpod_generator/integration/build_yaml/lib/main.g.dart +++ b/packages/riverpod_generator/integration/build_yaml/lib/main.g.dart @@ -6,950 +6,1212 @@ part of 'main.dart'; // RiverpodGenerator // ************************************************************************** -String _$countHash() => r'a31bb5cbb0ddb2466df2cc62a306709ea24fae12'; - -/// See also [count]. @ProviderFor(count) -final myCountPod = AutoDisposeProvider.internal( - count, - name: r'myCountPod', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$countHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CountRef = AutoDisposeProviderRef; -String _$countFutureHash() => r'c292214b486fdd9ec98a61e277812f29fc4b5802'; +const myCountPod = CountProvider._(); + +final class CountProvider extends $FunctionalProvider + with $Provider { + const CountProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myCountPod', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [countFuture]. -@ProviderFor(countFuture) -final myCountFuturePod = AutoDisposeFutureProvider.internal( - countFuture, - name: r'myCountFuturePod', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$countFutureHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CountFutureRef = AutoDisposeFutureProviderRef; -String _$countStreamHash() => r'472c06085fb994619f54de368f047b7cc8466872'; + final int Function( + Ref ref, + )? _createCb; -/// See also [countStream]. -@ProviderFor(countStream) -final myCountStreamPod = AutoDisposeStreamProvider.internal( - countStream, - name: r'myCountStreamPod', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$countStreamHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CountStreamRef = AutoDisposeStreamProviderRef; -String _$count2Hash() => r'4146ae486161f9d444b4d80ec846199b13eeaae2'; + @override + String debugGetCreateSourceHash() => _$countHash(); -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CountProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return CountProvider._(create: create); } - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + @override + int create(Ref ref) { + final _$cb = _createCb ?? count; + return _$cb(ref); } } -/// See also [count2]. -@ProviderFor(count2) -const myFamilyCount2ProviderFamily = Count2Family(); +String _$countHash() => r'a31bb5cbb0ddb2466df2cc62a306709ea24fae12'; -/// See also [count2]. -class Count2Family extends Family { - /// See also [count2]. - const Count2Family(); +@ProviderFor(countFuture) +const myCountFuturePod = CountFutureProvider._(); + +final class CountFutureProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const CountFutureProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myCountFuturePod', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// See also [count2]. - Count2Provider call( - int a, + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$countFutureHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + CountFutureProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, ) { - return Count2Provider( - a, - ); + return CountFutureProvider._(create: create); } @override - Count2Provider getProviderOverride( - covariant Count2Provider provider, - ) { - return call( - provider.a, - ); + FutureOr create(Ref ref) { + final _$cb = _createCb ?? countFuture; + return _$cb(ref); } +} + +String _$countFutureHash() => r'c292214b486fdd9ec98a61e277812f29fc4b5802'; + +@ProviderFor(countStream) +const myCountStreamPod = CountStreamProvider._(); + +final class CountStreamProvider + extends $FunctionalProvider, Stream> + with $FutureModifier, $StreamProvider { + const CountStreamProvider._( + {Stream Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myCountStreamPod', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - static const Iterable? _dependencies = null; + final Stream Function( + Ref ref, + )? _createCb; @override - Iterable? get dependencies => _dependencies; + String debugGetCreateSourceHash() => _$countStreamHash(); - static const Iterable? _allTransitiveDependencies = null; + @$internal + @override + $StreamProviderElement $createElement($ProviderPointer pointer) => + $StreamProviderElement(this, pointer); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + CountStreamProvider $copyWithCreate( + Stream Function( + Ref ref, + ) create, + ) { + return CountStreamProvider._(create: create); + } @override - String? get name => r'myFamilyCount2ProviderFamily'; + Stream create(Ref ref) { + final _$cb = _createCb ?? countStream; + return _$cb(ref); + } } -/// See also [count2]. -class Count2Provider extends AutoDisposeProvider { - /// See also [count2]. - Count2Provider( - int a, - ) : this._internal( - (ref) => count2( - ref as Count2Ref, - a, - ), - from: myFamilyCount2ProviderFamily, - name: r'myFamilyCount2ProviderFamily', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$count2Hash, - dependencies: Count2Family._dependencies, - allTransitiveDependencies: Count2Family._allTransitiveDependencies, - a: a, - ); +String _$countStreamHash() => r'472c06085fb994619f54de368f047b7cc8466872'; - Count2Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.a, - }) : super.internal(); +@ProviderFor(CountNotifier) +const myCountNotifierPod = CountNotifierProvider._(); + +final class CountNotifierProvider + extends $NotifierProvider { + const CountNotifierProvider._( + {super.runNotifierBuildOverride, CountNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myCountNotifierPod', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - final int a; + final CountNotifier Function()? _createCb; @override - Override overrideWith( - int Function(Count2Ref provider) create, - ) { - return ProviderOverride( + String debugGetCreateSourceHash() => _$countNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( origin: this, - override: Count2Provider._internal( - (ref) => create(ref as Count2Ref), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - a: a, - ), + providerOverride: $ValueProvider(value), ); } + @$internal @override - AutoDisposeProviderElement createElement() { - return _Count2ProviderElement(this); - } + CountNotifier create() => _createCb?.call() ?? CountNotifier(); + @$internal @override - bool operator ==(Object other) { - return other is Count2Provider && other.a == a; + CountNotifierProvider $copyWithCreate( + CountNotifier Function() create, + ) { + return CountNotifierProvider._(create: create); } + @$internal @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, a.hashCode); - - return _SystemHash.finish(hash); + CountNotifierProvider $copyWithBuild( + int Function( + Ref, + CountNotifier, + ) build, + ) { + return CountNotifierProvider._(runNotifierBuildOverride: build); } -} -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin Count2Ref on AutoDisposeProviderRef { - /// The parameter `a` of this provider. - int get a; + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); } -class _Count2ProviderElement extends AutoDisposeProviderElement - with Count2Ref { - _Count2ProviderElement(super.provider); +String _$countNotifierHash() => r'a8dd7a66ee0002b8af657245c4affaa206fd99ec'; +abstract class _$CountNotifier extends $Notifier { + int build(); + @$internal @override - int get a => (origin as Count2Provider).a; + int runBuild() => build(); } -String _$countFuture2Hash() => r'6acaa58de0116853fd831efb4ac1a8047205f12b'; +@ProviderFor(CountAsyncNotifier) +const myCountAsyncNotifierPod = CountAsyncNotifierProvider._(); + +final class CountAsyncNotifierProvider + extends $AsyncNotifierProvider { + const CountAsyncNotifierProvider._( + {super.runNotifierBuildOverride, CountAsyncNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myCountAsyncNotifierPod', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [countFuture2]. -@ProviderFor(countFuture2) -const myFamilyCountFuture2ProviderFamily = CountFuture2Family(); + final CountAsyncNotifier Function()? _createCb; -/// See also [countFuture2]. -class CountFuture2Family extends Family> { - /// See also [countFuture2]. - const CountFuture2Family(); + @override + String debugGetCreateSourceHash() => _$countAsyncNotifierHash(); - /// See also [countFuture2]. - CountFuture2Provider call( - int a, + @$internal + @override + CountAsyncNotifier create() => _createCb?.call() ?? CountAsyncNotifier(); + + @$internal + @override + CountAsyncNotifierProvider $copyWithCreate( + CountAsyncNotifier Function() create, ) { - return CountFuture2Provider( - a, - ); + return CountAsyncNotifierProvider._(create: create); } + @$internal @override - CountFuture2Provider getProviderOverride( - covariant CountFuture2Provider provider, + CountAsyncNotifierProvider $copyWithBuild( + FutureOr Function( + Ref, + CountAsyncNotifier, + ) build, ) { - return call( - provider.a, - ); + return CountAsyncNotifierProvider._(runNotifierBuildOverride: build); } - static const Iterable? _dependencies = null; - + @$internal @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; + $AsyncNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); +} - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; +String _$countAsyncNotifierHash() => + r'2a7049d864bf396e44a5937b4001efb4774a5f29'; +abstract class _$CountAsyncNotifier extends $AsyncNotifier { + FutureOr build(); + @$internal @override - String? get name => r'myFamilyCountFuture2ProviderFamily'; + FutureOr runBuild() => build(); } -/// See also [countFuture2]. -class CountFuture2Provider extends AutoDisposeFutureProvider { - /// See also [countFuture2]. - CountFuture2Provider( - int a, - ) : this._internal( - (ref) => countFuture2( - ref as CountFuture2Ref, - a, - ), - from: myFamilyCountFuture2ProviderFamily, - name: r'myFamilyCountFuture2ProviderFamily', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$countFuture2Hash, - dependencies: CountFuture2Family._dependencies, - allTransitiveDependencies: - CountFuture2Family._allTransitiveDependencies, - a: a, +@ProviderFor(CountStreamNotifier) +const myCountStreamNotifierPod = CountStreamNotifierProvider._(); + +final class CountStreamNotifierProvider + extends $StreamNotifierProvider { + const CountStreamNotifierProvider._( + {super.runNotifierBuildOverride, CountStreamNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myCountStreamNotifierPod', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - CountFuture2Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.a, - }) : super.internal(); - - final int a; + final CountStreamNotifier Function()? _createCb; @override - Override overrideWith( - FutureOr Function(CountFuture2Ref provider) create, - ) { - return ProviderOverride( - origin: this, - override: CountFuture2Provider._internal( - (ref) => create(ref as CountFuture2Ref), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - a: a, - ), - ); - } + String debugGetCreateSourceHash() => _$countStreamNotifierHash(); + @$internal @override - AutoDisposeFutureProviderElement createElement() { - return _CountFuture2ProviderElement(this); - } + CountStreamNotifier create() => _createCb?.call() ?? CountStreamNotifier(); + @$internal @override - bool operator ==(Object other) { - return other is CountFuture2Provider && other.a == a; + CountStreamNotifierProvider $copyWithCreate( + CountStreamNotifier Function() create, + ) { + return CountStreamNotifierProvider._(create: create); } + @$internal @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, a.hashCode); - - return _SystemHash.finish(hash); + CountStreamNotifierProvider $copyWithBuild( + Stream Function( + Ref, + CountStreamNotifier, + ) build, + ) { + return CountStreamNotifierProvider._(runNotifierBuildOverride: build); } -} -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin CountFuture2Ref on AutoDisposeFutureProviderRef { - /// The parameter `a` of this provider. - int get a; + @$internal + @override + $StreamNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $StreamNotifierProviderElement(this, pointer); } -class _CountFuture2ProviderElement extends AutoDisposeFutureProviderElement - with CountFuture2Ref { - _CountFuture2ProviderElement(super.provider); +String _$countStreamNotifierHash() => + r'61d2cd311c4808f8d7e8b2d67f5c7b85337666c6'; +abstract class _$CountStreamNotifier extends $StreamNotifier { + Stream build(); + @$internal @override - int get a => (origin as CountFuture2Provider).a; + Stream runBuild() => build(); } -String _$countStream2Hash() => r'96c9a0935240f1727986800c1fe6dea974b9accc'; +@ProviderFor(count2) +const myFamilyCount2ProviderFamily = Count2Family._(); + +final class Count2Provider extends $FunctionalProvider + with $Provider { + const Count2Provider._( + {required Count2Family super.from, + required int super.argument, + int Function( + Ref ref, + int a, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'myFamilyCount2ProviderFamily', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [countStream2]. -@ProviderFor(countStream2) -const myFamilyCountStream2ProviderFamily = CountStream2Family(); + final int Function( + Ref ref, + int a, + )? _createCb; -/// See also [countStream2]. -class CountStream2Family extends Family> { - /// See also [countStream2]. - const CountStream2Family(); + @override + String debugGetCreateSourceHash() => _$count2Hash(); - /// See also [countStream2]. - CountStream2Provider call( - int a, - ) { - return CountStream2Provider( - a, + @override + String toString() { + return r'myFamilyCount2ProviderFamily' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } + @$internal @override - CountStream2Provider getProviderOverride( - covariant CountStream2Provider provider, + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + Count2Provider $copyWithCreate( + int Function( + Ref ref, + ) create, ) { - return call( - provider.a, + return Count2Provider._( + argument: argument as int, + from: from! as Count2Family, + create: ( + ref, + int a, + ) => + create(ref)); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? count2; + final argument = this.argument as int; + return _$cb( + ref, + argument, ); } - static const Iterable? _dependencies = null; + @override + bool operator ==(Object other) { + return other is Count2Provider && other.argument == argument; + } @override - Iterable? get dependencies => _dependencies; + int get hashCode { + return argument.hashCode; + } +} - static const Iterable? _allTransitiveDependencies = null; +String _$count2Hash() => r'4146ae486161f9d444b4d80ec846199b13eeaae2'; + +final class Count2Family extends Family { + const Count2Family._() + : super( + retry: null, + name: r'myFamilyCount2ProviderFamily', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + Count2Provider call( + int a, + ) => + Count2Provider._(argument: a, from: this); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + String debugGetCreateSourceHash() => _$count2Hash(); @override - String? get name => r'myFamilyCountStream2ProviderFamily'; + String toString() => r'myFamilyCount2ProviderFamily'; + + /// {@macro riverpod.override_with} + Override overrideWith( + int Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as Count2Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } -/// See also [countStream2]. -class CountStream2Provider extends AutoDisposeStreamProvider { - /// See also [countStream2]. - CountStream2Provider( - int a, - ) : this._internal( - (ref) => countStream2( - ref as CountStream2Ref, - a, - ), - from: myFamilyCountStream2ProviderFamily, - name: r'myFamilyCountStream2ProviderFamily', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$countStream2Hash, - dependencies: CountStream2Family._dependencies, - allTransitiveDependencies: - CountStream2Family._allTransitiveDependencies, - a: a, +@ProviderFor(countFuture2) +const myFamilyCountFuture2ProviderFamily = CountFuture2Family._(); + +final class CountFuture2Provider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const CountFuture2Provider._( + {required CountFuture2Family super.from, + required int super.argument, + FutureOr Function( + Ref ref, + int a, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'myFamilyCountFuture2ProviderFamily', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - CountStream2Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.a, - }) : super.internal(); + final FutureOr Function( + Ref ref, + int a, + )? _createCb; - final int a; + @override + String debugGetCreateSourceHash() => _$countFuture2Hash(); @override - Override overrideWith( - Stream Function(CountStream2Ref provider) create, + String toString() { + return r'myFamilyCountFuture2ProviderFamily' + '' + '($argument)'; + } + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + CountFuture2Provider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, ) { - return ProviderOverride( - origin: this, - override: CountStream2Provider._internal( - (ref) => create(ref as CountStream2Ref), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - a: a, - ), - ); + return CountFuture2Provider._( + argument: argument as int, + from: from! as CountFuture2Family, + create: ( + ref, + int a, + ) => + create(ref)); } @override - AutoDisposeStreamProviderElement createElement() { - return _CountStream2ProviderElement(this); + FutureOr create(Ref ref) { + final _$cb = _createCb ?? countFuture2; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); } @override bool operator ==(Object other) { - return other is CountStream2Provider && other.a == a; + return other is CountFuture2Provider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, a.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin CountStream2Ref on AutoDisposeStreamProviderRef { - /// The parameter `a` of this provider. - int get a; -} +String _$countFuture2Hash() => r'6acaa58de0116853fd831efb4ac1a8047205f12b'; + +final class CountFuture2Family extends Family { + const CountFuture2Family._() + : super( + retry: null, + name: r'myFamilyCountFuture2ProviderFamily', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); -class _CountStream2ProviderElement extends AutoDisposeStreamProviderElement - with CountStream2Ref { - _CountStream2ProviderElement(super.provider); + CountFuture2Provider call( + int a, + ) => + CountFuture2Provider._(argument: a, from: this); @override - int get a => (origin as CountStream2Provider).a; -} + String debugGetCreateSourceHash() => _$countFuture2Hash(); -String _$countNotifierHash() => r'a8dd7a66ee0002b8af657245c4affaa206fd99ec'; + @override + String toString() => r'myFamilyCountFuture2ProviderFamily'; -/// See also [CountNotifier]. -@ProviderFor(CountNotifier) -final myCountNotifierPod = - AutoDisposeNotifierProvider.internal( - CountNotifier.new, - name: r'myCountNotifierPod', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$countNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$CountNotifier = AutoDisposeNotifier; -String _$countAsyncNotifierHash() => - r'2a7049d864bf396e44a5937b4001efb4774a5f29'; + /// {@macro riverpod.override_with} + Override overrideWith( + FutureOr Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as CountFuture2Provider; -/// See also [CountAsyncNotifier]. -@ProviderFor(CountAsyncNotifier) -final myCountAsyncNotifierPod = - AutoDisposeAsyncNotifierProvider.internal( - CountAsyncNotifier.new, - name: r'myCountAsyncNotifierPod', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$countAsyncNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$CountAsyncNotifier = AutoDisposeAsyncNotifier; -String _$countStreamNotifierHash() => - r'61d2cd311c4808f8d7e8b2d67f5c7b85337666c6'; + final argument = provider.argument as int; -/// See also [CountStreamNotifier]. -@ProviderFor(CountStreamNotifier) -final myCountStreamNotifierPod = - AutoDisposeStreamNotifierProvider.internal( - CountStreamNotifier.new, - name: r'myCountStreamNotifierPod', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$countStreamNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$CountStreamNotifier = AutoDisposeStreamNotifier; -String _$countNotifier2Hash() => r'ef12bb4f94add336804ae43bcdbcd8e9b0bec420'; + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} -abstract class _$CountNotifier2 extends BuildlessAutoDisposeNotifier { - late final int a; +@ProviderFor(countStream2) +const myFamilyCountStream2ProviderFamily = CountStream2Family._(); + +final class CountStream2Provider + extends $FunctionalProvider, Stream> + with $FutureModifier, $StreamProvider { + const CountStream2Provider._( + {required CountStream2Family super.from, + required int super.argument, + Stream Function( + Ref ref, + int a, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'myFamilyCountStream2ProviderFamily', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - int build( + final Stream Function( + Ref ref, int a, - ); -} + )? _createCb; -/// See also [CountNotifier2]. -@ProviderFor(CountNotifier2) -const myFamilyCountNotifier2ProviderFamily = CountNotifier2Family(); + @override + String debugGetCreateSourceHash() => _$countStream2Hash(); -/// See also [CountNotifier2]. -class CountNotifier2Family extends Family { - /// See also [CountNotifier2]. - const CountNotifier2Family(); + @override + String toString() { + return r'myFamilyCountStream2ProviderFamily' + '' + '($argument)'; + } - /// See also [CountNotifier2]. - CountNotifier2Provider call( - int a, + @$internal + @override + $StreamProviderElement $createElement($ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + + @override + CountStream2Provider $copyWithCreate( + Stream Function( + Ref ref, + ) create, ) { - return CountNotifier2Provider( - a, - ); + return CountStream2Provider._( + argument: argument as int, + from: from! as CountStream2Family, + create: ( + ref, + int a, + ) => + create(ref)); } @override - CountNotifier2Provider getProviderOverride( - covariant CountNotifier2Provider provider, - ) { - return call( - provider.a, + Stream create(Ref ref) { + final _$cb = _createCb ?? countStream2; + final argument = this.argument as int; + return _$cb( + ref, + argument, ); } - static const Iterable? _dependencies = null; + @override + bool operator ==(Object other) { + return other is CountStream2Provider && other.argument == argument; + } @override - Iterable? get dependencies => _dependencies; + int get hashCode { + return argument.hashCode; + } +} - static const Iterable? _allTransitiveDependencies = null; +String _$countStream2Hash() => r'96c9a0935240f1727986800c1fe6dea974b9accc'; + +final class CountStream2Family extends Family { + const CountStream2Family._() + : super( + retry: null, + name: r'myFamilyCountStream2ProviderFamily', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + CountStream2Provider call( + int a, + ) => + CountStream2Provider._(argument: a, from: this); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + String debugGetCreateSourceHash() => _$countStream2Hash(); @override - String? get name => r'myFamilyCountNotifier2ProviderFamily'; + String toString() => r'myFamilyCountStream2ProviderFamily'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Stream Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as CountStream2Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } -/// See also [CountNotifier2]. -class CountNotifier2Provider - extends AutoDisposeNotifierProviderImpl { - /// See also [CountNotifier2]. - CountNotifier2Provider( - int a, - ) : this._internal( - () => CountNotifier2()..a = a, - from: myFamilyCountNotifier2ProviderFamily, +@ProviderFor(CountNotifier2) +const myFamilyCountNotifier2ProviderFamily = CountNotifier2Family._(); + +final class CountNotifier2Provider + extends $NotifierProvider { + const CountNotifier2Provider._( + {required CountNotifier2Family super.from, + required int super.argument, + super.runNotifierBuildOverride, + CountNotifier2 Function()? create}) + : _createCb = create, + super( + retry: null, name: r'myFamilyCountNotifier2ProviderFamily', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$countNotifier2Hash, - dependencies: CountNotifier2Family._dependencies, - allTransitiveDependencies: - CountNotifier2Family._allTransitiveDependencies, - a: a, + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - CountNotifier2Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.a, - }) : super.internal(); + final CountNotifier2 Function()? _createCb; - final int a; + @override + String debugGetCreateSourceHash() => _$countNotifier2Hash(); @override - int runNotifierBuild( - covariant CountNotifier2 notifier, - ) { - return notifier.build( - a, - ); + String toString() { + return r'myFamilyCountNotifier2ProviderFamily' + '' + '($argument)'; } - @override - Override overrideWith(CountNotifier2 Function() create) { - return ProviderOverride( + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( origin: this, - override: CountNotifier2Provider._internal( - () => create()..a = a, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - a: a, - ), + providerOverride: $ValueProvider(value), ); } + @$internal @override - AutoDisposeNotifierProviderElement createElement() { - return _CountNotifier2ProviderElement(this); - } + CountNotifier2 create() => _createCb?.call() ?? CountNotifier2(); + @$internal @override - bool operator ==(Object other) { - return other is CountNotifier2Provider && other.a == a; + CountNotifier2Provider $copyWithCreate( + CountNotifier2 Function() create, + ) { + return CountNotifier2Provider._( + argument: argument as int, + from: from! as CountNotifier2Family, + create: create); } + @$internal @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, a.hashCode); - - return _SystemHash.finish(hash); + CountNotifier2Provider $copyWithBuild( + int Function( + Ref, + CountNotifier2, + ) build, + ) { + return CountNotifier2Provider._( + argument: argument as int, + from: from! as CountNotifier2Family, + runNotifierBuildOverride: build); } -} -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin CountNotifier2Ref on AutoDisposeNotifierProviderRef { - /// The parameter `a` of this provider. - int get a; -} + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); -class _CountNotifier2ProviderElement - extends AutoDisposeNotifierProviderElement - with CountNotifier2Ref { - _CountNotifier2ProviderElement(super.provider); + @override + bool operator ==(Object other) { + return other is CountNotifier2Provider && other.argument == argument; + } @override - int get a => (origin as CountNotifier2Provider).a; + int get hashCode { + return argument.hashCode; + } } -String _$countAsyncNotifier2Hash() => - r'e4bd4d858edbb47fa0d7581f3cfa72e13c914d3d'; +String _$countNotifier2Hash() => r'ef12bb4f94add336804ae43bcdbcd8e9b0bec420'; -abstract class _$CountAsyncNotifier2 - extends BuildlessAutoDisposeAsyncNotifier { - late final int a; +final class CountNotifier2Family extends Family { + const CountNotifier2Family._() + : super( + retry: null, + name: r'myFamilyCountNotifier2ProviderFamily', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); - FutureOr build( + CountNotifier2Provider call( int a, - ); -} + ) => + CountNotifier2Provider._(argument: a, from: this); -/// See also [CountAsyncNotifier2]. -@ProviderFor(CountAsyncNotifier2) -const myFamilyCountAsyncNotifier2ProviderFamily = CountAsyncNotifier2Family(); + @override + String debugGetCreateSourceHash() => _$countNotifier2Hash(); -/// See also [CountAsyncNotifier2]. -class CountAsyncNotifier2Family extends Family> { - /// See also [CountAsyncNotifier2]. - const CountAsyncNotifier2Family(); + @override + String toString() => r'myFamilyCountNotifier2ProviderFamily'; - /// See also [CountAsyncNotifier2]. - CountAsyncNotifier2Provider call( - int a, + /// {@macro riverpod.override_with} + Override overrideWith( + CountNotifier2 Function( + int args, + ) create, ) { - return CountAsyncNotifier2Provider( - a, - ); - } + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as CountNotifier2Provider; - @override - CountAsyncNotifier2Provider getProviderOverride( - covariant CountAsyncNotifier2Provider provider, - ) { - return call( - provider.a, + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, ); } - static const Iterable? _dependencies = null; + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function(Ref ref, CountNotifier2 notifier, int argument) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as CountNotifier2Provider; - @override - Iterable? get dependencies => _dependencies; + final argument = provider.argument as int; - static const Iterable? _allTransitiveDependencies = null; + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; +abstract class _$CountNotifier2 extends $Notifier { + late final _$args = ref.$arg as int; + int get a => _$args; + int build( + int a, + ); + @$internal @override - String? get name => r'myFamilyCountAsyncNotifier2ProviderFamily'; + int runBuild() => build( + _$args, + ); } -/// See also [CountAsyncNotifier2]. -class CountAsyncNotifier2Provider - extends AutoDisposeAsyncNotifierProviderImpl { - /// See also [CountAsyncNotifier2]. - CountAsyncNotifier2Provider( - int a, - ) : this._internal( - () => CountAsyncNotifier2()..a = a, - from: myFamilyCountAsyncNotifier2ProviderFamily, +@ProviderFor(CountAsyncNotifier2) +const myFamilyCountAsyncNotifier2ProviderFamily = CountAsyncNotifier2Family._(); + +final class CountAsyncNotifier2Provider + extends $AsyncNotifierProvider { + const CountAsyncNotifier2Provider._( + {required CountAsyncNotifier2Family super.from, + required int super.argument, + super.runNotifierBuildOverride, + CountAsyncNotifier2 Function()? create}) + : _createCb = create, + super( + retry: null, name: r'myFamilyCountAsyncNotifier2ProviderFamily', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$countAsyncNotifier2Hash, - dependencies: CountAsyncNotifier2Family._dependencies, - allTransitiveDependencies: - CountAsyncNotifier2Family._allTransitiveDependencies, - a: a, + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - CountAsyncNotifier2Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.a, - }) : super.internal(); + final CountAsyncNotifier2 Function()? _createCb; - final int a; + @override + String debugGetCreateSourceHash() => _$countAsyncNotifier2Hash(); @override - FutureOr runNotifierBuild( - covariant CountAsyncNotifier2 notifier, - ) { - return notifier.build( - a, - ); + String toString() { + return r'myFamilyCountAsyncNotifier2ProviderFamily' + '' + '($argument)'; } + @$internal @override - Override overrideWith(CountAsyncNotifier2 Function() create) { - return ProviderOverride( - origin: this, - override: CountAsyncNotifier2Provider._internal( - () => create()..a = a, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - a: a, - ), - ); - } + CountAsyncNotifier2 create() => _createCb?.call() ?? CountAsyncNotifier2(); + @$internal @override - AutoDisposeAsyncNotifierProviderElement - createElement() { - return _CountAsyncNotifier2ProviderElement(this); + CountAsyncNotifier2Provider $copyWithCreate( + CountAsyncNotifier2 Function() create, + ) { + return CountAsyncNotifier2Provider._( + argument: argument as int, + from: from! as CountAsyncNotifier2Family, + create: create); } + @$internal @override - bool operator ==(Object other) { - return other is CountAsyncNotifier2Provider && other.a == a; + CountAsyncNotifier2Provider $copyWithBuild( + FutureOr Function( + Ref, + CountAsyncNotifier2, + ) build, + ) { + return CountAsyncNotifier2Provider._( + argument: argument as int, + from: from! as CountAsyncNotifier2Family, + runNotifierBuildOverride: build); } + @$internal @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, a.hashCode); + $AsyncNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); - return _SystemHash.finish(hash); + @override + bool operator ==(Object other) { + return other is CountAsyncNotifier2Provider && other.argument == argument; } -} - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin CountAsyncNotifier2Ref on AutoDisposeAsyncNotifierProviderRef { - /// The parameter `a` of this provider. - int get a; -} - -class _CountAsyncNotifier2ProviderElement - extends AutoDisposeAsyncNotifierProviderElement - with CountAsyncNotifier2Ref { - _CountAsyncNotifier2ProviderElement(super.provider); @override - int get a => (origin as CountAsyncNotifier2Provider).a; + int get hashCode { + return argument.hashCode; + } } -String _$countStreamNotifier2Hash() => - r'13be1b7aa32801b33c68f2a228851d2fb6a4a9ee'; +String _$countAsyncNotifier2Hash() => + r'e4bd4d858edbb47fa0d7581f3cfa72e13c914d3d'; -abstract class _$CountStreamNotifier2 - extends BuildlessAutoDisposeStreamNotifier { - late final int a; +final class CountAsyncNotifier2Family extends Family { + const CountAsyncNotifier2Family._() + : super( + retry: null, + name: r'myFamilyCountAsyncNotifier2ProviderFamily', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); - Stream build( + CountAsyncNotifier2Provider call( int a, - ); -} + ) => + CountAsyncNotifier2Provider._(argument: a, from: this); -/// See also [CountStreamNotifier2]. -@ProviderFor(CountStreamNotifier2) -const myFamilyCountStreamNotifier2ProviderFamily = CountStreamNotifier2Family(); + @override + String debugGetCreateSourceHash() => _$countAsyncNotifier2Hash(); -/// See also [CountStreamNotifier2]. -class CountStreamNotifier2Family extends Family> { - /// See also [CountStreamNotifier2]. - const CountStreamNotifier2Family(); + @override + String toString() => r'myFamilyCountAsyncNotifier2ProviderFamily'; - /// See also [CountStreamNotifier2]. - CountStreamNotifier2Provider call( - int a, + /// {@macro riverpod.override_with} + Override overrideWith( + CountAsyncNotifier2 Function( + int args, + ) create, ) { - return CountStreamNotifier2Provider( - a, - ); - } + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as CountAsyncNotifier2Provider; - @override - CountStreamNotifier2Provider getProviderOverride( - covariant CountStreamNotifier2Provider provider, - ) { - return call( - provider.a, + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, ); } - static const Iterable? _dependencies = null; + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + FutureOr Function(Ref ref, CountAsyncNotifier2 notifier, int argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as CountAsyncNotifier2Provider; - @override - Iterable? get dependencies => _dependencies; + final argument = provider.argument as int; - static const Iterable? _allTransitiveDependencies = null; + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; +abstract class _$CountAsyncNotifier2 extends $AsyncNotifier { + late final _$args = ref.$arg as int; + int get a => _$args; + FutureOr build( + int a, + ); + @$internal @override - String? get name => r'myFamilyCountStreamNotifier2ProviderFamily'; + FutureOr runBuild() => build( + _$args, + ); } -/// See also [CountStreamNotifier2]. -class CountStreamNotifier2Provider - extends AutoDisposeStreamNotifierProviderImpl { - /// See also [CountStreamNotifier2]. - CountStreamNotifier2Provider( - int a, - ) : this._internal( - () => CountStreamNotifier2()..a = a, - from: myFamilyCountStreamNotifier2ProviderFamily, +@ProviderFor(CountStreamNotifier2) +const myFamilyCountStreamNotifier2ProviderFamily = + CountStreamNotifier2Family._(); + +final class CountStreamNotifier2Provider + extends $StreamNotifierProvider { + const CountStreamNotifier2Provider._( + {required CountStreamNotifier2Family super.from, + required int super.argument, + super.runNotifierBuildOverride, + CountStreamNotifier2 Function()? create}) + : _createCb = create, + super( + retry: null, name: r'myFamilyCountStreamNotifier2ProviderFamily', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$countStreamNotifier2Hash, - dependencies: CountStreamNotifier2Family._dependencies, - allTransitiveDependencies: - CountStreamNotifier2Family._allTransitiveDependencies, - a: a, + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - CountStreamNotifier2Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.a, - }) : super.internal(); + final CountStreamNotifier2 Function()? _createCb; - final int a; + @override + String debugGetCreateSourceHash() => _$countStreamNotifier2Hash(); @override - Stream runNotifierBuild( - covariant CountStreamNotifier2 notifier, - ) { - return notifier.build( - a, - ); + String toString() { + return r'myFamilyCountStreamNotifier2ProviderFamily' + '' + '($argument)'; } + @$internal @override - Override overrideWith(CountStreamNotifier2 Function() create) { - return ProviderOverride( - origin: this, - override: CountStreamNotifier2Provider._internal( - () => create()..a = a, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - a: a, - ), - ); + CountStreamNotifier2 create() => _createCb?.call() ?? CountStreamNotifier2(); + + @$internal + @override + CountStreamNotifier2Provider $copyWithCreate( + CountStreamNotifier2 Function() create, + ) { + return CountStreamNotifier2Provider._( + argument: argument as int, + from: from! as CountStreamNotifier2Family, + create: create); } + @$internal @override - AutoDisposeStreamNotifierProviderElement - createElement() { - return _CountStreamNotifier2ProviderElement(this); + CountStreamNotifier2Provider $copyWithBuild( + Stream Function( + Ref, + CountStreamNotifier2, + ) build, + ) { + return CountStreamNotifier2Provider._( + argument: argument as int, + from: from! as CountStreamNotifier2Family, + runNotifierBuildOverride: build); } + @$internal + @override + $StreamNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $StreamNotifierProviderElement(this, pointer); + @override bool operator ==(Object other) { - return other is CountStreamNotifier2Provider && other.a == a; + return other is CountStreamNotifier2Provider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, a.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin CountStreamNotifier2Ref on AutoDisposeStreamNotifierProviderRef { - /// The parameter `a` of this provider. - int get a; +String _$countStreamNotifier2Hash() => + r'13be1b7aa32801b33c68f2a228851d2fb6a4a9ee'; + +final class CountStreamNotifier2Family extends Family { + const CountStreamNotifier2Family._() + : super( + retry: null, + name: r'myFamilyCountStreamNotifier2ProviderFamily', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + CountStreamNotifier2Provider call( + int a, + ) => + CountStreamNotifier2Provider._(argument: a, from: this); + + @override + String debugGetCreateSourceHash() => _$countStreamNotifier2Hash(); + + @override + String toString() => r'myFamilyCountStreamNotifier2ProviderFamily'; + + /// {@macro riverpod.override_with} + Override overrideWith( + CountStreamNotifier2 Function( + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as CountStreamNotifier2Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + Stream Function(Ref ref, CountStreamNotifier2 notifier, int argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as CountStreamNotifier2Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } } -class _CountStreamNotifier2ProviderElement - extends AutoDisposeStreamNotifierProviderElement - with CountStreamNotifier2Ref { - _CountStreamNotifier2ProviderElement(super.provider); +abstract class _$CountStreamNotifier2 extends $StreamNotifier { + late final _$args = ref.$arg as int; + int get a => _$args; + Stream build( + int a, + ); + @$internal @override - int get a => (origin as CountStreamNotifier2Provider).a; + Stream runBuild() => build( + _$args, + ); } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/integration/build_yaml/pubspec.yaml b/packages/riverpod_generator/integration/build_yaml/pubspec.yaml index 595226515..c63b70ab6 100644 --- a/packages/riverpod_generator/integration/build_yaml/pubspec.yaml +++ b/packages/riverpod_generator/integration/build_yaml/pubspec.yaml @@ -3,7 +3,7 @@ description: A sample command-line application. publish_to: none environment: - sdk: ">=2.18.4 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: riverpod: @@ -11,6 +11,5 @@ dependencies: dev_dependencies: build_runner: ^2.3.2 - build_verify: ^3.0.0 riverpod_generator: test: ^1.16.0 diff --git a/packages/riverpod_generator/integration/build_yaml/test/build_yaml_test.dart b/packages/riverpod_generator/integration/build_yaml/test/build_yaml_test.dart index 9c375d8a2..115f5b11e 100644 --- a/packages/riverpod_generator/integration/build_yaml/test/build_yaml_test.dart +++ b/packages/riverpod_generator/integration/build_yaml/test/build_yaml_test.dart @@ -1,18 +1,8 @@ -import 'package:build_verify/build_verify.dart'; import 'package:build_yaml/dependencies.dart'; import 'package:build_yaml/main.dart'; import 'package:test/test.dart'; void main() { - test( - 'ensure_build', - () => expectBuildClean( - packageRelativeDirectory: - 'packages/riverpod_generator/integration/build_yaml', - ), - timeout: const Timeout(Duration(minutes: 1)), - ); - test('provider names', () { expect(myCountPod.name, 'myCountPod'); expect(myCountFuturePod.name, 'myCountFuturePod'); diff --git a/packages/riverpod_generator/lib/src/models.dart b/packages/riverpod_generator/lib/src/models.dart index 5f153f01b..bc32caddd 100644 --- a/packages/riverpod_generator/lib/src/models.dart +++ b/packages/riverpod_generator/lib/src/models.dart @@ -22,6 +22,8 @@ class BuildYamlOptions { } extension CaseChangeExtension on String { + String get encoded => replaceAll(r'$', r'\$'); + String get titled { return replaceFirstMapped( RegExp('[a-zA-Z]'), diff --git a/packages/riverpod_generator/lib/src/parse_generator.dart b/packages/riverpod_generator/lib/src/parse_generator.dart index 3c52f8359..3e7e477ed 100644 --- a/packages/riverpod_generator/lib/src/parse_generator.dart +++ b/packages/riverpod_generator/lib/src/parse_generator.dart @@ -20,7 +20,10 @@ abstract class ParserGenerator ) async { final firstAnnotatedElementFromUniqueSource = {}; - for (final annotated in library.annotatedWithExact(typeChecker)) { + for (final annotated in library.annotatedWithExact( + typeChecker, + throwOnUnresolved: false, + )) { firstAnnotatedElementFromUniqueSource.putIfAbsent( annotated.element.source!.uri, () => annotated.element, diff --git a/packages/riverpod_generator/lib/src/riverpod_generator.dart b/packages/riverpod_generator/lib/src/riverpod_generator.dart index 38889e1cf..abbae9ba9 100644 --- a/packages/riverpod_generator/lib/src/riverpod_generator.dart +++ b/packages/riverpod_generator/lib/src/riverpod_generator.dart @@ -1,18 +1,24 @@ import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/token.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:meta/meta.dart'; import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; + // ignore: implementation_imports, safe as we are the one controlling this file import 'package:riverpod_annotation/src/riverpod_annotation.dart'; import 'package:source_gen/source_gen.dart'; import 'models.dart'; import 'parse_generator.dart'; -import 'templates/class_based_provider.dart'; +import 'templates/element.dart'; import 'templates/family.dart'; -import 'templates/functional_provider.dart'; - -const riverpodTypeChecker = TypeChecker.fromRuntime(Riverpod); +import 'templates/hash.dart'; +import 'templates/mutation.dart'; +import 'templates/notifier.dart'; +import 'templates/parameters.dart'; +import 'templates/provider.dart'; +import 'templates/provider_variable.dart'; +import 'type.dart'; String providerDocFor(Element element) { return element.documentationComment == null @@ -20,17 +26,20 @@ String providerDocFor(Element element) { : '${element.documentationComment}\n///\n/// Copied from [${element.name}].'; } -String _hashFn(GeneratorProviderDeclaration provider, String hashName) { - return "String $hashName() => r'${provider.computeProviderHash()}';"; -} - -String _hashFnName(ProviderDeclaration provider) { - return '_\$${provider.providerElement.name.public.lowerFirst}Hash'; -} +String metaAnnotations(NodeList metadata) { + final buffer = StringBuffer(); + for (final annotation in metadata) { + final element = annotation.elementAnnotation; + if (element == null) continue; + if (element.isDeprecated || + element.isVisibleForTesting || + element.isProtected) { + buffer.writeln('$annotation'); + continue; + } + } -String _hashFnIdentifier(String hashFnName) { - return "const bool.fromEnvironment('dart.vm.product') ? " - 'null : $hashFnName'; + return buffer.toString(); } const _defaultProviderNamePrefix = ''; @@ -43,12 +52,8 @@ class RiverpodInvalidGenerationSourceError super.message, { super.todo = '', super.element, - this.astNode, - }); - - final AstNode? astNode; - - // TODO overrride toString to render AST nodes. + AstNode? astNode, + }) : super(node: astNode); } @immutable @@ -60,12 +65,20 @@ class RiverpodGenerator extends ParserGenerator { @override String generateForUnit(List compilationUnits) { - final riverpodResult = ResolvedRiverpodLibraryResult.from(compilationUnits); - return runGenerator(riverpodResult); - } + final buffer = StringBuffer(); + + final errors = []; + final previousErrorReporter = errorReporter; + + try { + errorReporter = errors.add; + _generate(compilationUnits, buffer); + } finally { + errorReporter = previousErrorReporter; + } - String runGenerator(ResolvedRiverpodLibraryResult riverpodResult) { - for (final error in riverpodResult.errors) { + // Running at the end to aggregate all errors. + for (final error in errors) { throw RiverpodInvalidGenerationSourceError( error.message, element: error.targetElement, @@ -73,9 +86,23 @@ class RiverpodGenerator extends ParserGenerator { ); } - final buffer = StringBuffer(); + return buffer.toString(); + } - riverpodResult.visitChildren(_RiverpodGeneratorVisitor(buffer, options)); + void _generate(List units, StringBuffer buffer) { + final visitor = _RiverpodGeneratorVisitor(buffer, options); + for (final unit in units.expand((e) => e.declarations)) { + final provider = unit.provider; + + switch (provider) { + case ClassBasedProviderDeclaration(): + visitor.visitClassBasedProviderDeclaration(provider); + case FunctionalProviderDeclaration(): + visitor.visitFunctionalProviderDeclaration(provider); + default: + continue; + } + } // Only emit the header if we actually generated something if (buffer.isNotEmpty) { @@ -84,126 +111,354 @@ class RiverpodGenerator extends ParserGenerator { // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package '''); } - - return buffer.toString(); } } -class _RiverpodGeneratorVisitor extends RecursiveRiverpodAstVisitor { +class _RiverpodGeneratorVisitor { _RiverpodGeneratorVisitor(this.buffer, this.options); final StringBuffer buffer; final BuildYamlOptions options; String get prefix => options.providerNamePrefix ?? _defaultProviderNamePrefix; + String get familyPrefix => options.providerFamilyNamePrefix ?? prefix; + String get suffix => options.providerNameSuffix ?? _defaultProviderNameSuffix; + String get familySuffix => options.providerFamilyNameSuffix ?? suffix; - var _didEmitHashUtils = false; - void maybeEmitHashUtils() { - if (_didEmitHashUtils) return; + void visitGeneratorProviderDeclaration( + GeneratorProviderDeclaration provider, + ) { + final allTransitiveDependencies = + _computeAllTransitiveDependencies(provider); + + ProviderVariableTemplate(provider, options).run(buffer); + ProviderTemplate( + provider, + options, + allTransitiveDependencies: allTransitiveDependencies, + ).run(buffer); + HashFnTemplate(provider).run(buffer); + FamilyTemplate( + provider, + options, + allTransitiveDependencies: allTransitiveDependencies, + ).run(buffer); + } - _didEmitHashUtils = true; - buffer.write(''' -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); + List? _computeAllTransitiveDependencies( + GeneratorProviderDeclaration provider, + ) { + final dependencies = provider.annotation.dependencyList?.values; + if (dependencies == null) return null; - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } + final allTransitiveDependencies = []; - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} -'''); + Iterable + computeAllTransitiveDependencies( + GeneratorProviderDeclarationElement provider, + ) sync* { + final deps = provider.annotation.dependencies; + if (deps == null) return; + + final uniqueDependencies = {}; + + for (final transitiveDependency in deps) { + if (!uniqueDependencies.add(transitiveDependency)) continue; + yield transitiveDependency; + yield* computeAllTransitiveDependencies(transitiveDependency); + } + } + + final uniqueDependencies = {}; + for (final dependency in dependencies) { + if (!uniqueDependencies.add(dependency.provider)) continue; + + allTransitiveDependencies.add(dependency.provider.providerName(options)); + + final uniqueTransitiveDependencies = + computeAllTransitiveDependencies(dependency.provider) + // Since generated code trims duplicate dependencies, + // we have to trim them back when parsing the dependencies to + // keep the index correct. + .toSet() + .indexed; + + for (final (index, transitiveDependency) + in uniqueTransitiveDependencies) { + if (!uniqueDependencies.add(transitiveDependency)) continue; + + allTransitiveDependencies.add( + '${dependency.provider.providerTypeName}.\$allTransitiveDependencies$index', + ); + } + } + + return allTransitiveDependencies; } - @override void visitClassBasedProviderDeclaration( ClassBasedProviderDeclaration provider, ) { - super.visitClassBasedProviderDeclaration(provider); - - final parameters = provider.buildMethod.parameters?.parameters; - if (parameters == null) return; - - final hashFunctionName = _hashFnName(provider); - final hashFn = _hashFnIdentifier(hashFunctionName); - buffer.write(_hashFn(provider, hashFunctionName)); - - if (parameters.isEmpty) { - final rawProviderName = provider.providerElement.name; - final providerName = - '$prefix${prefix.isEmpty ? rawProviderName.lowerFirst : rawProviderName.titled}$suffix'; - final notifierTypedefName = providerName.startsWith('_') - ? '_\$${provider.providerElement.name.substring(1)}' - : '_\$${provider.providerElement.name}'; - - ClassBasedProviderTemplate( - provider, - options: options, - notifierTypedefName: notifierTypedefName, - hashFn: hashFn, - ).run(buffer); - } else { - final rawProviderName = provider.providerElement.name; - final providerName = - '$prefix${prefix.isEmpty ? rawProviderName.lowerFirst : rawProviderName.titled}$suffix'; - final notifierTypedefName = providerName.startsWith('_') - ? '_\$${provider.providerElement.name.substring(1)}' - : '_\$${provider.providerElement.name}'; - - maybeEmitHashUtils(); - FamilyTemplate.classBased( - provider, - options: options, - notifierTypedefName: notifierTypedefName, - hashFn: hashFn, - ).run(buffer); + visitGeneratorProviderDeclaration(provider); + NotifierTemplate(provider).run(buffer); + ElementTemplate(provider).run(buffer); + for (final mutation in provider.mutations) { + MutationTemplate(mutation, provider).run(buffer); } } - @override void visitFunctionalProviderDeclaration( FunctionalProviderDeclaration provider, ) { - super.visitFunctionalProviderDeclaration(provider); - - final parameters = provider.node.functionExpression.parameters?.parameters; - if (parameters == null) return; - - final hashFunctionName = _hashFnName(provider); - final hashFn = _hashFnIdentifier(hashFunctionName); - buffer.write(_hashFn(provider, hashFunctionName)); - - // Using >1 as functional providers always have at least one parameter: ref - // So a provider is a "family" only if it has parameters besides the ref. - if (parameters.length > 1) { - maybeEmitHashUtils(); - FamilyTemplate.functional( - provider, - options: options, - hashFn: hashFn, - ).run(buffer); + visitGeneratorProviderDeclaration(provider); + } +} + +extension ProviderElementNames on GeneratorProviderDeclarationElement { + String providerName(BuildYamlOptions options) { + final prefix = (isFamily + ? options.providerFamilyNamePrefix + : options.providerNamePrefix) ?? + _defaultProviderNamePrefix; + final suffix = (isFamily + ? options.providerFamilyNameSuffix + : options.providerNameSuffix) ?? + _defaultProviderNameSuffix; + + return '$prefix${prefix.isEmpty ? name.lowerFirst : name.titled}$suffix'; + } + + String get providerTypeName => '${name.titled}Provider'; + + String get familyTypeName => '${name.titled}Family'; + + String dependencies(BuildYamlOptions options) { + var dependencies = annotation.dependencies?.toSet(); + if (dependencies == null && !isScoped) return 'null'; + dependencies ??= {}; + + final buffer = StringBuffer('const '); + buffer.write('['); + + buffer.writeAll( + dependencies.map((e) => e.providerName(options)), + ',', + ); + + buffer.write(']'); + return buffer.toString(); + } + + String allTransitiveDependencies(List? deps) { + var allTransitiveDependencies = deps; + if (deps == null && !isScoped) return 'null'; + allTransitiveDependencies ??= []; + + final buffer = StringBuffer('const '); + if (allTransitiveDependencies.length < 4) { + buffer.write('['); + } else { + buffer.write('{'); + } + + for (var i = 0; i < allTransitiveDependencies.length; i++) { + buffer.write('$providerTypeName.\$allTransitiveDependencies$i,'); + } + + if (allTransitiveDependencies.length < 4) { + buffer.write(']'); } else { - final refName = '${provider.providerElement.name.titled}Ref'; - FunctionalProviderTemplate( - provider, - refName: refName, - options: options, - hashFn: hashFn, - ).run(buffer); + buffer.write('}'); } + + return buffer.toString(); + } +} + +extension ProviderNames on GeneratorProviderDeclaration { + String providerName(BuildYamlOptions options) { + return providerElement.providerName(options); + } + + String get providerTypeName => providerElement.providerTypeName; + + String get familyTypeName => providerElement.familyTypeName; + + String get argumentRecordType { + // Encode the list of parameters into a record. + // We do so only if there are at least two parameters. + switch (parameters) { + case [_]: + return parameters.first.typeDisplayString; + case []: + return 'Never'; + case [...]: + return '(${buildParamDefinitionQuery(parameters, asRecord: true)})'; + } + } + + Iterable get metadata { + return ['@ProviderFor($name)'].followedBy( + node.metadata.where((e) { + if (e.elementAnnotation!.isDoNotStore) return false; + + final valueType = e.elementAnnotation!.computeConstantValue()?.type; + if (valueType == null) return false; + + return !riverpodType.isExactlyType(valueType); + }).map((e) => e.toString()), + ); + } + + String get doc => node.doc; + + String get argumentCast { + final type = argumentRecordType; + if (type == 'Object?') return ''; + return ' as $type'; + } + + String argumentToRecord({String? variableName}) { + switch (parameters) { + case [final p]: + return variableName ?? p.name.toString(); + case [...]: + return '(${buildParamInvocationQuery({ + for (final parameter in parameters) + if (variableName != null) + parameter: '$variableName.${parameter.name}' + else + parameter: parameter.name.toString(), + })})'; + } + } + + String dependencies(BuildYamlOptions options) => + providerElement.dependencies(options); + + String allTransitiveDependencies(List? allTransitiveDependencies) { + return providerElement.allTransitiveDependencies(allTransitiveDependencies); + } + + TypeParameterList? get typeParameters => switch (this) { + final FunctionalProviderDeclaration p => + p.node.functionExpression.typeParameters, + final ClassBasedProviderDeclaration p => p.node.typeParameters + }; + + String generics() => typeParameters.genericUsageDisplayString(); + String genericsDefinition() => + typeParameters.genericDefinitionDisplayString(); + + String notifierBuildType({ + bool withGenericDefinition = false, + bool withArguments = false, + }) { + final genericsDefinition = + withGenericDefinition ? this.genericsDefinition() : ''; + final notifierType = '$name${generics()}'; + + final parameters = withArguments + ? buildParamDefinitionQuery( + this.parameters, + withDefaults: false, + ) + : ''; + + return '$createdTypeDisplayString Function$genericsDefinition(Ref, $notifierType, $parameters)'; + } + + String createType({ + bool withArguments = true, + bool withGenericDefinition = false, + }) { + final generics = this.generics(); + final genericsDefinition = + withGenericDefinition ? this.genericsDefinition() : ''; + + final provider = this; + switch (provider) { + case FunctionalProviderDeclaration(): + final params = withArguments + ? buildParamDefinitionQuery( + parameters, + withDefaults: false, + ) + : ''; + + return '${provider.createdTypeDisplayString} Function$genericsDefinition(Ref ref, $params)'; + case ClassBasedProviderDeclaration(): + return '${provider.name}$generics Function$genericsDefinition()'; + } + } + + String get generatedElementName => '_\$${providerElement.name.public}Element'; + + String get internalElementName => switch (this) { + ClassBasedProviderDeclaration() => switch (createdType) { + SupportedCreatedType.future => r'$AsyncNotifierProviderElement', + SupportedCreatedType.stream => r'$StreamNotifierProviderElement', + SupportedCreatedType.value => r'$NotifierProviderElement', + }, + FunctionalProviderDeclaration() => switch (createdType) { + SupportedCreatedType.future => r'$FutureProviderElement', + SupportedCreatedType.stream => r'$StreamProviderElement', + SupportedCreatedType.value => r'$ProviderElement', + }, + }; + + String get hashFnName => '_\$${providerElement.name.public.lowerFirst}Hash'; + + List get parameters { + final provider = this; + switch (provider) { + case FunctionalProviderDeclaration(): + return provider.node.functionExpression.parameters!.parameters + .skip(1) + .toList(); + case ClassBasedProviderDeclaration(): + return provider.buildMethod.parameters!.parameters.toList(); + } + } +} + +extension TypeX on TypeParameterList? { + String genericDefinitionDisplayString() { + return this?.toSource() ?? ''; + } + + String genericUsageDisplayString() { + if (this == null) { + return ''; + } + + return '<${this!.typeParameters.map((e) => e.name.lexeme).join(', ')}>'; + } +} + +extension ParameterDoc on AstNode { + String get doc { + final builder = StringBuffer(); + final that = this; + + switch (that) { + case AnnotatedNode(): + for (var token = that.documentationComment?.beginToken; + token != null; + token = token.next) { + builder.writeln(token); + } + case _: + for (Token? token = beginToken.precedingComments; + token != null; + token = token.next) { + builder.writeln(token); + } + } + + return builder.toString(); } } diff --git a/packages/riverpod_generator/lib/src/templates/class_based_provider.dart b/packages/riverpod_generator/lib/src/templates/class_based_provider.dart deleted file mode 100644 index 6cab85b04..000000000 --- a/packages/riverpod_generator/lib/src/templates/class_based_provider.dart +++ /dev/null @@ -1,152 +0,0 @@ -import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; - -import '../models.dart'; -import '../riverpod_generator.dart'; -import '../validation.dart'; -import 'family.dart'; -import 'template.dart'; - -String providerNameFor( - ProviderDeclarationElement provider, - BuildYamlOptions options, -) { - final prefix = options.providerNamePrefix ?? ''; - final rawProviderName = provider.name; - final suffix = options.providerNameSuffix ?? 'Provider'; - return '$prefix${prefix.isEmpty ? rawProviderName.lowerFirst : rawProviderName.titled}$suffix'; -} - -String? serializeDependencies( - RiverpodAnnotationElement annotation, - BuildYamlOptions options, -) { - final dependencies = annotation.dependencies; - if (dependencies == null) return 'null'; - - final buffer = StringBuffer( - '${dependencies.isEmpty ? 'const ' : ''}', - ); - // Use list vs set based on the number of dependencies to optimize "contains" call - if (dependencies.length < 4) { - buffer.write('['); - } else { - buffer.write('{'); - } - - buffer.writeAll( - dependencies.map((e) => e.providerNameByElementFor(options)), - ',', - ); - - if (dependencies.length < 4) { - buffer.write(']'); - } else { - buffer.write('}'); - } - return buffer.toString(); -} - -String? serializeAllTransitiveDependencies( - RiverpodAnnotationElement annotation, - BuildYamlOptions options, -) { - // Not optimizing based off "allTransitiveDependencies" yet due to https://github.com/dart-lang/language/issues/3037 - // This could be worked around by having the "Provider" type expose - // the transitive dependencies. - // But this assumes that all providers have their custom Provider class. - final dependencies = annotation.dependencies; - if (dependencies == null) return 'null'; - - final buffer = StringBuffer( - '${dependencies.isEmpty ? 'const ' : ''}', - ); - - buffer.write('{'); - buffer.writeAll( - dependencies - .map((e) => e.providerNameByElementFor(options)) - .map((e) => '$e, ...?$e.allTransitiveDependencies'), - ',', - ); - buffer.write('}'); - - return buffer.toString(); -} - -class ClassBasedProviderTemplate extends Template { - ClassBasedProviderTemplate( - this.provider, { - required this.notifierTypedefName, - required this.hashFn, - required this.options, - }) { - if (provider.buildMethod.parameters!.parameters.isNotEmpty) { - throw ArgumentError.value( - provider.buildMethod.parameters?.toSource(), - 'provider', - 'Expected a class-based provider with no parameter', - ); - } - - validateClassBasedProvider(provider); - } - - final ClassBasedProviderDeclaration provider; - final String notifierTypedefName; - final String hashFn; - final BuildYamlOptions options; - - @override - void run(StringBuffer buffer) { - var leading = ''; - if (!provider.annotation.element.keepAlive) { - leading = 'AutoDispose'; - } - - var notifierBaseType = '${leading}Notifier'; - var providerType = '${leading}NotifierProvider'; - - final providerName = providerNameFor(provider.providerElement, options); - final returnType = provider.createdTypeNode?.type; - if (returnType != null && !returnType.isRaw) { - if ((returnType.isDartAsyncFutureOr) || (returnType.isDartAsyncFuture)) { - notifierBaseType = '${leading}AsyncNotifier'; - providerType = '${leading}AsyncNotifierProvider'; - } else if (returnType.isDartAsyncStream) { - notifierBaseType = '${leading}StreamNotifier'; - providerType = '${leading}StreamNotifierProvider'; - } - } - - buffer.write(''' -${providerDocFor(provider.providerElement.element)} -@ProviderFor(${provider.name}) -final $providerName = $providerType<${provider.name}, ${provider.valueTypeDisplayString}>.internal( - ${provider.providerElement.name}.new, - name: r'$providerName', - debugGetCreateSourceHash: $hashFn, - dependencies: ${serializeDependencies(provider.providerElement.annotation, options)}, - allTransitiveDependencies: ${serializeAllTransitiveDependencies(provider.providerElement.annotation, options)}, -); - -typedef $notifierTypedefName = $notifierBaseType<${provider.valueTypeDisplayString}>; -'''); - } -} - -extension on GeneratorProviderDeclarationElement { - String providerNameByElementFor(BuildYamlOptions options) { - final e = this; - if (e is ClassBasedProviderDeclarationElement) { - return e.buildMethod.parameters.isNotEmpty - ? providerFamilyNameFor(e, options) - : providerNameFor(e, options); - } - if (e is FunctionalProviderDeclarationElement) { - return e.element.parameters.length > 1 - ? providerFamilyNameFor(e, options) - : providerNameFor(e, options); - } - return providerNameFor(e, options); - } -} diff --git a/packages/riverpod_generator/lib/src/templates/element.dart b/packages/riverpod_generator/lib/src/templates/element.dart new file mode 100644 index 000000000..37680bd65 --- /dev/null +++ b/packages/riverpod_generator/lib/src/templates/element.dart @@ -0,0 +1,90 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; + +import '../models.dart'; +import '../riverpod_generator.dart'; +import 'template.dart'; + +extension MutationX on Mutation { + String get elementFieldName => '_\$${name.lowerFirst}'; + String get generatedMutationInterfaceName => + '${(node.parent! as ClassDeclaration).name}\$${name.titled}'; + String get generatedMutationImplName => + '_\$${(node.parent! as ClassDeclaration).name}\$${name.titled}'; +} + +class ElementTemplate extends Template { + ElementTemplate(this.provider); + + final ClassBasedProviderDeclaration provider; + late final _generics = provider.generics(); + late final _genericsDefinition = provider.genericsDefinition(); + + @override + void run(StringBuffer buffer) { + if (provider.mutations.isEmpty) return; + + buffer.write(''' +class ${provider.generatedElementName}$_genericsDefinition extends ${provider.internalElementName}<${provider.name}$_generics, ${provider.valueTypeDisplayString}> { + ${provider.generatedElementName}(super.provider, super.pointer) { +'''); + + _constructorBody(buffer); + buffer.writeln('}'); + + _fields(buffer); + _overrideMount(buffer); + _overrideVisitChildren(buffer); + + buffer.writeln('}'); + } + + void _constructorBody(StringBuffer buffer) { + for (final mutation in provider.mutations) { + buffer.writeln( + ' ${mutation.elementFieldName}.result = \$Result.data(${mutation.generatedMutationImplName}(this));', + ); + } + } + + void _overrideVisitChildren(StringBuffer buffer) { + buffer.writeln(r''' + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); +'''); + + for (final mutation in provider.mutations) { + buffer.writeln(' listenableVisitor(${mutation.elementFieldName});'); + } + + buffer.write(''' + } + '''); + } + + void _fields(StringBuffer buffer) { + for (final mutation in provider.mutations) { + buffer.writeln( + ' final ${mutation.elementFieldName} = \$ElementLense<${mutation.generatedMutationImplName}>();', + ); + } + } + + void _overrideMount(StringBuffer buffer) { + buffer.write(''' + @override + void mount() { + super.mount(); +'''); + for (final mutation in provider.mutations) { + buffer.writeln( + ' ${mutation.elementFieldName}.result!.stateOrNull!.reset();', + ); + } + buffer.writeln(''' + }'''); + } +} diff --git a/packages/riverpod_generator/lib/src/templates/family.dart b/packages/riverpod_generator/lib/src/templates/family.dart index fd1c5a482..14145855d 100644 --- a/packages/riverpod_generator/lib/src/templates/family.dart +++ b/packages/riverpod_generator/lib/src/templates/family.dart @@ -1,367 +1,227 @@ -import 'package:analyzer/dart/element/element.dart'; -import 'package:collection/collection.dart'; import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; import '../models.dart'; import '../riverpod_generator.dart'; -import '../validation.dart'; -import 'class_based_provider.dart'; import 'parameters.dart'; import 'template.dart'; -String providerFamilyNameFor( - ProviderDeclarationElement provider, - BuildYamlOptions options, -) { - final prefix = - options.providerFamilyNamePrefix ?? options.providerNamePrefix ?? ''; - final rawProviderName = provider.name; - final suffix = options.providerFamilyNameSuffix ?? - options.providerNameSuffix ?? - 'Provider'; - return '$prefix${prefix.isEmpty ? rawProviderName.lowerFirst : rawProviderName.titled}$suffix'; -} - class FamilyTemplate extends Template { - FamilyTemplate._( - this.provider, { - required this.options, - required this.parameters, - required this.providerType, - required this.refType, - required this.elementType, - required this.providerGenerics, - required this.providerCreate, - required this.parametersPassThrough, - required this.hashFn, - this.other = '', - this.providerOther = '', - }) { - if (parameters.isEmpty) { - throw ArgumentError.value( - parameters, - 'provider', - 'Expected a provider with parameters', - ); - } - } - - factory FamilyTemplate.functional( - FunctionalProviderDeclaration provider, { - required String hashFn, - required BuildYamlOptions options, - }) { - var leading = ''; - if (!provider.annotation.element.keepAlive) { - leading = 'AutoDispose'; - } - - var providerType = '${leading}Provider'; - var refType = '${leading}ProviderRef'; - var elementType = '${leading}ProviderElement'; - var createdType = provider.createdTypeDisplayString; - - final returnType = provider.createdTypeNode?.type; - if (returnType != null && !returnType.isRaw) { - if (returnType.isDartAsyncFutureOr || returnType.isDartAsyncFuture) { - providerType = '${leading}FutureProvider'; - refType = '${leading}FutureProviderRef'; - elementType = '${leading}FutureProviderElement'; - // Always use FutureOr in overrideWith as return value - // or otherwise we get a compilation error. - createdType = 'FutureOr<${provider.valueTypeDisplayString}>'; - } else if (returnType.isDartAsyncStream) { - providerType = '${leading}StreamProvider'; - refType = '${leading}StreamProviderRef'; - elementType = '${leading}StreamProviderElement'; - } - } + FamilyTemplate( + this.provider, + this.options, { + required this.allTransitiveDependencies, + }); - final parameters = - provider.node.functionExpression.parameters!.parameterElements - // ignore: deprecated_member_use, stuck with SDK >=2.x.0 for now - .whereNotNull() - .skip(1) - .toList(); + final GeneratorProviderDeclaration provider; + final BuildYamlOptions options; + final List? allTransitiveDependencies; - final parametersPassThrough = buildParamInvocationQuery({ - for (final parameter in parameters) parameter: parameter.name, - }); + late final _argumentRecordType = provider.argumentRecordType; - return FamilyTemplate._( - provider, - options: options, - parameters: parameters, - hashFn: hashFn, - elementType: elementType, - refType: refType, - providerGenerics: '<${provider.valueTypeDisplayString}>', - providerCreate: - '(ref) => ${provider.name}(ref as ${provider._refImplName}, $parametersPassThrough)', - providerType: providerType, - parametersPassThrough: parametersPassThrough, - providerOther: ''' + late final _generics = provider.generics(); + late final _genericsDefinition = provider.genericsDefinition(); + late final _parameterDefinition = + buildParamDefinitionQuery(provider.parameters); + late final _notifierType = '${provider.name}$_generics'; + late final _argumentCast = provider.argumentCast; @override - Override overrideWith( - $createdType Function(${provider._refImplName} provider) create, - ) { - return ProviderOverride( - origin: this, - override: ${provider._providerImplName}._internal( - (ref) => create(ref as ${provider._refImplName}), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, -${parameters.map((e) => ' ${e.name}: ${e.name},\n').join()} - ), - ); - } -''', - ); - } - - factory FamilyTemplate.classBased( - ClassBasedProviderDeclaration provider, { - required String notifierTypedefName, - required String hashFn, - required BuildYamlOptions options, - }) { - validateClassBasedProvider(provider); - - var leading = ''; - if (!provider.annotation.element.keepAlive) { - leading = 'AutoDispose'; - } - - var providerType = '${leading}NotifierProviderImpl'; - var refType = '${leading}NotifierProviderRef'; - var notifierBaseType = 'Buildless${leading}Notifier'; - var elementType = '${leading}NotifierProviderElement'; - - final returnType = provider.createdTypeNode?.type; - if (returnType != null && !returnType.isRaw) { - if (returnType.isDartAsyncFutureOr || returnType.isDartAsyncFuture) { - providerType = '${leading}AsyncNotifierProviderImpl'; - refType = '${leading}AsyncNotifierProviderRef'; - notifierBaseType = 'Buildless${leading}AsyncNotifier'; - elementType = '${leading}AsyncNotifierProviderElement'; - } else if (returnType.isDartAsyncStream) { - providerType = '${leading}StreamNotifierProviderImpl'; - refType = '${leading}StreamNotifierProviderRef'; - notifierBaseType = 'Buildless${leading}StreamNotifier'; - elementType = '${leading}StreamNotifierProviderElement'; - } - } - - final parameters = provider.buildMethod.parameters!.parameterElements - // ignore: deprecated_member_use, stuck with SDK >=2.x.0 for now - .whereNotNull() - .toList(); - final parameterDefinition = buildParamDefinitionQuery(parameters); - final cascadePropertyInit = - parameters.map((e) => '..${e.name} = ${e.name}').join('\n'); + void run(StringBuffer buffer) { + if (!provider.providerElement.isFamily) return; + + final topLevelBuffer = StringBuffer(); + + final parametersPassThrough = provider.argumentToRecord(); + final argument = + provider.parameters.isEmpty ? '' : 'argument: $parametersPassThrough,'; + + buffer.writeln(''' +${provider.doc} final class ${provider.familyTypeName} extends Family { + const ${provider.familyTypeName}._() + : super( + retry: ${provider.annotation.retryNode?.name ?? 'null'}, + name: r'${provider.providerName(options)}', + dependencies: ${provider.dependencies(options)}, + allTransitiveDependencies: ${provider.allTransitiveDependencies(allTransitiveDependencies)}, + isAutoDispose: ${provider.providerElement.isAutoDispose}, + ); - final parametersPassThrough = buildParamInvocationQuery({ - for (final parameter in parameters) parameter: parameter.name, - }); + ${provider.doc} ${provider.providerTypeName}$_generics call$_genericsDefinition($_parameterDefinition) + => ${provider.providerTypeName}$_generics._( + $argument + from: this + ); - return FamilyTemplate._( - provider, - options: options, - parameters: parameters, - hashFn: hashFn, - elementType: elementType, - refType: refType, - providerGenerics: - '<${provider.name}, ${provider.valueTypeDisplayString}>', - providerType: providerType, - providerCreate: '() => ${provider.name}()$cascadePropertyInit', - parametersPassThrough: parametersPassThrough, - other: ''' -abstract class $notifierTypedefName extends $notifierBaseType<${provider.valueTypeDisplayString}> { - ${parameters.map((e) => 'late final ${e.type} ${e.name};').join('\n')} - - ${provider.createdTypeDisplayString} build($parameterDefinition); -} -''', - providerOther: ''' @override - ${provider.createdTypeDisplayString} runNotifierBuild( - covariant ${provider.name} notifier, - ) { - return notifier.build($parametersPassThrough); - } - + String debugGetCreateSourceHash() => ${provider.hashFnName}(); + @override - Override overrideWith(${provider.name} Function() create) { - return ProviderOverride( - origin: this, - override: ${provider._providerImplName}._internal( - () => create()$cascadePropertyInit, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, -${parameters.map((e) => ' ${e.name}: ${e.name},\n').join()} - ), - ); - } -''', - ); - } + String toString() => r'${provider.providerName(options)}'; +'''); - final GeneratorProviderDeclaration provider; - final List parameters; - final BuildYamlOptions options; - final String refType; - final String elementType; - final String providerType; - final String providerGenerics; - final String providerCreate; - final String other; - final String providerOther; - final String parametersPassThrough; - final String hashFn; + _writeOverrides(buffer, topLevelBuffer: topLevelBuffer); - @override - void run(StringBuffer buffer) { - final providerTypeNameImpl = provider._providerImplName; - final refNameImpl = provider._refImplName; - final elementNameImpl = '_${providerTypeNameImpl.public}Element'; - final familyName = '${provider.providerElement.name.titled}Family'; - - final parameterDefinition = buildParamDefinitionQuery(parameters); - final parameterProviderPassThrough = buildParamInvocationQuery({ - for (final parameter in parameters) - parameter: 'provider.${parameter.name}', - }); - - final docs = providerDocFor(provider.providerElement.element); - final providerName = - providerFamilyNameFor(provider.providerElement, options); - - final dependenciesKeyword = - provider.providerElement.annotation.dependencies == null - ? 'const Iterable?' - : 'final Iterable'; - - buffer.write(''' -$other - -$docs -@ProviderFor(${provider.name}) -const $providerName = $familyName(); - -$docs -class $familyName extends Family<${provider.exposedTypeDisplayString}> { - $docs - const $familyName(); - - $docs - $providerTypeNameImpl call($parameterDefinition) { - return $providerTypeNameImpl($parametersPassThrough); - } + buffer.writeln('}'); - @override - $providerTypeNameImpl getProviderOverride( - covariant $providerTypeNameImpl provider, - ) { - return call($parameterProviderPassThrough); + buffer.write(topLevelBuffer); } - static $dependenciesKeyword _dependencies = ${serializeDependencies(provider.providerElement.annotation, options)}; - - @override - Iterable? get dependencies => _dependencies; - - static $dependenciesKeyword _allTransitiveDependencies = ${serializeAllTransitiveDependencies(provider.providerElement.annotation, options)}; + void _writeOverrides( + StringBuffer buffer, { + required StringBuffer topLevelBuffer, + }) { + // overrideWith + _writeOverrideWith( + buffer, + topLevelBuffer: topLevelBuffer, + ); - @override - Iterable? get allTransitiveDependencies => _allTransitiveDependencies; + // overrideWithBuild + final provider = this.provider; + if (provider is ClassBasedProviderDeclaration) { + _writeOverrideWithBuild( + buffer, + provider, + topLevelBuffer: topLevelBuffer, + ); + } + } - @override - String? get name => r'$providerName'; -} + void _writeOverrideWith( + StringBuffer buffer, { + required StringBuffer topLevelBuffer, + }) { + final createType = switch (provider) { + FunctionalProviderDeclaration(parameters: [_, ...]) => + '${provider.createdTypeDisplayString} Function$_genericsDefinition(Ref ref, $_argumentRecordType args,)', + FunctionalProviderDeclaration(parameters: []) => + '${provider.createdTypeDisplayString} Function$_genericsDefinition(Ref ref)', + ClassBasedProviderDeclaration(parameters: [_, ...]) => + '$_notifierType Function$_genericsDefinition($_argumentRecordType args,)', + ClassBasedProviderDeclaration() => + '$_notifierType Function$_genericsDefinition()', + }; + + buffer.writeln(''' +/// {@macro riverpod.override_with} +Override overrideWith($createType create,) { + return \$FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ${provider.providerTypeName}; +'''); -$docs -class $providerTypeNameImpl extends $providerType$providerGenerics { - $docs - $providerTypeNameImpl($parameterDefinition) : this._internal( - $providerCreate, - from: $providerName, - name: r'$providerName', - debugGetCreateSourceHash: $hashFn, - dependencies: $familyName._dependencies, - allTransitiveDependencies: $familyName._allTransitiveDependencies, - ${parameters.map((e) => '${e.name}: ${e.name},\n').join()} + switch (( + hasParameters: provider.parameters.isNotEmpty, + hasGenerics: provider.typeParameters?.typeParameters.isNotEmpty ?? false, + provider, + )) { + case (hasParameters: false, hasGenerics: false, _): + buffer.writeln( + r'return provider.$copyWithCreate(create).$createElement(pointer);', + ); + case (hasParameters: true, hasGenerics: false, _): + buffer.writeln(''' + final argument = provider.argument$_argumentCast; + + return provider.\$copyWithCreate(${switch (provider) { + FunctionalProviderDeclaration() => '(ref) => create(ref, argument)', + ClassBasedProviderDeclaration() => '() => create(argument)', + }}).\$createElement(pointer); + '''); + + case (hasParameters: false, hasGenerics: true, _): + buffer.writeln( + r'return provider._copyWithCreate(create).$createElement(pointer);', ); - $providerTypeNameImpl._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - ${buildParamDefinitionQuery( - parameters, - asThisParameter: true, - writeBrackets: false, - asRequiredNamed: true, - )} - }) : super.internal(); - -${parameters.map((e) => 'final ${e.type.getDisplayString()} ${e.name};').join()} - -$providerOther - - @override - $elementType$providerGenerics createElement() { - return $elementNameImpl(this); - } + case ( + hasParameters: true, + hasGenerics: true, + FunctionalProviderDeclaration() + ): + buffer.writeln(''' + return provider._copyWithCreate($_genericsDefinition(ref, $_parameterDefinition) { + return create(ref, ${provider.argumentToRecord()}); + }).\$createElement(pointer); + '''); + case ( + hasParameters: true, + hasGenerics: true, + ClassBasedProviderDeclaration() + ): + buffer.writeln(''' + return provider._copyWithCreate($_genericsDefinition() { + final argument = provider.argument$_argumentCast; + + return create(argument); + }).\$createElement(pointer); + '''); + } - @override - bool operator ==(Object other) { - return ${[ - 'other is $providerTypeNameImpl', - ...parameters.map((e) => 'other.${e.name} == ${e.name}'), - ].join(' && ')}; + buffer.writeln(''' + }, + ); +}'''); } - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); -${parameters.map((e) => 'hash = _SystemHash.combine(hash, ${e.name}.hashCode);').join()} + void _writeOverrideWithBuild( + StringBuffer buffer, + ClassBasedProviderDeclaration provider, { + required StringBuffer topLevelBuffer, + }) { + final runNotifierBuildType = ''' +${provider.createdTypeDisplayString} Function$_genericsDefinition( + Ref ref, + $_notifierType notifier + ${switch (provider.parameters) { + [] => '', + [_, ...] => ', $_argumentRecordType argument', + }} +)'''; + + buffer.writeln(''' +/// {@macro riverpod.override_with_build} +Override overrideWithBuild($runNotifierBuildType build,) { + return \$FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ${provider.providerTypeName}; +'''); - return _SystemHash.finish(hash); - } -} + switch (( + hasParameters: provider.parameters.isNotEmpty, + hasGenerics: provider.typeParameters?.typeParameters.isNotEmpty ?? false, + )) { + case (hasParameters: false, hasGenerics: false): + buffer.writeln( + r'return provider.$copyWithBuild(build).$createElement(pointer);', + ); + case (hasParameters: true, hasGenerics: false): + buffer.writeln(''' + final argument = provider.argument$_argumentCast; -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin $refNameImpl on $refType<${provider.valueTypeDisplayString}> { - ${parameters.map((e) { - return ''' -/// The parameter `${e.name}` of this provider. -${e.type} get ${e.name};'''; - }).join()} -} + return provider.\$copyWithBuild((ref, notifier) => build(ref, notifier, argument)).\$createElement(pointer); + '''); -class $elementNameImpl extends $elementType$providerGenerics with $refNameImpl { - $elementNameImpl(super.provider); + case (hasParameters: false, hasGenerics: true): + buffer.writeln( + r'return provider._copyWithBuild(build).$createElement(pointer);', + ); -${parameters.map((e) => '@override ${e.type} get ${e.name} => (origin as $providerTypeNameImpl).${e.name};').join()} -} -'''); - } -} + case (hasParameters: true, hasGenerics: true): + buffer.writeln(''' + return provider._copyWithBuild($_genericsDefinition(ref, notifier) { + final argument = provider.argument$_argumentCast; -extension on GeneratorProviderDeclaration { - String get _providerImplName => '${providerElement.name.titled}Provider'; + return build(ref, notifier, argument); + }).\$createElement(pointer); + '''); + } - String get _refImplName => '${providerElement.name.titled}Ref'; + buffer.writeln(''' + }, + ); +}'''); + } } diff --git a/packages/riverpod_generator/lib/src/templates/functional_provider.dart b/packages/riverpod_generator/lib/src/templates/functional_provider.dart deleted file mode 100644 index 3cee989c6..000000000 --- a/packages/riverpod_generator/lib/src/templates/functional_provider.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; -import '../models.dart'; -import '../riverpod_generator.dart'; -import 'class_based_provider.dart'; -import 'template.dart'; - -class FunctionalProviderTemplate extends Template { - FunctionalProviderTemplate( - this.provider, { - required this.refName, - required this.hashFn, - required this.options, - }) { - if (provider.node.functionExpression.parameters!.parameters.length > 1) { - throw ArgumentError.value( - provider.node.functionExpression.parameters?.toSource(), - 'provider', - 'Expected a functional provider with no parameter', - ); - } - } - - final FunctionalProviderDeclaration provider; - final String refName; - final String hashFn; - final BuildYamlOptions options; - - @override - void run(StringBuffer buffer) { - var leading = ''; - - if (!provider.annotation.element.keepAlive) { - leading = 'AutoDispose'; - } - - var providerType = '${leading}Provider'; - - final returnType = provider.createdTypeNode?.type; - if (returnType != null && !returnType.isRaw) { - if ((returnType.isDartAsyncFutureOr) || (returnType.isDartAsyncFuture)) { - providerType = '${leading}FutureProvider'; - } else if (returnType.isDartAsyncStream) { - providerType = '${leading}StreamProvider'; - } - } - - final providerName = providerNameFor(provider.providerElement, options); - - final createFn = provider.node.externalKeyword == null - ? provider.providerElement.name - : ''' -(_) => throw UnsupportedError( - 'The provider "$providerName" is expected to get overridden/scoped, ' - 'but was accessed without an override.', -) -'''; - - buffer.write(''' -${providerDocFor(provider.providerElement.element)} -@ProviderFor(${provider.name}) -final $providerName = $providerType<${provider.valueTypeDisplayString}>.internal( - $createFn, - name: r'$providerName', - debugGetCreateSourceHash: $hashFn, - dependencies: ${serializeDependencies(provider.providerElement.annotation, options)}, - allTransitiveDependencies: ${serializeAllTransitiveDependencies(provider.providerElement.annotation, options)}, -); - - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef $refName = ${providerType}Ref<${provider.valueTypeDisplayString}>; -'''); - } -} diff --git a/packages/riverpod_generator/lib/src/templates/hash.dart b/packages/riverpod_generator/lib/src/templates/hash.dart new file mode 100644 index 000000000..71a5946d9 --- /dev/null +++ b/packages/riverpod_generator/lib/src/templates/hash.dart @@ -0,0 +1,17 @@ +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; + +import '../riverpod_generator.dart'; +import 'template.dart'; + +class HashFnTemplate extends Template { + HashFnTemplate(this.provider); + + final GeneratorProviderDeclaration provider; + + @override + void run(StringBuffer buffer) { + buffer.writeln( + "String ${provider.hashFnName}() => r'${provider.computeProviderHash()}';\n", + ); + } +} diff --git a/packages/riverpod_generator/lib/src/templates/mutation.dart b/packages/riverpod_generator/lib/src/templates/mutation.dart new file mode 100644 index 000000000..902569b52 --- /dev/null +++ b/packages/riverpod_generator/lib/src/templates/mutation.dart @@ -0,0 +1,100 @@ +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; + +import '../riverpod_generator.dart'; +import '../type.dart'; +import 'element.dart'; +import 'parameters.dart'; +import 'template.dart'; + +class MutationTemplate extends Template { + MutationTemplate(this.mutation, this.provider); + + final Mutation mutation; + final ClassBasedProviderDeclaration provider; + + @override + void run(StringBuffer buffer) { + final parametersPassThrough = buildParamInvocationQuery({ + for (final parameter in mutation.node.parameters!.parameters) + parameter: parameter.name.toString(), + }); + + final mutationBase = switch (provider.createdType) { + SupportedCreatedType.future => r'$AsyncMutationBase', + SupportedCreatedType.stream => r'$AsyncMutationBase', + SupportedCreatedType.value => r'$SyncMutationBase', + }; + + buffer.writeln(''' +sealed class ${mutation.generatedMutationInterfaceName} extends MutationBase<${provider.valueTypeDisplayString}> { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [${provider.name}.${mutation.name}] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// Lastly, if the method completes without throwing, the Notifier's state + /// will be updated with the new value. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future<${provider.valueTypeDisplayString}> call${mutation.node.typeParameters.genericDefinitionDisplayString()}${mutation.node.parameters}; +} + +final class ${mutation.generatedMutationImplName} + extends $mutationBase<${provider.valueTypeDisplayString}, ${mutation.generatedMutationImplName}, ${provider.name}> + implements ${mutation.generatedMutationInterfaceName} { + ${mutation.generatedMutationImplName}(this.element, {super.state, super.key}); + + @override + final ${provider.generatedElementName} element; + + @override + \$ElementLense<${mutation.generatedMutationImplName}> get listenable => element.${mutation.elementFieldName}; + + @override + Future<${provider.valueTypeDisplayString}> call${mutation.node.typeParameters.genericDefinitionDisplayString()}${mutation.node.parameters} { + return mutateAsync( + ${_mutationInvocation()}, + (\$notifier) => \$notifier.${mutation.name}${mutation.node.typeParameters.genericUsageDisplayString()}($parametersPassThrough), + ); + } + + @override + ${mutation.generatedMutationImplName} copyWith(MutationState<${provider.valueTypeDisplayString}> state, {Object? key}) => ${mutation.generatedMutationImplName}(element, state: state, key: key); +} +'''); + } + + String _mutationInvocation() { + final positional = mutation.node.parameters?.parameters + .where((e) => e.isPositional) + .map((e) => e.name!.lexeme) + .toList() ?? + const []; + Map? named = Map.fromEntries( + mutation.node.parameters?.parameters + .where((e) => e.isNamed) + .map((e) => MapEntry('#${e.name!.lexeme}', e.name!.lexeme)) ?? + const [], + ); + + if (named.isEmpty) named = null; + + final typeParams = mutation.node.typeParameters?.typeParameters + .map((e) => e.name.lexeme) + .toList() ?? + const []; + if (typeParams.isEmpty) { + return 'Invocation.method(#${mutation.name}, $positional, ${named ?? ''})'; + } + + return 'Invocation.genericMethod(#${mutation.name}, $typeParams, $positional, ${named ?? ''})'; + } +} diff --git a/packages/riverpod_generator/lib/src/templates/notifier.dart b/packages/riverpod_generator/lib/src/templates/notifier.dart new file mode 100644 index 000000000..1ae7569c0 --- /dev/null +++ b/packages/riverpod_generator/lib/src/templates/notifier.dart @@ -0,0 +1,81 @@ +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; + +import '../models.dart'; +import '../riverpod_generator.dart'; +import '../type.dart'; +import 'parameters.dart'; +import 'template.dart'; + +class NotifierTemplate extends Template { + NotifierTemplate(this.provider); + + final ClassBasedProviderDeclaration provider; + + @override + void run(StringBuffer buffer) { + final notifierBaseName = '_\$${provider.name.lexeme.public}'; + final genericsDefinition = provider.genericsDefinition(); + + final baseClass = switch (provider.createdType) { + SupportedCreatedType.future => + '\$AsyncNotifier<${provider.valueTypeDisplayString}>', + SupportedCreatedType.stream => + '\$StreamNotifier<${provider.valueTypeDisplayString}>', + SupportedCreatedType.value => + '\$Notifier<${provider.valueTypeDisplayString}>', + }; + + final paramsPassThrough = buildParamInvocationQuery({ + for (final (index, parameter) in provider.parameters.indexed) + if (provider.parameters.length == 1) + parameter: r'_$args' + else if (parameter.isPositional) + parameter: '_\$args.\$${index + 1}' + else + parameter: '_\$args.${parameter.name!.lexeme}', + }); + + final _$args = 'late final _\$args = ref.\$arg${provider.argumentCast};'; + + var paramOffset = 0; + final parametersAsFields = provider.parameters.map( + (p) { + final metadata = p.metadata.isNotEmpty + ? '${p.metadata.map((e) => e.toSource()).join(' ')} ' + : ''; + return '${p.doc} $metadata ${p.typeDisplayString} get ${p.name!.lexeme} => ${switch (provider.parameters) { + [_] => r'_$args;', + _ => + '_\$args.${p.isPositional ? '\$${++paramOffset}' : p.name!.lexeme};', + }}'; + }, + ).join(); + + buffer.writeln(''' +abstract class $notifierBaseName$genericsDefinition extends $baseClass { + ${provider.parameters.isNotEmpty ? _$args : ''} + $parametersAsFields +'''); + + _writeBuild(buffer); + + buffer.writeln(''' + @\$internal + @override + ${provider.createdTypeDisplayString} runBuild() => build($paramsPassThrough); +} +'''); + } + + void _writeBuild(StringBuffer buffer) { + final buildParams = buildParamDefinitionQuery(provider.parameters); + + buffer.write('${provider.createdTypeDisplayString} build($buildParams)'); + + if (provider.buildMethod.isAbstract) { + buffer.writeln('=> throw MissingScopeException(ref);'); + } else { + buffer.writeln(';'); + } + } +} diff --git a/packages/riverpod_generator/lib/src/templates/parameters.dart b/packages/riverpod_generator/lib/src/templates/parameters.dart index 55a302b8a..7710322e0 100644 --- a/packages/riverpod_generator/lib/src/templates/parameters.dart +++ b/packages/riverpod_generator/lib/src/templates/parameters.dart @@ -1,21 +1,23 @@ -import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/ast/ast.dart'; String buildParamDefinitionQuery( - List parameters, { + List parameters, { bool asThisParameter = false, bool asSuperParameter = false, bool writeBrackets = true, bool asRequiredNamed = false, + bool asRecord = false, + bool withDefaults = true, }) { assert( !asThisParameter || !asSuperParameter, 'Cannot enable both asThisParameter and asSuperParameter', ); - final requiredPositionals = parameters + final requiredPositional = parameters .where((element) => element.isRequiredPositional && !asRequiredNamed) .toList(); - final optionalPositionals = parameters + final optionalPositional = parameters .where((element) => element.isOptionalPositional && !asRequiredNamed) .toList(); final named = parameters @@ -23,25 +25,45 @@ String buildParamDefinitionQuery( .toList(); final buffer = StringBuffer(); - String encodeParameter(ParameterElement e) { - final leading = e.isRequiredNamed || asRequiredNamed ? 'required ' : ''; - final trailing = e.defaultValueCode != null && !asRequiredNamed - ? '= ${e.defaultValueCode}' + + String encodeParameter(FormalParameter parameter) { + if (asRecord) { + final type = parameter.typeDisplayString.isEmpty + ? 'dynamic' + : parameter.typeDisplayString; + if (parameter.isNamed) { + return '$type ${parameter.name}'; + } + return type; + } + + late final metadata = parameter.metadata.isNotEmpty + ? '${parameter.metadata.map((e) => e.toSource()).join(' ')} ' : ''; - if (asThisParameter) return '${leading}this.${e.name}$trailing'; - if (asSuperParameter) return '${leading}super.${e.name}$trailing'; - return '$leading${e.type} ${e.name}$trailing'; + + late final element = parameter.declaredElement!; + late final leading = parameter.isRequiredNamed || asRequiredNamed + ? 'required $metadata' + : metadata; + late final trailing = + element.defaultValueCode != null && !asRequiredNamed && withDefaults + ? '= ${element.defaultValueCode}' + : ''; + if (asThisParameter) return '${leading}this.${parameter.name}$trailing'; + if (asSuperParameter) return '${leading}super.${parameter.name}$trailing'; + + return '$leading${parameter.typeDisplayString} ${parameter.name}$trailing'; } buffer.writeAll( - requiredPositionals.map(encodeParameter).expand((e) => [e, ',']), + requiredPositional.map(encodeParameter).expand((e) => [e, ',']), ); - if (optionalPositionals.isNotEmpty) { - if (writeBrackets) buffer.write('['); + if (optionalPositional.isNotEmpty) { + if (writeBrackets && !asRecord) buffer.write('['); buffer.writeAll( - optionalPositionals.map(encodeParameter).expand((e) => [e, ',']), + optionalPositional.map(encodeParameter).expand((e) => [e, ',']), ); - if (writeBrackets) buffer.write(']'); + if (writeBrackets && !asRecord) buffer.write(']'); } if (named.isNotEmpty) { if (writeBrackets) buffer.write('{'); @@ -53,7 +75,7 @@ String buildParamDefinitionQuery( } String buildParamInvocationQuery( - Map parameters, { + Map parameters, { bool asThisParameter = false, }) { final buffer = StringBuffer(); @@ -67,3 +89,22 @@ String buildParamInvocationQuery( return buffer.toString(); } + +extension ParameterType on FormalParameter { + String get typeDisplayString { + final that = this; + switch (that) { + case DefaultFormalParameter(): + return that.parameter.typeDisplayString; + case SimpleFormalParameter(): + // No type, so let's just return 'dynamic' + return that.type?.toSource() ?? 'dynamic'; + case FieldFormalParameter(): + case FunctionTypedFormalParameter(): + case SuperFormalParameter(): + throw UnsupportedError( + 'Only parameters of the form "Type name" are supported', + ); + } + } +} diff --git a/packages/riverpod_generator/lib/src/templates/provider.dart b/packages/riverpod_generator/lib/src/templates/provider.dart new file mode 100644 index 000000000..46729b2fb --- /dev/null +++ b/packages/riverpod_generator/lib/src/templates/provider.dart @@ -0,0 +1,382 @@ +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; + +import '../models.dart'; +import '../riverpod_generator.dart'; +import '../type.dart'; +import 'element.dart'; +import 'parameters.dart'; +import 'template.dart'; + +class ProviderTemplate extends Template { + ProviderTemplate( + this.provider, + this.options, { + required this.allTransitiveDependencies, + }); + + final GeneratorProviderDeclaration provider; + final BuildYamlOptions options; + final List? allTransitiveDependencies; + + late final _argumentRecordType = provider.argumentRecordType; + + late final _generics = provider.generics(); + late final _genericsDefinition = provider.genericsDefinition(); + + @override + void run(StringBuffer buffer) { + final provider = this.provider; + + final name = provider.providerTypeName; + final exposedType = provider.exposedTypeDisplayString; + final createdType = provider.createdTypeDisplayString; + final valueType = provider.valueTypeDisplayString; + + switch (provider) { + case FunctionalProviderDeclaration(): + final List modifiers; + + switch (provider.createdType) { + case SupportedCreatedType.future: + modifiers = [ + '\$FutureModifier<$valueType>', + '\$FutureProvider<$valueType>', + ]; + case SupportedCreatedType.stream: + modifiers = [ + '\$FutureModifier<$valueType>', + '\$StreamProvider<$valueType>', + ]; + case SupportedCreatedType.value: + modifiers = ['\$Provider<$valueType>']; + } + + final mixins = modifiers.isEmpty ? '' : ' with ${modifiers.join(', ')}'; + + buffer.writeln(''' +${provider.doc} final class $name$_genericsDefinition + extends \$FunctionalProvider< + $exposedType, + $createdType + > + $mixins { +'''); + + case ClassBasedProviderDeclaration(): + final notifierType = '${provider.name}$_generics'; + + final String baseClass; + + switch (provider.createdType) { + case SupportedCreatedType.future: + baseClass = '\$AsyncNotifierProvider<$notifierType, $valueType>'; + case SupportedCreatedType.stream: + baseClass = '\$StreamNotifierProvider<$notifierType, $valueType>'; + case SupportedCreatedType.value: + baseClass = '\$NotifierProvider<$notifierType, $valueType>'; + } + + buffer.writeln( + '${provider.doc} final class $name$_genericsDefinition extends $baseClass {', + ); + } + + _writeMembers(buffer); + + buffer.writeln('}'); + } + + void _writeConstructor(StringBuffer buffer) { + final superParameters = [ + if (!provider.providerElement.isFamily) 'from: null,', + if (provider.parameters.isEmpty) 'argument: null,', + ].join(); + + final constructorParameters = [ + if (provider.providerElement.isFamily) + 'required ${provider.familyTypeName} super.from,', + if (provider.parameters.isNotEmpty) + 'required $_argumentRecordType super.argument,', + if (provider is ClassBasedProviderDeclaration) + 'super.runNotifierBuildOverride,', + ].join(); + + buffer.writeln(''' + ${provider.doc} const ${provider.providerTypeName}._({ + $constructorParameters + ${provider.createType()}? create + }): _createCb = create, + super( + $superParameters + retry: ${provider.annotation.retryNode?.name ?? 'null'}, + name: r'${provider.providerName(options)}', + isAutoDispose: ${!provider.annotation.element.keepAlive}, + dependencies: ${!provider.providerElement.isFamily ? provider.dependencies(options) : 'null'}, + allTransitiveDependencies: ${!provider.providerElement.isFamily ? provider.allTransitiveDependencies(allTransitiveDependencies) : 'null'}, + ); +'''); + } + + void _writeDependencies(StringBuffer buffer) { + final allTransitiveDependencies = this.allTransitiveDependencies; + if (allTransitiveDependencies == null) return; + + for (final (index, transitiveDependency) + in allTransitiveDependencies.indexed) { + buffer.writeln( + 'static const \$allTransitiveDependencies$index = $transitiveDependency;', + ); + } + + buffer.writeln(); + } + + void _writeMembers(StringBuffer buffer) { + _writeConstructor(buffer); + _writeDependencies(buffer); + + buffer.writeln(''' + final ${provider.createType()}? _createCb; + + @override + String debugGetCreateSourceHash() => ${provider.hashFnName}(); +'''); + + final copyParameters = [ + if (provider.parameters.isNotEmpty) + 'argument: argument${provider.argumentCast},', + if (provider.providerElement.isFamily) + 'from: from! as ${provider.familyTypeName},', + ].join(); + + _writeGenericCopyWith(buffer, copyParameters: copyParameters); + _writeToString(buffer); + _writeOverrideWithValue(buffer); + + switch (provider) { + case FunctionalProviderDeclaration(): + final createParams = buildParamDefinitionQuery(provider.parameters); + final createFn = provider.parameters.isEmpty + ? 'create' + : '(ref, $createParams) => create(ref)'; + + buffer.writeln(''' + @\$internal + @override + ${provider.internalElementName}<${provider.valueTypeDisplayString}> \$createElement( + \$ProviderPointer pointer + ) => ${provider.internalElementName}(this, pointer); + + @override + ${provider.providerTypeName}$_generics \$copyWithCreate( + ${provider.createType(withArguments: false)} create, + ) { + return ${provider.providerTypeName}$_generics._( + $copyParameters + create: $createFn + ); + } +'''); + + _writeFunctionalCreate(buffer); + + case ClassBasedProviderDeclaration(:final mutations): + final notifierType = '${provider.name}$_generics'; + + buffer.writeln(''' + @\$internal + @override + $notifierType create() => _createCb?.call() ?? $notifierType(); + + @\$internal + @override + ${provider.providerTypeName}$_generics \$copyWithCreate( + $notifierType Function() create, + ) { + return ${provider.providerTypeName}$_generics._( + $copyParameters + create: create + ); + } + + @\$internal + @override + ${provider.providerTypeName}$_generics \$copyWithBuild( + ${provider.notifierBuildType()} build, + ) { + return ${provider.providerTypeName}$_generics._( + $copyParameters + runNotifierBuildOverride: build + ); + } +'''); + + _classCreateElement(mutations, buffer, notifierType); + + for (final mutation in mutations) { + buffer.writeln(''' + ProviderListenable<${mutation.generatedMutationInterfaceName}> get ${mutation.name} + => \$LazyProxyListenable<${mutation.generatedMutationInterfaceName}, ${provider.exposedTypeDisplayString}>( + this, + (element) { + element as ${provider.generatedElementName}$_generics; + + return element.${mutation.elementFieldName}; + }, + ); + '''); + } + } + + _writeEqual(buffer); + } + + void _classCreateElement( + List mutations, + StringBuffer buffer, + String notifierType, + ) { + if (mutations.isEmpty) { + buffer.writeln(''' + @\$internal + @override + ${provider.internalElementName}<$notifierType, ${provider.valueTypeDisplayString}> \$createElement( + \$ProviderPointer pointer + ) => ${provider.internalElementName}(this, pointer); +'''); + + return; + } + + buffer.writeln(''' + @\$internal + @override + ${provider.generatedElementName}$_generics \$createElement( + \$ProviderPointer pointer + ) => ${provider.generatedElementName}(this, pointer); +'''); + } + + void _writeOverrideWithValue(StringBuffer buffer) { + if (provider.createdType != SupportedCreatedType.value) return; + + buffer.writeln(''' + /// {@macro riverpod.override_with_value} + Override overrideWithValue(${provider.exposedTypeDisplayString} value) { + return \$ProviderOverride( + origin: this, + providerOverride: \$ValueProvider<${provider.exposedTypeDisplayString}>(value), + ); + } +'''); + } + + void _writeGenericCopyWith( + StringBuffer buffer, { + required String copyParameters, + }) { + if (provider.typeParameters?.typeParameters.isEmpty ?? true) return; + + buffer.writeln(''' + ${provider.providerTypeName}$_generics _copyWithCreate( + ${provider.createType(withGenericDefinition: true)} create, + ) { + return ${provider.providerTypeName}$_generics._( + $copyParameters + create: create$_generics + ); + } + '''); + + if (provider is ClassBasedProviderDeclaration) { + buffer.writeln(''' + ${provider.providerTypeName}$_generics _copyWithBuild( + ${provider.notifierBuildType(withGenericDefinition: true)} build, + ) { + return ${provider.providerTypeName}$_generics._( + $copyParameters + runNotifierBuildOverride: build$_generics + ); + } + '''); + } + } + + void _writeFunctionalCreate(StringBuffer buffer) { + buffer.write(''' + @override + ${provider.createdTypeDisplayString} create(Ref ref) { + final _\$cb = _createCb ?? ${provider.name}$_generics; +'''); + + switch (provider.parameters) { + case []: + buffer.writeln(r'return _$cb(ref);'); + case [...]: + final paramsPassThrough = buildParamInvocationQuery({ + for (final (index, parameter) in provider.parameters.indexed) + if (provider.parameters.length == 1) + parameter: 'argument' + else if (parameter.isPositional) + parameter: 'argument.\$${index + 1}' + else + parameter: 'argument.${parameter.name!.lexeme}', + }); + + buffer.writeln(''' + final argument = this.argument${provider.argumentCast}; + return _\$cb(ref, $paramsPassThrough); + '''); + } + + buffer.writeln('}'); + } + + void _writeEqual(StringBuffer buffer) { + if (!provider.providerElement.isFamily) return; + + buffer.writeln(''' + @override + bool operator ==(Object other) { + return ${[ + 'other is ${provider.providerTypeName}', + // If there are type parameters, check the runtimeType to check them too. + if (provider.typeParameters?.typeParameters.isNotEmpty ?? false) + 'other.runtimeType == runtimeType', + 'other.argument == argument', + ].join(' && ')}; + } + + @override + int get hashCode { + return ${switch (provider.typeParameters?.typeParameters ?? []) { + [] => 'argument.hashCode', + [_, ...] => 'Object.hash(runtimeType, argument)', + }}; + } +'''); + } + + void _writeToString(StringBuffer buffer) { + if (!provider.providerElement.isFamily) return; + + final encodedGenerics = provider.typeParameters?.typeParameters.isEmpty ?? + true + ? '' + : '<${provider.typeParameters!.typeParameters.map((e) => '\${${e.name}}').join(', ')}>'; + + buffer.writeln(''' +@override +String toString() { + return r'${provider.providerName(options)}' + '$encodedGenerics' + '${switch (provider.parameters) { + [] => '()', + [_] => r'($argument)', + // Calling toString on a record, do we don't add the () to avoid `provider((...))` + [_, ...] => r'$argument', + }}'; +} +'''); + } +} diff --git a/packages/riverpod_generator/lib/src/templates/provider_variable.dart b/packages/riverpod_generator/lib/src/templates/provider_variable.dart new file mode 100644 index 000000000..6be3b03c4 --- /dev/null +++ b/packages/riverpod_generator/lib/src/templates/provider_variable.dart @@ -0,0 +1,30 @@ +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; + +import '../models.dart'; +import '../riverpod_generator.dart'; +import 'template.dart'; + +class ProviderVariableTemplate extends Template { + ProviderVariableTemplate(this.provider, this.options); + + final GeneratorProviderDeclaration provider; + final BuildYamlOptions options; + + @override + void run(StringBuffer buffer) { + final providerName = provider.providerName(options); + + buffer.write(provider.doc); + provider.metadata.forEach(buffer.writeln); + + switch (provider) { + case _ when provider.providerElement.isFamily: + buffer + .writeln('const $providerName = ${provider.familyTypeName}._();\n'); + + case _: + final providerType = provider.providerTypeName; + buffer.writeln('const $providerName = $providerType._();\n'); + } + } +} diff --git a/packages/riverpod_generator/lib/src/type.dart b/packages/riverpod_generator/lib/src/type.dart new file mode 100644 index 000000000..1883c4d61 --- /dev/null +++ b/packages/riverpod_generator/lib/src/type.dart @@ -0,0 +1,23 @@ +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; + +extension SwitchCreatedType on GeneratorProviderDeclaration { + SupportedCreatedType get createdType { + final dartType = createdTypeNode?.type; + switch (dartType) { + case != null + when !dartType.isRaw && + (dartType.isDartAsyncFutureOr || dartType.isDartAsyncFuture): + return SupportedCreatedType.future; + case != null when !dartType.isRaw && dartType.isDartAsyncStream: + return SupportedCreatedType.stream; + case _: + return SupportedCreatedType.value; + } + } +} + +enum SupportedCreatedType { + future, + stream, + value, +} diff --git a/packages/riverpod_generator/pubspec.yaml b/packages/riverpod_generator/pubspec.yaml index b39923c4f..e15049315 100644 --- a/packages/riverpod_generator/pubspec.yaml +++ b/packages/riverpod_generator/pubspec.yaml @@ -1,13 +1,13 @@ name: riverpod_generator description: A code generator for Riverpod. This both simplifies the syntax empowers it, such as allowing stateful hot-reload. -version: 2.6.4 +version: 3.0.0-dev.11 repository: https://github.com/rrousselGit/riverpod issue_tracker: https://github.com/rrousselGit/riverpod/issues funding: - https://github.com/sponsors/rrousselGit/ environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: analyzer: ^7.0.0 @@ -16,13 +16,16 @@ dependencies: collection: ^1.15.0 crypto: ^3.0.2 meta: ^1.7.0 + mockito: ^5.4.4 path: ^1.8.0 - riverpod_analyzer_utils: 0.5.9 - riverpod_annotation: 2.6.1 + riverpod_analyzer_utils: 1.0.0-dev.1 + riverpod_annotation: 3.0.0-dev.3 source_gen: ^2.0.0 dev_dependencies: build_runner: ^2.1.7 build_verify: ^3.0.0 - riverpod: 2.6.1 + freezed: ^2.4.3 + freezed_annotation: ^2.4.1 + riverpod: 3.0.0-dev.3 test: ^1.21.0 diff --git a/packages/riverpod_generator/pubspec_overrides.yaml b/packages/riverpod_generator/pubspec_overrides.yaml index 12a8e9de4..88de991b8 100644 --- a/packages/riverpod_generator/pubspec_overrides.yaml +++ b/packages/riverpod_generator/pubspec_overrides.yaml @@ -2,7 +2,7 @@ dependency_overrides: riverpod: path: ../riverpod - riverpod_annotation: - path: ../riverpod_annotation riverpod_analyzer_utils: path: ../riverpod_analyzer_utils + riverpod_annotation: + path: ../riverpod_annotation diff --git a/packages/riverpod_generator/test/analysis_options.yaml b/packages/riverpod_generator/test/analysis_options.yaml new file mode 100644 index 000000000..ca4fba333 --- /dev/null +++ b/packages/riverpod_generator/test/analysis_options.yaml @@ -0,0 +1,5 @@ +include: ../analysis_options.yaml +analyzer: + errors: + # We have a tight constraint on Riverpod to use its internal APIs + invalid_use_of_internal_member: ignore diff --git a/packages/riverpod_generator/test/annotated_test.dart b/packages/riverpod_generator/test/annotated_test.dart new file mode 100644 index 000000000..4ac18578e --- /dev/null +++ b/packages/riverpod_generator/test/annotated_test.dart @@ -0,0 +1,114 @@ +import 'dart:io'; + +import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/dart/analysis/utilities.dart'; +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:test/test.dart'; + +void main() async { + final file = File('test/integration/annotated.g.dart').absolute; + final result = await resolveFile2(path: file.path) as ResolvedUnitResult; + final declarations = result.unit.declarations; + + test('Annotations on parameters', () { + final notifier = + declarations.findNamed(r'_$ClassBased') as ClassDeclaration; + final id = notifier.members.findNamed('id'); + final family = + declarations.findNamed('ClassBasedFamily') as ClassDeclaration; + final call = family.members.findNamed('call') as MethodDeclaration; + + expect( + id.metadata.toString(), + "[@Deprecated('field')]", + ); + expect( + call.parameters!.parameters + .firstWhere((e) => e.name!.lexeme == 'id') + .metadata + .toString(), + "[@Deprecated('field')]", + ); + }); + + test('Annotations on generated functionalProvider', () { + final annotations = + declarations.findNamed('functionalProvider').metadata.toString(); + + expect( + annotations, + "[@ProviderFor(functional), @Deprecated('Deprecation message'), @visibleForTesting, @protected]", + ); + }); + + test('Annotations on generated classBasedProvider', () { + final annotations = + declarations.findNamed('classBasedProvider').metadata.toString(); + + expect( + annotations, + "[@ProviderFor(ClassBased), @Deprecated('Deprecation message'), @visibleForTesting, @protected]", + ); + }); + + test('Annotations on generated familyProvider', () { + final annotations = + declarations.findNamed('familyProvider').metadata.toString(); + expect( + annotations, + "[@ProviderFor(family), @Deprecated('Deprecation message'), @visibleForTesting, @protected]", + ); + }); + + test('Annotations on generated notCopiedFunctionalProvider', () { + final annotations = declarations + .findNamed('notCopiedFunctionalProvider') + .metadata + .toString(); + expect( + annotations, + '[@ProviderFor(notCopiedFunctional)]', + ); + }); + + test('Annotations on generated notCopiedClassBasedProvider', () { + final annotations = declarations + .findNamed('notCopiedClassBasedProvider') + .metadata + .toString(); + expect( + annotations, + '[@ProviderFor(NotCopiedClassBased)]', + ); + }); + + test('Annotations on generated notCopiedFamilyProvider', () { + final annotations = + declarations.findNamed('notCopiedFamilyProvider').metadata.toString(); + expect( + annotations, + '[@ProviderFor(notCopiedFamily)]', + ); + }); +} + +extension TopLevelVariableDeclarationFindNamedX on List { + Declaration findNamed(String name) { + return singleWhere((element) { + switch (element) { + case TopLevelVariableDeclaration(): + return element.variables.variables.first.name.lexeme == name; + case FieldDeclaration(): + return element.fields.variables.first.name.lexeme == name; + case MethodDeclaration(): + return element.name.lexeme == name; + case NamedCompilationUnitMember(): + return element.name.lexeme == name; + case ConstructorDeclaration(): + return element.name?.lexeme == name; + case _: + throw UnsupportedError(element.runtimeType.toString()); + } + }); + } +} diff --git a/packages/riverpod_generator/test/async_notifier_test.dart b/packages/riverpod_generator/test/async_notifier_test.dart index c8664e5d2..a5ba8c171 100644 --- a/packages/riverpod_generator/test/async_notifier_test.dart +++ b/packages/riverpod_generator/test/async_notifier_test.dart @@ -1,19 +1,17 @@ // ignore_for_file: omit_local_variable_types, unused_local_variable - +import 'package:riverpod/riverpod.dart' show ProviderBase; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:test/test.dart'; import 'integration/async.dart'; -import 'utils.dart'; void main() { test( 'Creates an AsyncNotifierProvider if @riverpod is used on an async class', () { - final container = createContainer(); + final container = ProviderContainer.test(); - final AutoDisposeAsyncNotifierProvider provider = - publicClassProvider; + const ProviderBase> provider = publicClassProvider; final AsyncValue result = container.read(publicClassProvider); expect(result, const AsyncData('Hello world')); @@ -28,7 +26,7 @@ void main() { }); test('Supports overriding non-family notifiers', () { - final container = createContainer( + final container = ProviderContainer.test( overrides: [ publicClassProvider.overrideWith(() => PublicClass('Hello world')), ], @@ -37,12 +35,13 @@ void main() { final notifier = container.read(publicClassProvider.notifier); expect(notifier.param, 'Hello world'); + // ignore: invalid_use_of_protected_member expect(notifier.ref, isNotNull); expect(notifier.state, isNotNull); }); test('Supports overriding family notifiers', () { - final container = createContainer( + final container = ProviderContainer.test( overrides: [ familyClassProvider(42, third: .42) .overrideWith(() => FamilyClass('Hello world')), @@ -58,6 +57,7 @@ void main() { expect(notifier.fourth, true); expect(notifier.fifth, null); + // ignore: invalid_use_of_protected_member expect(notifier.ref, isNotNull); expect(notifier.state, isNotNull); }); @@ -65,7 +65,7 @@ void main() { test( 'Creates a NotifierProvider.family if @riverpod is used on a synchronous function with parameters', () async { - final container = createContainer(); + final container = ProviderContainer.test(); const FamilyClassFamily family = familyClassProvider; @@ -90,8 +90,6 @@ void main() { familyClassProvider( 42, third: .42, - // ignore: avoid_redundant_argument_values - fourth: true, ), ); expect( @@ -99,8 +97,6 @@ void main() { familyClassProvider( 42, third: .42, - // ignore: avoid_redundant_argument_values - fourth: true, ).hashCode, ); @@ -112,14 +108,7 @@ void main() { fifth: const ['x42'], ); // ignore: invalid_use_of_internal_member - final AutoDisposeAsyncNotifierProviderImpl - futureProvider = provider; - - expect(provider.first, 42); - expect(provider.second, 'x42'); - expect(provider.third, .42); - expect(provider.fourth, false); - expect(provider.fifth, same(const ['x42'])); + final ProviderBase> futureProvider = provider; final sub = container.listen( familyClassProvider( diff --git a/packages/riverpod_generator/test/async_test.dart b/packages/riverpod_generator/test/async_test.dart index cfb2bf807..c5c73e046 100644 --- a/packages/riverpod_generator/test/async_test.dart +++ b/packages/riverpod_generator/test/async_test.dart @@ -1,18 +1,18 @@ // ignore_for_file: omit_local_variable_types, unused_local_variable +import 'package:riverpod/riverpod.dart' show ProviderBase; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:test/test.dart'; import 'integration/async.dart'; -import 'utils.dart'; void main() { test( 'Creates a FutureProvider if @riverpod is used on a FutureOr function', () { - final container = createContainer(); + final container = ProviderContainer.test(); - final AutoDisposeFutureProvider provider = publicProvider; + const ProviderBase> provider = publicProvider; final AsyncValue result = container.read(publicProvider); expect(result, const AsyncData('Hello world')); @@ -27,7 +27,7 @@ void main() { }); test('Supports overriding non-family providers', () async { - final container = createContainer( + final container = ProviderContainer.test( overrides: [ publicProvider.overrideWith((ref) => Future.value('Hello world')), ], @@ -38,12 +38,15 @@ void main() { }); test('Supports overriding family providers', () async { - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - familyProvider(42, third: .42).overrideWith( - (ref) => Future.value( - 'Hello world ${ref.first} ${ref.second} ' - '${ref.third} ${ref.fourth} ${ref.fifth}', + familyProvider(21, third: .21).overrideWith( + (ref) => Future.value('Override'), + ), + familyProvider.overrideWith( + (ref, args) => Future.value( + 'Hello world ${args.$1} ${args.second} ' + '${args.third} ${args.fourth} ${args.fifth}', ), ), ], @@ -51,12 +54,15 @@ void main() { final result = container.read(familyProvider(42, third: .42).future); expect(await result, 'Hello world 42 null 0.42 true null'); + + final result2 = container.read(familyProvider(21, third: .21).future); + expect(await result2, 'Override'); }); test( 'Creates a Provider.family if @riverpod is used on a synchronous function with parameters', () async { - final container = createContainer(); + final container = ProviderContainer.test(); const FamilyFamily family = familyProvider; @@ -81,8 +87,6 @@ void main() { familyProvider( 42, third: .42, - // ignore: avoid_redundant_argument_values - fourth: true, ), ); expect( @@ -90,8 +94,6 @@ void main() { familyProvider( 42, third: .42, - // ignore: avoid_redundant_argument_values - fourth: true, ).hashCode, ); @@ -102,13 +104,7 @@ void main() { fourth: false, fifth: const ['x42'], ); - final AutoDisposeFutureProvider futureProvider = provider; - - expect(provider.first, 42); - expect(provider.second, 'x42'); - expect(provider.third, .42); - expect(provider.fourth, false); - expect(provider.fifth, same(const ['x42'])); + final ProviderBase> futureProvider = provider; final sub = container.listen( familyProvider( @@ -139,4 +135,48 @@ void main() { ), ); }); + + test('can overrideWith', () { + final container = ProviderContainer.test( + overrides: [ + publicProvider.overrideWith((ref) { + const FutureOr result = 'test'; + return result; + }), + publicClassProvider.overrideWith(() => PublicClass(42)), + familyProvider.overrideWith( + (ref, args) { + final FutureOr result = + 'test (first: ${args.$1}, second: ${args.second}, third: ${args.third}, fourth: ${args.fourth}, fifth: ${args.fifth})'; + return result; + }, + ), + familyClassProvider.overrideWith(FamilyClass.new), + ], + ); + + expect(container.read(publicProvider).requireValue, 'test'); + expect(container.read(publicClassProvider.notifier).param, 42); + expect( + container.read(familyProvider(42, second: '42', third: .42)).requireValue, + 'test (first: 42, second: 42, third: 0.42, fourth: true, fifth: null)', + ); + expect( + container + .read(familyClassProvider(42, second: '42', third: .42).notifier) + .param, + (42, second: '42', third: 0.42, fourth: true, fifth: null), + ); + }); + + test('can overrideWithValue providers ', () { + final container = ProviderContainer.test( + overrides: [ + publicProvider.overrideWithValue(const AsyncData('test')), + ], + ); + + expect(container.read(publicProvider), const AsyncData('test')); + expect(container.read(publicProvider.future), completion('test')); + }); } diff --git a/packages/riverpod_generator/test/auto_dispose_test.dart b/packages/riverpod_generator/test/auto_dispose_test.dart index affc1e10e..a5415e9f9 100644 --- a/packages/riverpod_generator/test/auto_dispose_test.dart +++ b/packages/riverpod_generator/test/auto_dispose_test.dart @@ -1,24 +1,10 @@ -// ignore_for_file: omit_local_variable_types, unused_local_variable - -import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:test/test.dart'; - import 'integration/auto_dispose.dart'; -import 'utils.dart'; void main() { test('Respects keepAlive parameter', () { - final container = createContainer(); - - container.read(keepAliveProvider); - - expect( - container.readProviderElement(keepAliveProvider), - isA>(), - ); - expect( - container.readProviderElement(keepAliveProvider), - isNot(isA>()), - ); + expect(keepAliveProvider.isAutoDispose, isFalse); + expect(notKeepAliveProvider.isAutoDispose, isTrue); + expect(defaultKeepAliveProvider.isAutoDispose, isTrue); }); } diff --git a/packages/riverpod_generator/test/dependencies_test.dart b/packages/riverpod_generator/test/dependencies_test.dart index 1553811a9..b57635134 100644 --- a/packages/riverpod_generator/test/dependencies_test.dart +++ b/packages/riverpod_generator/test/dependencies_test.dart @@ -76,15 +76,22 @@ void main() { expect( emptyDependenciesFunctionalProvider.allTransitiveDependencies, - same(const {}), + same(const []), ); expect( emptyDependenciesClassBasedProvider.allTransitiveDependencies, - same(const {}), + same(const []), ); }); + test( + 'On families, passes `null` as dependencies/allTransitiveDependencies to the providers', + () { + expect(provider4Provider(42).dependencies, null); + expect(provider4Provider(42).allTransitiveDependencies, null); + }); + test('Caches dependencies', () { expect( providerProvider.dependencies, @@ -136,4 +143,39 @@ void main() { same(smallTransitiveDependencyCountProvider.allTransitiveDependencies), ); }); + + test('remove duplicate dependencies', () { + expect( + duplicateDependenciesProvider.dependencies, + same(const [depProvider, dep2Provider]), + ); + expect( + duplicateDependenciesProvider.allTransitiveDependencies, + same(const [depProvider, dep2Provider]), + ); + + expect( + transitiveDuplicateDependenciesProvider.allTransitiveDependencies, + same(const { + duplicateDependenciesProvider, + depProvider, + dep2Provider, + duplicateDependencies2Provider, + familyProvider, + family2Provider, + }), + ); + }); + + test('uses a set or a list based on the length', () { + expect( + smallTransitiveDependencyCountProvider.allTransitiveDependencies, + isA>(), + ); + + expect( + transitiveDependenciesProvider.allTransitiveDependencies, + isA>(), + ); + }); } diff --git a/packages/riverpod_generator/test/doc_test.dart b/packages/riverpod_generator/test/doc_test.dart new file mode 100644 index 000000000..e1b357b03 --- /dev/null +++ b/packages/riverpod_generator/test/doc_test.dart @@ -0,0 +1,49 @@ +import 'dart:io'; + +import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/dart/analysis/utilities.dart'; +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:riverpod_generator/src/riverpod_generator.dart'; +import 'package:test/test.dart'; + +import 'annotated_test.dart'; + +void main() async { + final file = File('test/integration/documented.g.dart').absolute; + final result = await resolveFile2(path: file.path) as ResolvedUnitResult; + final topLevelDeclarations = result.unit.declarations.toList(); + + test('Doc on generated variables', () async { + final doc = topLevelDeclarations.findNamed('functionalProvider').doc; + + expect(doc, '/// Hello world\n// Foo\n'); + }); + + test('Doc on generated provider', () async { + final doc = topLevelDeclarations.findNamed('FunctionalProvider').doc; + + expect(doc, '/// Hello world\n// Foo\n'); + }); + + test('Doc on generated family', () async { + final family = + topLevelDeclarations.findNamed('FamilyFamily') as ClassDeclaration; + + expect( + family.members.findNamed('call').doc, + '/// Hello world\n// Foo\n', + ); + + expect(family.doc, '/// Hello world\n// Foo\n'); + }); + + test('Doc on generated class', () async { + final classBased = topLevelDeclarations.findNamed(r'_$ClassFamilyBased') + as ClassDeclaration; + + expect( + classBased.members.findNamed('id').doc, + '/// Hello world\n// Foo\n', + ); + }); +} diff --git a/packages/riverpod_generator/test/hash_test.dart b/packages/riverpod_generator/test/hash_test.dart index 377699172..0967e11c8 100644 --- a/packages/riverpod_generator/test/hash_test.dart +++ b/packages/riverpod_generator/test/hash_test.dart @@ -7,29 +7,29 @@ import 'integration/hash/hash1.dart'; void main() { test('Generates hash function for providers', () { expect( - simpleProvider.debugGetCreateSourceHash!(), - simpleProvider.debugGetCreateSourceHash!(), + simpleProvider.debugGetCreateSourceHash(), + simpleProvider.debugGetCreateSourceHash(), ); expect( - simple2Provider.debugGetCreateSourceHash!(), - simple2Provider.debugGetCreateSourceHash!(), + simple2Provider.debugGetCreateSourceHash(), + simple2Provider.debugGetCreateSourceHash(), ); expect( - simpleClassProvider.debugGetCreateSourceHash!(), - simpleClassProvider.debugGetCreateSourceHash!(), + simpleClassProvider.debugGetCreateSourceHash(), + simpleClassProvider.debugGetCreateSourceHash(), ); expect( - simpleProvider.debugGetCreateSourceHash!(), - isNot(simple2Provider.debugGetCreateSourceHash!()), + simpleProvider.debugGetCreateSourceHash(), + isNot(simple2Provider.debugGetCreateSourceHash()), ); expect( - simpleProvider.debugGetCreateSourceHash!(), - isNot(simpleClassProvider.debugGetCreateSourceHash!()), + simpleProvider.debugGetCreateSourceHash(), + isNot(simpleClassProvider.debugGetCreateSourceHash()), ); expect( - simpleProvider.debugGetCreateSourceHash!(), - isNot(simple2Provider.debugGetCreateSourceHash!()), + simpleProvider.debugGetCreateSourceHash(), + isNot(simple2Provider.debugGetCreateSourceHash()), ); }); } diff --git a/packages/riverpod_generator/test/integration/annotated.dart b/packages/riverpod_generator/test/integration/annotated.dart new file mode 100644 index 000000000..76dfa1100 --- /dev/null +++ b/packages/riverpod_generator/test/integration/annotated.dart @@ -0,0 +1,40 @@ +import 'package:meta/meta.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'annotated.g.dart'; + +@riverpod +@Deprecated('Deprecation message') +@visibleForTesting +@protected +String functional(Ref ref, @Deprecated('field') int id) => 'functional'; + +@riverpod +@Deprecated('Deprecation message') +@visibleForTesting +@protected +class ClassBased extends _$ClassBased { + @override + String build(@Deprecated('field') int id) => 'ClassBased'; +} + +@riverpod +@Deprecated('Deprecation message') +@visibleForTesting +@protected +String family(Ref ref, int id) => 'family $id'; + +@riverpod +@doNotStore +String notCopiedFunctional(Ref ref) => 'notCopiedFunctional'; + +@riverpod +@doNotStore +class NotCopiedClassBased extends _$NotCopiedClassBased { + @override + String build() => 'NotCopiedClassBased'; +} + +@riverpod +@doNotStore +String notCopiedFamily(Ref ref, int id) => 'notCopiedFamily $id'; diff --git a/packages/riverpod_generator/test/integration/annotated.g.dart b/packages/riverpod_generator/test/integration/annotated.g.dart new file mode 100644 index 000000000..f8f5a688a --- /dev/null +++ b/packages/riverpod_generator/test/integration/annotated.g.dart @@ -0,0 +1,702 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'annotated.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +@ProviderFor(functional) +@Deprecated('Deprecation message') +@visibleForTesting +@protected +const functionalProvider = FunctionalFamily._(); + +final class FunctionalProvider extends $FunctionalProvider + with $Provider { + const FunctionalProvider._( + {required FunctionalFamily super.from, + required int super.argument, + String Function( + Ref ref, + @Deprecated('field') int id, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'functionalProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + @Deprecated('field') int id, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$functionalHash(); + + @override + String toString() { + return r'functionalProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + FunctionalProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return FunctionalProvider._( + argument: argument as int, + from: from! as FunctionalFamily, + create: ( + ref, + @Deprecated('field') int id, + ) => + create(ref)); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? functional; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is FunctionalProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$functionalHash() => r'ba8606cd0526e2dde0f775eb8f4c9d8b5b6fdf2c'; + +final class FunctionalFamily extends Family { + const FunctionalFamily._() + : super( + retry: null, + name: r'functionalProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + FunctionalProvider call( + @Deprecated('field') int id, + ) => + FunctionalProvider._(argument: id, from: this); + + @override + String debugGetCreateSourceHash() => _$functionalHash(); + + @override + String toString() => r'functionalProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + String Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FunctionalProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +@ProviderFor(ClassBased) +@Deprecated('Deprecation message') +@visibleForTesting +@protected +const classBasedProvider = ClassBasedFamily._(); + +final class ClassBasedProvider extends $NotifierProvider { + const ClassBasedProvider._( + {required ClassBasedFamily super.from, + required int super.argument, + super.runNotifierBuildOverride, + ClassBased Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'classBasedProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final ClassBased Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$classBasedHash(); + + @override + String toString() { + return r'classBasedProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + ClassBased create() => _createCb?.call() ?? ClassBased(); + + @$internal + @override + ClassBasedProvider $copyWithCreate( + ClassBased Function() create, + ) { + return ClassBasedProvider._( + argument: argument as int, + from: from! as ClassBasedFamily, + create: create); + } + + @$internal + @override + ClassBasedProvider $copyWithBuild( + String Function( + Ref, + ClassBased, + ) build, + ) { + return ClassBasedProvider._( + argument: argument as int, + from: from! as ClassBasedFamily, + runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is ClassBasedProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$classBasedHash() => r'92b444806ef8a304c6e0dc3d8e2383601e781183'; + +final class ClassBasedFamily extends Family { + const ClassBasedFamily._() + : super( + retry: null, + name: r'classBasedProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + ClassBasedProvider call( + @Deprecated('field') int id, + ) => + ClassBasedProvider._(argument: id, from: this); + + @override + String debugGetCreateSourceHash() => _$classBasedHash(); + + @override + String toString() => r'classBasedProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + ClassBased Function( + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ClassBasedProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + String Function(Ref ref, ClassBased notifier, int argument) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ClassBasedProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} + +abstract class _$ClassBased extends $Notifier { + late final _$args = ref.$arg as int; + @Deprecated('field') + int get id => _$args; + + String build( + @Deprecated('field') int id, + ); + @$internal + @override + String runBuild() => build( + _$args, + ); +} + +@ProviderFor(family) +@Deprecated('Deprecation message') +@visibleForTesting +@protected +const familyProvider = FamilyFamily._(); + +final class FamilyProvider extends $FunctionalProvider + with $Provider { + const FamilyProvider._( + {required FamilyFamily super.from, + required int super.argument, + String Function( + Ref ref, + int id, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'familyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + int id, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$familyHash(); + + @override + String toString() { + return r'familyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + FamilyProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return FamilyProvider._( + argument: argument as int, + from: from! as FamilyFamily, + create: ( + ref, + int id, + ) => + create(ref)); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? family; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is FamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$familyHash() => r'14b97009aec20a0332208f8a60bc177b44c9d1d4'; + +final class FamilyFamily extends Family { + const FamilyFamily._() + : super( + retry: null, + name: r'familyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + FamilyProvider call( + int id, + ) => + FamilyProvider._(argument: id, from: this); + + @override + String debugGetCreateSourceHash() => _$familyHash(); + + @override + String toString() => r'familyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + String Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +@ProviderFor(notCopiedFunctional) +const notCopiedFunctionalProvider = NotCopiedFunctionalProvider._(); + +final class NotCopiedFunctionalProvider + extends $FunctionalProvider with $Provider { + const NotCopiedFunctionalProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'notCopiedFunctionalProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$notCopiedFunctionalHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + NotCopiedFunctionalProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return NotCopiedFunctionalProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? notCopiedFunctional; + return _$cb(ref); + } +} + +String _$notCopiedFunctionalHash() => + r'7b2cd9abef57493eebc1c05b1d2b4e2743ddbea2'; + +@ProviderFor(NotCopiedClassBased) +const notCopiedClassBasedProvider = NotCopiedClassBasedProvider._(); + +final class NotCopiedClassBasedProvider + extends $NotifierProvider { + const NotCopiedClassBasedProvider._( + {super.runNotifierBuildOverride, NotCopiedClassBased Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'notCopiedClassBasedProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final NotCopiedClassBased Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$notCopiedClassBasedHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + NotCopiedClassBased create() => _createCb?.call() ?? NotCopiedClassBased(); + + @$internal + @override + NotCopiedClassBasedProvider $copyWithCreate( + NotCopiedClassBased Function() create, + ) { + return NotCopiedClassBasedProvider._(create: create); + } + + @$internal + @override + NotCopiedClassBasedProvider $copyWithBuild( + String Function( + Ref, + NotCopiedClassBased, + ) build, + ) { + return NotCopiedClassBasedProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$notCopiedClassBasedHash() => + r'd2aefd08a78e3bb4c02000d4931a3bf15c01b495'; + +abstract class _$NotCopiedClassBased extends $Notifier { + String build(); + @$internal + @override + String runBuild() => build(); +} + +@ProviderFor(notCopiedFamily) +const notCopiedFamilyProvider = NotCopiedFamilyFamily._(); + +final class NotCopiedFamilyProvider extends $FunctionalProvider + with $Provider { + const NotCopiedFamilyProvider._( + {required NotCopiedFamilyFamily super.from, + required int super.argument, + String Function( + Ref ref, + int id, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'notCopiedFamilyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + int id, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$notCopiedFamilyHash(); + + @override + String toString() { + return r'notCopiedFamilyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + NotCopiedFamilyProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return NotCopiedFamilyProvider._( + argument: argument as int, + from: from! as NotCopiedFamilyFamily, + create: ( + ref, + int id, + ) => + create(ref)); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? notCopiedFamily; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is NotCopiedFamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$notCopiedFamilyHash() => r'ea652776532e2bf993a249b25b5254fc3dfff4b9'; + +final class NotCopiedFamilyFamily extends Family { + const NotCopiedFamilyFamily._() + : super( + retry: null, + name: r'notCopiedFamilyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + NotCopiedFamilyProvider call( + int id, + ) => + NotCopiedFamilyProvider._(argument: id, from: this); + + @override + String debugGetCreateSourceHash() => _$notCopiedFamilyHash(); + + @override + String toString() => r'notCopiedFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + String Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as NotCopiedFamilyProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/test/integration/async.dart b/packages/riverpod_generator/test/integration/async.dart index 4babeda7f..b9550f58f 100644 --- a/packages/riverpod_generator/test/integration/async.dart +++ b/packages/riverpod_generator/test/integration/async.dart @@ -1,14 +1,26 @@ -import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'async.g.dart'; +@riverpod +Future> generic(Ref ref) async { + return []; +} + +@riverpod +class GenericClass extends _$GenericClass { + @override + Future> build() async { + return []; + } +} + @riverpod FutureOr public(Ref ref) { return 'Hello world'; } -final privateProvider = _privateProvider; +const privateProvider = _privateProvider; @riverpod Future _private(Ref ref) async { @@ -44,8 +56,9 @@ class PublicClass extends _$PublicClass { } } -final privateClassProvider = _privateClassProvider; +const privateClassProvider = _privateClassProvider; +// @riverpod class _PrivateClass extends _$PrivateClass { @override @@ -79,3 +92,21 @@ class FamilyClass extends _$FamilyClass { return '(first: $first, second: $second, third: $third, fourth: $fourth, fifth: $fifth)'; } } + +// Regression test for https://github.com/rrousselGit/riverpod/issues/3490 +typedef Regression3490Cb = Future<(int, Cursor)> Function({ + Map filters, + Sort? sort, + Cursor? cursor, +}); + +@riverpod +class Regression3490 + extends _$Regression3490 { + @override + void build({ + required String type, + required Regression3490Cb getData, + String? parentId, + }) {} +} diff --git a/packages/riverpod_generator/test/integration/async.g.dart b/packages/riverpod_generator/test/integration/async.g.dart index dd81a0422..f98630ea7 100644 --- a/packages/riverpod_generator/test/integration/async.g.dart +++ b/packages/riverpod_generator/test/integration/async.g.dart @@ -6,783 +6,1422 @@ part of 'async.dart'; // RiverpodGenerator // ************************************************************************** -String _$publicHash() => r'19bceccf795e4c3a26ad1e613fd6f41aad949e2b'; +@ProviderFor(generic) +const genericProvider = GenericFamily._(); + +final class GenericProvider + extends $FunctionalProvider>, FutureOr>> + with $FutureModifier>, $FutureProvider> { + const GenericProvider._( + {required GenericFamily super.from, + FutureOr> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'genericProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [public]. -@ProviderFor(public) -final publicProvider = AutoDisposeFutureProvider.internal( - public, - name: r'publicProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$publicHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef PublicRef = AutoDisposeFutureProviderRef; -String _$privateHash() => r'7f0d1ff55a21e520b8471bbabc4649b5336221d4'; + final FutureOr> Function( + Ref ref, + )? _createCb; -/// See also [_private]. -@ProviderFor(_private) -final _privateProvider = AutoDisposeFutureProvider.internal( - _private, - name: r'_privateProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$privateHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef _PrivateRef = AutoDisposeFutureProviderRef; -String _$familyOrHash() => r'97cce80a626e228202fa30b87c07ae8319b48023'; + @override + String debugGetCreateSourceHash() => _$genericHash(); + + GenericProvider _copyWithCreate( + FutureOr> Function( + Ref ref, + ) create, + ) { + return GenericProvider._( + from: from! as GenericFamily, create: create); + } + + @override + String toString() { + return r'genericProvider' + '<${T}>' + '()'; + } + + @$internal + @override + $FutureProviderElement> $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); + @override + GenericProvider $copyWithCreate( + FutureOr> Function( + Ref ref, + ) create, + ) { + return GenericProvider._(from: from! as GenericFamily, create: create); + } - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); + @override + FutureOr> create(Ref ref) { + final _$cb = _createCb ?? generic; + return _$cb(ref); } - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + @override + bool operator ==(Object other) { + return other is GenericProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } + + @override + int get hashCode { + return Object.hash(runtimeType, argument); } } -/// See also [familyOr]. -@ProviderFor(familyOr) -const familyOrProvider = FamilyOrFamily(); +String _$genericHash() => r'b7413a59722e9d62ae99c8a7ee0b4a24417fc3b4'; + +final class GenericFamily extends Family { + const GenericFamily._() + : super( + retry: null, + name: r'genericProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); -/// See also [familyOr]. -class FamilyOrFamily extends Family> { - /// See also [familyOr]. - const FamilyOrFamily(); + GenericProvider call() => GenericProvider._(from: this); - /// See also [familyOr]. - FamilyOrProvider call( - int first, + @override + String debugGetCreateSourceHash() => _$genericHash(); + + @override + String toString() => r'genericProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + FutureOr> Function(Ref ref) create, ) { - return FamilyOrProvider( - first, + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericProvider; + + return provider._copyWithCreate(create).$createElement(pointer); + }, ); } +} + +@ProviderFor(GenericClass) +const genericClassProvider = GenericClassFamily._(); + +final class GenericClassProvider + extends $AsyncNotifierProvider, List> { + const GenericClassProvider._( + {required GenericClassFamily super.from, + super.runNotifierBuildOverride, + GenericClass Function()? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'genericClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final GenericClass Function()? _createCb; @override - FamilyOrProvider getProviderOverride( - covariant FamilyOrProvider provider, + String debugGetCreateSourceHash() => _$genericClassHash(); + + GenericClassProvider _copyWithCreate( + GenericClass Function() create, ) { - return call( - provider.first, - ); + return GenericClassProvider._( + from: from! as GenericClassFamily, create: create); } - static const Iterable? _dependencies = null; + GenericClassProvider _copyWithBuild( + FutureOr> Function( + Ref, + GenericClass, + ) build, + ) { + return GenericClassProvider._( + from: from! as GenericClassFamily, runNotifierBuildOverride: build); + } @override - Iterable? get dependencies => _dependencies; + String toString() { + return r'genericClassProvider' + '<${T}>' + '()'; + } - static const Iterable? _allTransitiveDependencies = null; + @$internal + @override + GenericClass create() => _createCb?.call() ?? GenericClass(); + @$internal @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + GenericClassProvider $copyWithCreate( + GenericClass Function() create, + ) { + return GenericClassProvider._( + from: from! as GenericClassFamily, create: create); + } + + @$internal + @override + GenericClassProvider $copyWithBuild( + FutureOr> Function( + Ref, + GenericClass, + ) build, + ) { + return GenericClassProvider._( + from: from! as GenericClassFamily, runNotifierBuildOverride: build); + } + + @$internal + @override + $AsyncNotifierProviderElement, List> $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is GenericClassProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } @override - String? get name => r'familyOrProvider'; + int get hashCode { + return Object.hash(runtimeType, argument); + } } -/// See also [familyOr]. -class FamilyOrProvider extends AutoDisposeFutureProvider { - /// See also [familyOr]. - FamilyOrProvider( - int first, - ) : this._internal( - (ref) => familyOr( - ref as FamilyOrRef, - first, - ), - from: familyOrProvider, - name: r'familyOrProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$familyOrHash, - dependencies: FamilyOrFamily._dependencies, - allTransitiveDependencies: FamilyOrFamily._allTransitiveDependencies, - first: first, +String _$genericClassHash() => r'd3c4acc9cdae12f6c666fbf1f89aee212bb086db'; + +final class GenericClassFamily extends Family { + const GenericClassFamily._() + : super( + retry: null, + name: r'genericClassProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, ); - FamilyOrProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.first, - }) : super.internal(); + GenericClassProvider call() => + GenericClassProvider._(from: this); - final int first; + @override + String debugGetCreateSourceHash() => _$genericClassHash(); @override + String toString() => r'genericClassProvider'; + + /// {@macro riverpod.override_with} Override overrideWith( - FutureOr Function(FamilyOrRef provider) create, + GenericClass Function() create, ) { - return ProviderOverride( - origin: this, - override: FamilyOrProvider._internal( - (ref) => create(ref as FamilyOrRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - first: first, - ), + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericClassProvider; + + return provider._copyWithCreate(create).$createElement(pointer); + }, ); } - @override - AutoDisposeFutureProviderElement createElement() { - return _FamilyOrProviderElement(this); + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + FutureOr> Function(Ref ref, GenericClass notifier) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericClassProvider; + + return provider._copyWithBuild(build).$createElement(pointer); + }, + ); } +} +abstract class _$GenericClass extends $AsyncNotifier> { + FutureOr> build(); + @$internal @override - bool operator ==(Object other) { - return other is FamilyOrProvider && other.first == first; - } + FutureOr> runBuild() => build(); +} + +@ProviderFor(public) +const publicProvider = PublicProvider._(); + +final class PublicProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const PublicProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'publicProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, first.hashCode); + String debugGetCreateSourceHash() => _$publicHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); - return _SystemHash.finish(hash); + @override + PublicProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return PublicProvider._(create: create); } -} -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FamilyOrRef on AutoDisposeFutureProviderRef { - /// The parameter `first` of this provider. - int get first; + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? public; + return _$cb(ref); + } } -class _FamilyOrProviderElement extends AutoDisposeFutureProviderElement - with FamilyOrRef { - _FamilyOrProviderElement(super.provider); +String _$publicHash() => r'19bceccf795e4c3a26ad1e613fd6f41aad949e2b'; + +@ProviderFor(_private) +const _privateProvider = _PrivateProvider._(); + +final class _PrivateProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const _PrivateProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'_privateProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$privateHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + _PrivateProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return _PrivateProvider._(create: create); + } @override - int get first => (origin as FamilyOrProvider).first; + FutureOr create(Ref ref) { + final _$cb = _createCb ?? _private; + return _$cb(ref); + } } -String _$familyHash() => r'1da6c928ee85a03729a1c147f33e018521bb9c89'; +String _$privateHash() => r'7f0d1ff55a21e520b8471bbabc4649b5336221d4'; -/// See also [family]. -@ProviderFor(family) -const familyProvider = FamilyFamily(); +@ProviderFor(familyOr) +const familyOrProvider = FamilyOrFamily._(); + +final class FamilyOrProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const FamilyOrProvider._( + {required FamilyOrFamily super.from, + required int super.argument, + FutureOr Function( + Ref ref, + int first, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'familyOrProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [family]. -class FamilyFamily extends Family> { - /// See also [family]. - const FamilyFamily(); + final FutureOr Function( + Ref ref, + int first, + )? _createCb; - /// See also [family]. - FamilyProvider call( - int first, { - String? second, - required double third, - bool fourth = true, - List? fifth, - }) { - return FamilyProvider( - first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ); + @override + String debugGetCreateSourceHash() => _$familyOrHash(); + + @override + String toString() { + return r'familyOrProvider' + '' + '($argument)'; } + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + @override - FamilyProvider getProviderOverride( - covariant FamilyProvider provider, + FamilyOrProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, ) { - return call( - provider.first, - second: provider.second, - third: provider.third, - fourth: provider.fourth, - fifth: provider.fifth, + return FamilyOrProvider._( + argument: argument as int, + from: from! as FamilyOrFamily, + create: ( + ref, + int first, + ) => + create(ref)); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? familyOr; + final argument = this.argument as int; + return _$cb( + ref, + argument, ); } - static const Iterable? _dependencies = null; + @override + bool operator ==(Object other) { + return other is FamilyOrProvider && other.argument == argument; + } @override - Iterable? get dependencies => _dependencies; + int get hashCode { + return argument.hashCode; + } +} - static const Iterable? _allTransitiveDependencies = null; +String _$familyOrHash() => r'97cce80a626e228202fa30b87c07ae8319b48023'; + +final class FamilyOrFamily extends Family { + const FamilyOrFamily._() + : super( + retry: null, + name: r'familyOrProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + FamilyOrProvider call( + int first, + ) => + FamilyOrProvider._(argument: first, from: this); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + String debugGetCreateSourceHash() => _$familyOrHash(); @override - String? get name => r'familyProvider'; + String toString() => r'familyOrProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + FutureOr Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyOrProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } -/// See also [family]. -class FamilyProvider extends AutoDisposeFutureProvider { - /// See also [family]. - FamilyProvider( +@ProviderFor(family) +const familyProvider = FamilyFamily._(); + +final class FamilyProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const FamilyProvider._( + {required FamilyFamily super.from, + required ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) + super.argument, + FutureOr Function( + Ref ref, + int first, { + String? second, + required double third, + bool fourth, + List? fifth, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'familyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, int first, { String? second, required double third, - bool fourth = true, + bool fourth, List? fifth, - }) : this._internal( - (ref) => family( - ref as FamilyRef, - first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ), - from: familyProvider, - name: r'familyProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$familyHash, - dependencies: FamilyFamily._dependencies, - allTransitiveDependencies: FamilyFamily._allTransitiveDependencies, - first: first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ); + })? _createCb; - FamilyProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.first, - required this.second, - required this.third, - required this.fourth, - required this.fifth, - }) : super.internal(); - - final int first; - final String? second; - final double third; - final bool fourth; - final List? fifth; + @override + String debugGetCreateSourceHash() => _$familyHash(); @override - Override overrideWith( - FutureOr Function(FamilyRef provider) create, + String toString() { + return r'familyProvider' + '' + '$argument'; + } + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + FamilyProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, ) { - return ProviderOverride( - origin: this, - override: FamilyProvider._internal( - (ref) => create(ref as FamilyRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - first: first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ), - ); + return FamilyProvider._( + argument: argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }), + from: from! as FamilyFamily, + create: ( + ref, + int first, { + String? second, + required double third, + bool fourth = true, + List? fifth, + }) => + create(ref)); } @override - AutoDisposeFutureProviderElement createElement() { - return _FamilyProviderElement(this); + FutureOr create(Ref ref) { + final _$cb = _createCb ?? family; + final argument = this.argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + return _$cb( + ref, + argument.$1, + second: argument.second, + third: argument.third, + fourth: argument.fourth, + fifth: argument.fifth, + ); } @override bool operator ==(Object other) { - return other is FamilyProvider && - other.first == first && - other.second == second && - other.third == third && - other.fourth == fourth && - other.fifth == fifth; + return other is FamilyProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, first.hashCode); - hash = _SystemHash.combine(hash, second.hashCode); - hash = _SystemHash.combine(hash, third.hashCode); - hash = _SystemHash.combine(hash, fourth.hashCode); - hash = _SystemHash.combine(hash, fifth.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FamilyRef on AutoDisposeFutureProviderRef { - /// The parameter `first` of this provider. - int get first; +String _$familyHash() => r'1da6c928ee85a03729a1c147f33e018521bb9c89'; - /// The parameter `second` of this provider. - String? get second; +final class FamilyFamily extends Family { + const FamilyFamily._() + : super( + retry: null, + name: r'familyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); - /// The parameter `third` of this provider. - double get third; + FamilyProvider call( + int first, { + String? second, + required double third, + bool fourth = true, + List? fifth, + }) => + FamilyProvider._(argument: ( + first, + second: second, + third: third, + fourth: fourth, + fifth: fifth, + ), from: this); - /// The parameter `fourth` of this provider. - bool get fourth; + @override + String debugGetCreateSourceHash() => _$familyHash(); + + @override + String toString() => r'familyProvider'; - /// The parameter `fifth` of this provider. - List? get fifth; + /// {@macro riverpod.override_with} + Override overrideWith( + FutureOr Function( + Ref ref, + ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyProvider; + + final argument = provider.argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } -class _FamilyProviderElement extends AutoDisposeFutureProviderElement - with FamilyRef { - _FamilyProviderElement(super.provider); +@ProviderFor(PublicClass) +const publicClassProvider = PublicClassProvider._(); + +final class PublicClassProvider + extends $AsyncNotifierProvider { + const PublicClassProvider._( + {super.runNotifierBuildOverride, PublicClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'publicClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final PublicClass Function()? _createCb; @override - int get first => (origin as FamilyProvider).first; + String debugGetCreateSourceHash() => _$publicClassHash(); + + @$internal @override - String? get second => (origin as FamilyProvider).second; + PublicClass create() => _createCb?.call() ?? PublicClass(); + + @$internal @override - double get third => (origin as FamilyProvider).third; + PublicClassProvider $copyWithCreate( + PublicClass Function() create, + ) { + return PublicClassProvider._(create: create); + } + + @$internal @override - bool get fourth => (origin as FamilyProvider).fourth; + PublicClassProvider $copyWithBuild( + FutureOr Function( + Ref, + PublicClass, + ) build, + ) { + return PublicClassProvider._(runNotifierBuildOverride: build); + } + + @$internal @override - List? get fifth => (origin as FamilyProvider).fifth; + $AsyncNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); } String _$publicClassHash() => r'e9bc69e44b72e8ed77d423524c0d74ad460d629d'; -/// See also [PublicClass]. -@ProviderFor(PublicClass) -final publicClassProvider = - AutoDisposeAsyncNotifierProvider.internal( - PublicClass.new, - name: r'publicClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$publicClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$PublicClass = AutoDisposeAsyncNotifier; -String _$privateClassHash() => r'7e69cffe8315999710e4cb6bb3de9f179d3f2f5d'; +abstract class _$PublicClass extends $AsyncNotifier { + FutureOr build(); + @$internal + @override + FutureOr runBuild() => build(); +} -/// See also [_PrivateClass]. @ProviderFor(_PrivateClass) -final _privateClassProvider = - AutoDisposeAsyncNotifierProvider<_PrivateClass, String>.internal( - _PrivateClass.new, - name: r'_privateClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$privateClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$PrivateClass = AutoDisposeAsyncNotifier; -String _$familyOrClassHash() => r'b4882d4e79a03c63005d35eb7a021c9c4373a8d9'; - -abstract class _$FamilyOrClass - extends BuildlessAutoDisposeAsyncNotifier { - late final int first; +const _privateClassProvider = _PrivateClassProvider._(); + +final class _PrivateClassProvider + extends $AsyncNotifierProvider<_PrivateClass, String> { + const _PrivateClassProvider._( + {super.runNotifierBuildOverride, _PrivateClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'_privateClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - FutureOr build( - int first, - ); -} + final _PrivateClass Function()? _createCb; -/// See also [FamilyOrClass]. -@ProviderFor(FamilyOrClass) -const familyOrClassProvider = FamilyOrClassFamily(); + @override + String debugGetCreateSourceHash() => _$privateClassHash(); -/// See also [FamilyOrClass]. -class FamilyOrClassFamily extends Family> { - /// See also [FamilyOrClass]. - const FamilyOrClassFamily(); + @$internal + @override + _PrivateClass create() => _createCb?.call() ?? _PrivateClass(); - /// See also [FamilyOrClass]. - FamilyOrClassProvider call( - int first, + @$internal + @override + _PrivateClassProvider $copyWithCreate( + _PrivateClass Function() create, ) { - return FamilyOrClassProvider( - first, - ); + return _PrivateClassProvider._(create: create); } + @$internal @override - FamilyOrClassProvider getProviderOverride( - covariant FamilyOrClassProvider provider, + _PrivateClassProvider $copyWithBuild( + FutureOr Function( + Ref, + _PrivateClass, + ) build, ) { - return call( - provider.first, - ); + return _PrivateClassProvider._(runNotifierBuildOverride: build); } - static const Iterable? _dependencies = null; - + @$internal @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; + $AsyncNotifierProviderElement<_PrivateClass, String> $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); +} - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; +String _$privateClassHash() => r'7e69cffe8315999710e4cb6bb3de9f179d3f2f5d'; +abstract class _$PrivateClass extends $AsyncNotifier { + FutureOr build(); + @$internal @override - String? get name => r'familyOrClassProvider'; + FutureOr runBuild() => build(); } -/// See also [FamilyOrClass]. -class FamilyOrClassProvider - extends AutoDisposeAsyncNotifierProviderImpl { - /// See also [FamilyOrClass]. - FamilyOrClassProvider( - int first, - ) : this._internal( - () => FamilyOrClass()..first = first, - from: familyOrClassProvider, +@ProviderFor(FamilyOrClass) +const familyOrClassProvider = FamilyOrClassFamily._(); + +final class FamilyOrClassProvider + extends $AsyncNotifierProvider { + const FamilyOrClassProvider._( + {required FamilyOrClassFamily super.from, + required int super.argument, + super.runNotifierBuildOverride, + FamilyOrClass Function()? create}) + : _createCb = create, + super( + retry: null, name: r'familyOrClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$familyOrClassHash, - dependencies: FamilyOrClassFamily._dependencies, - allTransitiveDependencies: - FamilyOrClassFamily._allTransitiveDependencies, - first: first, + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - FamilyOrClassProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.first, - }) : super.internal(); + final FamilyOrClass Function()? _createCb; - final int first; + @override + String debugGetCreateSourceHash() => _$familyOrClassHash(); @override - FutureOr runNotifierBuild( - covariant FamilyOrClass notifier, - ) { - return notifier.build( - first, - ); + String toString() { + return r'familyOrClassProvider' + '' + '($argument)'; } + @$internal @override - Override overrideWith(FamilyOrClass Function() create) { - return ProviderOverride( - origin: this, - override: FamilyOrClassProvider._internal( - () => create()..first = first, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - first: first, - ), - ); + FamilyOrClass create() => _createCb?.call() ?? FamilyOrClass(); + + @$internal + @override + FamilyOrClassProvider $copyWithCreate( + FamilyOrClass Function() create, + ) { + return FamilyOrClassProvider._( + argument: argument as int, + from: from! as FamilyOrClassFamily, + create: create); } + @$internal @override - AutoDisposeAsyncNotifierProviderElement - createElement() { - return _FamilyOrClassProviderElement(this); + FamilyOrClassProvider $copyWithBuild( + FutureOr Function( + Ref, + FamilyOrClass, + ) build, + ) { + return FamilyOrClassProvider._( + argument: argument as int, + from: from! as FamilyOrClassFamily, + runNotifierBuildOverride: build); } + @$internal + @override + $AsyncNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); + @override bool operator ==(Object other) { - return other is FamilyOrClassProvider && other.first == first; + return other is FamilyOrClassProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, first.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FamilyOrClassRef on AutoDisposeAsyncNotifierProviderRef { - /// The parameter `first` of this provider. - int get first; -} +String _$familyOrClassHash() => r'b4882d4e79a03c63005d35eb7a021c9c4373a8d9'; + +final class FamilyOrClassFamily extends Family { + const FamilyOrClassFamily._() + : super( + retry: null, + name: r'familyOrClassProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); -class _FamilyOrClassProviderElement - extends AutoDisposeAsyncNotifierProviderElement - with FamilyOrClassRef { - _FamilyOrClassProviderElement(super.provider); + FamilyOrClassProvider call( + int first, + ) => + FamilyOrClassProvider._(argument: first, from: this); @override - int get first => (origin as FamilyOrClassProvider).first; -} + String debugGetCreateSourceHash() => _$familyOrClassHash(); -String _$familyClassHash() => r'b7e3ca6091f12bbc99972e961acd885e05f42a15'; + @override + String toString() => r'familyOrClassProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + FamilyOrClass Function( + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyOrClassProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + FutureOr Function(Ref ref, FamilyOrClass notifier, int argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyOrClassProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} -abstract class _$FamilyClass extends BuildlessAutoDisposeAsyncNotifier { - late final int first; - late final String? second; - late final double third; - late final bool fourth; - late final List? fifth; +abstract class _$FamilyOrClass extends $AsyncNotifier { + late final _$args = ref.$arg as int; + int get first => _$args; FutureOr build( - int first, { - String? second, - required double third, - bool fourth = true, - List? fifth, - }); + int first, + ); + @$internal + @override + FutureOr runBuild() => build( + _$args, + ); } -/// See also [FamilyClass]. @ProviderFor(FamilyClass) -const familyClassProvider = FamilyClassFamily(); +const familyClassProvider = FamilyClassFamily._(); + +final class FamilyClassProvider + extends $AsyncNotifierProvider { + const FamilyClassProvider._( + {required FamilyClassFamily super.from, + required ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) + super.argument, + super.runNotifierBuildOverride, + FamilyClass Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'familyClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [FamilyClass]. -class FamilyClassFamily extends Family> { - /// See also [FamilyClass]. - const FamilyClassFamily(); + final FamilyClass Function()? _createCb; - /// See also [FamilyClass]. - FamilyClassProvider call( - int first, { - String? second, - required double third, - bool fourth = true, - List? fifth, - }) { - return FamilyClassProvider( - first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ); + @override + String debugGetCreateSourceHash() => _$familyClassHash(); + + @override + String toString() { + return r'familyClassProvider' + '' + '$argument'; } + @$internal @override - FamilyClassProvider getProviderOverride( - covariant FamilyClassProvider provider, + FamilyClass create() => _createCb?.call() ?? FamilyClass(); + + @$internal + @override + FamilyClassProvider $copyWithCreate( + FamilyClass Function() create, ) { - return call( - provider.first, - second: provider.second, - third: provider.third, - fourth: provider.fourth, - fifth: provider.fifth, - ); + return FamilyClassProvider._( + argument: argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }), + from: from! as FamilyClassFamily, + create: create); } - static const Iterable? _dependencies = null; + @$internal + @override + FamilyClassProvider $copyWithBuild( + FutureOr Function( + Ref, + FamilyClass, + ) build, + ) { + return FamilyClassProvider._( + argument: argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }), + from: from! as FamilyClassFamily, + runNotifierBuildOverride: build); + } + @$internal @override - Iterable? get dependencies => _dependencies; + $AsyncNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); - static const Iterable? _allTransitiveDependencies = null; + @override + bool operator ==(Object other) { + return other is FamilyClassProvider && other.argument == argument; + } @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + int get hashCode { + return argument.hashCode; + } +} + +String _$familyClassHash() => r'b7e3ca6091f12bbc99972e961acd885e05f42a15'; + +final class FamilyClassFamily extends Family { + const FamilyClassFamily._() + : super( + retry: null, + name: r'familyClassProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + FamilyClassProvider call( + int first, { + String? second, + required double third, + bool fourth = true, + List? fifth, + }) => + FamilyClassProvider._(argument: ( + first, + second: second, + third: third, + fourth: fourth, + fifth: fifth, + ), from: this); + + @override + String debugGetCreateSourceHash() => _$familyClassHash(); @override - String? get name => r'familyClassProvider'; + String toString() => r'familyClassProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + FamilyClass Function( + ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyClassProvider; + + final argument = provider.argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + FutureOr Function( + Ref ref, + FamilyClass notifier, + ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyClassProvider; + + final argument = provider.argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } } -/// See also [FamilyClass]. -class FamilyClassProvider - extends AutoDisposeAsyncNotifierProviderImpl { - /// See also [FamilyClass]. - FamilyClassProvider( +abstract class _$FamilyClass extends $AsyncNotifier { + late final _$args = ref.$arg as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + int get first => _$args.$1; + String? get second => _$args.second; + double get third => _$args.third; + bool get fourth => _$args.fourth; + List? get fifth => _$args.fifth; + + FutureOr build( int first, { String? second, required double third, bool fourth = true, List? fifth, - }) : this._internal( - () => FamilyClass() - ..first = first - ..second = second - ..third = third - ..fourth = fourth - ..fifth = fifth, - from: familyClassProvider, - name: r'familyClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$familyClassHash, - dependencies: FamilyClassFamily._dependencies, - allTransitiveDependencies: - FamilyClassFamily._allTransitiveDependencies, - first: first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, + }); + @$internal + @override + FutureOr runBuild() => build( + _$args.$1, + second: _$args.second, + third: _$args.third, + fourth: _$args.fourth, + fifth: _$args.fifth, + ); +} + +@ProviderFor(Regression3490) +const regression3490Provider = Regression3490Family._(); + +final class Regression3490Provider + extends $NotifierProvider, void> { + const Regression3490Provider._( + {required Regression3490Family super.from, + required ({ + String type, + Regression3490Cb getData, + String? parentId, + }) + super.argument, + super.runNotifierBuildOverride, + Regression3490 Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'regression3490Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - FamilyClassProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.first, - required this.second, - required this.third, - required this.fourth, - required this.fifth, - }) : super.internal(); - - final int first; - final String? second; - final double third; - final bool fourth; - final List? fifth; - - @override - FutureOr runNotifierBuild( - covariant FamilyClass notifier, - ) { - return notifier.build( - first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ); + final Regression3490 Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$regression3490Hash(); + + Regression3490Provider _copyWithCreate( + Regression3490 Function() create, + ) { + return Regression3490Provider._( + argument: argument as ({ + String type, + Regression3490Cb getData, + String? parentId, + }), + from: from! as Regression3490Family, + create: create); + } + + Regression3490Provider _copyWithBuild( + void Function( + Ref, + Regression3490, + ) build, + ) { + return Regression3490Provider._( + argument: argument as ({ + String type, + Regression3490Cb getData, + String? parentId, + }), + from: from! as Regression3490Family, + runNotifierBuildOverride: build); } @override - Override overrideWith(FamilyClass Function() create) { - return ProviderOverride( + String toString() { + return r'regression3490Provider' + '<${Model}, ${Sort}, ${Cursor}>' + '$argument'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(void value) { + return $ProviderOverride( origin: this, - override: FamilyClassProvider._internal( - () => create() - ..first = first - ..second = second - ..third = third - ..fourth = fourth - ..fifth = fifth, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - first: first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ), + providerOverride: $ValueProvider(value), ); } + @$internal + @override + Regression3490 create() => + _createCb?.call() ?? Regression3490(); + + @$internal @override - AutoDisposeAsyncNotifierProviderElement createElement() { - return _FamilyClassProviderElement(this); + Regression3490Provider $copyWithCreate( + Regression3490 Function() create, + ) { + return Regression3490Provider._( + argument: argument as ({ + String type, + Regression3490Cb getData, + String? parentId, + }), + from: from! as Regression3490Family, + create: create); + } + + @$internal + @override + Regression3490Provider $copyWithBuild( + void Function( + Ref, + Regression3490, + ) build, + ) { + return Regression3490Provider._( + argument: argument as ({ + String type, + Regression3490Cb getData, + String? parentId, + }), + from: from! as Regression3490Family, + runNotifierBuildOverride: build); } + @$internal + @override + $NotifierProviderElement, void> + $createElement($ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + @override bool operator ==(Object other) { - return other is FamilyClassProvider && - other.first == first && - other.second == second && - other.third == third && - other.fourth == fourth && - other.fifth == fifth; + return other is Regression3490Provider && + other.runtimeType == runtimeType && + other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, first.hashCode); - hash = _SystemHash.combine(hash, second.hashCode); - hash = _SystemHash.combine(hash, third.hashCode); - hash = _SystemHash.combine(hash, fourth.hashCode); - hash = _SystemHash.combine(hash, fifth.hashCode); - - return _SystemHash.finish(hash); + return Object.hash(runtimeType, argument); } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FamilyClassRef on AutoDisposeAsyncNotifierProviderRef { - /// The parameter `first` of this provider. - int get first; +String _$regression3490Hash() => r'9d5d48cbde589961d0cdac395f68111ec17b194a'; - /// The parameter `second` of this provider. - String? get second; +final class Regression3490Family extends Family { + const Regression3490Family._() + : super( + retry: null, + name: r'regression3490Provider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); - /// The parameter `third` of this provider. - double get third; + Regression3490Provider call({ + required String type, + required Regression3490Cb getData, + String? parentId, + }) => + Regression3490Provider._(argument: ( + type: type, + getData: getData, + parentId: parentId, + ), from: this); - /// The parameter `fourth` of this provider. - bool get fourth; + @override + String debugGetCreateSourceHash() => _$regression3490Hash(); - /// The parameter `fifth` of this provider. - List? get fifth; -} + @override + String toString() => r'regression3490Provider'; -class _FamilyClassProviderElement - extends AutoDisposeAsyncNotifierProviderElement - with FamilyClassRef { - _FamilyClassProviderElement(super.provider); + /// {@macro riverpod.override_with} + Override overrideWith( + Regression3490 Function( + ({ + String type, + Regression3490Cb getData, + String? parentId, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as Regression3490Provider; + + return provider._copyWithCreate(() { + final argument = provider.argument as ({ + String type, + Regression3490Cb getData, + String? parentId, + }); + + return create(argument); + }).$createElement(pointer); + }, + ); + } + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + void Function( + Ref ref, + Regression3490 notifier, + ({ + String type, + Regression3490Cb getData, + String? parentId, + }) argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as Regression3490Provider; + + return provider._copyWithBuild((ref, notifier) { + final argument = provider.argument as ({ + String type, + Regression3490Cb getData, + String? parentId, + }); + + return build(ref, notifier, argument); + }).$createElement(pointer); + }, + ); + } +} + +abstract class _$Regression3490 extends $Notifier { + late final _$args = ref.$arg as ({ + String type, + Regression3490Cb getData, + String? parentId, + }); + String get type => _$args.type; + Regression3490Cb get getData => _$args.getData; + String? get parentId => _$args.parentId; + + void build({ + required String type, + required Regression3490Cb getData, + String? parentId, + }); + @$internal @override - int get first => (origin as FamilyClassProvider).first; - @override - String? get second => (origin as FamilyClassProvider).second; - @override - double get third => (origin as FamilyClassProvider).third; - @override - bool get fourth => (origin as FamilyClassProvider).fourth; - @override - List? get fifth => (origin as FamilyClassProvider).fifth; + void runBuild() => build( + type: _$args.type, + getData: _$args.getData, + parentId: _$args.parentId, + ); } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/test/integration/auto_dispose.dart b/packages/riverpod_generator/test/integration/auto_dispose.dart index 4be1319b3..26c824040 100644 --- a/packages/riverpod_generator/test/integration/auto_dispose.dart +++ b/packages/riverpod_generator/test/integration/auto_dispose.dart @@ -1,4 +1,3 @@ -import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'auto_dispose.g.dart'; @@ -13,3 +12,24 @@ int notKeepAlive(Ref ref) { ref.keepAlive(); return 0; } + +@riverpod +int defaultKeepAlive(Ref ref) { + return 0; +} + +@Riverpod(keepAlive: true) +int keepAliveFamily(Ref ref, int a) { + return 0; +} + +@Riverpod(keepAlive: false) +int notKeepAliveFamily(Ref ref, int a) { + ref.keepAlive(); + return 0; +} + +@riverpod +int defaultKeepAliveFamily(Ref ref, int a) { + return 0; +} diff --git a/packages/riverpod_generator/test/integration/auto_dispose.g.dart b/packages/riverpod_generator/test/integration/auto_dispose.g.dart index a1efc0726..a207548e0 100644 --- a/packages/riverpod_generator/test/integration/auto_dispose.g.dart +++ b/packages/riverpod_generator/test/integration/auto_dispose.g.dart @@ -6,37 +6,574 @@ part of 'auto_dispose.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(keepAlive) +const keepAliveProvider = KeepAliveProvider._(); + +final class KeepAliveProvider extends $FunctionalProvider + with $Provider { + const KeepAliveProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'keepAliveProvider', + isAutoDispose: false, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$keepAliveHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + KeepAliveProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return KeepAliveProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? keepAlive; + return _$cb(ref); + } +} + String _$keepAliveHash() => r'44af50bf7e6dcfddc61a1f32855855b534a7fe4f'; -/// See also [keepAlive]. -@ProviderFor(keepAlive) -final keepAliveProvider = Provider.internal( - keepAlive, - name: r'keepAliveProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$keepAliveHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef KeepAliveRef = ProviderRef; +@ProviderFor(notKeepAlive) +const notKeepAliveProvider = NotKeepAliveProvider._(); + +final class NotKeepAliveProvider extends $FunctionalProvider + with $Provider { + const NotKeepAliveProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'notKeepAliveProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$notKeepAliveHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + NotKeepAliveProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return NotKeepAliveProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? notKeepAlive; + return _$cb(ref); + } +} + String _$notKeepAliveHash() => r'e60c952d04ffd7548294908c2e1ef472614c284b'; -/// See also [notKeepAlive]. -@ProviderFor(notKeepAlive) -final notKeepAliveProvider = AutoDisposeProvider.internal( - notKeepAlive, - name: r'notKeepAliveProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$notKeepAliveHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef NotKeepAliveRef = AutoDisposeProviderRef; +@ProviderFor(defaultKeepAlive) +const defaultKeepAliveProvider = DefaultKeepAliveProvider._(); + +final class DefaultKeepAliveProvider extends $FunctionalProvider + with $Provider { + const DefaultKeepAliveProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'defaultKeepAliveProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$defaultKeepAliveHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + DefaultKeepAliveProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return DefaultKeepAliveProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? defaultKeepAlive; + return _$cb(ref); + } +} + +String _$defaultKeepAliveHash() => r'76485c3c7574c38dcba6dda28c94a59c09b339c0'; + +@ProviderFor(keepAliveFamily) +const keepAliveFamilyProvider = KeepAliveFamilyFamily._(); + +final class KeepAliveFamilyProvider extends $FunctionalProvider + with $Provider { + const KeepAliveFamilyProvider._( + {required KeepAliveFamilyFamily super.from, + required int super.argument, + int Function( + Ref ref, + int a, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'keepAliveFamilyProvider', + isAutoDispose: false, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + int a, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$keepAliveFamilyHash(); + + @override + String toString() { + return r'keepAliveFamilyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + KeepAliveFamilyProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return KeepAliveFamilyProvider._( + argument: argument as int, + from: from! as KeepAliveFamilyFamily, + create: ( + ref, + int a, + ) => + create(ref)); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? keepAliveFamily; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is KeepAliveFamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$keepAliveFamilyHash() => r'd1eb1243ea9463617b08a6e9cc5ae6b2df499ba2'; + +final class KeepAliveFamilyFamily extends Family { + const KeepAliveFamilyFamily._() + : super( + retry: null, + name: r'keepAliveFamilyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: false, + ); + + KeepAliveFamilyProvider call( + int a, + ) => + KeepAliveFamilyProvider._(argument: a, from: this); + + @override + String debugGetCreateSourceHash() => _$keepAliveFamilyHash(); + + @override + String toString() => r'keepAliveFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + int Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as KeepAliveFamilyProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +@ProviderFor(notKeepAliveFamily) +const notKeepAliveFamilyProvider = NotKeepAliveFamilyFamily._(); + +final class NotKeepAliveFamilyProvider extends $FunctionalProvider + with $Provider { + const NotKeepAliveFamilyProvider._( + {required NotKeepAliveFamilyFamily super.from, + required int super.argument, + int Function( + Ref ref, + int a, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'notKeepAliveFamilyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + int a, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$notKeepAliveFamilyHash(); + + @override + String toString() { + return r'notKeepAliveFamilyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + NotKeepAliveFamilyProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return NotKeepAliveFamilyProvider._( + argument: argument as int, + from: from! as NotKeepAliveFamilyFamily, + create: ( + ref, + int a, + ) => + create(ref)); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? notKeepAliveFamily; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is NotKeepAliveFamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$notKeepAliveFamilyHash() => + r'a721713b026088b65be6c0f7f9beb1083a377a7c'; + +final class NotKeepAliveFamilyFamily extends Family { + const NotKeepAliveFamilyFamily._() + : super( + retry: null, + name: r'notKeepAliveFamilyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + NotKeepAliveFamilyProvider call( + int a, + ) => + NotKeepAliveFamilyProvider._(argument: a, from: this); + + @override + String debugGetCreateSourceHash() => _$notKeepAliveFamilyHash(); + + @override + String toString() => r'notKeepAliveFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + int Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as NotKeepAliveFamilyProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +@ProviderFor(defaultKeepAliveFamily) +const defaultKeepAliveFamilyProvider = DefaultKeepAliveFamilyFamily._(); + +final class DefaultKeepAliveFamilyProvider extends $FunctionalProvider + with $Provider { + const DefaultKeepAliveFamilyProvider._( + {required DefaultKeepAliveFamilyFamily super.from, + required int super.argument, + int Function( + Ref ref, + int a, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'defaultKeepAliveFamilyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + int a, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$defaultKeepAliveFamilyHash(); + + @override + String toString() { + return r'defaultKeepAliveFamilyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + DefaultKeepAliveFamilyProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return DefaultKeepAliveFamilyProvider._( + argument: argument as int, + from: from! as DefaultKeepAliveFamilyFamily, + create: ( + ref, + int a, + ) => + create(ref)); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? defaultKeepAliveFamily; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is DefaultKeepAliveFamilyProvider && + other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$defaultKeepAliveFamilyHash() => + r'e79f3d9ccd6713aade34c1701699c578f9236e9e'; + +final class DefaultKeepAliveFamilyFamily extends Family { + const DefaultKeepAliveFamilyFamily._() + : super( + retry: null, + name: r'defaultKeepAliveFamilyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + DefaultKeepAliveFamilyProvider call( + int a, + ) => + DefaultKeepAliveFamilyProvider._(argument: a, from: this); + + @override + String debugGetCreateSourceHash() => _$defaultKeepAliveFamilyHash(); + + @override + String toString() => r'defaultKeepAliveFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + int Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as DefaultKeepAliveFamilyProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/test/integration/dependencies.dart b/packages/riverpod_generator/test/integration/dependencies.dart index 68e109aed..c350b1ee3 100644 --- a/packages/riverpod_generator/test/integration/dependencies.dart +++ b/packages/riverpod_generator/test/integration/dependencies.dart @@ -1,4 +1,3 @@ -import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'dependencies.g.dart'; @@ -64,3 +63,12 @@ int _privateDep(Ref ref) => 0; @riverpod int publicDep(Ref ref) => 0; + +@Riverpod(dependencies: [dep, dep, Dep2, Dep2]) +int duplicateDependencies(Ref ref) => 0; + +@Riverpod(dependencies: [family, family, Family2, Family2]) +int duplicateDependencies2(Ref ref) => 0; + +@Riverpod(dependencies: [duplicateDependencies, duplicateDependencies2]) +int transitiveDuplicateDependencies(Ref ref) => 0; diff --git a/packages/riverpod_generator/test/integration/dependencies.g.dart b/packages/riverpod_generator/test/integration/dependencies.g.dart index 5e8cd2862..7e88c4c4c 100644 --- a/packages/riverpod_generator/test/integration/dependencies.g.dart +++ b/packages/riverpod_generator/test/integration/dependencies.g.dart @@ -6,709 +6,1508 @@ part of 'dependencies.dart'; // RiverpodGenerator // ************************************************************************** -String _$depHash() => r'1b3ec5231cd2328602151de9ceacdcd102a1d2e2'; - -/// See also [dep]. @ProviderFor(dep) -final depProvider = AutoDisposeProvider.internal( - dep, - name: r'depProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$depHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef DepRef = AutoDisposeProviderRef; -String _$familyHash() => r'940eb87eb11206499f73f05791a6266b38cda88a'; +const depProvider = DepProvider._(); + +final class DepProvider extends $FunctionalProvider + with $Provider { + const DepProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'depProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); + final int Function( + Ref ref, + )? _createCb; - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); + @override + String debugGetCreateSourceHash() => _$depHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); } - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + DepProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return DepProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? dep; + return _$cb(ref); } } -/// See also [family]. -@ProviderFor(family) -const familyProvider = FamilyFamily(); +String _$depHash() => r'1b3ec5231cd2328602151de9ceacdcd102a1d2e2'; -/// See also [family]. -class FamilyFamily extends Family { - /// See also [family]. - const FamilyFamily(); +@ProviderFor(family) +const familyProvider = FamilyFamily._(); + +final class FamilyProvider extends $FunctionalProvider + with $Provider { + const FamilyProvider._( + {required FamilyFamily super.from, + required int super.argument, + int Function( + Ref ref, + int id, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'familyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// See also [family]. - FamilyProvider call( + final int Function( + Ref ref, int id, - ) { - return FamilyProvider( - id, - ); - } + )? _createCb; @override - FamilyProvider getProviderOverride( - covariant FamilyProvider provider, - ) { - return call( - provider.id, + String debugGetCreateSourceHash() => _$familyHash(); + + @override + String toString() { + return r'familyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } - static const Iterable? _dependencies = null; + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - Iterable? get dependencies => _dependencies; + FamilyProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return FamilyProvider._( + argument: argument as int, + from: from! as FamilyFamily, + create: ( + ref, + int id, + ) => + create(ref)); + } - static const Iterable? _allTransitiveDependencies = null; + @override + int create(Ref ref) { + final _$cb = _createCb ?? family; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); + } @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + bool operator ==(Object other) { + return other is FamilyProvider && other.argument == argument; + } @override - String? get name => r'familyProvider'; + int get hashCode { + return argument.hashCode; + } } -/// See also [family]. -class FamilyProvider extends AutoDisposeProvider { - /// See also [family]. - FamilyProvider( - int id, - ) : this._internal( - (ref) => family( - ref as FamilyRef, - id, - ), - from: familyProvider, +String _$familyHash() => r'940eb87eb11206499f73f05791a6266b38cda88a'; + +final class FamilyFamily extends Family { + const FamilyFamily._() + : super( + retry: null, name: r'familyProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$familyHash, - dependencies: FamilyFamily._dependencies, - allTransitiveDependencies: FamilyFamily._allTransitiveDependencies, - id: id, + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, ); - FamilyProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.id, - }) : super.internal(); + FamilyProvider call( + int id, + ) => + FamilyProvider._(argument: id, from: this); - final int id; + @override + String debugGetCreateSourceHash() => _$familyHash(); @override + String toString() => r'familyProvider'; + + /// {@macro riverpod.override_with} Override overrideWith( - int Function(FamilyRef provider) create, + int Function( + Ref ref, + int args, + ) create, ) { - return ProviderOverride( - origin: this, - override: FamilyProvider._internal( - (ref) => create(ref as FamilyRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - id: id, - ), + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, ); } +} + +@ProviderFor(Dep2) +const dep2Provider = Dep2Provider._(); + +final class Dep2Provider extends $NotifierProvider { + const Dep2Provider._( + {super.runNotifierBuildOverride, Dep2 Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'dep2Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Dep2 Function()? _createCb; @override - AutoDisposeProviderElement createElement() { - return _FamilyProviderElement(this); + String debugGetCreateSourceHash() => _$dep2Hash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); } + @$internal @override - bool operator ==(Object other) { - return other is FamilyProvider && other.id == id; - } + Dep2 create() => _createCb?.call() ?? Dep2(); + @$internal @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, id.hashCode); + Dep2Provider $copyWithCreate( + Dep2 Function() create, + ) { + return Dep2Provider._(create: create); + } - return _SystemHash.finish(hash); + @$internal + @override + Dep2Provider $copyWithBuild( + int Function( + Ref, + Dep2, + ) build, + ) { + return Dep2Provider._(runNotifierBuildOverride: build); } -} -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FamilyRef on AutoDisposeProviderRef { - /// The parameter `id` of this provider. - int get id; + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); } -class _FamilyProviderElement extends AutoDisposeProviderElement - with FamilyRef { - _FamilyProviderElement(super.provider); +String _$dep2Hash() => r'2778537df77f6431148c2ce400724da3e2ab4b94'; +abstract class _$Dep2 extends $Notifier { + int build(); + @$internal @override - int get id => (origin as FamilyProvider).id; + int runBuild() => build(); } -String _$providerHash() => r'1be7ae7ac2100d39b949af50ec50fce48b26cdd1'; +@ProviderFor(Family2) +const family2Provider = Family2Family._(); + +final class Family2Provider extends $NotifierProvider { + const Family2Provider._( + {required Family2Family super.from, + required int super.argument, + super.runNotifierBuildOverride, + Family2 Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'family2Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [provider]. -@ProviderFor(provider) -final providerProvider = AutoDisposeProvider.internal( - provider, - name: r'providerProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$providerHash, - dependencies: { - depProvider, - familyProvider, - dep2Provider, - family2Provider - }, - allTransitiveDependencies: { - depProvider, - ...?depProvider.allTransitiveDependencies, - familyProvider, - ...?familyProvider.allTransitiveDependencies, - dep2Provider, - ...?dep2Provider.allTransitiveDependencies, - family2Provider, - ...?family2Provider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ProviderRef = AutoDisposeProviderRef; -String _$provider2Hash() => r'30f81430b57f0116f621a4a309c458fce0536378'; + final Family2 Function()? _createCb; -/// See also [provider2]. -@ProviderFor(provider2) -final provider2Provider = AutoDisposeProvider.internal( - provider2, - name: r'provider2Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$provider2Hash, - dependencies: { - depProvider, - familyProvider, - dep2Provider, - family2Provider - }, - allTransitiveDependencies: { - depProvider, - ...?depProvider.allTransitiveDependencies, - familyProvider, - ...?familyProvider.allTransitiveDependencies, - dep2Provider, - ...?dep2Provider.allTransitiveDependencies, - family2Provider, - ...?family2Provider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef Provider2Ref = AutoDisposeProviderRef; -String _$transitiveDependenciesHash() => - r'909d45403831b521177ec15b1dd78554e261d3be'; + @override + String debugGetCreateSourceHash() => _$family2Hash(); -/// See also [transitiveDependencies]. -@ProviderFor(transitiveDependencies) -final transitiveDependenciesProvider = AutoDisposeProvider.internal( - transitiveDependencies, - name: r'transitiveDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$transitiveDependenciesHash, - dependencies: [providerProvider], - allTransitiveDependencies: { - providerProvider, - ...?providerProvider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef TransitiveDependenciesRef = AutoDisposeProviderRef; -String _$smallTransitiveDependencyCountHash() => - r'f67b369dd99e35a6e6211004b45c87c5ba4ac1c7'; + @override + String toString() { + return r'family2Provider' + '' + '($argument)'; + } -/// See also [smallTransitiveDependencyCount]. -@ProviderFor(smallTransitiveDependencyCount) -final smallTransitiveDependencyCountProvider = - AutoDisposeProvider.internal( - smallTransitiveDependencyCount, - name: r'smallTransitiveDependencyCountProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$smallTransitiveDependencyCountHash, - dependencies: [depProvider, familyProvider, dep2Provider], - allTransitiveDependencies: { - depProvider, - ...?depProvider.allTransitiveDependencies, - familyProvider, - ...?familyProvider.allTransitiveDependencies, - dep2Provider, - ...?dep2Provider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef SmallTransitiveDependencyCountRef = AutoDisposeProviderRef; -String _$emptyDependenciesFunctionalHash() => - r'77289071cab8a10da8f5b7b40932864510a1ee38'; + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } -/// See also [emptyDependenciesFunctional]. -@ProviderFor(emptyDependenciesFunctional) -final emptyDependenciesFunctionalProvider = AutoDisposeProvider.internal( - emptyDependenciesFunctional, - name: r'emptyDependenciesFunctionalProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$emptyDependenciesFunctionalHash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef EmptyDependenciesFunctionalRef = AutoDisposeProviderRef; -String _$providerWithDependenciesHash() => - r'7d40c994fc2d4ba9e6a0bb4a3d100f8da874eb5e'; + @$internal + @override + Family2 create() => _createCb?.call() ?? Family2(); -/// See also [providerWithDependencies]. -@ProviderFor(providerWithDependencies) -final providerWithDependenciesProvider = AutoDisposeProvider.internal( - providerWithDependencies, - name: r'providerWithDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$providerWithDependenciesHash, - dependencies: [_privateDepProvider, publicDepProvider], - allTransitiveDependencies: { - _privateDepProvider, - ...?_privateDepProvider.allTransitiveDependencies, - publicDepProvider, - ...?publicDepProvider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ProviderWithDependenciesRef = AutoDisposeProviderRef; -String _$privateDepHash() => r'92ff5cc515ecf2455cb04773f1b49f23b17ea2e2'; + @$internal + @override + Family2Provider $copyWithCreate( + Family2 Function() create, + ) { + return Family2Provider._( + argument: argument as int, + from: from! as Family2Family, + create: create); + } -/// See also [_privateDep]. -@ProviderFor(_privateDep) -final _privateDepProvider = AutoDisposeProvider.internal( - _privateDep, - name: r'_privateDepProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$privateDepHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef _PrivateDepRef = AutoDisposeProviderRef; -String _$publicDepHash() => r'a9c461ae174577183ab4c0ff8d8267cc7a64a2c5'; + @$internal + @override + Family2Provider $copyWithBuild( + int Function( + Ref, + Family2, + ) build, + ) { + return Family2Provider._( + argument: argument as int, + from: from! as Family2Family, + runNotifierBuildOverride: build); + } -/// See also [publicDep]. -@ProviderFor(publicDep) -final publicDepProvider = AutoDisposeProvider.internal( - publicDep, - name: r'publicDepProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$publicDepHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef PublicDepRef = AutoDisposeProviderRef; -String _$dep2Hash() => r'2778537df77f6431148c2ce400724da3e2ab4b94'; + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is Family2Provider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} -/// See also [Dep2]. -@ProviderFor(Dep2) -final dep2Provider = AutoDisposeNotifierProvider.internal( - Dep2.new, - name: r'dep2Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$dep2Hash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Dep2 = AutoDisposeNotifier; String _$family2Hash() => r'ce727b262aae067b0d4f703f03670abb70ad8977'; -abstract class _$Family2 extends BuildlessAutoDisposeNotifier { - late final int id; +final class Family2Family extends Family { + const Family2Family._() + : super( + retry: null, + name: r'family2Provider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + Family2Provider call( + int id, + ) => + Family2Provider._(argument: id, from: this); + + @override + String debugGetCreateSourceHash() => _$family2Hash(); + + @override + String toString() => r'family2Provider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Family2 Function( + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as Family2Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function(Ref ref, Family2 notifier, int argument) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as Family2Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} + +abstract class _$Family2 extends $Notifier { + late final _$args = ref.$arg as int; + int get id => _$args; int build( int id, ); + @$internal + @override + int runBuild() => build( + _$args, + ); } -/// See also [Family2]. -@ProviderFor(Family2) -const family2Provider = Family2Family(); +@ProviderFor(provider) +const providerProvider = ProviderProvider._(); + +final class ProviderProvider extends $FunctionalProvider + with $Provider { + const ProviderProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'providerProvider', + isAutoDispose: true, + dependencies: const [ + depProvider, + familyProvider, + dep2Provider, + family2Provider + ], + allTransitiveDependencies: const { + ProviderProvider.$allTransitiveDependencies0, + ProviderProvider.$allTransitiveDependencies1, + ProviderProvider.$allTransitiveDependencies2, + ProviderProvider.$allTransitiveDependencies3, + }, + ); -/// See also [Family2]. -class Family2Family extends Family { - /// See also [Family2]. - const Family2Family(); + static const $allTransitiveDependencies0 = depProvider; + static const $allTransitiveDependencies1 = familyProvider; + static const $allTransitiveDependencies2 = dep2Provider; + static const $allTransitiveDependencies3 = family2Provider; - /// See also [Family2]. - Family2Provider call( - int id, - ) { - return Family2Provider( - id, + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$providerHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + @override - Family2Provider getProviderOverride( - covariant Family2Provider provider, + ProviderProvider $copyWithCreate( + int Function( + Ref ref, + ) create, ) { - return call( - provider.id, - ); + return ProviderProvider._(create: create); } - static const Iterable? _dependencies = null; + @override + int create(Ref ref) { + final _$cb = _createCb ?? provider; + return _$cb(ref); + } +} + +String _$providerHash() => r'1be7ae7ac2100d39b949af50ec50fce48b26cdd1'; + +@ProviderFor(provider2) +const provider2Provider = Provider2Provider._(); + +final class Provider2Provider extends $FunctionalProvider + with $Provider { + const Provider2Provider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'provider2Provider', + isAutoDispose: true, + dependencies: const [ + depProvider, + familyProvider, + dep2Provider, + family2Provider + ], + allTransitiveDependencies: const { + Provider2Provider.$allTransitiveDependencies0, + Provider2Provider.$allTransitiveDependencies1, + Provider2Provider.$allTransitiveDependencies2, + Provider2Provider.$allTransitiveDependencies3, + }, + ); + + static const $allTransitiveDependencies0 = depProvider; + static const $allTransitiveDependencies1 = familyProvider; + static const $allTransitiveDependencies2 = dep2Provider; + static const $allTransitiveDependencies3 = family2Provider; + + final int Function( + Ref ref, + )? _createCb; @override - Iterable? get dependencies => _dependencies; + String debugGetCreateSourceHash() => _$provider2Hash(); - static const Iterable? _allTransitiveDependencies = null; + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + Provider2Provider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return Provider2Provider._(create: create); + } @override - String? get name => r'family2Provider'; + int create(Ref ref) { + final _$cb = _createCb ?? provider2; + return _$cb(ref); + } } -/// See also [Family2]. -class Family2Provider extends AutoDisposeNotifierProviderImpl { - /// See also [Family2]. - Family2Provider( - int id, - ) : this._internal( - () => Family2()..id = id, - from: family2Provider, - name: r'family2Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$family2Hash, - dependencies: Family2Family._dependencies, - allTransitiveDependencies: Family2Family._allTransitiveDependencies, - id: id, +String _$provider2Hash() => r'30f81430b57f0116f621a4a309c458fce0536378'; + +@ProviderFor(Provider3) +const provider3Provider = Provider3Provider._(); + +final class Provider3Provider extends $NotifierProvider { + const Provider3Provider._( + {super.runNotifierBuildOverride, Provider3 Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'provider3Provider', + isAutoDispose: true, + dependencies: const [ + depProvider, + familyProvider, + dep2Provider, + family2Provider + ], + allTransitiveDependencies: const { + Provider3Provider.$allTransitiveDependencies0, + Provider3Provider.$allTransitiveDependencies1, + Provider3Provider.$allTransitiveDependencies2, + Provider3Provider.$allTransitiveDependencies3, + }, ); - Family2Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.id, - }) : super.internal(); + static const $allTransitiveDependencies0 = depProvider; + static const $allTransitiveDependencies1 = familyProvider; + static const $allTransitiveDependencies2 = dep2Provider; + static const $allTransitiveDependencies3 = family2Provider; - final int id; + final Provider3 Function()? _createCb; @override - int runNotifierBuild( - covariant Family2 notifier, - ) { - return notifier.build( - id, + String debugGetCreateSourceHash() => _$provider3Hash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } + @$internal @override - Override overrideWith(Family2 Function() create) { - return ProviderOverride( + Provider3 create() => _createCb?.call() ?? Provider3(); + + @$internal + @override + Provider3Provider $copyWithCreate( + Provider3 Function() create, + ) { + return Provider3Provider._(create: create); + } + + @$internal + @override + Provider3Provider $copyWithBuild( + int Function( + Ref, + Provider3, + ) build, + ) { + return Provider3Provider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$provider3Hash() => r'dfdd6dec6cfee543c73d99593ce98d68f4db385c'; + +abstract class _$Provider3 extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + +@ProviderFor(Provider4) +const provider4Provider = Provider4Family._(); + +final class Provider4Provider extends $NotifierProvider { + const Provider4Provider._( + {required Provider4Family super.from, + required int super.argument, + super.runNotifierBuildOverride, + Provider4 Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'provider4Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + static const $allTransitiveDependencies0 = depProvider; + static const $allTransitiveDependencies1 = familyProvider; + static const $allTransitiveDependencies2 = dep2Provider; + static const $allTransitiveDependencies3 = family2Provider; + + final Provider4 Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$provider4Hash(); + + @override + String toString() { + return r'provider4Provider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( origin: this, - override: Family2Provider._internal( - () => create()..id = id, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - id: id, - ), + providerOverride: $ValueProvider(value), ); } + @$internal @override - AutoDisposeNotifierProviderElement createElement() { - return _Family2ProviderElement(this); + Provider4 create() => _createCb?.call() ?? Provider4(); + + @$internal + @override + Provider4Provider $copyWithCreate( + Provider4 Function() create, + ) { + return Provider4Provider._( + argument: argument as int, + from: from! as Provider4Family, + create: create); + } + + @$internal + @override + Provider4Provider $copyWithBuild( + int Function( + Ref, + Provider4, + ) build, + ) { + return Provider4Provider._( + argument: argument as int, + from: from! as Provider4Family, + runNotifierBuildOverride: build); } + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + @override bool operator ==(Object other) { - return other is Family2Provider && other.id == id; + return other is Provider4Provider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, id.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin Family2Ref on AutoDisposeNotifierProviderRef { - /// The parameter `id` of this provider. - int get id; -} +String _$provider4Hash() => r'1c955214d99695bb694c96374b277aac58e734df'; + +final class Provider4Family extends Family { + const Provider4Family._() + : super( + retry: null, + name: r'provider4Provider', + dependencies: const [ + depProvider, + familyProvider, + dep2Provider, + family2Provider + ], + allTransitiveDependencies: const { + Provider4Provider.$allTransitiveDependencies0, + Provider4Provider.$allTransitiveDependencies1, + Provider4Provider.$allTransitiveDependencies2, + Provider4Provider.$allTransitiveDependencies3, + }, + isAutoDispose: true, + ); -class _Family2ProviderElement - extends AutoDisposeNotifierProviderElement with Family2Ref { - _Family2ProviderElement(super.provider); + Provider4Provider call( + int id, + ) => + Provider4Provider._(argument: id, from: this); @override - int get id => (origin as Family2Provider).id; -} + String debugGetCreateSourceHash() => _$provider4Hash(); -String _$provider3Hash() => r'dfdd6dec6cfee543c73d99593ce98d68f4db385c'; + @override + String toString() => r'provider4Provider'; -/// See also [Provider3]. -@ProviderFor(Provider3) -final provider3Provider = AutoDisposeNotifierProvider.internal( - Provider3.new, - name: r'provider3Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$provider3Hash, - dependencies: { - depProvider, - familyProvider, - dep2Provider, - family2Provider - }, - allTransitiveDependencies: { - depProvider, - ...?depProvider.allTransitiveDependencies, - familyProvider, - ...?familyProvider.allTransitiveDependencies, - dep2Provider, - ...?dep2Provider.allTransitiveDependencies, - family2Provider, - ...?family2Provider.allTransitiveDependencies - }, -); - -typedef _$Provider3 = AutoDisposeNotifier; -String _$provider4Hash() => r'1c955214d99695bb694c96374b277aac58e734df'; + /// {@macro riverpod.override_with} + Override overrideWith( + Provider4 Function( + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as Provider4Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function(Ref ref, Provider4 notifier, int argument) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as Provider4Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} -abstract class _$Provider4 extends BuildlessAutoDisposeNotifier { - late final int id; +abstract class _$Provider4 extends $Notifier { + late final _$args = ref.$arg as int; + int get id => _$args; int build( int id, ); + @$internal + @override + int runBuild() => build( + _$args, + ); } -/// See also [Provider4]. -@ProviderFor(Provider4) -const provider4Provider = Provider4Family(); +@ProviderFor(transitiveDependencies) +const transitiveDependenciesProvider = TransitiveDependenciesProvider._(); + +final class TransitiveDependenciesProvider extends $FunctionalProvider + with $Provider { + const TransitiveDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'transitiveDependenciesProvider', + isAutoDispose: true, + dependencies: const [providerProvider], + allTransitiveDependencies: const { + TransitiveDependenciesProvider.$allTransitiveDependencies0, + TransitiveDependenciesProvider.$allTransitiveDependencies1, + TransitiveDependenciesProvider.$allTransitiveDependencies2, + TransitiveDependenciesProvider.$allTransitiveDependencies3, + TransitiveDependenciesProvider.$allTransitiveDependencies4, + }, + ); -/// See also [Provider4]. -class Provider4Family extends Family { - /// See also [Provider4]. - const Provider4Family(); + static const $allTransitiveDependencies0 = providerProvider; + static const $allTransitiveDependencies1 = + ProviderProvider.$allTransitiveDependencies0; + static const $allTransitiveDependencies2 = + ProviderProvider.$allTransitiveDependencies1; + static const $allTransitiveDependencies3 = + ProviderProvider.$allTransitiveDependencies2; + static const $allTransitiveDependencies4 = + ProviderProvider.$allTransitiveDependencies3; - /// See also [Provider4]. - Provider4Provider call( - int id, + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$transitiveDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + TransitiveDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, ) { - return Provider4Provider( - id, + return TransitiveDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? transitiveDependencies; + return _$cb(ref); + } +} + +String _$transitiveDependenciesHash() => + r'909d45403831b521177ec15b1dd78554e261d3be'; + +@ProviderFor(smallTransitiveDependencyCount) +const smallTransitiveDependencyCountProvider = + SmallTransitiveDependencyCountProvider._(); + +final class SmallTransitiveDependencyCountProvider + extends $FunctionalProvider with $Provider { + const SmallTransitiveDependencyCountProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'smallTransitiveDependencyCountProvider', + isAutoDispose: true, + dependencies: const [ + depProvider, + familyProvider, + dep2Provider + ], + allTransitiveDependencies: const [ + SmallTransitiveDependencyCountProvider.$allTransitiveDependencies0, + SmallTransitiveDependencyCountProvider.$allTransitiveDependencies1, + SmallTransitiveDependencyCountProvider.$allTransitiveDependencies2, + ], + ); + + static const $allTransitiveDependencies0 = depProvider; + static const $allTransitiveDependencies1 = familyProvider; + static const $allTransitiveDependencies2 = dep2Provider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$smallTransitiveDependencyCountHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } + @$internal @override - Provider4Provider getProviderOverride( - covariant Provider4Provider provider, + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + SmallTransitiveDependencyCountProvider $copyWithCreate( + int Function( + Ref ref, + ) create, ) { - return call( - provider.id, + return SmallTransitiveDependencyCountProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? smallTransitiveDependencyCount; + return _$cb(ref); + } +} + +String _$smallTransitiveDependencyCountHash() => + r'f67b369dd99e35a6e6211004b45c87c5ba4ac1c7'; + +@ProviderFor(emptyDependenciesFunctional) +const emptyDependenciesFunctionalProvider = + EmptyDependenciesFunctionalProvider._(); + +final class EmptyDependenciesFunctionalProvider + extends $FunctionalProvider with $Provider { + const EmptyDependenciesFunctionalProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'emptyDependenciesFunctionalProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$emptyDependenciesFunctionalHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } - static final Iterable _dependencies = { - depProvider, - familyProvider, - dep2Provider, - family2Provider - }; + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + EmptyDependenciesFunctionalProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return EmptyDependenciesFunctionalProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? emptyDependenciesFunctional; + return _$cb(ref); + } +} + +String _$emptyDependenciesFunctionalHash() => + r'77289071cab8a10da8f5b7b40932864510a1ee38'; + +@ProviderFor(EmptyDependenciesClassBased) +const emptyDependenciesClassBasedProvider = + EmptyDependenciesClassBasedProvider._(); + +final class EmptyDependenciesClassBasedProvider + extends $NotifierProvider { + const EmptyDependenciesClassBasedProvider._( + {super.runNotifierBuildOverride, + EmptyDependenciesClassBased Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'emptyDependenciesClassBasedProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final EmptyDependenciesClassBased Function()? _createCb; @override - Iterable? get dependencies => _dependencies; + String debugGetCreateSourceHash() => _$emptyDependenciesClassBasedHash(); - static final Iterable _allTransitiveDependencies = - { - depProvider, - ...?depProvider.allTransitiveDependencies, - familyProvider, - ...?familyProvider.allTransitiveDependencies, - dep2Provider, - ...?dep2Provider.allTransitiveDependencies, - family2Provider, - ...?family2Provider.allTransitiveDependencies - }; + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + @$internal @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + EmptyDependenciesClassBased create() => + _createCb?.call() ?? EmptyDependenciesClassBased(); + + @$internal + @override + EmptyDependenciesClassBasedProvider $copyWithCreate( + EmptyDependenciesClassBased Function() create, + ) { + return EmptyDependenciesClassBasedProvider._(create: create); + } + @$internal @override - String? get name => r'provider4Provider'; + EmptyDependenciesClassBasedProvider $copyWithBuild( + int Function( + Ref, + EmptyDependenciesClassBased, + ) build, + ) { + return EmptyDependenciesClassBasedProvider._( + runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); } -/// See also [Provider4]. -class Provider4Provider - extends AutoDisposeNotifierProviderImpl { - /// See also [Provider4]. - Provider4Provider( - int id, - ) : this._internal( - () => Provider4()..id = id, - from: provider4Provider, - name: r'provider4Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$provider4Hash, - dependencies: Provider4Family._dependencies, - allTransitiveDependencies: Provider4Family._allTransitiveDependencies, - id: id, +String _$emptyDependenciesClassBasedHash() => + r'e20c18353984a81977b656e9879b3841dbaedc6c'; + +abstract class _$EmptyDependenciesClassBased extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + +@ProviderFor(providerWithDependencies) +const providerWithDependenciesProvider = ProviderWithDependenciesProvider._(); + +final class ProviderWithDependenciesProvider + extends $FunctionalProvider with $Provider { + const ProviderWithDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'providerWithDependenciesProvider', + isAutoDispose: true, + dependencies: const [ + _privateDepProvider, + publicDepProvider + ], + allTransitiveDependencies: const [ + ProviderWithDependenciesProvider.$allTransitiveDependencies0, + ProviderWithDependenciesProvider.$allTransitiveDependencies1, + ], ); - Provider4Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.id, - }) : super.internal(); + static const $allTransitiveDependencies0 = _privateDepProvider; + static const $allTransitiveDependencies1 = publicDepProvider; - final int id; + final int Function( + Ref ref, + )? _createCb; @override - int runNotifierBuild( - covariant Provider4 notifier, + String debugGetCreateSourceHash() => _$providerWithDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ProviderWithDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, ) { - return notifier.build( - id, + return ProviderWithDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? providerWithDependencies; + return _$cb(ref); + } +} + +String _$providerWithDependenciesHash() => + r'7d40c994fc2d4ba9e6a0bb4a3d100f8da874eb5e'; + +@ProviderFor(_privateDep) +const _privateDepProvider = _PrivateDepProvider._(); + +final class _PrivateDepProvider extends $FunctionalProvider + with $Provider { + const _PrivateDepProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'_privateDepProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$privateDepHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + _PrivateDepProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return _PrivateDepProvider._(create: create); + } + @override - Override overrideWith(Provider4 Function() create) { - return ProviderOverride( + int create(Ref ref) { + final _$cb = _createCb ?? _privateDep; + return _$cb(ref); + } +} + +String _$privateDepHash() => r'92ff5cc515ecf2455cb04773f1b49f23b17ea2e2'; + +@ProviderFor(publicDep) +const publicDepProvider = PublicDepProvider._(); + +final class PublicDepProvider extends $FunctionalProvider + with $Provider { + const PublicDepProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'publicDepProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$publicDepHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( origin: this, - override: Provider4Provider._internal( - () => create()..id = id, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - id: id, - ), + providerOverride: $ValueProvider(value), ); } + @$internal @override - AutoDisposeNotifierProviderElement createElement() { - return _Provider4ProviderElement(this); + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + PublicDepProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return PublicDepProvider._(create: create); } @override - bool operator ==(Object other) { - return other is Provider4Provider && other.id == id; + int create(Ref ref) { + final _$cb = _createCb ?? publicDep; + return _$cb(ref); } +} + +String _$publicDepHash() => r'a9c461ae174577183ab4c0ff8d8267cc7a64a2c5'; + +@ProviderFor(duplicateDependencies) +const duplicateDependenciesProvider = DuplicateDependenciesProvider._(); + +final class DuplicateDependenciesProvider extends $FunctionalProvider + with $Provider { + const DuplicateDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'duplicateDependenciesProvider', + isAutoDispose: true, + dependencies: const [depProvider, dep2Provider], + allTransitiveDependencies: const [ + DuplicateDependenciesProvider.$allTransitiveDependencies0, + DuplicateDependenciesProvider.$allTransitiveDependencies1, + ], + ); + + static const $allTransitiveDependencies0 = depProvider; + static const $allTransitiveDependencies1 = dep2Provider; + + final int Function( + Ref ref, + )? _createCb; @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, id.hashCode); + String debugGetCreateSourceHash() => _$duplicateDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } - return _SystemHash.finish(hash); + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + DuplicateDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return DuplicateDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? duplicateDependencies; + return _$cb(ref); } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin Provider4Ref on AutoDisposeNotifierProviderRef { - /// The parameter `id` of this provider. - int get id; +String _$duplicateDependenciesHash() => + r'ad48ecca57899ee55c69793c84a01235d6a49834'; + +@ProviderFor(duplicateDependencies2) +const duplicateDependencies2Provider = DuplicateDependencies2Provider._(); + +final class DuplicateDependencies2Provider extends $FunctionalProvider + with $Provider { + const DuplicateDependencies2Provider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'duplicateDependencies2Provider', + isAutoDispose: true, + dependencies: const [ + familyProvider, + family2Provider + ], + allTransitiveDependencies: const [ + DuplicateDependencies2Provider.$allTransitiveDependencies0, + DuplicateDependencies2Provider.$allTransitiveDependencies1, + ], + ); + + static const $allTransitiveDependencies0 = familyProvider; + static const $allTransitiveDependencies1 = family2Provider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$duplicateDependencies2Hash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + DuplicateDependencies2Provider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return DuplicateDependencies2Provider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? duplicateDependencies2; + return _$cb(ref); + } } -class _Provider4ProviderElement - extends AutoDisposeNotifierProviderElement - with Provider4Ref { - _Provider4ProviderElement(super.provider); +String _$duplicateDependencies2Hash() => + r'6e065325922dc36f408f85998cf2d7ba7a80ba56'; + +@ProviderFor(transitiveDuplicateDependencies) +const transitiveDuplicateDependenciesProvider = + TransitiveDuplicateDependenciesProvider._(); + +final class TransitiveDuplicateDependenciesProvider + extends $FunctionalProvider with $Provider { + const TransitiveDuplicateDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'transitiveDuplicateDependenciesProvider', + isAutoDispose: true, + dependencies: const [ + duplicateDependenciesProvider, + duplicateDependencies2Provider + ], + allTransitiveDependencies: const { + TransitiveDuplicateDependenciesProvider.$allTransitiveDependencies0, + TransitiveDuplicateDependenciesProvider.$allTransitiveDependencies1, + TransitiveDuplicateDependenciesProvider.$allTransitiveDependencies2, + TransitiveDuplicateDependenciesProvider.$allTransitiveDependencies3, + TransitiveDuplicateDependenciesProvider.$allTransitiveDependencies4, + TransitiveDuplicateDependenciesProvider.$allTransitiveDependencies5, + }, + ); + + static const $allTransitiveDependencies0 = duplicateDependenciesProvider; + static const $allTransitiveDependencies1 = + DuplicateDependenciesProvider.$allTransitiveDependencies0; + static const $allTransitiveDependencies2 = + DuplicateDependenciesProvider.$allTransitiveDependencies1; + static const $allTransitiveDependencies3 = duplicateDependencies2Provider; + static const $allTransitiveDependencies4 = + DuplicateDependencies2Provider.$allTransitiveDependencies0; + static const $allTransitiveDependencies5 = + DuplicateDependencies2Provider.$allTransitiveDependencies1; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$transitiveDuplicateDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + @$internal @override - int get id => (origin as Provider4Provider).id; + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + TransitiveDuplicateDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return TransitiveDuplicateDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? transitiveDuplicateDependencies; + return _$cb(ref); + } } -String _$emptyDependenciesClassBasedHash() => - r'e20c18353984a81977b656e9879b3841dbaedc6c'; +String _$transitiveDuplicateDependenciesHash() => + r'be6a85098fc66be440b6b201f58a6ce1c526caf6'; -/// See also [EmptyDependenciesClassBased]. -@ProviderFor(EmptyDependenciesClassBased) -final emptyDependenciesClassBasedProvider = - AutoDisposeNotifierProvider.internal( - EmptyDependenciesClassBased.new, - name: r'emptyDependenciesClassBasedProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$emptyDependenciesClassBasedHash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -typedef _$EmptyDependenciesClassBased = AutoDisposeNotifier; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/test/integration/dependencies2.dart b/packages/riverpod_generator/test/integration/dependencies2.dart index 89d04b4b2..92697505e 100644 --- a/packages/riverpod_generator/test/integration/dependencies2.dart +++ b/packages/riverpod_generator/test/integration/dependencies2.dart @@ -1,4 +1,3 @@ -import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'dependencies.dart'; diff --git a/packages/riverpod_generator/test/integration/dependencies2.g.dart b/packages/riverpod_generator/test/integration/dependencies2.g.dart index 1ca7daa6e..175a263bf 100644 --- a/packages/riverpod_generator/test/integration/dependencies2.g.dart +++ b/packages/riverpod_generator/test/integration/dependencies2.g.dart @@ -6,418 +6,627 @@ part of 'dependencies2.dart'; // RiverpodGenerator // ************************************************************************** -String _$providerWithDependencies2Hash() => - r'3a6100929120a9cf1ef7f1e0a5e9b8e4d4030ae2'; - -/// See also [providerWithDependencies2]. @ProviderFor(providerWithDependencies2) -final providerWithDependencies2Provider = AutoDisposeProvider.internal( - providerWithDependencies2, - name: r'providerWithDependencies2Provider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$providerWithDependencies2Hash, - dependencies: [ - providerWithDependenciesProvider, - _private2Provider, - public2Provider - ], - allTransitiveDependencies: { - providerWithDependenciesProvider, - ...?providerWithDependenciesProvider.allTransitiveDependencies, - _private2Provider, - ...?_private2Provider.allTransitiveDependencies, - public2Provider, - ...?public2Provider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ProviderWithDependencies2Ref = AutoDisposeProviderRef; -String _$familyWithDependencies2Hash() => - r'd064c06ca5a85a62cbe2b47943e98fc2e858fb03'; +const providerWithDependencies2Provider = ProviderWithDependencies2Provider._(); + +final class ProviderWithDependencies2Provider + extends $FunctionalProvider with $Provider { + const ProviderWithDependencies2Provider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'providerWithDependencies2Provider', + isAutoDispose: true, + dependencies: const [ + providerWithDependenciesProvider, + _private2Provider, + public2Provider + ], + allTransitiveDependencies: const { + ProviderWithDependencies2Provider.$allTransitiveDependencies0, + ProviderWithDependencies2Provider.$allTransitiveDependencies1, + ProviderWithDependencies2Provider.$allTransitiveDependencies2, + ProviderWithDependencies2Provider.$allTransitiveDependencies3, + ProviderWithDependencies2Provider.$allTransitiveDependencies4, + }, + ); + + static const $allTransitiveDependencies0 = providerWithDependenciesProvider; + static const $allTransitiveDependencies1 = + ProviderWithDependenciesProvider.$allTransitiveDependencies0; + static const $allTransitiveDependencies2 = + ProviderWithDependenciesProvider.$allTransitiveDependencies1; + static const $allTransitiveDependencies3 = _private2Provider; + static const $allTransitiveDependencies4 = public2Provider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$providerWithDependencies2Hash(); -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); + @override + ProviderWithDependencies2Provider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return ProviderWithDependencies2Provider._(create: create); } - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + @override + int create(Ref ref) { + final _$cb = _createCb ?? providerWithDependencies2; + return _$cb(ref); } } -/// See also [familyWithDependencies2]. +String _$providerWithDependencies2Hash() => + r'3a6100929120a9cf1ef7f1e0a5e9b8e4d4030ae2'; + @ProviderFor(familyWithDependencies2) -const familyWithDependencies2Provider = FamilyWithDependencies2Family(); +const familyWithDependencies2Provider = FamilyWithDependencies2Family._(); + +final class FamilyWithDependencies2Provider + extends $FunctionalProvider with $Provider { + const FamilyWithDependencies2Provider._( + {required FamilyWithDependencies2Family super.from, + required int? super.argument, + int Function( + Ref ref, { + int? id, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'familyWithDependencies2Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [familyWithDependencies2]. -class FamilyWithDependencies2Family extends Family { - /// See also [familyWithDependencies2]. - const FamilyWithDependencies2Family(); + static const $allTransitiveDependencies0 = providerWithDependenciesProvider; + static const $allTransitiveDependencies1 = + ProviderWithDependenciesProvider.$allTransitiveDependencies0; + static const $allTransitiveDependencies2 = + ProviderWithDependenciesProvider.$allTransitiveDependencies1; + static const $allTransitiveDependencies3 = _private2Provider; + static const $allTransitiveDependencies4 = public2Provider; - /// See also [familyWithDependencies2]. - FamilyWithDependencies2Provider call({ + final int Function( + Ref ref, { int? id, - }) { - return FamilyWithDependencies2Provider( - id: id, - ); - } + })? _createCb; @override - FamilyWithDependencies2Provider getProviderOverride( - covariant FamilyWithDependencies2Provider provider, - ) { - return call( - id: provider.id, + String debugGetCreateSourceHash() => _$familyWithDependencies2Hash(); + + @override + String toString() { + return r'familyWithDependencies2Provider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } - static final Iterable _dependencies = [ - providerWithDependenciesProvider, - _private2Provider, - public2Provider - ]; + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - Iterable? get dependencies => _dependencies; + FamilyWithDependencies2Provider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return FamilyWithDependencies2Provider._( + argument: argument as int?, + from: from! as FamilyWithDependencies2Family, + create: ( + ref, { + int? id, + }) => + create(ref)); + } - static final Iterable _allTransitiveDependencies = - { - providerWithDependenciesProvider, - ...?providerWithDependenciesProvider.allTransitiveDependencies, - _private2Provider, - ...?_private2Provider.allTransitiveDependencies, - public2Provider, - ...?public2Provider.allTransitiveDependencies - }; + @override + int create(Ref ref) { + final _$cb = _createCb ?? familyWithDependencies2; + final argument = this.argument as int?; + return _$cb( + ref, + id: argument, + ); + } @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + bool operator ==(Object other) { + return other is FamilyWithDependencies2Provider && + other.argument == argument; + } @override - String? get name => r'familyWithDependencies2Provider'; + int get hashCode { + return argument.hashCode; + } } -/// See also [familyWithDependencies2]. -class FamilyWithDependencies2Provider extends AutoDisposeProvider { - /// See also [familyWithDependencies2]. - FamilyWithDependencies2Provider({ - int? id, - }) : this._internal( - (ref) => familyWithDependencies2( - ref as FamilyWithDependencies2Ref, - id: id, - ), - from: familyWithDependencies2Provider, +String _$familyWithDependencies2Hash() => + r'd064c06ca5a85a62cbe2b47943e98fc2e858fb03'; + +final class FamilyWithDependencies2Family extends Family { + const FamilyWithDependencies2Family._() + : super( + retry: null, name: r'familyWithDependencies2Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$familyWithDependencies2Hash, - dependencies: FamilyWithDependencies2Family._dependencies, - allTransitiveDependencies: - FamilyWithDependencies2Family._allTransitiveDependencies, - id: id, + dependencies: const [ + providerWithDependenciesProvider, + _private2Provider, + public2Provider + ], + allTransitiveDependencies: const { + FamilyWithDependencies2Provider.$allTransitiveDependencies0, + FamilyWithDependencies2Provider.$allTransitiveDependencies1, + FamilyWithDependencies2Provider.$allTransitiveDependencies2, + FamilyWithDependencies2Provider.$allTransitiveDependencies3, + FamilyWithDependencies2Provider.$allTransitiveDependencies4, + }, + isAutoDispose: true, ); - FamilyWithDependencies2Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.id, - }) : super.internal(); + FamilyWithDependencies2Provider call({ + int? id, + }) => + FamilyWithDependencies2Provider._(argument: id, from: this); - final int? id; + @override + String debugGetCreateSourceHash() => _$familyWithDependencies2Hash(); @override + String toString() => r'familyWithDependencies2Provider'; + + /// {@macro riverpod.override_with} Override overrideWith( - int Function(FamilyWithDependencies2Ref provider) create, + int Function( + Ref ref, + int? args, + ) create, ) { - return ProviderOverride( - origin: this, - override: FamilyWithDependencies2Provider._internal( - (ref) => create(ref as FamilyWithDependencies2Ref), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - id: id, - ), + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyWithDependencies2Provider; + + final argument = provider.argument as int?; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, ); } +} - @override - AutoDisposeProviderElement createElement() { - return _FamilyWithDependencies2ProviderElement(this); - } +@ProviderFor(NotifierWithDependencies) +const notifierWithDependenciesProvider = NotifierWithDependenciesProvider._(); + +final class NotifierWithDependenciesProvider + extends $NotifierProvider { + const NotifierWithDependenciesProvider._( + {super.runNotifierBuildOverride, + NotifierWithDependencies Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'notifierWithDependenciesProvider', + isAutoDispose: true, + dependencies: const [ + providerWithDependenciesProvider, + _private2Provider, + public2Provider + ], + allTransitiveDependencies: const { + NotifierWithDependenciesProvider.$allTransitiveDependencies0, + NotifierWithDependenciesProvider.$allTransitiveDependencies1, + NotifierWithDependenciesProvider.$allTransitiveDependencies2, + NotifierWithDependenciesProvider.$allTransitiveDependencies3, + NotifierWithDependenciesProvider.$allTransitiveDependencies4, + }, + ); + + static const $allTransitiveDependencies0 = providerWithDependenciesProvider; + static const $allTransitiveDependencies1 = + ProviderWithDependenciesProvider.$allTransitiveDependencies0; + static const $allTransitiveDependencies2 = + ProviderWithDependenciesProvider.$allTransitiveDependencies1; + static const $allTransitiveDependencies3 = _private2Provider; + static const $allTransitiveDependencies4 = public2Provider; + + final NotifierWithDependencies Function()? _createCb; @override - bool operator ==(Object other) { - return other is FamilyWithDependencies2Provider && other.id == id; + String debugGetCreateSourceHash() => _$notifierWithDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); } + @$internal @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, id.hashCode); + NotifierWithDependencies create() => + _createCb?.call() ?? NotifierWithDependencies(); - return _SystemHash.finish(hash); + @$internal + @override + NotifierWithDependenciesProvider $copyWithCreate( + NotifierWithDependencies Function() create, + ) { + return NotifierWithDependenciesProvider._(create: create); } -} - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FamilyWithDependencies2Ref on AutoDisposeProviderRef { - /// The parameter `id` of this provider. - int? get id; -} -class _FamilyWithDependencies2ProviderElement - extends AutoDisposeProviderElement with FamilyWithDependencies2Ref { - _FamilyWithDependencies2ProviderElement(super.provider); + @$internal + @override + NotifierWithDependenciesProvider $copyWithBuild( + int Function( + Ref, + NotifierWithDependencies, + ) build, + ) { + return NotifierWithDependenciesProvider._(runNotifierBuildOverride: build); + } + @$internal @override - int? get id => (origin as FamilyWithDependencies2Provider).id; + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); } -String _$private2Hash() => r'e420875c8fbd9bf33eff945f2b7276b585032a38'; - -/// See also [_private2]. -@ProviderFor(_private2) -final _private2Provider = AutoDisposeProvider.internal( - _private2, - name: r'_private2Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$private2Hash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef _Private2Ref = AutoDisposeProviderRef; -String _$public2Hash() => r'20eb4f82e5f25fafc72775e7b86021d70ebb5579'; - -/// See also [public2]. -@ProviderFor(public2) -final public2Provider = AutoDisposeProvider.internal( - public2, - name: r'public2Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$public2Hash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef Public2Ref = AutoDisposeProviderRef; String _$notifierWithDependenciesHash() => r'becc68e5a54b0cc2b8277a6d54b74edef93bfe89'; -/// See also [NotifierWithDependencies]. -@ProviderFor(NotifierWithDependencies) -final notifierWithDependenciesProvider = - AutoDisposeNotifierProvider.internal( - NotifierWithDependencies.new, - name: r'notifierWithDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$notifierWithDependenciesHash, - dependencies: [ - providerWithDependenciesProvider, - _private2Provider, - public2Provider - ], - allTransitiveDependencies: { - providerWithDependenciesProvider, - ...?providerWithDependenciesProvider.allTransitiveDependencies, - _private2Provider, - ...?_private2Provider.allTransitiveDependencies, - public2Provider, - ...?public2Provider.allTransitiveDependencies - }, -); - -typedef _$NotifierWithDependencies = AutoDisposeNotifier; -String _$notifierFamilyWithDependenciesHash() => - r'b185ba93857cd028964c1412e748ee887dbd45c8'; - -abstract class _$NotifierFamilyWithDependencies - extends BuildlessAutoDisposeNotifier { - late final int? id; - - int build({ - int? id, - }); +abstract class _$NotifierWithDependencies extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); } -/// See also [NotifierFamilyWithDependencies]. @ProviderFor(NotifierFamilyWithDependencies) const notifierFamilyWithDependenciesProvider = - NotifierFamilyWithDependenciesFamily(); + NotifierFamilyWithDependenciesFamily._(); + +final class NotifierFamilyWithDependenciesProvider + extends $NotifierProvider { + const NotifierFamilyWithDependenciesProvider._( + {required NotifierFamilyWithDependenciesFamily super.from, + required int? super.argument, + super.runNotifierBuildOverride, + NotifierFamilyWithDependencies Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'notifierFamilyWithDependenciesProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [NotifierFamilyWithDependencies]. -class NotifierFamilyWithDependenciesFamily extends Family { - /// See also [NotifierFamilyWithDependencies]. - const NotifierFamilyWithDependenciesFamily(); + static const $allTransitiveDependencies0 = providerWithDependenciesProvider; + static const $allTransitiveDependencies1 = + ProviderWithDependenciesProvider.$allTransitiveDependencies0; + static const $allTransitiveDependencies2 = + ProviderWithDependenciesProvider.$allTransitiveDependencies1; + static const $allTransitiveDependencies3 = _private2Provider; + static const $allTransitiveDependencies4 = public2Provider; - /// See also [NotifierFamilyWithDependencies]. - NotifierFamilyWithDependenciesProvider call({ - int? id, - }) { - return NotifierFamilyWithDependenciesProvider( - id: id, + final NotifierFamilyWithDependencies Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$notifierFamilyWithDependenciesHash(); + + @override + String toString() { + return r'notifierFamilyWithDependenciesProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } + @$internal + @override + NotifierFamilyWithDependencies create() => + _createCb?.call() ?? NotifierFamilyWithDependencies(); + + @$internal @override - NotifierFamilyWithDependenciesProvider getProviderOverride( - covariant NotifierFamilyWithDependenciesProvider provider, + NotifierFamilyWithDependenciesProvider $copyWithCreate( + NotifierFamilyWithDependencies Function() create, ) { - return call( - id: provider.id, - ); + return NotifierFamilyWithDependenciesProvider._( + argument: argument as int?, + from: from! as NotifierFamilyWithDependenciesFamily, + create: create); } - static final Iterable _dependencies = [ - providerWithDependenciesProvider, - _private2Provider, - public2Provider - ]; - + @$internal @override - Iterable? get dependencies => _dependencies; + NotifierFamilyWithDependenciesProvider $copyWithBuild( + int Function( + Ref, + NotifierFamilyWithDependencies, + ) build, + ) { + return NotifierFamilyWithDependenciesProvider._( + argument: argument as int?, + from: from! as NotifierFamilyWithDependenciesFamily, + runNotifierBuildOverride: build); + } - static final Iterable _allTransitiveDependencies = - { - providerWithDependenciesProvider, - ...?providerWithDependenciesProvider.allTransitiveDependencies, - _private2Provider, - ...?_private2Provider.allTransitiveDependencies, - public2Provider, - ...?public2Provider.allTransitiveDependencies - }; + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + bool operator ==(Object other) { + return other is NotifierFamilyWithDependenciesProvider && + other.argument == argument; + } @override - String? get name => r'notifierFamilyWithDependenciesProvider'; + int get hashCode { + return argument.hashCode; + } } -/// See also [NotifierFamilyWithDependencies]. -class NotifierFamilyWithDependenciesProvider - extends AutoDisposeNotifierProviderImpl { - /// See also [NotifierFamilyWithDependencies]. - NotifierFamilyWithDependenciesProvider({ - int? id, - }) : this._internal( - () => NotifierFamilyWithDependencies()..id = id, - from: notifierFamilyWithDependenciesProvider, +String _$notifierFamilyWithDependenciesHash() => + r'b185ba93857cd028964c1412e748ee887dbd45c8'; + +final class NotifierFamilyWithDependenciesFamily extends Family { + const NotifierFamilyWithDependenciesFamily._() + : super( + retry: null, name: r'notifierFamilyWithDependenciesProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$notifierFamilyWithDependenciesHash, - dependencies: NotifierFamilyWithDependenciesFamily._dependencies, - allTransitiveDependencies: - NotifierFamilyWithDependenciesFamily._allTransitiveDependencies, - id: id, + dependencies: const [ + providerWithDependenciesProvider, + _private2Provider, + public2Provider + ], + allTransitiveDependencies: const { + NotifierFamilyWithDependenciesProvider.$allTransitiveDependencies0, + NotifierFamilyWithDependenciesProvider.$allTransitiveDependencies1, + NotifierFamilyWithDependenciesProvider.$allTransitiveDependencies2, + NotifierFamilyWithDependenciesProvider.$allTransitiveDependencies3, + NotifierFamilyWithDependenciesProvider.$allTransitiveDependencies4, + }, + isAutoDispose: true, ); - NotifierFamilyWithDependenciesProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.id, - }) : super.internal(); + NotifierFamilyWithDependenciesProvider call({ + int? id, + }) => + NotifierFamilyWithDependenciesProvider._(argument: id, from: this); - final int? id; + @override + String debugGetCreateSourceHash() => _$notifierFamilyWithDependenciesHash(); @override - int runNotifierBuild( - covariant NotifierFamilyWithDependencies notifier, + String toString() => r'notifierFamilyWithDependenciesProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + NotifierFamilyWithDependencies Function( + int? args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = + pointer.origin as NotifierFamilyWithDependenciesProvider; + + final argument = provider.argument as int?; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function( + Ref ref, NotifierFamilyWithDependencies notifier, int? argument) + build, ) { - return notifier.build( - id: id, + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = + pointer.origin as NotifierFamilyWithDependenciesProvider; + + final argument = provider.argument as int?; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, ); } +} + +abstract class _$NotifierFamilyWithDependencies extends $Notifier { + late final _$args = ref.$arg as int?; + int? get id => _$args; + + int build({ + int? id, + }); + @$internal + @override + int runBuild() => build( + id: _$args, + ); +} + +@ProviderFor(_private2) +const _private2Provider = _Private2Provider._(); + +final class _Private2Provider extends $FunctionalProvider + with $Provider { + const _Private2Provider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'_private2Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; @override - Override overrideWith(NotifierFamilyWithDependencies Function() create) { - return ProviderOverride( + String debugGetCreateSourceHash() => _$private2Hash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( origin: this, - override: NotifierFamilyWithDependenciesProvider._internal( - () => create()..id = id, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - id: id, - ), + providerOverride: $ValueProvider(value), ); } + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + @override - AutoDisposeNotifierProviderElement - createElement() { - return _NotifierFamilyWithDependenciesProviderElement(this); + _Private2Provider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return _Private2Provider._(create: create); } @override - bool operator ==(Object other) { - return other is NotifierFamilyWithDependenciesProvider && other.id == id; + int create(Ref ref) { + final _$cb = _createCb ?? _private2; + return _$cb(ref); } +} + +String _$private2Hash() => r'e420875c8fbd9bf33eff945f2b7276b585032a38'; + +@ProviderFor(public2) +const public2Provider = Public2Provider._(); + +final class Public2Provider extends $FunctionalProvider + with $Provider { + const Public2Provider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'public2Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, id.hashCode); + String debugGetCreateSourceHash() => _$public2Hash(); - return _SystemHash.finish(hash); + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); } -} -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin NotifierFamilyWithDependenciesRef on AutoDisposeNotifierProviderRef { - /// The parameter `id` of this provider. - int? get id; -} + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); -class _NotifierFamilyWithDependenciesProviderElement - extends AutoDisposeNotifierProviderElement with NotifierFamilyWithDependenciesRef { - _NotifierFamilyWithDependenciesProviderElement(super.provider); + @override + Public2Provider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return Public2Provider._(create: create); + } @override - int? get id => (origin as NotifierFamilyWithDependenciesProvider).id; + int create(Ref ref) { + final _$cb = _createCb ?? public2; + return _$cb(ref); + } } + +String _$public2Hash() => r'20eb4f82e5f25fafc72775e7b86021d70ebb5579'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/test/integration/documented.dart b/packages/riverpod_generator/test/integration/documented.dart new file mode 100644 index 000000000..a395963be --- /dev/null +++ b/packages/riverpod_generator/test/integration/documented.dart @@ -0,0 +1,41 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'documented.g.dart'; + +/// Hello world +// Foo +@riverpod +String functional(Ref ref) => 'functional'; + +/// Hello world +// Foo +@riverpod +class ClassBased extends _$ClassBased { + @override + String build() => 'ClassBased'; +} + +/// Hello world +// Foo +@riverpod +String family( + Ref ref, + + /// Hello Id + int id, +) { + return 'family $id'; +} + +/// Hello world +// Foo +@riverpod +class ClassFamilyBased extends _$ClassFamilyBased { + @override + String build( + /// Hello world + // Foo + int id, + ) => + 'ClassBased'; +} diff --git a/packages/riverpod_generator/test/integration/documented.g.dart b/packages/riverpod_generator/test/integration/documented.g.dart new file mode 100644 index 000000000..fb33e8058 --- /dev/null +++ b/packages/riverpod_generator/test/integration/documented.g.dart @@ -0,0 +1,464 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'documented.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +/// Hello world +// Foo +@ProviderFor(functional) +const functionalProvider = FunctionalProvider._(); + +/// Hello world +// Foo +final class FunctionalProvider extends $FunctionalProvider + with $Provider { + /// Hello world +// Foo + const FunctionalProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'functionalProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$functionalHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + FunctionalProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return FunctionalProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? functional; + return _$cb(ref); + } +} + +String _$functionalHash() => r'52eddcd28b005800da9cf6c22df77f2f040bfb34'; + +/// Hello world +// Foo +@ProviderFor(ClassBased) +const classBasedProvider = ClassBasedProvider._(); + +/// Hello world +// Foo +final class ClassBasedProvider extends $NotifierProvider { + /// Hello world +// Foo + const ClassBasedProvider._( + {super.runNotifierBuildOverride, ClassBased Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'classBasedProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final ClassBased Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$classBasedHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + ClassBased create() => _createCb?.call() ?? ClassBased(); + + @$internal + @override + ClassBasedProvider $copyWithCreate( + ClassBased Function() create, + ) { + return ClassBasedProvider._(create: create); + } + + @$internal + @override + ClassBasedProvider $copyWithBuild( + String Function( + Ref, + ClassBased, + ) build, + ) { + return ClassBasedProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$classBasedHash() => r'f1139017b1fcf38017402b514c61fb32dae40c39'; + +abstract class _$ClassBased extends $Notifier { + String build(); + @$internal + @override + String runBuild() => build(); +} + +/// Hello world +// Foo +@ProviderFor(family) +const familyProvider = FamilyFamily._(); + +/// Hello world +// Foo +final class FamilyProvider extends $FunctionalProvider + with $Provider { + /// Hello world +// Foo + const FamilyProvider._( + {required FamilyFamily super.from, + required int super.argument, + String Function( + Ref ref, + int id, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'familyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + int id, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$familyHash(); + + @override + String toString() { + return r'familyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + FamilyProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return FamilyProvider._( + argument: argument as int, + from: from! as FamilyFamily, + create: ( + ref, + int id, + ) => + create(ref)); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? family; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is FamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$familyHash() => r'5164f4ea1f2d6c741e5c600c48a1b2ac2be7a1eb'; + +/// Hello world +// Foo +final class FamilyFamily extends Family { + const FamilyFamily._() + : super( + retry: null, + name: r'familyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + /// Hello world +// Foo + FamilyProvider call( + int id, + ) => + FamilyProvider._(argument: id, from: this); + + @override + String debugGetCreateSourceHash() => _$familyHash(); + + @override + String toString() => r'familyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + String Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +/// Hello world +// Foo +@ProviderFor(ClassFamilyBased) +const classFamilyBasedProvider = ClassFamilyBasedFamily._(); + +/// Hello world +// Foo +final class ClassFamilyBasedProvider + extends $NotifierProvider { + /// Hello world +// Foo + const ClassFamilyBasedProvider._( + {required ClassFamilyBasedFamily super.from, + required int super.argument, + super.runNotifierBuildOverride, + ClassFamilyBased Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'classFamilyBasedProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final ClassFamilyBased Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$classFamilyBasedHash(); + + @override + String toString() { + return r'classFamilyBasedProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + ClassFamilyBased create() => _createCb?.call() ?? ClassFamilyBased(); + + @$internal + @override + ClassFamilyBasedProvider $copyWithCreate( + ClassFamilyBased Function() create, + ) { + return ClassFamilyBasedProvider._( + argument: argument as int, + from: from! as ClassFamilyBasedFamily, + create: create); + } + + @$internal + @override + ClassFamilyBasedProvider $copyWithBuild( + String Function( + Ref, + ClassFamilyBased, + ) build, + ) { + return ClassFamilyBasedProvider._( + argument: argument as int, + from: from! as ClassFamilyBasedFamily, + runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is ClassFamilyBasedProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$classFamilyBasedHash() => r'8d83e9a88356796298419574f360e8bf95aa0729'; + +/// Hello world +// Foo +final class ClassFamilyBasedFamily extends Family { + const ClassFamilyBasedFamily._() + : super( + retry: null, + name: r'classFamilyBasedProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + /// Hello world +// Foo + ClassFamilyBasedProvider call( + int id, + ) => + ClassFamilyBasedProvider._(argument: id, from: this); + + @override + String debugGetCreateSourceHash() => _$classFamilyBasedHash(); + + @override + String toString() => r'classFamilyBasedProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + ClassFamilyBased Function( + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ClassFamilyBasedProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + String Function(Ref ref, ClassFamilyBased notifier, int argument) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ClassFamilyBasedProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} + +abstract class _$ClassFamilyBased extends $Notifier { + late final _$args = ref.$arg as int; + + /// Hello world +// Foo + int get id => _$args; + + String build( + int id, + ); + @$internal + @override + String runBuild() => build( + _$args, + ); +} + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/test/integration/generated.dart b/packages/riverpod_generator/test/integration/generated.dart new file mode 100644 index 000000000..a00374139 --- /dev/null +++ b/packages/riverpod_generator/test/integration/generated.dart @@ -0,0 +1,84 @@ +// ignore_for_file: library_private_types_in_public_api, inference_failure_on_function_return_type, always_declare_return_types, type_annotate_public_apis + +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'generated.freezed.dart'; +part 'generated.g.dart'; + +@freezed +class Test with _$Test { + factory Test() = _Test; +} + +@riverpod +_Test generated(Ref ref) => _Test(); + +@riverpod +_Test generatedFamily(Ref ref, _Test test) => _Test(); + +@riverpod +class GeneratedClass extends _$GeneratedClass { + @override + _Test build() => _Test(); +} + +@riverpod +class GeneratedClassFamily extends _$GeneratedClassFamily { + @override + _Test build(_Test test) => _Test(); +} + +@riverpod +$dynamic(Ref ref) => _Test(); + +@riverpod +$dynamicFamily(Ref ref, test) => _Test(); + +@riverpod +class $DynamicClass extends _$$DynamicClass { + @override + build() => _Test(); +} + +@riverpod +class $DynamicClassFamily extends _$$DynamicClassFamily { + @override + build(test) => _Test(); +} + +const dynamicProvider = _dynamicProvider; + +@riverpod +_dynamic(Ref ref, test) => 0; + +@riverpod +AsyncValue alias(Ref ref) { + return const AsyncData(42); +} + +@riverpod +AsyncValue aliasFamily( + Ref ref, + AsyncValue test, +) { + return const AsyncData(42); +} + +@riverpod +class AliasClass extends _$AliasClass { + @override + AsyncValue build() { + return const AsyncData(42); + } +} + +@riverpod +class AliasClassFamily extends _$AliasClassFamily { + @override + AsyncValue build( + AsyncValue test, + ) { + return const AsyncData(42); + } +} diff --git a/packages/riverpod_generator/test/integration/generated.freezed.dart b/packages/riverpod_generator/test/integration/generated.freezed.dart new file mode 100644 index 000000000..bf3f821d4 --- /dev/null +++ b/packages/riverpod_generator/test/integration/generated.freezed.dart @@ -0,0 +1,80 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'generated.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$Test {} + +/// @nodoc +abstract class $TestCopyWith<$Res> { + factory $TestCopyWith(Test value, $Res Function(Test) then) = + _$TestCopyWithImpl<$Res, Test>; +} + +/// @nodoc +class _$TestCopyWithImpl<$Res, $Val extends Test> + implements $TestCopyWith<$Res> { + _$TestCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of Test + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc +abstract class _$$TestImplCopyWith<$Res> { + factory _$$TestImplCopyWith( + _$TestImpl value, $Res Function(_$TestImpl) then) = + __$$TestImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$TestImplCopyWithImpl<$Res> + extends _$TestCopyWithImpl<$Res, _$TestImpl> + implements _$$TestImplCopyWith<$Res> { + __$$TestImplCopyWithImpl(_$TestImpl _value, $Res Function(_$TestImpl) _then) + : super(_value, _then); + + /// Create a copy of Test + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$TestImpl implements _Test { + _$TestImpl(); + + @override + String toString() { + return 'Test()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$TestImpl); + } + + @override + int get hashCode => runtimeType.hashCode; +} + +abstract class _Test implements Test { + factory _Test() = _$TestImpl; +} diff --git a/packages/riverpod_generator/test/integration/generated.g.dart b/packages/riverpod_generator/test/integration/generated.g.dart new file mode 100644 index 000000000..e835715b7 --- /dev/null +++ b/packages/riverpod_generator/test/integration/generated.g.dart @@ -0,0 +1,1409 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'generated.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +@ProviderFor(generated) +const generatedProvider = GeneratedProvider._(); + +final class GeneratedProvider extends $FunctionalProvider<_Test, _Test> + with $Provider<_Test> { + const GeneratedProvider._( + {_Test Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'generatedProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final _Test Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$generatedHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(_Test value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider<_Test>(value), + ); + } + + @$internal + @override + $ProviderElement<_Test> $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + GeneratedProvider $copyWithCreate( + _Test Function( + Ref ref, + ) create, + ) { + return GeneratedProvider._(create: create); + } + + @override + _Test create(Ref ref) { + final _$cb = _createCb ?? generated; + return _$cb(ref); + } +} + +String _$generatedHash() => r'0332eb232658688654514ff241ff380edbf4dbf6'; + +@ProviderFor(generatedFamily) +const generatedFamilyProvider = GeneratedFamilyFamily._(); + +final class GeneratedFamilyProvider extends $FunctionalProvider<_Test, _Test> + with $Provider<_Test> { + const GeneratedFamilyProvider._( + {required GeneratedFamilyFamily super.from, + required _Test super.argument, + _Test Function( + Ref ref, + _Test test, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'generatedFamilyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final _Test Function( + Ref ref, + _Test test, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$generatedFamilyHash(); + + @override + String toString() { + return r'generatedFamilyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(_Test value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider<_Test>(value), + ); + } + + @$internal + @override + $ProviderElement<_Test> $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + GeneratedFamilyProvider $copyWithCreate( + _Test Function( + Ref ref, + ) create, + ) { + return GeneratedFamilyProvider._( + argument: argument as _Test, + from: from! as GeneratedFamilyFamily, + create: ( + ref, + _Test test, + ) => + create(ref)); + } + + @override + _Test create(Ref ref) { + final _$cb = _createCb ?? generatedFamily; + final argument = this.argument as _Test; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is GeneratedFamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$generatedFamilyHash() => r'8ac3b633763cb8dbad6e0686a732df3a081a0d64'; + +final class GeneratedFamilyFamily extends Family { + const GeneratedFamilyFamily._() + : super( + retry: null, + name: r'generatedFamilyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + GeneratedFamilyProvider call( + _Test test, + ) => + GeneratedFamilyProvider._(argument: test, from: this); + + @override + String debugGetCreateSourceHash() => _$generatedFamilyHash(); + + @override + String toString() => r'generatedFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + _Test Function( + Ref ref, + _Test args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GeneratedFamilyProvider; + + final argument = provider.argument as _Test; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +@ProviderFor(GeneratedClass) +const generatedClassProvider = GeneratedClassProvider._(); + +final class GeneratedClassProvider + extends $NotifierProvider { + const GeneratedClassProvider._( + {super.runNotifierBuildOverride, GeneratedClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'generatedClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final GeneratedClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$generatedClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(_Test value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider<_Test>(value), + ); + } + + @$internal + @override + GeneratedClass create() => _createCb?.call() ?? GeneratedClass(); + + @$internal + @override + GeneratedClassProvider $copyWithCreate( + GeneratedClass Function() create, + ) { + return GeneratedClassProvider._(create: create); + } + + @$internal + @override + GeneratedClassProvider $copyWithBuild( + _Test Function( + Ref, + GeneratedClass, + ) build, + ) { + return GeneratedClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$generatedClassHash() => r'984153f97e25de687d2f19756b277aabd56f6e72'; + +abstract class _$GeneratedClass extends $Notifier<_Test> { + _Test build(); + @$internal + @override + _Test runBuild() => build(); +} + +@ProviderFor(GeneratedClassFamily) +const generatedClassFamilyProvider = GeneratedClassFamilyFamily._(); + +final class GeneratedClassFamilyProvider + extends $NotifierProvider { + const GeneratedClassFamilyProvider._( + {required GeneratedClassFamilyFamily super.from, + required _Test super.argument, + super.runNotifierBuildOverride, + GeneratedClassFamily Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'generatedClassFamilyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final GeneratedClassFamily Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$generatedClassFamilyHash(); + + @override + String toString() { + return r'generatedClassFamilyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(_Test value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider<_Test>(value), + ); + } + + @$internal + @override + GeneratedClassFamily create() => _createCb?.call() ?? GeneratedClassFamily(); + + @$internal + @override + GeneratedClassFamilyProvider $copyWithCreate( + GeneratedClassFamily Function() create, + ) { + return GeneratedClassFamilyProvider._( + argument: argument as _Test, + from: from! as GeneratedClassFamilyFamily, + create: create); + } + + @$internal + @override + GeneratedClassFamilyProvider $copyWithBuild( + _Test Function( + Ref, + GeneratedClassFamily, + ) build, + ) { + return GeneratedClassFamilyProvider._( + argument: argument as _Test, + from: from! as GeneratedClassFamilyFamily, + runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is GeneratedClassFamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$generatedClassFamilyHash() => + r'28d0a5a82af5b254f6ef07b492916e2feb7e6e63'; + +final class GeneratedClassFamilyFamily extends Family { + const GeneratedClassFamilyFamily._() + : super( + retry: null, + name: r'generatedClassFamilyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + GeneratedClassFamilyProvider call( + _Test test, + ) => + GeneratedClassFamilyProvider._(argument: test, from: this); + + @override + String debugGetCreateSourceHash() => _$generatedClassFamilyHash(); + + @override + String toString() => r'generatedClassFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + GeneratedClassFamily Function( + _Test args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GeneratedClassFamilyProvider; + + final argument = provider.argument as _Test; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + _Test Function(Ref ref, GeneratedClassFamily notifier, _Test argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GeneratedClassFamilyProvider; + + final argument = provider.argument as _Test; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} + +abstract class _$GeneratedClassFamily extends $Notifier<_Test> { + late final _$args = ref.$arg as _Test; + _Test get test => _$args; + + _Test build( + _Test test, + ); + @$internal + @override + _Test runBuild() => build( + _$args, + ); +} + +@ProviderFor($dynamic) +const $dynamicProvider = $DynamicProvider._(); + +final class $DynamicProvider extends $FunctionalProvider + with $Provider { + const $DynamicProvider._( + {Object? Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'$dynamicProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Object? Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$$dynamicHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Object? value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + $DynamicProvider $copyWithCreate( + Object? Function( + Ref ref, + ) create, + ) { + return $DynamicProvider._(create: create); + } + + @override + Object? create(Ref ref) { + final _$cb = _createCb ?? $dynamic; + return _$cb(ref); + } +} + +String _$$dynamicHash() => r'17c8e140446da2e3c026ebb51c4b074d2894b7ff'; + +@ProviderFor($dynamicFamily) +const $dynamicFamilyProvider = $DynamicFamilyFamily._(); + +final class $DynamicFamilyProvider extends $FunctionalProvider + with $Provider { + const $DynamicFamilyProvider._( + {required $DynamicFamilyFamily super.from, + required dynamic super.argument, + Object? Function( + Ref ref, + dynamic test, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'$dynamicFamilyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Object? Function( + Ref ref, + dynamic test, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$$dynamicFamilyHash(); + + @override + String toString() { + return r'$dynamicFamilyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Object? value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + $DynamicFamilyProvider $copyWithCreate( + Object? Function( + Ref ref, + ) create, + ) { + return $DynamicFamilyProvider._( + argument: argument as dynamic, + from: from! as $DynamicFamilyFamily, + create: ( + ref, + dynamic test, + ) => + create(ref)); + } + + @override + Object? create(Ref ref) { + final _$cb = _createCb ?? $dynamicFamily; + final argument = this.argument as dynamic; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is $DynamicFamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$$dynamicFamilyHash() => r'6897846251c8b4b5b2fa72d8d3e14ae3381c0c0f'; + +final class $DynamicFamilyFamily extends Family { + const $DynamicFamilyFamily._() + : super( + retry: null, + name: r'$dynamicFamilyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + $DynamicFamilyProvider call( + dynamic test, + ) => + $DynamicFamilyProvider._(argument: test, from: this); + + @override + String debugGetCreateSourceHash() => _$$dynamicFamilyHash(); + + @override + String toString() => r'$dynamicFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Object? Function( + Ref ref, + dynamic args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as $DynamicFamilyProvider; + + final argument = provider.argument as dynamic; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +@ProviderFor($DynamicClass) +const $dynamicClassProvider = $DynamicClassProvider._(); + +final class $DynamicClassProvider + extends $NotifierProvider<$DynamicClass, Object?> { + const $DynamicClassProvider._( + {super.runNotifierBuildOverride, $DynamicClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'$dynamicClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final $DynamicClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$$dynamicClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Object? value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $DynamicClass create() => _createCb?.call() ?? $DynamicClass(); + + @$internal + @override + $DynamicClassProvider $copyWithCreate( + $DynamicClass Function() create, + ) { + return $DynamicClassProvider._(create: create); + } + + @$internal + @override + $DynamicClassProvider $copyWithBuild( + Object? Function( + Ref, + $DynamicClass, + ) build, + ) { + return $DynamicClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement<$DynamicClass, Object?> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$$dynamicClassHash() => r'c6d8e5191c3f060df3ce3eee66107433fd4c3292'; + +abstract class _$$DynamicClass extends $Notifier { + Object? build(); + @$internal + @override + Object? runBuild() => build(); +} + +@ProviderFor($DynamicClassFamily) +const $dynamicClassFamilyProvider = $DynamicClassFamilyFamily._(); + +final class $DynamicClassFamilyProvider + extends $NotifierProvider<$DynamicClassFamily, Object?> { + const $DynamicClassFamilyProvider._( + {required $DynamicClassFamilyFamily super.from, + required dynamic super.argument, + super.runNotifierBuildOverride, + $DynamicClassFamily Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'$dynamicClassFamilyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final $DynamicClassFamily Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$$dynamicClassFamilyHash(); + + @override + String toString() { + return r'$dynamicClassFamilyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Object? value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $DynamicClassFamily create() => _createCb?.call() ?? $DynamicClassFamily(); + + @$internal + @override + $DynamicClassFamilyProvider $copyWithCreate( + $DynamicClassFamily Function() create, + ) { + return $DynamicClassFamilyProvider._( + argument: argument as dynamic, + from: from! as $DynamicClassFamilyFamily, + create: create); + } + + @$internal + @override + $DynamicClassFamilyProvider $copyWithBuild( + Object? Function( + Ref, + $DynamicClassFamily, + ) build, + ) { + return $DynamicClassFamilyProvider._( + argument: argument as dynamic, + from: from! as $DynamicClassFamilyFamily, + runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement<$DynamicClassFamily, Object?> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is $DynamicClassFamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$$dynamicClassFamilyHash() => + r'bdda961386f3b647c071d79293a8da441580c470'; + +final class $DynamicClassFamilyFamily extends Family { + const $DynamicClassFamilyFamily._() + : super( + retry: null, + name: r'$dynamicClassFamilyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + $DynamicClassFamilyProvider call( + dynamic test, + ) => + $DynamicClassFamilyProvider._(argument: test, from: this); + + @override + String debugGetCreateSourceHash() => _$$dynamicClassFamilyHash(); + + @override + String toString() => r'$dynamicClassFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + $DynamicClassFamily Function( + dynamic args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as $DynamicClassFamilyProvider; + + final argument = provider.argument as dynamic; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + Object? Function(Ref ref, $DynamicClassFamily notifier, dynamic argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as $DynamicClassFamilyProvider; + + final argument = provider.argument as dynamic; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} + +abstract class _$$DynamicClassFamily extends $Notifier { + late final _$args = ref.$arg as dynamic; + dynamic get test => _$args; + + Object? build( + dynamic test, + ); + @$internal + @override + Object? runBuild() => build( + _$args, + ); +} + +@ProviderFor(_dynamic) +const _dynamicProvider = _DynamicFamily._(); + +final class _DynamicProvider extends $FunctionalProvider + with $Provider { + const _DynamicProvider._( + {required _DynamicFamily super.from, + required dynamic super.argument, + Object? Function( + Ref ref, + dynamic test, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'_dynamicProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Object? Function( + Ref ref, + dynamic test, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$dynamicHash(); + + @override + String toString() { + return r'_dynamicProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Object? value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + _DynamicProvider $copyWithCreate( + Object? Function( + Ref ref, + ) create, + ) { + return _DynamicProvider._( + argument: argument as dynamic, + from: from! as _DynamicFamily, + create: ( + ref, + dynamic test, + ) => + create(ref)); + } + + @override + Object? create(Ref ref) { + final _$cb = _createCb ?? _dynamic; + final argument = this.argument as dynamic; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is _DynamicProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$dynamicHash() => r'e08bd08481e4ea0d3da2ab7c38f940c34e96ba7f'; + +final class _DynamicFamily extends Family { + const _DynamicFamily._() + : super( + retry: null, + name: r'_dynamicProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + _DynamicProvider call( + dynamic test, + ) => + _DynamicProvider._(argument: test, from: this); + + @override + String debugGetCreateSourceHash() => _$dynamicHash(); + + @override + String toString() => r'_dynamicProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Object? Function( + Ref ref, + dynamic args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as _DynamicProvider; + + final argument = provider.argument as dynamic; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +@ProviderFor(alias) +const aliasProvider = AliasProvider._(); + +final class AliasProvider + extends $FunctionalProvider, AsyncValue> + with $Provider> { + const AliasProvider._( + {AsyncValue Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'aliasProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final AsyncValue Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$aliasHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(AsyncValue value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + $ProviderElement> $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + AliasProvider $copyWithCreate( + AsyncValue Function( + Ref ref, + ) create, + ) { + return AliasProvider._(create: create); + } + + @override + AsyncValue create(Ref ref) { + final _$cb = _createCb ?? alias; + return _$cb(ref); + } +} + +String _$aliasHash() => r'3feb548aa9a314142b5c5e3c9c7664a316a10d11'; + +@ProviderFor(aliasFamily) +const aliasFamilyProvider = AliasFamilyFamily._(); + +final class AliasFamilyProvider + extends $FunctionalProvider, AsyncValue> + with $Provider> { + const AliasFamilyProvider._( + {required AliasFamilyFamily super.from, + required AsyncValue super.argument, + AsyncValue Function( + Ref ref, + AsyncValue test, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'aliasFamilyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final AsyncValue Function( + Ref ref, + AsyncValue test, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$aliasFamilyHash(); + + @override + String toString() { + return r'aliasFamilyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(AsyncValue value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + $ProviderElement> $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + AliasFamilyProvider $copyWithCreate( + AsyncValue Function( + Ref ref, + ) create, + ) { + return AliasFamilyProvider._( + argument: argument as AsyncValue, + from: from! as AliasFamilyFamily, + create: ( + ref, + AsyncValue test, + ) => + create(ref)); + } + + @override + AsyncValue create(Ref ref) { + final _$cb = _createCb ?? aliasFamily; + final argument = this.argument as AsyncValue; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is AliasFamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$aliasFamilyHash() => r'6afe0afc21cfd2f0f26862e9d8c1095eca5f6e42'; + +final class AliasFamilyFamily extends Family { + const AliasFamilyFamily._() + : super( + retry: null, + name: r'aliasFamilyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + AliasFamilyProvider call( + AsyncValue test, + ) => + AliasFamilyProvider._(argument: test, from: this); + + @override + String debugGetCreateSourceHash() => _$aliasFamilyHash(); + + @override + String toString() => r'aliasFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + AsyncValue Function( + Ref ref, + AsyncValue args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as AliasFamilyProvider; + + final argument = provider.argument as AsyncValue; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +@ProviderFor(AliasClass) +const aliasClassProvider = AliasClassProvider._(); + +final class AliasClassProvider + extends $NotifierProvider> { + const AliasClassProvider._( + {super.runNotifierBuildOverride, AliasClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'aliasClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final AliasClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$aliasClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(AsyncValue value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + AliasClass create() => _createCb?.call() ?? AliasClass(); + + @$internal + @override + AliasClassProvider $copyWithCreate( + AliasClass Function() create, + ) { + return AliasClassProvider._(create: create); + } + + @$internal + @override + AliasClassProvider $copyWithBuild( + AsyncValue Function( + Ref, + AliasClass, + ) build, + ) { + return AliasClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$aliasClassHash() => r'aac83936c14520c015f0fe8a0120d353c0baf602'; + +abstract class _$AliasClass extends $Notifier> { + AsyncValue build(); + @$internal + @override + AsyncValue runBuild() => build(); +} + +@ProviderFor(AliasClassFamily) +const aliasClassFamilyProvider = AliasClassFamilyFamily._(); + +final class AliasClassFamilyProvider + extends $NotifierProvider> { + const AliasClassFamilyProvider._( + {required AliasClassFamilyFamily super.from, + required AsyncValue super.argument, + super.runNotifierBuildOverride, + AliasClassFamily Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'aliasClassFamilyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final AliasClassFamily Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$aliasClassFamilyHash(); + + @override + String toString() { + return r'aliasClassFamilyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(AsyncValue value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + AliasClassFamily create() => _createCb?.call() ?? AliasClassFamily(); + + @$internal + @override + AliasClassFamilyProvider $copyWithCreate( + AliasClassFamily Function() create, + ) { + return AliasClassFamilyProvider._( + argument: argument as AsyncValue, + from: from! as AliasClassFamilyFamily, + create: create); + } + + @$internal + @override + AliasClassFamilyProvider $copyWithBuild( + AsyncValue Function( + Ref, + AliasClassFamily, + ) build, + ) { + return AliasClassFamilyProvider._( + argument: argument as AsyncValue, + from: from! as AliasClassFamilyFamily, + runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is AliasClassFamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$aliasClassFamilyHash() => r'd4374c0ffbbca9d65fb967255129b3ceddaa764e'; + +final class AliasClassFamilyFamily extends Family { + const AliasClassFamilyFamily._() + : super( + retry: null, + name: r'aliasClassFamilyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + AliasClassFamilyProvider call( + AsyncValue test, + ) => + AliasClassFamilyProvider._(argument: test, from: this); + + @override + String debugGetCreateSourceHash() => _$aliasClassFamilyHash(); + + @override + String toString() => r'aliasClassFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + AliasClassFamily Function( + AsyncValue args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as AliasClassFamilyProvider; + + final argument = provider.argument as AsyncValue; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + AsyncValue Function( + Ref ref, AliasClassFamily notifier, AsyncValue argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as AliasClassFamilyProvider; + + final argument = provider.argument as AsyncValue; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} + +abstract class _$AliasClassFamily extends $Notifier> { + late final _$args = ref.$arg as AsyncValue; + AsyncValue get test => _$args; + + AsyncValue build( + AsyncValue test, + ); + @$internal + @override + AsyncValue runBuild() => build( + _$args, + ); +} + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/test/integration/hash/hash1.dart b/packages/riverpod_generator/test/integration/hash/hash1.dart index 32b6febae..67fb2e74a 100644 --- a/packages/riverpod_generator/test/integration/hash/hash1.dart +++ b/packages/riverpod_generator/test/integration/hash/hash1.dart @@ -1,4 +1,3 @@ -import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'hash1.g.dart'; diff --git a/packages/riverpod_generator/test/integration/hash/hash1.g.dart b/packages/riverpod_generator/test/integration/hash/hash1.g.dart index 3357b4f3e..9842eb755 100644 --- a/packages/riverpod_generator/test/integration/hash/hash1.g.dart +++ b/packages/riverpod_generator/test/integration/hash/hash1.g.dart @@ -6,52 +6,190 @@ part of 'hash1.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(simple) +const simpleProvider = SimpleProvider._(); + +final class SimpleProvider extends $FunctionalProvider + with $Provider { + const SimpleProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'simpleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$simpleHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + SimpleProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return SimpleProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? simple; + return _$cb(ref); + } +} + String _$simpleHash() => r'f916b37e39d654e9acfc9c2bd7a244902197b306'; -/// See also [simple]. -@ProviderFor(simple) -final simpleProvider = AutoDisposeProvider.internal( - simple, - name: r'simpleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$simpleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef SimpleRef = AutoDisposeProviderRef; +@ProviderFor(simple2) +const simple2Provider = Simple2Provider._(); + +final class Simple2Provider extends $FunctionalProvider + with $Provider { + const Simple2Provider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'simple2Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$simple2Hash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + Simple2Provider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return Simple2Provider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? simple2; + return _$cb(ref); + } +} + String _$simple2Hash() => r'a60a8496fc391f5adf7ad45a12d0723f14f3127c'; -/// See also [simple2]. -@ProviderFor(simple2) -final simple2Provider = AutoDisposeProvider.internal( - simple2, - name: r'simple2Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$simple2Hash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef Simple2Ref = AutoDisposeProviderRef; +@ProviderFor(SimpleClass) +const simpleClassProvider = SimpleClassProvider._(); + +final class SimpleClassProvider extends $NotifierProvider { + const SimpleClassProvider._( + {super.runNotifierBuildOverride, SimpleClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'simpleClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final SimpleClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$simpleClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + SimpleClass create() => _createCb?.call() ?? SimpleClass(); + + @$internal + @override + SimpleClassProvider $copyWithCreate( + SimpleClass Function() create, + ) { + return SimpleClassProvider._(create: create); + } + + @$internal + @override + SimpleClassProvider $copyWithBuild( + String Function( + Ref, + SimpleClass, + ) build, + ) { + return SimpleClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$simpleClassHash() => r'958123cd6179c5b88da040cfeb71eb3061765277'; -/// See also [SimpleClass]. -@ProviderFor(SimpleClass) -final simpleClassProvider = - AutoDisposeNotifierProvider.internal( - SimpleClass.new, - name: r'simpleClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$simpleClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$SimpleClass = AutoDisposeNotifier; +abstract class _$SimpleClass extends $Notifier { + String build(); + @$internal + @override + String runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/test/integration/hash/retry.dart b/packages/riverpod_generator/test/integration/hash/retry.dart new file mode 100644 index 000000000..77b2d08af --- /dev/null +++ b/packages/riverpod_generator/test/integration/hash/retry.dart @@ -0,0 +1,12 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'retry.g.dart'; + +@Riverpod(retry: myRetry) +String a(Ref ref) => throw UnimplementedError(); + +Duration? myRetry(int retryCount, Object error) => null; +Duration? myRetry2(int retryCount, Object error) => null; + +@Riverpod(retry: myRetry2) +String b(Ref ref, int arg) => throw UnimplementedError(); diff --git a/packages/riverpod_generator/test/integration/hash/retry.g.dart b/packages/riverpod_generator/test/integration/hash/retry.g.dart new file mode 100644 index 000000000..97c31e493 --- /dev/null +++ b/packages/riverpod_generator/test/integration/hash/retry.g.dart @@ -0,0 +1,198 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'retry.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +@ProviderFor(a) +const aProvider = AProvider._(); + +final class AProvider extends $FunctionalProvider + with $Provider { + const AProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: myRetry, + name: r'aProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$aHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + AProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return AProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? a; + return _$cb(ref); + } +} + +String _$aHash() => r'83a9516d10f85dc72ca773837e042bfc6e36c1f1'; + +@ProviderFor(b) +const bProvider = BFamily._(); + +final class BProvider extends $FunctionalProvider + with $Provider { + const BProvider._( + {required BFamily super.from, + required int super.argument, + String Function( + Ref ref, + int arg, + )? create}) + : _createCb = create, + super( + retry: myRetry2, + name: r'bProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + int arg, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$bHash(); + + @override + String toString() { + return r'bProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + BProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return BProvider._( + argument: argument as int, + from: from! as BFamily, + create: ( + ref, + int arg, + ) => + create(ref)); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? b; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is BProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$bHash() => r'95798a157250c86a901bca5701b487f508f8a5a4'; + +final class BFamily extends Family { + const BFamily._() + : super( + retry: myRetry2, + name: r'bProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + BProvider call( + int arg, + ) => + BProvider._(argument: arg, from: this); + + @override + String debugGetCreateSourceHash() => _$bHash(); + + @override + String toString() => r'bProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + String Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as BProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/test/integration/mutation.dart b/packages/riverpod_generator/test/integration/mutation.dart new file mode 100644 index 000000000..ab8071f79 --- /dev/null +++ b/packages/riverpod_generator/test/integration/mutation.dart @@ -0,0 +1,100 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'mutation.g.dart'; + +@riverpod +class Simple extends _$Simple { + @override + int build() => 0; + + @mutation + Future increment([int inc = 1]) async => state + inc; + + @mutation + FutureOr incrementOr() => state + 1; + + @mutation + Future delegated(Future Function() fn) => fn(); +} + +@riverpod +class SimpleFamily extends _$SimpleFamily { + @override + int build(String arg) => 0; + + @mutation + Future increment([int inc = 1]) async => state + inc; + + @mutation + FutureOr incrementOr() => state + 1; +} + +@riverpod +class SimpleAsync extends _$SimpleAsync { + @override + Future build() async => 0; + + @mutation + Future increment([int inc = 1]) async => (await future) + inc; + + @mutation + Future delegated(Future Function() fn) async { + await future; + return fn(); + } +} + +@riverpod +class SimpleAsync2 extends _$SimpleAsync2 { + @override + Stream build(String arg) => Stream.value(0); + + @mutation + Future increment() async => (await future) + 1; +} + +@riverpod +class Generic extends _$Generic { + @override + Future build() async => 0; + + @mutation + Future increment() async => (await future) + 1; +} + +@riverpod +class GenericMut extends _$GenericMut { + @override + Future build() async => 0; + + @mutation + Future increment(T value) async => + (await future) + value.ceil(); +} + +@riverpod +class FailingCtor extends _$FailingCtor { + FailingCtor() { + throw StateError('err'); + } + + @override + int build() => 0; + + @mutation + Future increment([int inc = 1]) async => state + inc; +} + +@riverpod +class Typed extends _$Typed { + @override + String build() => 'typed'; + + @mutation + Future mutate( + String one, { + required String two, + required String three, + }) async => + '$one $two $three'; +} diff --git a/packages/riverpod_generator/test/integration/mutation.g.dart b/packages/riverpod_generator/test/integration/mutation.g.dart new file mode 100644 index 000000000..ebf37b024 --- /dev/null +++ b/packages/riverpod_generator/test/integration/mutation.g.dart @@ -0,0 +1,1696 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'mutation.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +@ProviderFor(Simple) +const simpleProvider = SimpleProvider._(); + +final class SimpleProvider extends $NotifierProvider { + const SimpleProvider._( + {super.runNotifierBuildOverride, Simple Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'simpleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Simple Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$simpleHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Simple create() => _createCb?.call() ?? Simple(); + + @$internal + @override + SimpleProvider $copyWithCreate( + Simple Function() create, + ) { + return SimpleProvider._(create: create); + } + + @$internal + @override + SimpleProvider $copyWithBuild( + int Function( + Ref, + Simple, + ) build, + ) { + return SimpleProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + _$SimpleElement $createElement($ProviderPointer pointer) => + _$SimpleElement(this, pointer); + + ProviderListenable get increment => + $LazyProxyListenable( + this, + (element) { + element as _$SimpleElement; + + return element._$increment; + }, + ); + + ProviderListenable get incrementOr => + $LazyProxyListenable( + this, + (element) { + element as _$SimpleElement; + + return element._$incrementOr; + }, + ); + + ProviderListenable get delegated => + $LazyProxyListenable( + this, + (element) { + element as _$SimpleElement; + + return element._$delegated; + }, + ); +} + +String _$simpleHash() => r'c84cd9b6e3b09516b19316b0b21ea5ba5bc08a07'; + +abstract class _$Simple extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + +class _$SimpleElement extends $NotifierProviderElement { + _$SimpleElement(super.provider, super.pointer) { + _$increment.result = $Result.data(_$Simple$Increment(this)); + _$incrementOr.result = $Result.data(_$Simple$IncrementOr(this)); + _$delegated.result = $Result.data(_$Simple$Delegated(this)); + } + final _$increment = $ElementLense<_$Simple$Increment>(); + final _$incrementOr = $ElementLense<_$Simple$IncrementOr>(); + final _$delegated = $ElementLense<_$Simple$Delegated>(); + @override + void mount() { + super.mount(); + _$increment.result!.stateOrNull!.reset(); + _$incrementOr.result!.stateOrNull!.reset(); + _$delegated.result!.stateOrNull!.reset(); + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + + listenableVisitor(_$increment); + listenableVisitor(_$incrementOr); + listenableVisitor(_$delegated); + } +} + +sealed class Simple$Increment extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [Simple.increment] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// Lastly, if the method completes without throwing, the Notifier's state + /// will be updated with the new value. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future call([int inc = 1]); +} + +final class _$Simple$Increment + extends $SyncMutationBase + implements Simple$Increment { + _$Simple$Increment(this.element, {super.state, super.key}); + + @override + final _$SimpleElement element; + + @override + $ElementLense<_$Simple$Increment> get listenable => element._$increment; + + @override + Future call([int inc = 1]) { + return mutateAsync( + Invocation.method( + #increment, + [inc], + ), + ($notifier) => $notifier.increment( + inc, + ), + ); + } + + @override + _$Simple$Increment copyWith(MutationState state, {Object? key}) => + _$Simple$Increment(element, state: state, key: key); +} + +sealed class Simple$IncrementOr extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [Simple.incrementOr] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// Lastly, if the method completes without throwing, the Notifier's state + /// will be updated with the new value. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future call(); +} + +final class _$Simple$IncrementOr + extends $SyncMutationBase + implements Simple$IncrementOr { + _$Simple$IncrementOr(this.element, {super.state, super.key}); + + @override + final _$SimpleElement element; + + @override + $ElementLense<_$Simple$IncrementOr> get listenable => element._$incrementOr; + + @override + Future call() { + return mutateAsync( + Invocation.method( + #incrementOr, + [], + ), + ($notifier) => $notifier.incrementOr(), + ); + } + + @override + _$Simple$IncrementOr copyWith(MutationState state, {Object? key}) => + _$Simple$IncrementOr(element, state: state, key: key); +} + +sealed class Simple$Delegated extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [Simple.delegated] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// Lastly, if the method completes without throwing, the Notifier's state + /// will be updated with the new value. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future call(Future Function() fn); +} + +final class _$Simple$Delegated + extends $SyncMutationBase + implements Simple$Delegated { + _$Simple$Delegated(this.element, {super.state, super.key}); + + @override + final _$SimpleElement element; + + @override + $ElementLense<_$Simple$Delegated> get listenable => element._$delegated; + + @override + Future call(Future Function() fn) { + return mutateAsync( + Invocation.method( + #delegated, + [fn], + ), + ($notifier) => $notifier.delegated( + fn, + ), + ); + } + + @override + _$Simple$Delegated copyWith(MutationState state, {Object? key}) => + _$Simple$Delegated(element, state: state, key: key); +} + +@ProviderFor(SimpleFamily) +const simpleFamilyProvider = SimpleFamilyFamily._(); + +final class SimpleFamilyProvider extends $NotifierProvider { + const SimpleFamilyProvider._( + {required SimpleFamilyFamily super.from, + required String super.argument, + super.runNotifierBuildOverride, + SimpleFamily Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'simpleFamilyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final SimpleFamily Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$simpleFamilyHash(); + + @override + String toString() { + return r'simpleFamilyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + SimpleFamily create() => _createCb?.call() ?? SimpleFamily(); + + @$internal + @override + SimpleFamilyProvider $copyWithCreate( + SimpleFamily Function() create, + ) { + return SimpleFamilyProvider._( + argument: argument as String, + from: from! as SimpleFamilyFamily, + create: create); + } + + @$internal + @override + SimpleFamilyProvider $copyWithBuild( + int Function( + Ref, + SimpleFamily, + ) build, + ) { + return SimpleFamilyProvider._( + argument: argument as String, + from: from! as SimpleFamilyFamily, + runNotifierBuildOverride: build); + } + + @$internal + @override + _$SimpleFamilyElement $createElement($ProviderPointer pointer) => + _$SimpleFamilyElement(this, pointer); + + ProviderListenable get increment => + $LazyProxyListenable( + this, + (element) { + element as _$SimpleFamilyElement; + + return element._$increment; + }, + ); + + ProviderListenable get incrementOr => + $LazyProxyListenable( + this, + (element) { + element as _$SimpleFamilyElement; + + return element._$incrementOr; + }, + ); + + @override + bool operator ==(Object other) { + return other is SimpleFamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$simpleFamilyHash() => r'7f7a9985568e147b78fbcd6ed7691a6677f75aeb'; + +final class SimpleFamilyFamily extends Family { + const SimpleFamilyFamily._() + : super( + retry: null, + name: r'simpleFamilyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + SimpleFamilyProvider call( + String arg, + ) => + SimpleFamilyProvider._(argument: arg, from: this); + + @override + String debugGetCreateSourceHash() => _$simpleFamilyHash(); + + @override + String toString() => r'simpleFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + SimpleFamily Function( + String args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as SimpleFamilyProvider; + + final argument = provider.argument as String; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function(Ref ref, SimpleFamily notifier, String argument) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as SimpleFamilyProvider; + + final argument = provider.argument as String; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} + +abstract class _$SimpleFamily extends $Notifier { + late final _$args = ref.$arg as String; + String get arg => _$args; + + int build( + String arg, + ); + @$internal + @override + int runBuild() => build( + _$args, + ); +} + +class _$SimpleFamilyElement + extends $NotifierProviderElement { + _$SimpleFamilyElement(super.provider, super.pointer) { + _$increment.result = $Result.data(_$SimpleFamily$Increment(this)); + _$incrementOr.result = $Result.data(_$SimpleFamily$IncrementOr(this)); + } + final _$increment = $ElementLense<_$SimpleFamily$Increment>(); + final _$incrementOr = $ElementLense<_$SimpleFamily$IncrementOr>(); + @override + void mount() { + super.mount(); + _$increment.result!.stateOrNull!.reset(); + _$incrementOr.result!.stateOrNull!.reset(); + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + + listenableVisitor(_$increment); + listenableVisitor(_$incrementOr); + } +} + +sealed class SimpleFamily$Increment extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [SimpleFamily.increment] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// Lastly, if the method completes without throwing, the Notifier's state + /// will be updated with the new value. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future call([int inc = 1]); +} + +final class _$SimpleFamily$Increment + extends $SyncMutationBase + implements SimpleFamily$Increment { + _$SimpleFamily$Increment(this.element, {super.state, super.key}); + + @override + final _$SimpleFamilyElement element; + + @override + $ElementLense<_$SimpleFamily$Increment> get listenable => element._$increment; + + @override + Future call([int inc = 1]) { + return mutateAsync( + Invocation.method( + #increment, + [inc], + ), + ($notifier) => $notifier.increment( + inc, + ), + ); + } + + @override + _$SimpleFamily$Increment copyWith(MutationState state, {Object? key}) => + _$SimpleFamily$Increment(element, state: state, key: key); +} + +sealed class SimpleFamily$IncrementOr extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [SimpleFamily.incrementOr] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// Lastly, if the method completes without throwing, the Notifier's state + /// will be updated with the new value. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future call(); +} + +final class _$SimpleFamily$IncrementOr + extends $SyncMutationBase + implements SimpleFamily$IncrementOr { + _$SimpleFamily$IncrementOr(this.element, {super.state, super.key}); + + @override + final _$SimpleFamilyElement element; + + @override + $ElementLense<_$SimpleFamily$IncrementOr> get listenable => + element._$incrementOr; + + @override + Future call() { + return mutateAsync( + Invocation.method( + #incrementOr, + [], + ), + ($notifier) => $notifier.incrementOr(), + ); + } + + @override + _$SimpleFamily$IncrementOr copyWith(MutationState state, + {Object? key}) => + _$SimpleFamily$IncrementOr(element, state: state, key: key); +} + +@ProviderFor(SimpleAsync) +const simpleAsyncProvider = SimpleAsyncProvider._(); + +final class SimpleAsyncProvider + extends $AsyncNotifierProvider { + const SimpleAsyncProvider._( + {super.runNotifierBuildOverride, SimpleAsync Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'simpleAsyncProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final SimpleAsync Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$simpleAsyncHash(); + + @$internal + @override + SimpleAsync create() => _createCb?.call() ?? SimpleAsync(); + + @$internal + @override + SimpleAsyncProvider $copyWithCreate( + SimpleAsync Function() create, + ) { + return SimpleAsyncProvider._(create: create); + } + + @$internal + @override + SimpleAsyncProvider $copyWithBuild( + FutureOr Function( + Ref, + SimpleAsync, + ) build, + ) { + return SimpleAsyncProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + _$SimpleAsyncElement $createElement($ProviderPointer pointer) => + _$SimpleAsyncElement(this, pointer); + + ProviderListenable get increment => + $LazyProxyListenable>( + this, + (element) { + element as _$SimpleAsyncElement; + + return element._$increment; + }, + ); + + ProviderListenable get delegated => + $LazyProxyListenable>( + this, + (element) { + element as _$SimpleAsyncElement; + + return element._$delegated; + }, + ); +} + +String _$simpleAsyncHash() => r'ed00b8e5170e48855d0b3cddddabd316fef466cf'; + +abstract class _$SimpleAsync extends $AsyncNotifier { + FutureOr build(); + @$internal + @override + FutureOr runBuild() => build(); +} + +class _$SimpleAsyncElement + extends $AsyncNotifierProviderElement { + _$SimpleAsyncElement(super.provider, super.pointer) { + _$increment.result = $Result.data(_$SimpleAsync$Increment(this)); + _$delegated.result = $Result.data(_$SimpleAsync$Delegated(this)); + } + final _$increment = $ElementLense<_$SimpleAsync$Increment>(); + final _$delegated = $ElementLense<_$SimpleAsync$Delegated>(); + @override + void mount() { + super.mount(); + _$increment.result!.stateOrNull!.reset(); + _$delegated.result!.stateOrNull!.reset(); + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + + listenableVisitor(_$increment); + listenableVisitor(_$delegated); + } +} + +sealed class SimpleAsync$Increment extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [SimpleAsync.increment] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// Lastly, if the method completes without throwing, the Notifier's state + /// will be updated with the new value. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future call([int inc = 1]); +} + +final class _$SimpleAsync$Increment + extends $AsyncMutationBase + implements SimpleAsync$Increment { + _$SimpleAsync$Increment(this.element, {super.state, super.key}); + + @override + final _$SimpleAsyncElement element; + + @override + $ElementLense<_$SimpleAsync$Increment> get listenable => element._$increment; + + @override + Future call([int inc = 1]) { + return mutateAsync( + Invocation.method( + #increment, + [inc], + ), + ($notifier) => $notifier.increment( + inc, + ), + ); + } + + @override + _$SimpleAsync$Increment copyWith(MutationState state, {Object? key}) => + _$SimpleAsync$Increment(element, state: state, key: key); +} + +sealed class SimpleAsync$Delegated extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [SimpleAsync.delegated] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// Lastly, if the method completes without throwing, the Notifier's state + /// will be updated with the new value. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future call(Future Function() fn); +} + +final class _$SimpleAsync$Delegated + extends $AsyncMutationBase + implements SimpleAsync$Delegated { + _$SimpleAsync$Delegated(this.element, {super.state, super.key}); + + @override + final _$SimpleAsyncElement element; + + @override + $ElementLense<_$SimpleAsync$Delegated> get listenable => element._$delegated; + + @override + Future call(Future Function() fn) { + return mutateAsync( + Invocation.method( + #delegated, + [fn], + ), + ($notifier) => $notifier.delegated( + fn, + ), + ); + } + + @override + _$SimpleAsync$Delegated copyWith(MutationState state, {Object? key}) => + _$SimpleAsync$Delegated(element, state: state, key: key); +} + +@ProviderFor(SimpleAsync2) +const simpleAsync2Provider = SimpleAsync2Family._(); + +final class SimpleAsync2Provider + extends $StreamNotifierProvider { + const SimpleAsync2Provider._( + {required SimpleAsync2Family super.from, + required String super.argument, + super.runNotifierBuildOverride, + SimpleAsync2 Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'simpleAsync2Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final SimpleAsync2 Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$simpleAsync2Hash(); + + @override + String toString() { + return r'simpleAsync2Provider' + '' + '($argument)'; + } + + @$internal + @override + SimpleAsync2 create() => _createCb?.call() ?? SimpleAsync2(); + + @$internal + @override + SimpleAsync2Provider $copyWithCreate( + SimpleAsync2 Function() create, + ) { + return SimpleAsync2Provider._( + argument: argument as String, + from: from! as SimpleAsync2Family, + create: create); + } + + @$internal + @override + SimpleAsync2Provider $copyWithBuild( + Stream Function( + Ref, + SimpleAsync2, + ) build, + ) { + return SimpleAsync2Provider._( + argument: argument as String, + from: from! as SimpleAsync2Family, + runNotifierBuildOverride: build); + } + + @$internal + @override + _$SimpleAsync2Element $createElement($ProviderPointer pointer) => + _$SimpleAsync2Element(this, pointer); + + ProviderListenable get increment => + $LazyProxyListenable>( + this, + (element) { + element as _$SimpleAsync2Element; + + return element._$increment; + }, + ); + + @override + bool operator ==(Object other) { + return other is SimpleAsync2Provider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$simpleAsync2Hash() => r'7b372f85f3e4f1c2a954402b82a9a7b68bbc1407'; + +final class SimpleAsync2Family extends Family { + const SimpleAsync2Family._() + : super( + retry: null, + name: r'simpleAsync2Provider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + SimpleAsync2Provider call( + String arg, + ) => + SimpleAsync2Provider._(argument: arg, from: this); + + @override + String debugGetCreateSourceHash() => _$simpleAsync2Hash(); + + @override + String toString() => r'simpleAsync2Provider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + SimpleAsync2 Function( + String args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as SimpleAsync2Provider; + + final argument = provider.argument as String; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + Stream Function(Ref ref, SimpleAsync2 notifier, String argument) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as SimpleAsync2Provider; + + final argument = provider.argument as String; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} + +abstract class _$SimpleAsync2 extends $StreamNotifier { + late final _$args = ref.$arg as String; + String get arg => _$args; + + Stream build( + String arg, + ); + @$internal + @override + Stream runBuild() => build( + _$args, + ); +} + +class _$SimpleAsync2Element + extends $StreamNotifierProviderElement { + _$SimpleAsync2Element(super.provider, super.pointer) { + _$increment.result = $Result.data(_$SimpleAsync2$Increment(this)); + } + final _$increment = $ElementLense<_$SimpleAsync2$Increment>(); + @override + void mount() { + super.mount(); + _$increment.result!.stateOrNull!.reset(); + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + + listenableVisitor(_$increment); + } +} + +sealed class SimpleAsync2$Increment extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [SimpleAsync2.increment] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// Lastly, if the method completes without throwing, the Notifier's state + /// will be updated with the new value. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future call(); +} + +final class _$SimpleAsync2$Increment + extends $AsyncMutationBase + implements SimpleAsync2$Increment { + _$SimpleAsync2$Increment(this.element, {super.state, super.key}); + + @override + final _$SimpleAsync2Element element; + + @override + $ElementLense<_$SimpleAsync2$Increment> get listenable => element._$increment; + + @override + Future call() { + return mutateAsync( + Invocation.method( + #increment, + [], + ), + ($notifier) => $notifier.increment(), + ); + } + + @override + _$SimpleAsync2$Increment copyWith(MutationState state, {Object? key}) => + _$SimpleAsync2$Increment(element, state: state, key: key); +} + +@ProviderFor(Generic) +const genericProvider = GenericFamily._(); + +final class GenericProvider + extends $AsyncNotifierProvider, int> { + const GenericProvider._( + {required GenericFamily super.from, + super.runNotifierBuildOverride, + Generic Function()? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'genericProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Generic Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$genericHash(); + + GenericProvider _copyWithCreate( + Generic Function() create, + ) { + return GenericProvider._( + from: from! as GenericFamily, create: create); + } + + GenericProvider _copyWithBuild( + FutureOr Function( + Ref, + Generic, + ) build, + ) { + return GenericProvider._( + from: from! as GenericFamily, runNotifierBuildOverride: build); + } + + @override + String toString() { + return r'genericProvider' + '<${T}>' + '()'; + } + + @$internal + @override + Generic create() => _createCb?.call() ?? Generic(); + + @$internal + @override + GenericProvider $copyWithCreate( + Generic Function() create, + ) { + return GenericProvider._(from: from! as GenericFamily, create: create); + } + + @$internal + @override + GenericProvider $copyWithBuild( + FutureOr Function( + Ref, + Generic, + ) build, + ) { + return GenericProvider._( + from: from! as GenericFamily, runNotifierBuildOverride: build); + } + + @$internal + @override + _$GenericElement $createElement($ProviderPointer pointer) => + _$GenericElement(this, pointer); + + ProviderListenable get increment => + $LazyProxyListenable>( + this, + (element) { + element as _$GenericElement; + + return element._$increment; + }, + ); + + @override + bool operator ==(Object other) { + return other is GenericProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } + + @override + int get hashCode { + return Object.hash(runtimeType, argument); + } +} + +String _$genericHash() => r'4089b4d9b08bfff0256ad67cf35780a6409f7a87'; + +final class GenericFamily extends Family { + const GenericFamily._() + : super( + retry: null, + name: r'genericProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + GenericProvider call() => GenericProvider._(from: this); + + @override + String debugGetCreateSourceHash() => _$genericHash(); + + @override + String toString() => r'genericProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Generic Function() create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericProvider; + + return provider._copyWithCreate(create).$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + FutureOr Function(Ref ref, Generic notifier) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericProvider; + + return provider._copyWithBuild(build).$createElement(pointer); + }, + ); + } +} + +abstract class _$Generic extends $AsyncNotifier { + FutureOr build(); + @$internal + @override + FutureOr runBuild() => build(); +} + +class _$GenericElement + extends $AsyncNotifierProviderElement, int> { + _$GenericElement(super.provider, super.pointer) { + _$increment.result = $Result.data(_$Generic$Increment(this)); + } + final _$increment = $ElementLense<_$Generic$Increment>(); + @override + void mount() { + super.mount(); + _$increment.result!.stateOrNull!.reset(); + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + + listenableVisitor(_$increment); + } +} + +sealed class Generic$Increment extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [Generic.increment] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// Lastly, if the method completes without throwing, the Notifier's state + /// will be updated with the new value. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future call(); +} + +final class _$Generic$Increment + extends $AsyncMutationBase + implements Generic$Increment { + _$Generic$Increment(this.element, {super.state, super.key}); + + @override + final _$GenericElement element; + + @override + $ElementLense<_$Generic$Increment> get listenable => element._$increment; + + @override + Future call() { + return mutateAsync( + Invocation.method( + #increment, + [], + ), + ($notifier) => $notifier.increment(), + ); + } + + @override + _$Generic$Increment copyWith(MutationState state, {Object? key}) => + _$Generic$Increment(element, state: state, key: key); +} + +@ProviderFor(GenericMut) +const genericMutProvider = GenericMutProvider._(); + +final class GenericMutProvider extends $AsyncNotifierProvider { + const GenericMutProvider._( + {super.runNotifierBuildOverride, GenericMut Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'genericMutProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final GenericMut Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$genericMutHash(); + + @$internal + @override + GenericMut create() => _createCb?.call() ?? GenericMut(); + + @$internal + @override + GenericMutProvider $copyWithCreate( + GenericMut Function() create, + ) { + return GenericMutProvider._(create: create); + } + + @$internal + @override + GenericMutProvider $copyWithBuild( + FutureOr Function( + Ref, + GenericMut, + ) build, + ) { + return GenericMutProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + _$GenericMutElement $createElement($ProviderPointer pointer) => + _$GenericMutElement(this, pointer); + + ProviderListenable get increment => + $LazyProxyListenable>( + this, + (element) { + element as _$GenericMutElement; + + return element._$increment; + }, + ); +} + +String _$genericMutHash() => r'43acfc1b7cf59fb05f31ed4c2d5470422198feb0'; + +abstract class _$GenericMut extends $AsyncNotifier { + FutureOr build(); + @$internal + @override + FutureOr runBuild() => build(); +} + +class _$GenericMutElement + extends $AsyncNotifierProviderElement { + _$GenericMutElement(super.provider, super.pointer) { + _$increment.result = $Result.data(_$GenericMut$Increment(this)); + } + final _$increment = $ElementLense<_$GenericMut$Increment>(); + @override + void mount() { + super.mount(); + _$increment.result!.stateOrNull!.reset(); + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + + listenableVisitor(_$increment); + } +} + +sealed class GenericMut$Increment extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [GenericMut.increment] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// Lastly, if the method completes without throwing, the Notifier's state + /// will be updated with the new value. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future call(T value); +} + +final class _$GenericMut$Increment + extends $AsyncMutationBase + implements GenericMut$Increment { + _$GenericMut$Increment(this.element, {super.state, super.key}); + + @override + final _$GenericMutElement element; + + @override + $ElementLense<_$GenericMut$Increment> get listenable => element._$increment; + + @override + Future call(T value) { + return mutateAsync( + Invocation.genericMethod( + #increment, + [T], + [value], + ), + ($notifier) => $notifier.increment( + value, + ), + ); + } + + @override + _$GenericMut$Increment copyWith(MutationState state, {Object? key}) => + _$GenericMut$Increment(element, state: state, key: key); +} + +@ProviderFor(FailingCtor) +const failingCtorProvider = FailingCtorProvider._(); + +final class FailingCtorProvider extends $NotifierProvider { + const FailingCtorProvider._( + {super.runNotifierBuildOverride, FailingCtor Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'failingCtorProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FailingCtor Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$failingCtorHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + FailingCtor create() => _createCb?.call() ?? FailingCtor(); + + @$internal + @override + FailingCtorProvider $copyWithCreate( + FailingCtor Function() create, + ) { + return FailingCtorProvider._(create: create); + } + + @$internal + @override + FailingCtorProvider $copyWithBuild( + int Function( + Ref, + FailingCtor, + ) build, + ) { + return FailingCtorProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + _$FailingCtorElement $createElement($ProviderPointer pointer) => + _$FailingCtorElement(this, pointer); + + ProviderListenable get increment => + $LazyProxyListenable( + this, + (element) { + element as _$FailingCtorElement; + + return element._$increment; + }, + ); +} + +String _$failingCtorHash() => r'6cdef257a2d783fa5a606b411be0d23744766cdc'; + +abstract class _$FailingCtor extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + +class _$FailingCtorElement extends $NotifierProviderElement { + _$FailingCtorElement(super.provider, super.pointer) { + _$increment.result = $Result.data(_$FailingCtor$Increment(this)); + } + final _$increment = $ElementLense<_$FailingCtor$Increment>(); + @override + void mount() { + super.mount(); + _$increment.result!.stateOrNull!.reset(); + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + + listenableVisitor(_$increment); + } +} + +sealed class FailingCtor$Increment extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [FailingCtor.increment] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// Lastly, if the method completes without throwing, the Notifier's state + /// will be updated with the new value. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future call([int inc = 1]); +} + +final class _$FailingCtor$Increment + extends $SyncMutationBase + implements FailingCtor$Increment { + _$FailingCtor$Increment(this.element, {super.state, super.key}); + + @override + final _$FailingCtorElement element; + + @override + $ElementLense<_$FailingCtor$Increment> get listenable => element._$increment; + + @override + Future call([int inc = 1]) { + return mutateAsync( + Invocation.method( + #increment, + [inc], + ), + ($notifier) => $notifier.increment( + inc, + ), + ); + } + + @override + _$FailingCtor$Increment copyWith(MutationState state, {Object? key}) => + _$FailingCtor$Increment(element, state: state, key: key); +} + +@ProviderFor(Typed) +const typedProvider = TypedProvider._(); + +final class TypedProvider extends $NotifierProvider { + const TypedProvider._( + {super.runNotifierBuildOverride, Typed Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'typedProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Typed Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$typedHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Typed create() => _createCb?.call() ?? Typed(); + + @$internal + @override + TypedProvider $copyWithCreate( + Typed Function() create, + ) { + return TypedProvider._(create: create); + } + + @$internal + @override + TypedProvider $copyWithBuild( + String Function( + Ref, + Typed, + ) build, + ) { + return TypedProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + _$TypedElement $createElement($ProviderPointer pointer) => + _$TypedElement(this, pointer); + + ProviderListenable get mutate => + $LazyProxyListenable( + this, + (element) { + element as _$TypedElement; + + return element._$mutate; + }, + ); +} + +String _$typedHash() => r'1f53e16796771d14fcdfec41d2b9f5eb70d875a7'; + +abstract class _$Typed extends $Notifier { + String build(); + @$internal + @override + String runBuild() => build(); +} + +class _$TypedElement extends $NotifierProviderElement { + _$TypedElement(super.provider, super.pointer) { + _$mutate.result = $Result.data(_$Typed$Mutate(this)); + } + final _$mutate = $ElementLense<_$Typed$Mutate>(); + @override + void mount() { + super.mount(); + _$mutate.result!.stateOrNull!.reset(); + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + + listenableVisitor(_$mutate); + } +} + +sealed class Typed$Mutate extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [Typed.mutate] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// Lastly, if the method completes without throwing, the Notifier's state + /// will be updated with the new value. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future call(String one, {required String two, required String three}); +} + +final class _$Typed$Mutate + extends $SyncMutationBase + implements Typed$Mutate { + _$Typed$Mutate(this.element, {super.state, super.key}); + + @override + final _$TypedElement element; + + @override + $ElementLense<_$Typed$Mutate> get listenable => element._$mutate; + + @override + Future call(String one, + {required String two, required String three}) { + return mutateAsync( + Invocation.method(#mutate, [one], {#two: two, #three: three}), + ($notifier) => $notifier.mutate( + one, + two: two, + three: three, + ), + ); + } + + @override + _$Typed$Mutate copyWith(MutationState state, {Object? key}) => + _$Typed$Mutate(element, state: state, key: key); +} + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/test/integration/scopes.dart b/packages/riverpod_generator/test/integration/scopes.dart index 9e0b223c5..d555c207b 100644 --- a/packages/riverpod_generator/test/integration/scopes.dart +++ b/packages/riverpod_generator/test/integration/scopes.dart @@ -3,4 +3,13 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'scopes.g.dart'; @riverpod -external int scoped(); +class ScopedClass extends _$ScopedClass { + @override + int build(); +} + +@riverpod +class ScopedClassFamily extends _$ScopedClassFamily { + @override + int build(int a); +} diff --git a/packages/riverpod_generator/test/integration/scopes.g.dart b/packages/riverpod_generator/test/integration/scopes.g.dart index c2b03513c..a719f7459 100644 --- a/packages/riverpod_generator/test/integration/scopes.g.dart +++ b/packages/riverpod_generator/test/integration/scopes.g.dart @@ -6,24 +6,236 @@ part of 'scopes.dart'; // RiverpodGenerator // ************************************************************************** -String _$scopedHash() => r'590f1a203323105e732397a2616fbd7dac65f0cc'; - -/// See also [scoped]. -@ProviderFor(scoped) -final scopedProvider = AutoDisposeProvider.internal( - (_) => throw UnsupportedError( - 'The provider "scopedProvider" is expected to get overridden/scoped, ' - 'but was accessed without an override.', - ), - name: r'scopedProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$scopedHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ScopedRef = AutoDisposeProviderRef; +@ProviderFor(ScopedClass) +const scopedClassProvider = ScopedClassProvider._(); + +final class ScopedClassProvider extends $NotifierProvider { + const ScopedClassProvider._( + {super.runNotifierBuildOverride, ScopedClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'scopedClassProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final ScopedClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$scopedClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + ScopedClass create() => _createCb?.call() ?? ScopedClass(); + + @$internal + @override + ScopedClassProvider $copyWithCreate( + ScopedClass Function() create, + ) { + return ScopedClassProvider._(create: create); + } + + @$internal + @override + ScopedClassProvider $copyWithBuild( + int Function( + Ref, + ScopedClass, + ) build, + ) { + return ScopedClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$scopedClassHash() => r'113acc46a2e61abfeb61cf4b89a1dc555e915793'; + +abstract class _$ScopedClass extends $Notifier { + int build() => throw MissingScopeException(ref); + @$internal + @override + int runBuild() => build(); +} + +@ProviderFor(ScopedClassFamily) +const scopedClassFamilyProvider = ScopedClassFamilyFamily._(); + +final class ScopedClassFamilyProvider + extends $NotifierProvider { + const ScopedClassFamilyProvider._( + {required ScopedClassFamilyFamily super.from, + required int super.argument, + super.runNotifierBuildOverride, + ScopedClassFamily Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'scopedClassFamilyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final ScopedClassFamily Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$scopedClassFamilyHash(); + + @override + String toString() { + return r'scopedClassFamilyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + ScopedClassFamily create() => _createCb?.call() ?? ScopedClassFamily(); + + @$internal + @override + ScopedClassFamilyProvider $copyWithCreate( + ScopedClassFamily Function() create, + ) { + return ScopedClassFamilyProvider._( + argument: argument as int, + from: from! as ScopedClassFamilyFamily, + create: create); + } + + @$internal + @override + ScopedClassFamilyProvider $copyWithBuild( + int Function( + Ref, + ScopedClassFamily, + ) build, + ) { + return ScopedClassFamilyProvider._( + argument: argument as int, + from: from! as ScopedClassFamilyFamily, + runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is ScopedClassFamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$scopedClassFamilyHash() => r'04aeb0bbfdc363e2c8714c7a5967368a7f990d58'; + +final class ScopedClassFamilyFamily extends Family { + const ScopedClassFamilyFamily._() + : super( + retry: null, + name: r'scopedClassFamilyProvider', + dependencies: const [], + allTransitiveDependencies: const [], + isAutoDispose: true, + ); + + ScopedClassFamilyProvider call( + int a, + ) => + ScopedClassFamilyProvider._(argument: a, from: this); + + @override + String debugGetCreateSourceHash() => _$scopedClassFamilyHash(); + + @override + String toString() => r'scopedClassFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + ScopedClassFamily Function( + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ScopedClassFamilyProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function(Ref ref, ScopedClassFamily notifier, int argument) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ScopedClassFamilyProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} + +abstract class _$ScopedClassFamily extends $Notifier { + late final _$args = ref.$arg as int; + int get a => _$args; + + int build( + int a, + ) => + throw MissingScopeException(ref); + @$internal + @override + int runBuild() => build( + _$args, + ); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/test/integration/split.dart b/packages/riverpod_generator/test/integration/split.dart index ab3c4fcc3..47a61d7b3 100644 --- a/packages/riverpod_generator/test/integration/split.dart +++ b/packages/riverpod_generator/test/integration/split.dart @@ -1,5 +1,4 @@ -// Regresion test for https://github.com/rrousselGit/riverpod/issues/2175 -import 'package:riverpod/riverpod.dart'; +// Regression test for https://github.com/rrousselGit/riverpod/issues/2175 import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'split2.dart'; diff --git a/packages/riverpod_generator/test/integration/split.g.dart b/packages/riverpod_generator/test/integration/split.g.dart index a59f85b4d..a3eb0f24a 100644 --- a/packages/riverpod_generator/test/integration/split.g.dart +++ b/packages/riverpod_generator/test/integration/split.g.dart @@ -6,37 +6,121 @@ part of 'split.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(counter2) +const counter2Provider = Counter2Provider._(); + +final class Counter2Provider extends $FunctionalProvider + with $Provider { + const Counter2Provider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counter2Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$counter2Hash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + Counter2Provider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return Counter2Provider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? counter2; + return _$cb(ref); + } +} + String _$counter2Hash() => r'ab7bef7da79217c780c76761a5ae0c0172ca097e'; -/// See also [counter2]. -@ProviderFor(counter2) -final counter2Provider = AutoDisposeProvider.internal( - counter2, - name: r'counter2Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$counter2Hash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef Counter2Ref = AutoDisposeProviderRef; +@ProviderFor(counter) +const counterProvider = CounterProvider._(); + +final class CounterProvider extends $FunctionalProvider + with $Provider { + const CounterProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counterProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$counterHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CounterProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return CounterProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? counter; + return _$cb(ref); + } +} + String _$counterHash() => r'784ece48cb20fcfdec1553774ecfbd381d1e081f'; -/// See also [counter]. -@ProviderFor(counter) -final counterProvider = AutoDisposeProvider.internal( - counter, - name: r'counterProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$counterHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CounterRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/test/integration/stream.dart b/packages/riverpod_generator/test/integration/stream.dart index c7ea89c53..d6454068a 100644 --- a/packages/riverpod_generator/test/integration/stream.dart +++ b/packages/riverpod_generator/test/integration/stream.dart @@ -1,14 +1,26 @@ -import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'stream.g.dart'; +@riverpod +Stream> generic(Ref ref) async* { + yield []; +} + +@riverpod +class GenericClass extends _$GenericClass { + @override + Stream> build() async* { + yield []; + } +} + @riverpod Stream public(Ref ref) { return Stream.value('Hello world'); } -final privateProvider = _privateProvider; +const privateProvider = _privateProvider; @riverpod Stream _private(Ref ref) { @@ -41,7 +53,7 @@ class PublicClass extends _$PublicClass { } } -final privateClassProvider = _privateClassProvider; +const privateClassProvider = _privateClassProvider; @riverpod class _PrivateClass extends _$PrivateClass { diff --git a/packages/riverpod_generator/test/integration/stream.g.dart b/packages/riverpod_generator/test/integration/stream.g.dart index 9ec46bd1b..77864bf8f 100644 --- a/packages/riverpod_generator/test/integration/stream.g.dart +++ b/packages/riverpod_generator/test/integration/stream.g.dart @@ -6,510 +6,902 @@ part of 'stream.dart'; // RiverpodGenerator // ************************************************************************** -String _$publicHash() => r'ed93527425175c4a2475e83a3f44223a2aa604d7'; +@ProviderFor(generic) +const genericProvider = GenericFamily._(); + +final class GenericProvider + extends $FunctionalProvider>, Stream>> + with $FutureModifier>, $StreamProvider> { + const GenericProvider._( + {required GenericFamily super.from, + Stream> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'genericProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [public]. -@ProviderFor(public) -final publicProvider = AutoDisposeStreamProvider.internal( - public, - name: r'publicProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$publicHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef PublicRef = AutoDisposeStreamProviderRef; -String _$privateHash() => r'7915ccdd16751e7dc6274bb024d1b273d78dc78b'; + final Stream> Function( + Ref ref, + )? _createCb; -/// See also [_private]. -@ProviderFor(_private) -final _privateProvider = AutoDisposeStreamProvider.internal( - _private, - name: r'_privateProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$privateHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef _PrivateRef = AutoDisposeStreamProviderRef; -String _$familyHash() => r'ba1df8eab0af0f3f71ae29d23ccb7a491d8e2825'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); + @override + String debugGetCreateSourceHash() => _$genericHash(); - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); + GenericProvider _copyWithCreate( + Stream> Function( + Ref ref, + ) create, + ) { + return GenericProvider._( + from: from! as GenericFamily, create: create); } - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + @override + String toString() { + return r'genericProvider' + '<${T}>' + '()'; } -} - -/// See also [family]. -@ProviderFor(family) -const familyProvider = FamilyFamily(); -/// See also [family]. -class FamilyFamily extends Family> { - /// See also [family]. - const FamilyFamily(); + @$internal + @override + $StreamProviderElement> $createElement($ProviderPointer pointer) => + $StreamProviderElement(this, pointer); - /// See also [family]. - FamilyProvider call( - int first, { - String? second, - required double third, - bool fourth = true, - List? fifth, - }) { - return FamilyProvider( - first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ); + @override + GenericProvider $copyWithCreate( + Stream> Function( + Ref ref, + ) create, + ) { + return GenericProvider._(from: from! as GenericFamily, create: create); } @override - FamilyProvider getProviderOverride( - covariant FamilyProvider provider, - ) { - return call( - provider.first, - second: provider.second, - third: provider.third, - fourth: provider.fourth, - fifth: provider.fifth, - ); + Stream> create(Ref ref) { + final _$cb = _createCb ?? generic; + return _$cb(ref); } - static const Iterable? _dependencies = null; + @override + bool operator ==(Object other) { + return other is GenericProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } @override - Iterable? get dependencies => _dependencies; + int get hashCode { + return Object.hash(runtimeType, argument); + } +} + +String _$genericHash() => r'eaaf15c08df1aba30b6d6e70d67622d669df977f'; + +final class GenericFamily extends Family { + const GenericFamily._() + : super( + retry: null, + name: r'genericProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); - static const Iterable? _allTransitiveDependencies = null; + GenericProvider call() => GenericProvider._(from: this); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + String debugGetCreateSourceHash() => _$genericHash(); @override - String? get name => r'familyProvider'; + String toString() => r'genericProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Stream> Function(Ref ref) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericProvider; + + return provider._copyWithCreate(create).$createElement(pointer); + }, + ); + } } -/// See also [family]. -class FamilyProvider extends AutoDisposeStreamProvider { - /// See also [family]. - FamilyProvider( - int first, { - String? second, - required double third, - bool fourth = true, - List? fifth, - }) : this._internal( - (ref) => family( - ref as FamilyRef, - first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ), - from: familyProvider, - name: r'familyProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$familyHash, - dependencies: FamilyFamily._dependencies, - allTransitiveDependencies: FamilyFamily._allTransitiveDependencies, - first: first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, +@ProviderFor(GenericClass) +const genericClassProvider = GenericClassFamily._(); + +final class GenericClassProvider + extends $StreamNotifierProvider, List> { + const GenericClassProvider._( + {required GenericClassFamily super.from, + super.runNotifierBuildOverride, + GenericClass Function()? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'genericClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - FamilyProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.first, - required this.second, - required this.third, - required this.fourth, - required this.fifth, - }) : super.internal(); - - final int first; - final String? second; - final double third; - final bool fourth; - final List? fifth; + final GenericClass Function()? _createCb; @override - Override overrideWith( - Stream Function(FamilyRef provider) create, + String debugGetCreateSourceHash() => _$genericClassHash(); + + GenericClassProvider _copyWithCreate( + GenericClass Function() create, ) { - return ProviderOverride( - origin: this, - override: FamilyProvider._internal( - (ref) => create(ref as FamilyRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - first: first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ), - ); + return GenericClassProvider._( + from: from! as GenericClassFamily, create: create); + } + + GenericClassProvider _copyWithBuild( + Stream> Function( + Ref, + GenericClass, + ) build, + ) { + return GenericClassProvider._( + from: from! as GenericClassFamily, runNotifierBuildOverride: build); + } + + @override + String toString() { + return r'genericClassProvider' + '<${T}>' + '()'; + } + + @$internal + @override + GenericClass create() => _createCb?.call() ?? GenericClass(); + + @$internal + @override + GenericClassProvider $copyWithCreate( + GenericClass Function() create, + ) { + return GenericClassProvider._( + from: from! as GenericClassFamily, create: create); } + @$internal @override - AutoDisposeStreamProviderElement createElement() { - return _FamilyProviderElement(this); + GenericClassProvider $copyWithBuild( + Stream> Function( + Ref, + GenericClass, + ) build, + ) { + return GenericClassProvider._( + from: from! as GenericClassFamily, runNotifierBuildOverride: build); } + @$internal + @override + $StreamNotifierProviderElement, List> $createElement( + $ProviderPointer pointer) => + $StreamNotifierProviderElement(this, pointer); + @override bool operator ==(Object other) { - return other is FamilyProvider && - other.first == first && - other.second == second && - other.third == third && - other.fourth == fourth && - other.fifth == fifth; + return other is GenericClassProvider && + other.runtimeType == runtimeType && + other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, first.hashCode); - hash = _SystemHash.combine(hash, second.hashCode); - hash = _SystemHash.combine(hash, third.hashCode); - hash = _SystemHash.combine(hash, fourth.hashCode); - hash = _SystemHash.combine(hash, fifth.hashCode); - - return _SystemHash.finish(hash); + return Object.hash(runtimeType, argument); } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FamilyRef on AutoDisposeStreamProviderRef { - /// The parameter `first` of this provider. - int get first; +String _$genericClassHash() => r'401ae1cfd97a4291dfd135a69ff8e1c436866e5a'; - /// The parameter `second` of this provider. - String? get second; +final class GenericClassFamily extends Family { + const GenericClassFamily._() + : super( + retry: null, + name: r'genericClassProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); - /// The parameter `third` of this provider. - double get third; + GenericClassProvider call() => + GenericClassProvider._(from: this); - /// The parameter `fourth` of this provider. - bool get fourth; + @override + String debugGetCreateSourceHash() => _$genericClassHash(); - /// The parameter `fifth` of this provider. - List? get fifth; -} + @override + String toString() => r'genericClassProvider'; -class _FamilyProviderElement extends AutoDisposeStreamProviderElement - with FamilyRef { - _FamilyProviderElement(super.provider); + /// {@macro riverpod.override_with} + Override overrideWith( + GenericClass Function() create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericClassProvider; + return provider._copyWithCreate(create).$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + Stream> Function(Ref ref, GenericClass notifier) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericClassProvider; + + return provider._copyWithBuild(build).$createElement(pointer); + }, + ); + } +} + +abstract class _$GenericClass extends $StreamNotifier> { + Stream> build(); + @$internal @override - int get first => (origin as FamilyProvider).first; + Stream> runBuild() => build(); +} + +@ProviderFor(public) +const publicProvider = PublicProvider._(); + +final class PublicProvider + extends $FunctionalProvider, Stream> + with $FutureModifier, $StreamProvider { + const PublicProvider._( + {Stream Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'publicProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Stream Function( + Ref ref, + )? _createCb; + @override - String? get second => (origin as FamilyProvider).second; + String debugGetCreateSourceHash() => _$publicHash(); + + @$internal @override - double get third => (origin as FamilyProvider).third; + $StreamProviderElement $createElement($ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + @override - bool get fourth => (origin as FamilyProvider).fourth; + PublicProvider $copyWithCreate( + Stream Function( + Ref ref, + ) create, + ) { + return PublicProvider._(create: create); + } + @override - List? get fifth => (origin as FamilyProvider).fifth; + Stream create(Ref ref) { + final _$cb = _createCb ?? public; + return _$cb(ref); + } } -String _$publicClassHash() => r'b1526943c8ff0aaa20642bf78e744e5833cf9d02'; +String _$publicHash() => r'ed93527425175c4a2475e83a3f44223a2aa604d7'; -/// See also [PublicClass]. -@ProviderFor(PublicClass) -final publicClassProvider = - AutoDisposeStreamNotifierProvider.internal( - PublicClass.new, - name: r'publicClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$publicClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$PublicClass = AutoDisposeStreamNotifier; -String _$privateClassHash() => r'8c0d52b7ab79c0546d0c84c011bb3512609e029e'; +@ProviderFor(_private) +const _privateProvider = _PrivateProvider._(); + +final class _PrivateProvider + extends $FunctionalProvider, Stream> + with $FutureModifier, $StreamProvider { + const _PrivateProvider._( + {Stream Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'_privateProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [_PrivateClass]. -@ProviderFor(_PrivateClass) -final _privateClassProvider = - AutoDisposeStreamNotifierProvider<_PrivateClass, String>.internal( - _PrivateClass.new, - name: r'_privateClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$privateClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$PrivateClass = AutoDisposeStreamNotifier; -String _$familyClassHash() => r'6ec16ca23da8df4c010ecb5eed72e3e655504460'; + final Stream Function( + Ref ref, + )? _createCb; -abstract class _$FamilyClass - extends BuildlessAutoDisposeStreamNotifier { - late final int first; - late final String? second; - late final double third; - late final bool fourth; - late final List? fifth; + @override + String debugGetCreateSourceHash() => _$privateHash(); - Stream build( + @$internal + @override + $StreamProviderElement $createElement($ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + + @override + _PrivateProvider $copyWithCreate( + Stream Function( + Ref ref, + ) create, + ) { + return _PrivateProvider._(create: create); + } + + @override + Stream create(Ref ref) { + final _$cb = _createCb ?? _private; + return _$cb(ref); + } +} + +String _$privateHash() => r'7915ccdd16751e7dc6274bb024d1b273d78dc78b'; + +@ProviderFor(family) +const familyProvider = FamilyFamily._(); + +final class FamilyProvider + extends $FunctionalProvider, Stream> + with $FutureModifier, $StreamProvider { + const FamilyProvider._( + {required FamilyFamily super.from, + required ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) + super.argument, + Stream Function( + Ref ref, + int first, { + String? second, + required double third, + bool fourth, + List? fifth, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'familyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Stream Function( + Ref ref, int first, { String? second, required double third, - bool fourth = true, + bool fourth, List? fifth, - }); + })? _createCb; + + @override + String debugGetCreateSourceHash() => _$familyHash(); + + @override + String toString() { + return r'familyProvider' + '' + '$argument'; + } + + @$internal + @override + $StreamProviderElement $createElement($ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + + @override + FamilyProvider $copyWithCreate( + Stream Function( + Ref ref, + ) create, + ) { + return FamilyProvider._( + argument: argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }), + from: from! as FamilyFamily, + create: ( + ref, + int first, { + String? second, + required double third, + bool fourth = true, + List? fifth, + }) => + create(ref)); + } + + @override + Stream create(Ref ref) { + final _$cb = _createCb ?? family; + final argument = this.argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + return _$cb( + ref, + argument.$1, + second: argument.second, + third: argument.third, + fourth: argument.fourth, + fifth: argument.fifth, + ); + } + + @override + bool operator ==(Object other) { + return other is FamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } } -/// See also [FamilyClass]. -@ProviderFor(FamilyClass) -const familyClassProvider = FamilyClassFamily(); +String _$familyHash() => r'ba1df8eab0af0f3f71ae29d23ccb7a491d8e2825'; -/// See also [FamilyClass]. -class FamilyClassFamily extends Family> { - /// See also [FamilyClass]. - const FamilyClassFamily(); +final class FamilyFamily extends Family { + const FamilyFamily._() + : super( + retry: null, + name: r'familyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); - /// See also [FamilyClass]. - FamilyClassProvider call( + FamilyProvider call( int first, { String? second, required double third, bool fourth = true, List? fifth, - }) { - return FamilyClassProvider( - first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, + }) => + FamilyProvider._(argument: ( + first, + second: second, + third: third, + fourth: fourth, + fifth: fifth, + ), from: this); + + @override + String debugGetCreateSourceHash() => _$familyHash(); + + @override + String toString() => r'familyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Stream Function( + Ref ref, + ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyProvider; + + final argument = provider.argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, ); } +} + +@ProviderFor(PublicClass) +const publicClassProvider = PublicClassProvider._(); + +final class PublicClassProvider + extends $StreamNotifierProvider { + const PublicClassProvider._( + {super.runNotifierBuildOverride, PublicClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'publicClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final PublicClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$publicClassHash(); + + @$internal + @override + PublicClass create() => _createCb?.call() ?? PublicClass(); + + @$internal + @override + PublicClassProvider $copyWithCreate( + PublicClass Function() create, + ) { + return PublicClassProvider._(create: create); + } + @$internal @override - FamilyClassProvider getProviderOverride( - covariant FamilyClassProvider provider, + PublicClassProvider $copyWithBuild( + Stream Function( + Ref, + PublicClass, + ) build, ) { - return call( - provider.first, - second: provider.second, - third: provider.third, - fourth: provider.fourth, - fifth: provider.fifth, - ); + return PublicClassProvider._(runNotifierBuildOverride: build); } - static const Iterable? _dependencies = null; + @$internal + @override + $StreamNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $StreamNotifierProviderElement(this, pointer); +} + +String _$publicClassHash() => r'b1526943c8ff0aaa20642bf78e744e5833cf9d02'; +abstract class _$PublicClass extends $StreamNotifier { + Stream build(); + @$internal @override - Iterable? get dependencies => _dependencies; + Stream runBuild() => build(); +} + +@ProviderFor(_PrivateClass) +const _privateClassProvider = _PrivateClassProvider._(); + +final class _PrivateClassProvider + extends $StreamNotifierProvider<_PrivateClass, String> { + const _PrivateClassProvider._( + {super.runNotifierBuildOverride, _PrivateClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'_privateClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final _PrivateClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$privateClassHash(); + + @$internal + @override + _PrivateClass create() => _createCb?.call() ?? _PrivateClass(); - static const Iterable? _allTransitiveDependencies = null; + @$internal + @override + _PrivateClassProvider $copyWithCreate( + _PrivateClass Function() create, + ) { + return _PrivateClassProvider._(create: create); + } + @$internal @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + _PrivateClassProvider $copyWithBuild( + Stream Function( + Ref, + _PrivateClass, + ) build, + ) { + return _PrivateClassProvider._(runNotifierBuildOverride: build); + } + @$internal @override - String? get name => r'familyClassProvider'; + $StreamNotifierProviderElement<_PrivateClass, String> $createElement( + $ProviderPointer pointer) => + $StreamNotifierProviderElement(this, pointer); } -/// See also [FamilyClass]. -class FamilyClassProvider - extends AutoDisposeStreamNotifierProviderImpl { - /// See also [FamilyClass]. - FamilyClassProvider( - int first, { - String? second, - required double third, - bool fourth = true, - List? fifth, - }) : this._internal( - () => FamilyClass() - ..first = first - ..second = second - ..third = third - ..fourth = fourth - ..fifth = fifth, - from: familyClassProvider, +String _$privateClassHash() => r'8c0d52b7ab79c0546d0c84c011bb3512609e029e'; + +abstract class _$PrivateClass extends $StreamNotifier { + Stream build(); + @$internal + @override + Stream runBuild() => build(); +} + +@ProviderFor(FamilyClass) +const familyClassProvider = FamilyClassFamily._(); + +final class FamilyClassProvider + extends $StreamNotifierProvider { + const FamilyClassProvider._( + {required FamilyClassFamily super.from, + required ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) + super.argument, + super.runNotifierBuildOverride, + FamilyClass Function()? create}) + : _createCb = create, + super( + retry: null, name: r'familyClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$familyClassHash, - dependencies: FamilyClassFamily._dependencies, - allTransitiveDependencies: - FamilyClassFamily._allTransitiveDependencies, - first: first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - FamilyClassProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.first, - required this.second, - required this.third, - required this.fourth, - required this.fifth, - }) : super.internal(); - - final int first; - final String? second; - final double third; - final bool fourth; - final List? fifth; - - @override - Stream runNotifierBuild( - covariant FamilyClass notifier, - ) { - return notifier.build( - first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ); + final FamilyClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$familyClassHash(); + + @override + String toString() { + return r'familyClassProvider' + '' + '$argument'; } + @$internal @override - Override overrideWith(FamilyClass Function() create) { - return ProviderOverride( - origin: this, - override: FamilyClassProvider._internal( - () => create() - ..first = first - ..second = second - ..third = third - ..fourth = fourth - ..fifth = fifth, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - first: first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ), - ); + FamilyClass create() => _createCb?.call() ?? FamilyClass(); + + @$internal + @override + FamilyClassProvider $copyWithCreate( + FamilyClass Function() create, + ) { + return FamilyClassProvider._( + argument: argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }), + from: from! as FamilyClassFamily, + create: create); } + @$internal @override - AutoDisposeStreamNotifierProviderElement - createElement() { - return _FamilyClassProviderElement(this); + FamilyClassProvider $copyWithBuild( + Stream Function( + Ref, + FamilyClass, + ) build, + ) { + return FamilyClassProvider._( + argument: argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }), + from: from! as FamilyClassFamily, + runNotifierBuildOverride: build); } + @$internal + @override + $StreamNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $StreamNotifierProviderElement(this, pointer); + @override bool operator ==(Object other) { - return other is FamilyClassProvider && - other.first == first && - other.second == second && - other.third == third && - other.fourth == fourth && - other.fifth == fifth; + return other is FamilyClassProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, first.hashCode); - hash = _SystemHash.combine(hash, second.hashCode); - hash = _SystemHash.combine(hash, third.hashCode); - hash = _SystemHash.combine(hash, fourth.hashCode); - hash = _SystemHash.combine(hash, fifth.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FamilyClassRef on AutoDisposeStreamNotifierProviderRef { - /// The parameter `first` of this provider. - int get first; +String _$familyClassHash() => r'6ec16ca23da8df4c010ecb5eed72e3e655504460'; + +final class FamilyClassFamily extends Family { + const FamilyClassFamily._() + : super( + retry: null, + name: r'familyClassProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + FamilyClassProvider call( + int first, { + String? second, + required double third, + bool fourth = true, + List? fifth, + }) => + FamilyClassProvider._(argument: ( + first, + second: second, + third: third, + fourth: fourth, + fifth: fifth, + ), from: this); - /// The parameter `second` of this provider. - String? get second; + @override + String debugGetCreateSourceHash() => _$familyClassHash(); - /// The parameter `third` of this provider. - double get third; + @override + String toString() => r'familyClassProvider'; - /// The parameter `fourth` of this provider. - bool get fourth; + /// {@macro riverpod.override_with} + Override overrideWith( + FamilyClass Function( + ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyClassProvider; + + final argument = provider.argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } - /// The parameter `fifth` of this provider. - List? get fifth; + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + Stream Function( + Ref ref, + FamilyClass notifier, + ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyClassProvider; + + final argument = provider.argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } } -class _FamilyClassProviderElement - extends AutoDisposeStreamNotifierProviderElement - with FamilyClassRef { - _FamilyClassProviderElement(super.provider); +abstract class _$FamilyClass extends $StreamNotifier { + late final _$args = ref.$arg as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + int get first => _$args.$1; + String? get second => _$args.second; + double get third => _$args.third; + bool get fourth => _$args.fourth; + List? get fifth => _$args.fifth; + Stream build( + int first, { + String? second, + required double third, + bool fourth = true, + List? fifth, + }); + @$internal @override - int get first => (origin as FamilyClassProvider).first; - @override - String? get second => (origin as FamilyClassProvider).second; - @override - double get third => (origin as FamilyClassProvider).third; - @override - bool get fourth => (origin as FamilyClassProvider).fourth; - @override - List? get fifth => (origin as FamilyClassProvider).fifth; + Stream runBuild() => build( + _$args.$1, + second: _$args.second, + third: _$args.third, + fourth: _$args.fourth, + fifth: _$args.fifth, + ); } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/test/integration/sync.dart b/packages/riverpod_generator/test/integration/sync.dart index d7e189531..1f0460bfe 100644 --- a/packages/riverpod_generator/test/integration/sync.dart +++ b/packages/riverpod_generator/test/integration/sync.dart @@ -3,6 +3,33 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'sync.g.dart'; +@riverpod +List generic(Ref ref) { + return [ + 'Hello world', + 42, + 3.14, + ].whereType().toList(); +} + +@riverpod +List complexGeneric( + Ref ref, { + required T param, + Foo? otherParam, +}) { + return []; +} + +@riverpod +class GenericClass extends _$GenericClass + with MyMixin, List> { + @override + List build() { + return []; + } +} + @riverpod Raw> rawFuture(Ref ref) async { return 'Hello world'; @@ -79,16 +106,18 @@ String family( return '(first: $first, second: $second, third: $third, fourth: $fourth, fifth: $fifth)'; } -final privateProvider = _privateProvider; +const privateProvider = _privateProvider; @riverpod String _private(Ref ref) { return 'Hello world'; } +mixin MyMixin on NotifierBase {} + /// This is some documentation @riverpod -class PublicClass extends _$PublicClass { +class PublicClass extends _$PublicClass with MyMixin { PublicClass([this.param]); final Object? param; @@ -99,10 +128,10 @@ class PublicClass extends _$PublicClass { } } -final privateClassProvider = _privateClassProvider; +const privateClassProvider = _privateClassProvider; @riverpod -class _PrivateClass extends _$PrivateClass { +class _PrivateClass extends _$PrivateClass with MyMixin { @override String build() { return 'Hello world'; @@ -111,7 +140,7 @@ class _PrivateClass extends _$PrivateClass { /// This is some documentation @riverpod -class FamilyClass extends _$FamilyClass { +class FamilyClass extends _$FamilyClass with MyMixin { FamilyClass([this.param]); final Object? param; @@ -129,13 +158,44 @@ class FamilyClass extends _$FamilyClass { } @riverpod -class Supports$InClassName extends _$Supports$InClassName { +String supports$InFnName(Ref ref) { + return 'Hello world'; +} + +const default$value = ''; + +@riverpod +String supports$InFnNameFamily( + Ref ref, + And$InT positional$arg, { + required And$InT named$arg, + String defaultArg = default$value, +}) { + return 'Hello world'; +} + +@riverpod +class Supports$InClassName extends _$Supports$InClassName + with MyMixin { @override String build() { return 'Hello world'; } } +@riverpod +class Supports$InClassFamilyName + extends _$Supports$InClassFamilyName { + @override + String build( + And$InT positional$arg, { + required And$InT named$arg, + String defaultArg = default$value, + }) { + return 'Hello world'; + } +} + @riverpod String generated(Ref ref) { return 'Just a simple normal generated provider'; @@ -149,3 +209,26 @@ final _someProvider = someProvider(); // Regression test for https://github.com/rrousselGit/riverpod/issues/2294 // ignore: unused_element final _other = _someProvider; + +// Regression test for now casting `as Object?` when not needed +@riverpod +String unnecessaryCast(Ref ref, Object? arg) { + return 'Just a simple normal generated provider'; +} + +@riverpod +class UnnecessaryCastClass extends _$UnnecessaryCastClass { + @override + String build(Object? arg) { + return 'Just a simple normal generated provider'; + } +} + +// Regression test for https://github.com/rrousselGit/riverpod/issues/3249 +class ManyProviderData {} + +@riverpod +Stream> manyDataStream( + Ref ref, + ManyProviderData pData, +) async* {} diff --git a/packages/riverpod_generator/test/integration/sync.g.dart b/packages/riverpod_generator/test/integration/sync.g.dart index e7288654e..fa219a7a7 100644 --- a/packages/riverpod_generator/test/integration/sync.g.dart +++ b/packages/riverpod_generator/test/integration/sync.g.dart @@ -6,1209 +6,3268 @@ part of 'sync.dart'; // RiverpodGenerator // ************************************************************************** -String _$rawFutureHash() => r'9d397f4c0a578a2741610f9ca6f17438ee8e5a34'; - -/// See also [rawFuture]. -@ProviderFor(rawFuture) -final rawFutureProvider = AutoDisposeProvider>>.internal( - rawFuture, - name: r'rawFutureProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$rawFutureHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef RawFutureRef = AutoDisposeProviderRef>>; -String _$rawStreamHash() => r'c7d6cd22f1f325827c866c3ec757d08315fd9858'; +@ProviderFor(generic) +const genericProvider = GenericFamily._(); + +final class GenericProvider + extends $FunctionalProvider, List> with $Provider> { + const GenericProvider._( + {required GenericFamily super.from, + List Function( + Ref ref, + )? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'genericProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [rawStream]. -@ProviderFor(rawStream) -final rawStreamProvider = AutoDisposeProvider>>.internal( - rawStream, - name: r'rawStreamProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$rawStreamHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef RawStreamRef = AutoDisposeProviderRef>>; -String _$rawFamilyFutureHash() => r'0ac70d7a2133691f1a9a38cedaeeb6b3bc667ade'; + final List Function( + Ref ref, + )? _createCb; -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); + @override + String debugGetCreateSourceHash() => _$genericHash(); - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); + GenericProvider _copyWithCreate( + List Function( + Ref ref, + ) create, + ) { + return GenericProvider._( + from: from! as GenericFamily, create: create); } - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + @override + String toString() { + return r'genericProvider' + '<${T}>' + '()'; } -} -/// See also [rawFamilyFuture]. -@ProviderFor(rawFamilyFuture) -const rawFamilyFutureProvider = RawFamilyFutureFamily(); + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } -/// See also [rawFamilyFuture]. -class RawFamilyFutureFamily extends Family>> { - /// See also [rawFamilyFuture]. - const RawFamilyFutureFamily(); + @$internal + @override + $ProviderElement> $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); - /// See also [rawFamilyFuture]. - RawFamilyFutureProvider call( - int id, + @override + GenericProvider $copyWithCreate( + List Function( + Ref ref, + ) create, ) { - return RawFamilyFutureProvider( - id, - ); + return GenericProvider._(from: from! as GenericFamily, create: create); } @override - RawFamilyFutureProvider getProviderOverride( - covariant RawFamilyFutureProvider provider, - ) { - return call( - provider.id, - ); + List create(Ref ref) { + final _$cb = _createCb ?? generic; + return _$cb(ref); } - static const Iterable? _dependencies = null; + @override + bool operator ==(Object other) { + return other is GenericProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } @override - Iterable? get dependencies => _dependencies; + int get hashCode { + return Object.hash(runtimeType, argument); + } +} - static const Iterable? _allTransitiveDependencies = null; +String _$genericHash() => r'4a2a38e246fc4ef25c46d93477010b66de01ff30'; + +final class GenericFamily extends Family { + const GenericFamily._() + : super( + retry: null, + name: r'genericProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + GenericProvider call() => GenericProvider._(from: this); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + String debugGetCreateSourceHash() => _$genericHash(); @override - String? get name => r'rawFamilyFutureProvider'; + String toString() => r'genericProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + List Function(Ref ref) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericProvider; + + return provider._copyWithCreate(create).$createElement(pointer); + }, + ); + } } -/// See also [rawFamilyFuture]. -class RawFamilyFutureProvider extends AutoDisposeProvider>> { - /// See also [rawFamilyFuture]. - RawFamilyFutureProvider( - int id, - ) : this._internal( - (ref) => rawFamilyFuture( - ref as RawFamilyFutureRef, - id, - ), - from: rawFamilyFutureProvider, - name: r'rawFamilyFutureProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$rawFamilyFutureHash, - dependencies: RawFamilyFutureFamily._dependencies, - allTransitiveDependencies: - RawFamilyFutureFamily._allTransitiveDependencies, - id: id, +@ProviderFor(complexGeneric) +const complexGenericProvider = ComplexGenericFamily._(); + +final class ComplexGenericProvider + extends $FunctionalProvider, List> with $Provider> { + const ComplexGenericProvider._( + {required ComplexGenericFamily super.from, + required ({ + T param, + Foo? otherParam, + }) + super.argument, + List Function( + Ref ref, { + required T param, + Foo? otherParam, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'complexGenericProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - RawFamilyFutureProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.id, - }) : super.internal(); - - final int id; + final List Function( + Ref ref, { + required T param, + Foo? otherParam, + })? _createCb; @override - Override overrideWith( - Raw> Function(RawFamilyFutureRef provider) create, + String debugGetCreateSourceHash() => _$complexGenericHash(); + + ComplexGenericProvider _copyWithCreate( + List Function( + Ref ref, { + required T param, + Foo? otherParam, + }) create, ) { - return ProviderOverride( + return ComplexGenericProvider._( + argument: argument as ({ + T param, + Foo? otherParam, + }), + from: from! as ComplexGenericFamily, + create: create); + } + + @override + String toString() { + return r'complexGenericProvider' + '<${T}, ${Foo}>' + '$argument'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( origin: this, - override: RawFamilyFutureProvider._internal( - (ref) => create(ref as RawFamilyFutureRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - id: id, - ), + providerOverride: $ValueProvider>(value), ); } + @$internal + @override + $ProviderElement> $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ComplexGenericProvider $copyWithCreate( + List Function( + Ref ref, + ) create, + ) { + return ComplexGenericProvider._( + argument: argument as ({ + T param, + Foo? otherParam, + }), + from: from! as ComplexGenericFamily, + create: ( + ref, { + required T param, + Foo? otherParam, + }) => + create(ref)); + } + @override - AutoDisposeProviderElement>> createElement() { - return _RawFamilyFutureProviderElement(this); + List create(Ref ref) { + final _$cb = _createCb ?? complexGeneric; + final argument = this.argument as ({ + T param, + Foo? otherParam, + }); + return _$cb( + ref, + param: argument.param, + otherParam: argument.otherParam, + ); } @override bool operator ==(Object other) { - return other is RawFamilyFutureProvider && other.id == id; + return other is ComplexGenericProvider && + other.runtimeType == runtimeType && + other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, id.hashCode); - - return _SystemHash.finish(hash); + return Object.hash(runtimeType, argument); } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin RawFamilyFutureRef on AutoDisposeProviderRef>> { - /// The parameter `id` of this provider. - int get id; -} - -class _RawFamilyFutureProviderElement - extends AutoDisposeProviderElement>> - with RawFamilyFutureRef { - _RawFamilyFutureProviderElement(super.provider); +String _$complexGenericHash() => r'bc3433c913396a238e833722a2dc3a78bdf844a4'; - @override - int get id => (origin as RawFamilyFutureProvider).id; -} +final class ComplexGenericFamily extends Family { + const ComplexGenericFamily._() + : super( + retry: null, + name: r'complexGenericProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); -String _$rawFamilyStreamHash() => r'6eacfa3a3576d884099c08c298751a3d395271be'; + ComplexGenericProvider call({ + required T param, + Foo? otherParam, + }) => + ComplexGenericProvider._(argument: ( + param: param, + otherParam: otherParam, + ), from: this); -/// See also [rawFamilyStream]. -@ProviderFor(rawFamilyStream) -const rawFamilyStreamProvider = RawFamilyStreamFamily(); + @override + String debugGetCreateSourceHash() => _$complexGenericHash(); -/// See also [rawFamilyStream]. -class RawFamilyStreamFamily extends Family>> { - /// See also [rawFamilyStream]. - const RawFamilyStreamFamily(); + @override + String toString() => r'complexGenericProvider'; - /// See also [rawFamilyStream]. - RawFamilyStreamProvider call( - int id, + /// {@macro riverpod.override_with} + Override overrideWith( + List Function( + Ref ref, + ({ + T param, + Foo? otherParam, + }) args, + ) create, ) { - return RawFamilyStreamProvider( - id, + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ComplexGenericProvider; + + return provider._copyWithCreate(( + ref, { + required T param, + Foo? otherParam, + }) { + return create(ref, ( + param: param, + otherParam: otherParam, + )); + }).$createElement(pointer); + }, ); } +} + +@ProviderFor(GenericClass) +const genericClassProvider = GenericClassFamily._(); + +final class GenericClassProvider + extends $NotifierProvider, List> { + const GenericClassProvider._( + {required GenericClassFamily super.from, + super.runNotifierBuildOverride, + GenericClass Function()? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'genericClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final GenericClass Function()? _createCb; @override - RawFamilyStreamProvider getProviderOverride( - covariant RawFamilyStreamProvider provider, + String debugGetCreateSourceHash() => _$genericClassHash(); + + GenericClassProvider _copyWithCreate( + GenericClass Function() create, ) { - return call( - provider.id, - ); + return GenericClassProvider._( + from: from! as GenericClassFamily, create: create); } - static const Iterable? _dependencies = null; + GenericClassProvider _copyWithBuild( + List Function( + Ref, + GenericClass, + ) build, + ) { + return GenericClassProvider._( + from: from! as GenericClassFamily, runNotifierBuildOverride: build); + } @override - Iterable? get dependencies => _dependencies; + String toString() { + return r'genericClassProvider' + '<${T}>' + '()'; + } - static const Iterable? _allTransitiveDependencies = null; + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + @$internal @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + GenericClass create() => _createCb?.call() ?? GenericClass(); + @$internal @override - String? get name => r'rawFamilyStreamProvider'; -} - -/// See also [rawFamilyStream]. -class RawFamilyStreamProvider extends AutoDisposeProvider>> { - /// See also [rawFamilyStream]. - RawFamilyStreamProvider( - int id, - ) : this._internal( - (ref) => rawFamilyStream( - ref as RawFamilyStreamRef, - id, - ), - from: rawFamilyStreamProvider, - name: r'rawFamilyStreamProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$rawFamilyStreamHash, - dependencies: RawFamilyStreamFamily._dependencies, - allTransitiveDependencies: - RawFamilyStreamFamily._allTransitiveDependencies, - id: id, - ); - - RawFamilyStreamProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.id, - }) : super.internal(); - - final int id; + GenericClassProvider $copyWithCreate( + GenericClass Function() create, + ) { + return GenericClassProvider._( + from: from! as GenericClassFamily, create: create); + } + @$internal @override - Override overrideWith( - Raw> Function(RawFamilyStreamRef provider) create, + GenericClassProvider $copyWithBuild( + List Function( + Ref, + GenericClass, + ) build, ) { - return ProviderOverride( - origin: this, - override: RawFamilyStreamProvider._internal( - (ref) => create(ref as RawFamilyStreamRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - id: id, - ), - ); + return GenericClassProvider._( + from: from! as GenericClassFamily, runNotifierBuildOverride: build); } + @$internal @override - AutoDisposeProviderElement>> createElement() { - return _RawFamilyStreamProviderElement(this); - } + $NotifierProviderElement, List> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); @override bool operator ==(Object other) { - return other is RawFamilyStreamProvider && other.id == id; + return other is GenericClassProvider && + other.runtimeType == runtimeType && + other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, id.hashCode); - - return _SystemHash.finish(hash); + return Object.hash(runtimeType, argument); } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin RawFamilyStreamRef on AutoDisposeProviderRef>> { - /// The parameter `id` of this provider. - int get id; -} - -class _RawFamilyStreamProviderElement - extends AutoDisposeProviderElement>> - with RawFamilyStreamRef { - _RawFamilyStreamProviderElement(super.provider); - - @override - int get id => (origin as RawFamilyStreamProvider).id; -} +String _$genericClassHash() => r'42ec5a5635796c0d597f0c9dac28ec2f61a486ff'; -String _$publicHash() => r'94bee36125844f9fe521363bb228632b9f3bfbc7'; +final class GenericClassFamily extends Family { + const GenericClassFamily._() + : super( + retry: null, + name: r'genericClassProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); -/// This is some documentation -/// -/// Copied from [public]. -@ProviderFor(public) -final publicProvider = AutoDisposeProvider.internal( - public, - name: r'publicProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$publicHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef PublicRef = AutoDisposeProviderRef; -String _$supports$inNamesHash() => r'8da1f9329f302ce75e38d03c96595de3260b4d2d'; + GenericClassProvider call() => + GenericClassProvider._(from: this); -/// See also [supports$inNames]. -@ProviderFor(supports$inNames) -final supports$inNamesProvider = AutoDisposeProvider.internal( - supports$inNames, - name: r'supports$inNamesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$supports$inNamesHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef Supports$inNamesRef = AutoDisposeProviderRef; -String _$familyHash() => r'f58149448f80f10ec054f2f8a6f37bae61e38f49'; + @override + String debugGetCreateSourceHash() => _$genericClassHash(); -/// This is some documentation -/// -/// Copied from [family]. -@ProviderFor(family) -const familyProvider = FamilyFamily(); + @override + String toString() => r'genericClassProvider'; -/// This is some documentation -/// -/// Copied from [family]. -class FamilyFamily extends Family { - /// This is some documentation - /// - /// Copied from [family]. - const FamilyFamily(); + /// {@macro riverpod.override_with} + Override overrideWith( + GenericClass Function() create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericClassProvider; - /// This is some documentation - /// - /// Copied from [family]. - FamilyProvider call( - int first, { - String? second, - required double third, - bool fourth = true, - List? fifth, - }) { - return FamilyProvider( - first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, + return provider._copyWithCreate(create).$createElement(pointer); + }, ); } - @override - FamilyProvider getProviderOverride( - covariant FamilyProvider provider, + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + List Function(Ref ref, GenericClass notifier) build, ) { - return call( - provider.first, - second: provider.second, - third: provider.third, - fourth: provider.fourth, - fifth: provider.fifth, + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericClassProvider; + + return provider._copyWithBuild(build).$createElement(pointer); + }, ); } +} - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - +abstract class _$GenericClass extends $Notifier> { + List build(); + @$internal @override - String? get name => r'familyProvider'; + List runBuild() => build(); } -/// This is some documentation -/// -/// Copied from [family]. -class FamilyProvider extends AutoDisposeProvider { - /// This is some documentation - /// - /// Copied from [family]. - FamilyProvider( - int first, { - String? second, - required double third, - bool fourth = true, - List? fifth, - }) : this._internal( - (ref) => family( - ref as FamilyRef, - first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ), - from: familyProvider, - name: r'familyProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$familyHash, - dependencies: FamilyFamily._dependencies, - allTransitiveDependencies: FamilyFamily._allTransitiveDependencies, - first: first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, +@ProviderFor(rawFuture) +const rawFutureProvider = RawFutureProvider._(); + +final class RawFutureProvider + extends $FunctionalProvider>, Raw>> + with $Provider>> { + const RawFutureProvider._( + {Raw> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'rawFutureProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - FamilyProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.first, - required this.second, - required this.third, - required this.fourth, - required this.fifth, - }) : super.internal(); - - final int first; - final String? second; - final double third; - final bool fourth; - final List? fifth; + final Raw> Function( + Ref ref, + )? _createCb; @override - Override overrideWith( - String Function(FamilyRef provider) create, - ) { - return ProviderOverride( + String debugGetCreateSourceHash() => _$rawFutureHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( origin: this, - override: FamilyProvider._internal( - (ref) => create(ref as FamilyRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - first: first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ), + providerOverride: $ValueProvider>>(value), ); } + @$internal @override - AutoDisposeProviderElement createElement() { - return _FamilyProviderElement(this); - } + $ProviderElement>> $createElement( + $ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - bool operator ==(Object other) { - return other is FamilyProvider && - other.first == first && - other.second == second && - other.third == third && - other.fourth == fourth && - other.fifth == fifth; + RawFutureProvider $copyWithCreate( + Raw> Function( + Ref ref, + ) create, + ) { + return RawFutureProvider._(create: create); } @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, first.hashCode); - hash = _SystemHash.combine(hash, second.hashCode); - hash = _SystemHash.combine(hash, third.hashCode); - hash = _SystemHash.combine(hash, fourth.hashCode); - hash = _SystemHash.combine(hash, fifth.hashCode); - - return _SystemHash.finish(hash); + Raw> create(Ref ref) { + final _$cb = _createCb ?? rawFuture; + return _$cb(ref); } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FamilyRef on AutoDisposeProviderRef { - /// The parameter `first` of this provider. - int get first; - - /// The parameter `second` of this provider. - String? get second; +String _$rawFutureHash() => r'9d397f4c0a578a2741610f9ca6f17438ee8e5a34'; - /// The parameter `third` of this provider. - double get third; +@ProviderFor(rawStream) +const rawStreamProvider = RawStreamProvider._(); + +final class RawStreamProvider + extends $FunctionalProvider>, Raw>> + with $Provider>> { + const RawStreamProvider._( + {Raw> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'rawStreamProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// The parameter `fourth` of this provider. - bool get fourth; + final Raw> Function( + Ref ref, + )? _createCb; - /// The parameter `fifth` of this provider. - List? get fifth; -} + @override + String debugGetCreateSourceHash() => _$rawStreamHash(); -class _FamilyProviderElement extends AutoDisposeProviderElement - with FamilyRef { - _FamilyProviderElement(super.provider); + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>>(value), + ); + } + @$internal @override - int get first => (origin as FamilyProvider).first; - @override - String? get second => (origin as FamilyProvider).second; - @override - double get third => (origin as FamilyProvider).third; + $ProviderElement>> $createElement( + $ProviderPointer pointer) => + $ProviderElement(this, pointer); + @override - bool get fourth => (origin as FamilyProvider).fourth; + RawStreamProvider $copyWithCreate( + Raw> Function( + Ref ref, + ) create, + ) { + return RawStreamProvider._(create: create); + } + @override - List? get fifth => (origin as FamilyProvider).fifth; + Raw> create(Ref ref) { + final _$cb = _createCb ?? rawStream; + return _$cb(ref); + } } -String _$privateHash() => r'834affaed42662bf46ce7f6203ac2495e1e8974b'; - -/// See also [_private]. -@ProviderFor(_private) -final _privateProvider = AutoDisposeProvider.internal( - _private, - name: r'_privateProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$privateHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef _PrivateRef = AutoDisposeProviderRef; -String _$generatedHash() => r'24bfb5df4dc529258ab568372e90a1cbfc2d8c24'; - -/// See also [generated]. -@ProviderFor(generated) -final generatedProvider = AutoDisposeProvider.internal( - generated, - name: r'generatedProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$generatedHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef GeneratedRef = AutoDisposeProviderRef; -String _$rawFutureClassHash() => r'bf66f1cdbd99118b8845d206e6a2611b3101f45c'; +String _$rawStreamHash() => r'c7d6cd22f1f325827c866c3ec757d08315fd9858'; -/// See also [RawFutureClass]. @ProviderFor(RawFutureClass) -final rawFutureClassProvider = - AutoDisposeNotifierProvider>>.internal( - RawFutureClass.new, - name: r'rawFutureClassProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$rawFutureClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$RawFutureClass = AutoDisposeNotifier>>; -String _$rawStreamClassHash() => r'712cffcb2018cfb4ff45012c1aa6e43c8cbe9d5d'; - -/// See also [RawStreamClass]. -@ProviderFor(RawStreamClass) -final rawStreamClassProvider = - AutoDisposeNotifierProvider>>.internal( - RawStreamClass.new, - name: r'rawStreamClassProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$rawStreamClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$RawStreamClass = AutoDisposeNotifier>>; -String _$rawFamilyFutureClassHash() => - r'd7cacb0f2c51697d107de6daa68b242c04085dca'; +const rawFutureClassProvider = RawFutureClassProvider._(); + +final class RawFutureClassProvider + extends $NotifierProvider>> { + const RawFutureClassProvider._( + {super.runNotifierBuildOverride, RawFutureClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'rawFutureClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -abstract class _$RawFamilyFutureClass - extends BuildlessAutoDisposeNotifier>> { - late final int id; + final RawFutureClass Function()? _createCb; - Raw> build( - int id, - ); -} + @override + String debugGetCreateSourceHash() => _$rawFutureClassHash(); -/// See also [RawFamilyFutureClass]. -@ProviderFor(RawFamilyFutureClass) -const rawFamilyFutureClassProvider = RawFamilyFutureClassFamily(); + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>>(value), + ); + } -/// See also [RawFamilyFutureClass]. -class RawFamilyFutureClassFamily extends Family>> { - /// See also [RawFamilyFutureClass]. - const RawFamilyFutureClassFamily(); + @$internal + @override + RawFutureClass create() => _createCb?.call() ?? RawFutureClass(); - /// See also [RawFamilyFutureClass]. - RawFamilyFutureClassProvider call( - int id, + @$internal + @override + RawFutureClassProvider $copyWithCreate( + RawFutureClass Function() create, ) { - return RawFamilyFutureClassProvider( - id, - ); + return RawFutureClassProvider._(create: create); } + @$internal @override - RawFamilyFutureClassProvider getProviderOverride( - covariant RawFamilyFutureClassProvider provider, + RawFutureClassProvider $copyWithBuild( + Raw> Function( + Ref, + RawFutureClass, + ) build, ) { - return call( - provider.id, - ); + return RawFutureClassProvider._(runNotifierBuildOverride: build); } - static const Iterable? _dependencies = null; - + @$internal @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; + $NotifierProviderElement>> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; +String _$rawFutureClassHash() => r'bf66f1cdbd99118b8845d206e6a2611b3101f45c'; +abstract class _$RawFutureClass extends $Notifier>> { + Raw> build(); + @$internal @override - String? get name => r'rawFamilyFutureClassProvider'; + Raw> runBuild() => build(); } -/// See also [RawFamilyFutureClass]. -class RawFamilyFutureClassProvider extends AutoDisposeNotifierProviderImpl< - RawFamilyFutureClass, Raw>> { - /// See also [RawFamilyFutureClass]. - RawFamilyFutureClassProvider( - int id, - ) : this._internal( - () => RawFamilyFutureClass()..id = id, - from: rawFamilyFutureClassProvider, - name: r'rawFamilyFutureClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$rawFamilyFutureClassHash, - dependencies: RawFamilyFutureClassFamily._dependencies, - allTransitiveDependencies: - RawFamilyFutureClassFamily._allTransitiveDependencies, - id: id, +@ProviderFor(RawStreamClass) +const rawStreamClassProvider = RawStreamClassProvider._(); + +final class RawStreamClassProvider + extends $NotifierProvider>> { + const RawStreamClassProvider._( + {super.runNotifierBuildOverride, RawStreamClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'rawStreamClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - RawFamilyFutureClassProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.id, - }) : super.internal(); - - final int id; + final RawStreamClass Function()? _createCb; @override - Raw> runNotifierBuild( - covariant RawFamilyFutureClass notifier, - ) { - return notifier.build( - id, - ); - } + String debugGetCreateSourceHash() => _$rawStreamClassHash(); - @override - Override overrideWith(RawFamilyFutureClass Function() create) { - return ProviderOverride( + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( origin: this, - override: RawFamilyFutureClassProvider._internal( - () => create()..id = id, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - id: id, - ), + providerOverride: $ValueProvider>>(value), ); } + @$internal @override - AutoDisposeNotifierProviderElement>> - createElement() { - return _RawFamilyFutureClassProviderElement(this); - } + RawStreamClass create() => _createCb?.call() ?? RawStreamClass(); + @$internal @override - bool operator ==(Object other) { - return other is RawFamilyFutureClassProvider && other.id == id; + RawStreamClassProvider $copyWithCreate( + RawStreamClass Function() create, + ) { + return RawStreamClassProvider._(create: create); } + @$internal @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, id.hashCode); - - return _SystemHash.finish(hash); + RawStreamClassProvider $copyWithBuild( + Raw> Function( + Ref, + RawStreamClass, + ) build, + ) { + return RawStreamClassProvider._(runNotifierBuildOverride: build); } -} -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin RawFamilyFutureClassRef - on AutoDisposeNotifierProviderRef>> { - /// The parameter `id` of this provider. - int get id; + @$internal + @override + $NotifierProviderElement>> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); } -class _RawFamilyFutureClassProviderElement - extends AutoDisposeNotifierProviderElement>> with RawFamilyFutureClassRef { - _RawFamilyFutureClassProviderElement(super.provider); +String _$rawStreamClassHash() => r'712cffcb2018cfb4ff45012c1aa6e43c8cbe9d5d'; +abstract class _$RawStreamClass extends $Notifier>> { + Raw> build(); + @$internal @override - int get id => (origin as RawFamilyFutureClassProvider).id; + Raw> runBuild() => build(); } -String _$rawFamilyStreamClassHash() => - r'321796a0befc43fb83f7ccfdcb6b011fc8c7c599'; - -abstract class _$RawFamilyStreamClass - extends BuildlessAutoDisposeNotifier>> { - late final int id; +@ProviderFor(rawFamilyFuture) +const rawFamilyFutureProvider = RawFamilyFutureFamily._(); + +final class RawFamilyFutureProvider + extends $FunctionalProvider>, Raw>> + with $Provider>> { + const RawFamilyFutureProvider._( + {required RawFamilyFutureFamily super.from, + required int super.argument, + Raw> Function( + Ref ref, + int id, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'rawFamilyFutureProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - Raw> build( + final Raw> Function( + Ref ref, int id, - ); -} - -/// See also [RawFamilyStreamClass]. -@ProviderFor(RawFamilyStreamClass) -const rawFamilyStreamClassProvider = RawFamilyStreamClassFamily(); + )? _createCb; -/// See also [RawFamilyStreamClass]. -class RawFamilyStreamClassFamily extends Family>> { - /// See also [RawFamilyStreamClass]. - const RawFamilyStreamClassFamily(); + @override + String debugGetCreateSourceHash() => _$rawFamilyFutureHash(); - /// See also [RawFamilyStreamClass]. - RawFamilyStreamClassProvider call( - int id, - ) { - return RawFamilyStreamClassProvider( - id, - ); + @override + String toString() { + return r'rawFamilyFutureProvider' + '' + '($argument)'; } - @override - RawFamilyStreamClassProvider getProviderOverride( - covariant RawFamilyStreamClassProvider provider, - ) { - return call( - provider.id, + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>>(value), ); } - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - + @$internal @override - String? get name => r'rawFamilyStreamClassProvider'; -} - -/// See also [RawFamilyStreamClass]. -class RawFamilyStreamClassProvider extends AutoDisposeNotifierProviderImpl< - RawFamilyStreamClass, Raw>> { - /// See also [RawFamilyStreamClass]. - RawFamilyStreamClassProvider( - int id, - ) : this._internal( - () => RawFamilyStreamClass()..id = id, - from: rawFamilyStreamClassProvider, - name: r'rawFamilyStreamClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$rawFamilyStreamClassHash, - dependencies: RawFamilyStreamClassFamily._dependencies, - allTransitiveDependencies: - RawFamilyStreamClassFamily._allTransitiveDependencies, - id: id, - ); - - RawFamilyStreamClassProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.id, - }) : super.internal(); - - final int id; + $ProviderElement>> $createElement( + $ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - Raw> runNotifierBuild( - covariant RawFamilyStreamClass notifier, + RawFamilyFutureProvider $copyWithCreate( + Raw> Function( + Ref ref, + ) create, ) { - return notifier.build( - id, - ); + return RawFamilyFutureProvider._( + argument: argument as int, + from: from! as RawFamilyFutureFamily, + create: ( + ref, + int id, + ) => + create(ref)); } @override - Override overrideWith(RawFamilyStreamClass Function() create) { - return ProviderOverride( - origin: this, - override: RawFamilyStreamClassProvider._internal( - () => create()..id = id, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - id: id, - ), + Raw> create(Ref ref) { + final _$cb = _createCb ?? rawFamilyFuture; + final argument = this.argument as int; + return _$cb( + ref, + argument, ); } - @override - AutoDisposeNotifierProviderElement>> - createElement() { - return _RawFamilyStreamClassProviderElement(this); - } - @override bool operator ==(Object other) { - return other is RawFamilyStreamClassProvider && other.id == id; + return other is RawFamilyFutureProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, id.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin RawFamilyStreamClassRef - on AutoDisposeNotifierProviderRef>> { - /// The parameter `id` of this provider. - int get id; +String _$rawFamilyFutureHash() => r'0ac70d7a2133691f1a9a38cedaeeb6b3bc667ade'; + +final class RawFamilyFutureFamily extends Family { + const RawFamilyFutureFamily._() + : super( + retry: null, + name: r'rawFamilyFutureProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + RawFamilyFutureProvider call( + int id, + ) => + RawFamilyFutureProvider._(argument: id, from: this); + + @override + String debugGetCreateSourceHash() => _$rawFamilyFutureHash(); + + @override + String toString() => r'rawFamilyFutureProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Raw> Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as RawFamilyFutureProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +@ProviderFor(rawFamilyStream) +const rawFamilyStreamProvider = RawFamilyStreamFamily._(); + +final class RawFamilyStreamProvider + extends $FunctionalProvider>, Raw>> + with $Provider>> { + const RawFamilyStreamProvider._( + {required RawFamilyStreamFamily super.from, + required int super.argument, + Raw> Function( + Ref ref, + int id, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'rawFamilyStreamProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Raw> Function( + Ref ref, + int id, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$rawFamilyStreamHash(); + + @override + String toString() { + return r'rawFamilyStreamProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>>(value), + ); + } + + @$internal + @override + $ProviderElement>> $createElement( + $ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + RawFamilyStreamProvider $copyWithCreate( + Raw> Function( + Ref ref, + ) create, + ) { + return RawFamilyStreamProvider._( + argument: argument as int, + from: from! as RawFamilyStreamFamily, + create: ( + ref, + int id, + ) => + create(ref)); + } + + @override + Raw> create(Ref ref) { + final _$cb = _createCb ?? rawFamilyStream; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is RawFamilyStreamProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$rawFamilyStreamHash() => r'6eacfa3a3576d884099c08c298751a3d395271be'; + +final class RawFamilyStreamFamily extends Family { + const RawFamilyStreamFamily._() + : super( + retry: null, + name: r'rawFamilyStreamProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + RawFamilyStreamProvider call( + int id, + ) => + RawFamilyStreamProvider._(argument: id, from: this); + + @override + String debugGetCreateSourceHash() => _$rawFamilyStreamHash(); + + @override + String toString() => r'rawFamilyStreamProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Raw> Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as RawFamilyStreamProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +@ProviderFor(RawFamilyFutureClass) +const rawFamilyFutureClassProvider = RawFamilyFutureClassFamily._(); + +final class RawFamilyFutureClassProvider + extends $NotifierProvider>> { + const RawFamilyFutureClassProvider._( + {required RawFamilyFutureClassFamily super.from, + required int super.argument, + super.runNotifierBuildOverride, + RawFamilyFutureClass Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'rawFamilyFutureClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final RawFamilyFutureClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$rawFamilyFutureClassHash(); + + @override + String toString() { + return r'rawFamilyFutureClassProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>>(value), + ); + } + + @$internal + @override + RawFamilyFutureClass create() => _createCb?.call() ?? RawFamilyFutureClass(); + + @$internal + @override + RawFamilyFutureClassProvider $copyWithCreate( + RawFamilyFutureClass Function() create, + ) { + return RawFamilyFutureClassProvider._( + argument: argument as int, + from: from! as RawFamilyFutureClassFamily, + create: create); + } + + @$internal + @override + RawFamilyFutureClassProvider $copyWithBuild( + Raw> Function( + Ref, + RawFamilyFutureClass, + ) build, + ) { + return RawFamilyFutureClassProvider._( + argument: argument as int, + from: from! as RawFamilyFutureClassFamily, + runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement>> + $createElement($ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is RawFamilyFutureClassProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$rawFamilyFutureClassHash() => + r'd7cacb0f2c51697d107de6daa68b242c04085dca'; + +final class RawFamilyFutureClassFamily extends Family { + const RawFamilyFutureClassFamily._() + : super( + retry: null, + name: r'rawFamilyFutureClassProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + RawFamilyFutureClassProvider call( + int id, + ) => + RawFamilyFutureClassProvider._(argument: id, from: this); + + @override + String debugGetCreateSourceHash() => _$rawFamilyFutureClassHash(); + + @override + String toString() => r'rawFamilyFutureClassProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + RawFamilyFutureClass Function( + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as RawFamilyFutureClassProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + Raw> Function( + Ref ref, RawFamilyFutureClass notifier, int argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as RawFamilyFutureClassProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} + +abstract class _$RawFamilyFutureClass extends $Notifier>> { + late final _$args = ref.$arg as int; + int get id => _$args; + + Raw> build( + int id, + ); + @$internal + @override + Raw> runBuild() => build( + _$args, + ); +} + +@ProviderFor(RawFamilyStreamClass) +const rawFamilyStreamClassProvider = RawFamilyStreamClassFamily._(); + +final class RawFamilyStreamClassProvider + extends $NotifierProvider>> { + const RawFamilyStreamClassProvider._( + {required RawFamilyStreamClassFamily super.from, + required int super.argument, + super.runNotifierBuildOverride, + RawFamilyStreamClass Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'rawFamilyStreamClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final RawFamilyStreamClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$rawFamilyStreamClassHash(); + + @override + String toString() { + return r'rawFamilyStreamClassProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>>(value), + ); + } + + @$internal + @override + RawFamilyStreamClass create() => _createCb?.call() ?? RawFamilyStreamClass(); + + @$internal + @override + RawFamilyStreamClassProvider $copyWithCreate( + RawFamilyStreamClass Function() create, + ) { + return RawFamilyStreamClassProvider._( + argument: argument as int, + from: from! as RawFamilyStreamClassFamily, + create: create); + } + + @$internal + @override + RawFamilyStreamClassProvider $copyWithBuild( + Raw> Function( + Ref, + RawFamilyStreamClass, + ) build, + ) { + return RawFamilyStreamClassProvider._( + argument: argument as int, + from: from! as RawFamilyStreamClassFamily, + runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement>> + $createElement($ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is RawFamilyStreamClassProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$rawFamilyStreamClassHash() => + r'321796a0befc43fb83f7ccfdcb6b011fc8c7c599'; + +final class RawFamilyStreamClassFamily extends Family { + const RawFamilyStreamClassFamily._() + : super( + retry: null, + name: r'rawFamilyStreamClassProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + RawFamilyStreamClassProvider call( + int id, + ) => + RawFamilyStreamClassProvider._(argument: id, from: this); + + @override + String debugGetCreateSourceHash() => _$rawFamilyStreamClassHash(); + + @override + String toString() => r'rawFamilyStreamClassProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + RawFamilyStreamClass Function( + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as RawFamilyStreamClassProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + Raw> Function( + Ref ref, RawFamilyStreamClass notifier, int argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as RawFamilyStreamClassProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} + +abstract class _$RawFamilyStreamClass extends $Notifier>> { + late final _$args = ref.$arg as int; + int get id => _$args; + + Raw> build( + int id, + ); + @$internal + @override + Raw> runBuild() => build( + _$args, + ); +} + +/// This is some documentation +@ProviderFor(public) +const publicProvider = PublicProvider._(); + +/// This is some documentation +final class PublicProvider extends $FunctionalProvider + with $Provider { + /// This is some documentation + const PublicProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'publicProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$publicHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + PublicProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return PublicProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? public; + return _$cb(ref); + } +} + +String _$publicHash() => r'94bee36125844f9fe521363bb228632b9f3bfbc7'; + +@ProviderFor(supports$inNames) +const supports$inNamesProvider = Supports$inNamesProvider._(); + +final class Supports$inNamesProvider extends $FunctionalProvider + with $Provider { + const Supports$inNamesProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'supports$inNamesProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$supports$inNamesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + Supports$inNamesProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return Supports$inNamesProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? supports$inNames; + return _$cb(ref); + } +} + +String _$supports$inNamesHash() => r'8da1f9329f302ce75e38d03c96595de3260b4d2d'; + +/// This is some documentation +@ProviderFor(family) +const familyProvider = FamilyFamily._(); + +/// This is some documentation +final class FamilyProvider extends $FunctionalProvider + with $Provider { + /// This is some documentation + const FamilyProvider._( + {required FamilyFamily super.from, + required ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) + super.argument, + String Function( + Ref ref, + int first, { + String? second, + required double third, + bool fourth, + List? fifth, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'familyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + int first, { + String? second, + required double third, + bool fourth, + List? fifth, + })? _createCb; + + @override + String debugGetCreateSourceHash() => _$familyHash(); + + @override + String toString() { + return r'familyProvider' + '' + '$argument'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + FamilyProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return FamilyProvider._( + argument: argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }), + from: from! as FamilyFamily, + create: ( + ref, + int first, { + String? second, + required double third, + bool fourth = true, + List? fifth, + }) => + create(ref)); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? family; + final argument = this.argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + return _$cb( + ref, + argument.$1, + second: argument.second, + third: argument.third, + fourth: argument.fourth, + fifth: argument.fifth, + ); + } + + @override + bool operator ==(Object other) { + return other is FamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$familyHash() => r'f58149448f80f10ec054f2f8a6f37bae61e38f49'; + +/// This is some documentation +final class FamilyFamily extends Family { + const FamilyFamily._() + : super( + retry: null, + name: r'familyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + /// This is some documentation + FamilyProvider call( + int first, { + String? second, + required double third, + bool fourth = true, + List? fifth, + }) => + FamilyProvider._(argument: ( + first, + second: second, + third: third, + fourth: fourth, + fifth: fifth, + ), from: this); + + @override + String debugGetCreateSourceHash() => _$familyHash(); + + @override + String toString() => r'familyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + String Function( + Ref ref, + ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyProvider; + + final argument = provider.argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +@ProviderFor(_private) +const _privateProvider = _PrivateProvider._(); + +final class _PrivateProvider extends $FunctionalProvider + with $Provider { + const _PrivateProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'_privateProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$privateHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + _PrivateProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return _PrivateProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? _private; + return _$cb(ref); + } +} + +String _$privateHash() => r'834affaed42662bf46ce7f6203ac2495e1e8974b'; + +/// This is some documentation +@ProviderFor(PublicClass) +const publicClassProvider = PublicClassProvider._(); + +/// This is some documentation +final class PublicClassProvider extends $NotifierProvider { + /// This is some documentation + const PublicClassProvider._( + {super.runNotifierBuildOverride, PublicClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'publicClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final PublicClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$publicClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + PublicClass create() => _createCb?.call() ?? PublicClass(); + + @$internal + @override + PublicClassProvider $copyWithCreate( + PublicClass Function() create, + ) { + return PublicClassProvider._(create: create); + } + + @$internal + @override + PublicClassProvider $copyWithBuild( + String Function( + Ref, + PublicClass, + ) build, + ) { + return PublicClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$publicClassHash() => r'd261f9eb927ca71440a5e1bdb24558c25fae4833'; + +abstract class _$PublicClass extends $Notifier { + String build(); + @$internal + @override + String runBuild() => build(); +} + +@ProviderFor(_PrivateClass) +const _privateClassProvider = _PrivateClassProvider._(); + +final class _PrivateClassProvider + extends $NotifierProvider<_PrivateClass, String> { + const _PrivateClassProvider._( + {super.runNotifierBuildOverride, _PrivateClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'_privateClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final _PrivateClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$privateClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + _PrivateClass create() => _createCb?.call() ?? _PrivateClass(); + + @$internal + @override + _PrivateClassProvider $copyWithCreate( + _PrivateClass Function() create, + ) { + return _PrivateClassProvider._(create: create); + } + + @$internal + @override + _PrivateClassProvider $copyWithBuild( + String Function( + Ref, + _PrivateClass, + ) build, + ) { + return _PrivateClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement<_PrivateClass, String> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$privateClassHash() => r'796e16abb79d7ad77728f9288d24566e429643f2'; + +abstract class _$PrivateClass extends $Notifier { + String build(); + @$internal + @override + String runBuild() => build(); +} + +/// This is some documentation +@ProviderFor(FamilyClass) +const familyClassProvider = FamilyClassFamily._(); + +/// This is some documentation +final class FamilyClassProvider extends $NotifierProvider { + /// This is some documentation + const FamilyClassProvider._( + {required FamilyClassFamily super.from, + required ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) + super.argument, + super.runNotifierBuildOverride, + FamilyClass Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'familyClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FamilyClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$familyClassHash(); + + @override + String toString() { + return r'familyClassProvider' + '' + '$argument'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + FamilyClass create() => _createCb?.call() ?? FamilyClass(); + + @$internal + @override + FamilyClassProvider $copyWithCreate( + FamilyClass Function() create, + ) { + return FamilyClassProvider._( + argument: argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }), + from: from! as FamilyClassFamily, + create: create); + } + + @$internal + @override + FamilyClassProvider $copyWithBuild( + String Function( + Ref, + FamilyClass, + ) build, + ) { + return FamilyClassProvider._( + argument: argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }), + from: from! as FamilyClassFamily, + runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is FamilyClassProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$familyClassHash() => r'ac5aba6b9cbee66236d6e1fa3d18b9b6ffb2c5f1'; + +/// This is some documentation +final class FamilyClassFamily extends Family { + const FamilyClassFamily._() + : super( + retry: null, + name: r'familyClassProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + /// This is some documentation + FamilyClassProvider call( + int first, { + String? second, + required double third, + bool fourth = true, + List? fifth, + }) => + FamilyClassProvider._(argument: ( + first, + second: second, + third: third, + fourth: fourth, + fifth: fifth, + ), from: this); + + @override + String debugGetCreateSourceHash() => _$familyClassHash(); + + @override + String toString() => r'familyClassProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + FamilyClass Function( + ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyClassProvider; + + final argument = provider.argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + String Function( + Ref ref, + FamilyClass notifier, + ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }) argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyClassProvider; + + final argument = provider.argument as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} + +abstract class _$FamilyClass extends $Notifier { + late final _$args = ref.$arg as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + int get first => _$args.$1; + String? get second => _$args.second; + double get third => _$args.third; + bool get fourth => _$args.fourth; + List? get fifth => _$args.fifth; + + String build( + int first, { + String? second, + required double third, + bool fourth = true, + List? fifth, + }); + @$internal + @override + String runBuild() => build( + _$args.$1, + second: _$args.second, + third: _$args.third, + fourth: _$args.fourth, + fifth: _$args.fifth, + ); +} + +@ProviderFor(supports$InFnName) +const supports$InFnNameProvider = Supports$InFnNameFamily._(); + +final class Supports$InFnNameProvider + extends $FunctionalProvider with $Provider { + const Supports$InFnNameProvider._( + {required Supports$InFnNameFamily super.from, + String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'supports$InFnNameProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$supports$InFnNameHash(); + + Supports$InFnNameProvider _copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return Supports$InFnNameProvider._( + from: from! as Supports$InFnNameFamily, create: create); + } + + @override + String toString() { + return r'supports$InFnNameProvider' + '<${And$InT}>' + '()'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + Supports$InFnNameProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return Supports$InFnNameProvider._( + from: from! as Supports$InFnNameFamily, create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? supports$InFnName; + return _$cb(ref); + } + + @override + bool operator ==(Object other) { + return other is Supports$InFnNameProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } + + @override + int get hashCode { + return Object.hash(runtimeType, argument); + } +} + +String _$supports$InFnNameHash() => r'09636911da6a98293c260aad55b89bea5296136b'; + +final class Supports$InFnNameFamily extends Family { + const Supports$InFnNameFamily._() + : super( + retry: null, + name: r'supports$InFnNameProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + Supports$InFnNameProvider call() => + Supports$InFnNameProvider._(from: this); + + @override + String debugGetCreateSourceHash() => _$supports$InFnNameHash(); + + @override + String toString() => r'supports$InFnNameProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + String Function(Ref ref) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as Supports$InFnNameProvider; + + return provider._copyWithCreate(create).$createElement(pointer); + }, + ); + } +} + +@ProviderFor(supports$InFnNameFamily) +const supports$InFnNameFamilyProvider = Supports$InFnNameFamilyFamily._(); + +final class Supports$InFnNameFamilyProvider + extends $FunctionalProvider with $Provider { + const Supports$InFnNameFamilyProvider._( + {required Supports$InFnNameFamilyFamily super.from, + required ( + And$InT, { + And$InT named$arg, + String defaultArg, + }) + super.argument, + String Function( + Ref ref, + And$InT positional$arg, { + required And$InT named$arg, + String defaultArg, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'supports$InFnNameFamilyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + And$InT positional$arg, { + required And$InT named$arg, + String defaultArg, + })? _createCb; + + @override + String debugGetCreateSourceHash() => _$supports$InFnNameFamilyHash(); + + Supports$InFnNameFamilyProvider _copyWithCreate( + String Function( + Ref ref, + And$InT positional$arg, { + required And$InT named$arg, + String defaultArg, + }) create, + ) { + return Supports$InFnNameFamilyProvider._( + argument: argument as ( + And$InT, { + And$InT named$arg, + String defaultArg, + }), + from: from! as Supports$InFnNameFamilyFamily, + create: create); + } + + @override + String toString() { + return r'supports$InFnNameFamilyProvider' + '<${And$InT}>' + '$argument'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + Supports$InFnNameFamilyProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return Supports$InFnNameFamilyProvider._( + argument: argument as ( + And$InT, { + And$InT named$arg, + String defaultArg, + }), + from: from! as Supports$InFnNameFamilyFamily, + create: ( + ref, + And$InT positional$arg, { + required And$InT named$arg, + String defaultArg = default$value, + }) => + create(ref)); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? supports$InFnNameFamily; + final argument = this.argument as ( + And$InT, { + And$InT named$arg, + String defaultArg, + }); + return _$cb( + ref, + argument.$1, + named$arg: argument.named$arg, + defaultArg: argument.defaultArg, + ); + } + + @override + bool operator ==(Object other) { + return other is Supports$InFnNameFamilyProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } + + @override + int get hashCode { + return Object.hash(runtimeType, argument); + } +} + +String _$supports$InFnNameFamilyHash() => + r'3124634e8535d4db655d6384b0827f0f195a75ef'; + +final class Supports$InFnNameFamilyFamily extends Family { + const Supports$InFnNameFamilyFamily._() + : super( + retry: null, + name: r'supports$InFnNameFamilyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + Supports$InFnNameFamilyProvider call( + And$InT positional$arg, { + required And$InT named$arg, + String defaultArg = default$value, + }) => + Supports$InFnNameFamilyProvider._(argument: ( + positional$arg, + named$arg: named$arg, + defaultArg: defaultArg, + ), from: this); + + @override + String debugGetCreateSourceHash() => _$supports$InFnNameFamilyHash(); + + @override + String toString() => r'supports$InFnNameFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + String Function( + Ref ref, + ( + And$InT, { + And$InT named$arg, + String defaultArg, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as Supports$InFnNameFamilyProvider; + + return provider._copyWithCreate(( + ref, + And$InT positional$arg, { + required And$InT named$arg, + String defaultArg = default$value, + }) { + return create(ref, ( + positional$arg, + named$arg: named$arg, + defaultArg: defaultArg, + )); + }).$createElement(pointer); + }, + ); + } +} + +@ProviderFor(Supports$InClassName) +const supports$InClassNameProvider = Supports$InClassNameFamily._(); + +final class Supports$InClassNameProvider + extends $NotifierProvider, String> { + const Supports$InClassNameProvider._( + {required Supports$InClassNameFamily super.from, + super.runNotifierBuildOverride, + Supports$InClassName Function()? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'supports$InClassNameProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Supports$InClassName Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$supports$InClassNameHash(); + + Supports$InClassNameProvider _copyWithCreate( + Supports$InClassName Function() create, + ) { + return Supports$InClassNameProvider._( + from: from! as Supports$InClassNameFamily, create: create); + } + + Supports$InClassNameProvider _copyWithBuild( + String Function( + Ref, + Supports$InClassName, + ) build, + ) { + return Supports$InClassNameProvider._( + from: from! as Supports$InClassNameFamily, + runNotifierBuildOverride: build); + } + + @override + String toString() { + return r'supports$InClassNameProvider' + '<${And$InT}>' + '()'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Supports$InClassName create() => + _createCb?.call() ?? Supports$InClassName(); + + @$internal + @override + Supports$InClassNameProvider $copyWithCreate( + Supports$InClassName Function() create, + ) { + return Supports$InClassNameProvider._( + from: from! as Supports$InClassNameFamily, create: create); + } + + @$internal + @override + Supports$InClassNameProvider $copyWithBuild( + String Function( + Ref, + Supports$InClassName, + ) build, + ) { + return Supports$InClassNameProvider._( + from: from! as Supports$InClassNameFamily, + runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement, String> + $createElement($ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is Supports$InClassNameProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } + + @override + int get hashCode { + return Object.hash(runtimeType, argument); + } } -class _RawFamilyStreamClassProviderElement - extends AutoDisposeNotifierProviderElement>> with RawFamilyStreamClassRef { - _RawFamilyStreamClassProviderElement(super.provider); +String _$supports$InClassNameHash() => + r'848e57774639582ed170dce5765340e1c1cb89b3'; + +final class Supports$InClassNameFamily extends Family { + const Supports$InClassNameFamily._() + : super( + retry: null, + name: r'supports$InClassNameProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + Supports$InClassNameProvider call() => + Supports$InClassNameProvider._(from: this); + + @override + String debugGetCreateSourceHash() => _$supports$InClassNameHash(); + + @override + String toString() => r'supports$InClassNameProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Supports$InClassName Function() create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as Supports$InClassNameProvider; + + return provider._copyWithCreate(create).$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + String Function(Ref ref, Supports$InClassName notifier) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as Supports$InClassNameProvider; + + return provider._copyWithBuild(build).$createElement(pointer); + }, + ); + } +} +abstract class _$Supports$InClassName extends $Notifier { + String build(); + @$internal @override - int get id => (origin as RawFamilyStreamClassProvider).id; + String runBuild() => build(); } -String _$publicClassHash() => r'c8e7eec9e202acf8394e02496857cbe49405bf62'; +@ProviderFor(Supports$InClassFamilyName) +const supports$InClassFamilyNameProvider = Supports$InClassFamilyNameFamily._(); + +final class Supports$InClassFamilyNameProvider + extends $NotifierProvider, String> { + const Supports$InClassFamilyNameProvider._( + {required Supports$InClassFamilyNameFamily super.from, + required ( + And$InT, { + And$InT named$arg, + String defaultArg, + }) + super.argument, + super.runNotifierBuildOverride, + Supports$InClassFamilyName Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'supports$InClassFamilyNameProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// This is some documentation -/// -/// Copied from [PublicClass]. -@ProviderFor(PublicClass) -final publicClassProvider = - AutoDisposeNotifierProvider.internal( - PublicClass.new, - name: r'publicClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$publicClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$PublicClass = AutoDisposeNotifier; -String _$privateClassHash() => r'6d41def3ffdc1f79e593beaefb3304ce4b211a77'; - -/// See also [_PrivateClass]. -@ProviderFor(_PrivateClass) -final _privateClassProvider = - AutoDisposeNotifierProvider<_PrivateClass, String>.internal( - _PrivateClass.new, - name: r'_privateClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$privateClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$PrivateClass = AutoDisposeNotifier; -String _$familyClassHash() => r'01e3b9cb4d6d0bf12a2284761b1a11819d97d249'; - -abstract class _$FamilyClass extends BuildlessAutoDisposeNotifier { - late final int first; - late final String? second; - late final double third; - late final bool fourth; - late final List? fifth; + final Supports$InClassFamilyName Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$supports$InClassFamilyNameHash(); + + Supports$InClassFamilyNameProvider _copyWithCreate( + Supports$InClassFamilyName Function() create, + ) { + return Supports$InClassFamilyNameProvider._( + argument: argument as ( + And$InT, { + And$InT named$arg, + String defaultArg, + }), + from: from! as Supports$InClassFamilyNameFamily, + create: create); + } + + Supports$InClassFamilyNameProvider _copyWithBuild( + String Function( + Ref, + Supports$InClassFamilyName, + ) build, + ) { + return Supports$InClassFamilyNameProvider._( + argument: argument as ( + And$InT, { + And$InT named$arg, + String defaultArg, + }), + from: from! as Supports$InClassFamilyNameFamily, + runNotifierBuildOverride: build); + } + + @override + String toString() { + return r'supports$InClassFamilyNameProvider' + '<${And$InT}>' + '$argument'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Supports$InClassFamilyName create() => + _createCb?.call() ?? Supports$InClassFamilyName(); + + @$internal + @override + Supports$InClassFamilyNameProvider $copyWithCreate( + Supports$InClassFamilyName Function() create, + ) { + return Supports$InClassFamilyNameProvider._( + argument: argument as ( + And$InT, { + And$InT named$arg, + String defaultArg, + }), + from: from! as Supports$InClassFamilyNameFamily, + create: create); + } + + @$internal + @override + Supports$InClassFamilyNameProvider $copyWithBuild( + String Function( + Ref, + Supports$InClassFamilyName, + ) build, + ) { + return Supports$InClassFamilyNameProvider._( + argument: argument as ( + And$InT, { + And$InT named$arg, + String defaultArg, + }), + from: from! as Supports$InClassFamilyNameFamily, + runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement, String> + $createElement($ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is Supports$InClassFamilyNameProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } + + @override + int get hashCode { + return Object.hash(runtimeType, argument); + } +} + +String _$supports$InClassFamilyNameHash() => + r'39e844561e4f4727011bb2f97169d0334c928b20'; + +final class Supports$InClassFamilyNameFamily extends Family { + const Supports$InClassFamilyNameFamily._() + : super( + retry: null, + name: r'supports$InClassFamilyNameProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + Supports$InClassFamilyNameProvider call( + And$InT positional$arg, { + required And$InT named$arg, + String defaultArg = default$value, + }) => + Supports$InClassFamilyNameProvider._(argument: ( + positional$arg, + named$arg: named$arg, + defaultArg: defaultArg, + ), from: this); + + @override + String debugGetCreateSourceHash() => _$supports$InClassFamilyNameHash(); + + @override + String toString() => r'supports$InClassFamilyNameProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Supports$InClassFamilyName Function( + ( + And$InT, { + And$InT named$arg, + String defaultArg, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as Supports$InClassFamilyNameProvider; + + return provider._copyWithCreate(() { + final argument = provider.argument as ( + And$InT, { + And$InT named$arg, + String defaultArg, + }); + + return create(argument); + }).$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + String Function( + Ref ref, + Supports$InClassFamilyName notifier, + ( + And$InT, { + And$InT named$arg, + String defaultArg, + }) argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as Supports$InClassFamilyNameProvider; + + return provider._copyWithBuild((ref, notifier) { + final argument = provider.argument as ( + And$InT, { + And$InT named$arg, + String defaultArg, + }); + + return build(ref, notifier, argument); + }).$createElement(pointer); + }, + ); + } +} + +abstract class _$Supports$InClassFamilyName extends $Notifier { + late final _$args = ref.$arg as ( + And$InT, { + And$InT named$arg, + String defaultArg, + }); + And$InT get positional$arg => _$args.$1; + And$InT get named$arg => _$args.named$arg; + String get defaultArg => _$args.defaultArg; String build( - int first, { - String? second, - required double third, - bool fourth = true, - List? fifth, + And$InT positional$arg, { + required And$InT named$arg, + String defaultArg = default$value, }); + @$internal + @override + String runBuild() => build( + _$args.$1, + named$arg: _$args.named$arg, + defaultArg: _$args.defaultArg, + ); } -/// This is some documentation -/// -/// Copied from [FamilyClass]. -@ProviderFor(FamilyClass) -const familyClassProvider = FamilyClassFamily(); +@ProviderFor(generated) +const generatedProvider = GeneratedProvider._(); + +final class GeneratedProvider extends $FunctionalProvider + with $Provider { + const GeneratedProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'generatedProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// This is some documentation -/// -/// Copied from [FamilyClass]. -class FamilyClassFamily extends Family { - /// This is some documentation - /// - /// Copied from [FamilyClass]. - const FamilyClassFamily(); + final String Function( + Ref ref, + )? _createCb; - /// This is some documentation - /// - /// Copied from [FamilyClass]. - FamilyClassProvider call( - int first, { - String? second, - required double third, - bool fourth = true, - List? fifth, - }) { - return FamilyClassProvider( - first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, + @override + String debugGetCreateSourceHash() => _$generatedHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + @override - FamilyClassProvider getProviderOverride( - covariant FamilyClassProvider provider, + GeneratedProvider $copyWithCreate( + String Function( + Ref ref, + ) create, ) { - return call( - provider.first, - second: provider.second, - third: provider.third, - fourth: provider.fourth, - fifth: provider.fifth, + return GeneratedProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? generated; + return _$cb(ref); + } +} + +String _$generatedHash() => r'24bfb5df4dc529258ab568372e90a1cbfc2d8c24'; + +@ProviderFor(unnecessaryCast) +const unnecessaryCastProvider = UnnecessaryCastFamily._(); + +final class UnnecessaryCastProvider extends $FunctionalProvider + with $Provider { + const UnnecessaryCastProvider._( + {required UnnecessaryCastFamily super.from, + required Object? super.argument, + String Function( + Ref ref, + Object? arg, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'unnecessaryCastProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + Object? arg, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$unnecessaryCastHash(); + + @override + String toString() { + return r'unnecessaryCastProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } - static const Iterable? _dependencies = null; + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - Iterable? get dependencies => _dependencies; + UnnecessaryCastProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return UnnecessaryCastProvider._( + argument: argument, + from: from! as UnnecessaryCastFamily, + create: ( + ref, + Object? arg, + ) => + create(ref)); + } - static const Iterable? _allTransitiveDependencies = null; + @override + String create(Ref ref) { + final _$cb = _createCb ?? unnecessaryCast; + final argument = this.argument; + return _$cb( + ref, + argument, + ); + } @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + bool operator ==(Object other) { + return other is UnnecessaryCastProvider && other.argument == argument; + } @override - String? get name => r'familyClassProvider'; + int get hashCode { + return argument.hashCode; + } } -/// This is some documentation -/// -/// Copied from [FamilyClass]. -class FamilyClassProvider - extends AutoDisposeNotifierProviderImpl { - /// This is some documentation - /// - /// Copied from [FamilyClass]. - FamilyClassProvider( - int first, { - String? second, - required double third, - bool fourth = true, - List? fifth, - }) : this._internal( - () => FamilyClass() - ..first = first - ..second = second - ..third = third - ..fourth = fourth - ..fifth = fifth, - from: familyClassProvider, - name: r'familyClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$familyClassHash, - dependencies: FamilyClassFamily._dependencies, - allTransitiveDependencies: - FamilyClassFamily._allTransitiveDependencies, - first: first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, +String _$unnecessaryCastHash() => r'c64330124f4b03a3e6757e787f62966a32bf83ad'; + +final class UnnecessaryCastFamily extends Family { + const UnnecessaryCastFamily._() + : super( + retry: null, + name: r'unnecessaryCastProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, ); - FamilyClassProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.first, - required this.second, - required this.third, - required this.fourth, - required this.fifth, - }) : super.internal(); - - final int first; - final String? second; - final double third; - final bool fourth; - final List? fifth; - - @override - String runNotifierBuild( - covariant FamilyClass notifier, - ) { - return notifier.build( - first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, + UnnecessaryCastProvider call( + Object? arg, + ) => + UnnecessaryCastProvider._(argument: arg, from: this); + + @override + String debugGetCreateSourceHash() => _$unnecessaryCastHash(); + + @override + String toString() => r'unnecessaryCastProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + String Function( + Ref ref, + Object? args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as UnnecessaryCastProvider; + + final argument = provider.argument; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, ); } +} + +@ProviderFor(UnnecessaryCastClass) +const unnecessaryCastClassProvider = UnnecessaryCastClassFamily._(); + +final class UnnecessaryCastClassProvider + extends $NotifierProvider { + const UnnecessaryCastClassProvider._( + {required UnnecessaryCastClassFamily super.from, + required Object? super.argument, + super.runNotifierBuildOverride, + UnnecessaryCastClass Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'unnecessaryCastClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final UnnecessaryCastClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$unnecessaryCastClassHash(); @override - Override overrideWith(FamilyClass Function() create) { - return ProviderOverride( + String toString() { + return r'unnecessaryCastClassProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( origin: this, - override: FamilyClassProvider._internal( - () => create() - ..first = first - ..second = second - ..third = third - ..fourth = fourth - ..fifth = fifth, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - first: first, - second: second, - third: third, - fourth: fourth, - fifth: fifth, - ), + providerOverride: $ValueProvider(value), ); } + @$internal + @override + UnnecessaryCastClass create() => _createCb?.call() ?? UnnecessaryCastClass(); + + @$internal + @override + UnnecessaryCastClassProvider $copyWithCreate( + UnnecessaryCastClass Function() create, + ) { + return UnnecessaryCastClassProvider._( + argument: argument, + from: from! as UnnecessaryCastClassFamily, + create: create); + } + + @$internal @override - AutoDisposeNotifierProviderElement createElement() { - return _FamilyClassProviderElement(this); + UnnecessaryCastClassProvider $copyWithBuild( + String Function( + Ref, + UnnecessaryCastClass, + ) build, + ) { + return UnnecessaryCastClassProvider._( + argument: argument, + from: from! as UnnecessaryCastClassFamily, + runNotifierBuildOverride: build); } + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + @override bool operator ==(Object other) { - return other is FamilyClassProvider && - other.first == first && - other.second == second && - other.third == third && - other.fourth == fourth && - other.fifth == fifth; + return other is UnnecessaryCastClassProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, first.hashCode); - hash = _SystemHash.combine(hash, second.hashCode); - hash = _SystemHash.combine(hash, third.hashCode); - hash = _SystemHash.combine(hash, fourth.hashCode); - hash = _SystemHash.combine(hash, fifth.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FamilyClassRef on AutoDisposeNotifierProviderRef { - /// The parameter `first` of this provider. - int get first; +String _$unnecessaryCastClassHash() => + r'8cbf80b29c4edf7f5401e4447feca553e921e734'; + +final class UnnecessaryCastClassFamily extends Family { + const UnnecessaryCastClassFamily._() + : super( + retry: null, + name: r'unnecessaryCastClassProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + UnnecessaryCastClassProvider call( + Object? arg, + ) => + UnnecessaryCastClassProvider._(argument: arg, from: this); + + @override + String debugGetCreateSourceHash() => _$unnecessaryCastClassHash(); + + @override + String toString() => r'unnecessaryCastClassProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + UnnecessaryCastClass Function( + Object? args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as UnnecessaryCastClassProvider; + + final argument = provider.argument; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + String Function(Ref ref, UnnecessaryCastClass notifier, Object? argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as UnnecessaryCastClassProvider; - /// The parameter `second` of this provider. - String? get second; + final argument = provider.argument; - /// The parameter `third` of this provider. - double get third; + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} - /// The parameter `fourth` of this provider. - bool get fourth; +abstract class _$UnnecessaryCastClass extends $Notifier { + late final _$args = ref.$arg; + Object? get arg => _$args; - /// The parameter `fifth` of this provider. - List? get fifth; + String build( + Object? arg, + ); + @$internal + @override + String runBuild() => build( + _$args, + ); } -class _FamilyClassProviderElement - extends AutoDisposeNotifierProviderElement - with FamilyClassRef { - _FamilyClassProviderElement(super.provider); +@ProviderFor(manyDataStream) +const manyDataStreamProvider = ManyDataStreamFamily._(); + +final class ManyDataStreamProvider + extends $FunctionalProvider>, Stream>> + with $FutureModifier>, $StreamProvider> { + const ManyDataStreamProvider._( + {required ManyDataStreamFamily super.from, + required ManyProviderData super.argument, + Stream> Function( + Ref ref, + ManyProviderData pData, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'manyDataStreamProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Stream> Function( + Ref ref, + ManyProviderData pData, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$manyDataStreamHash(); + + ManyDataStreamProvider _copyWithCreate( + Stream> Function( + Ref ref, + ManyProviderData pData, + ) create, + ) { + return ManyDataStreamProvider._( + argument: argument as ManyProviderData, + from: from! as ManyDataStreamFamily, + create: create); + } + + @override + String toString() { + return r'manyDataStreamProvider' + '<${T}, ${S}>' + '($argument)'; + } + @$internal @override - int get first => (origin as FamilyClassProvider).first; + $StreamProviderElement> $createElement($ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + @override - String? get second => (origin as FamilyClassProvider).second; + ManyDataStreamProvider $copyWithCreate( + Stream> Function( + Ref ref, + ) create, + ) { + return ManyDataStreamProvider._( + argument: argument as ManyProviderData, + from: from! as ManyDataStreamFamily, + create: ( + ref, + ManyProviderData pData, + ) => + create(ref)); + } + @override - double get third => (origin as FamilyClassProvider).third; + Stream> create(Ref ref) { + final _$cb = _createCb ?? manyDataStream; + final argument = this.argument as ManyProviderData; + return _$cb( + ref, + argument, + ); + } + @override - bool get fourth => (origin as FamilyClassProvider).fourth; + bool operator ==(Object other) { + return other is ManyDataStreamProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } + @override - List? get fifth => (origin as FamilyClassProvider).fifth; + int get hashCode { + return Object.hash(runtimeType, argument); + } } -String _$supports$InClassNameHash() => - r'4e99f433d9cb3598faaf4d172edf9f28b9e68091'; +String _$manyDataStreamHash() => r'5f389757cba176868a47b89b14b1f96afe20d728'; -/// See also [Supports$InClassName]. -@ProviderFor(Supports$InClassName) -final supports$InClassNameProvider = - AutoDisposeNotifierProvider.internal( - Supports$InClassName.new, - name: r'supports$InClassNameProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$supports$InClassNameHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Supports$InClassName = AutoDisposeNotifier; +final class ManyDataStreamFamily extends Family { + const ManyDataStreamFamily._() + : super( + retry: null, + name: r'manyDataStreamProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + ManyDataStreamProvider call( + ManyProviderData pData, + ) => + ManyDataStreamProvider._(argument: pData, from: this); + + @override + String debugGetCreateSourceHash() => _$manyDataStreamHash(); + + @override + String toString() => r'manyDataStreamProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Stream> Function( + Ref ref, + ManyProviderData args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ManyDataStreamProvider; + + return provider._copyWithCreate(( + ref, + ManyProviderData pData, + ) { + return create(ref, pData); + }).$createElement(pointer); + }, + ); + } +} // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_generator/test/mock.dart b/packages/riverpod_generator/test/mock.dart new file mode 100644 index 000000000..04efbf137 --- /dev/null +++ b/packages/riverpod_generator/test/mock.dart @@ -0,0 +1,201 @@ +import 'package:mockito/mockito.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:test/test.dart'; + +class ListenerMock with Mock { + void call(Object? a, Object? b); +} + +typedef VerifyOnly = VerificationResult Function( + Mock mock, + T matchingInvocations, +); + +/// Syntax sugar for: +/// +/// ```dart +/// verify(mock()).called(1); +/// verifyNoMoreInteractions(mock); +/// ``` +VerifyOnly get verifyOnly { + final verification = verify; + + return (mock, invocation) { + final result = verification(invocation); + result.called(1); + verifyNoMoreInteractions(mock); + return result; + }; +} + +TypeMatcher> isMutationBase({ + TypeMatcher>? state, +}) { + var matcher = isA>(); + + if (state != null) { + matcher = matcher.having((e) => e.state, 'state', state); + } + + return matcher; +} + +TypeMatcher> isIdleMutationState() { + return isA>(); +} + +TypeMatcher> isPendingMutationState() { + return isA>(); +} + +TypeMatcher> isSuccessMutationState(T value) { + return isA>().having((e) => e.value, 'value', value); +} + +TypeMatcher> isErrorMutationState(Object error) { + return isA>().having((e) => e.error, 'error', error); +} + +enum InvocationKind { + method, + getter, + setter, +} + +TypeMatcher isInvocation({ + Object? memberName, + List? positionalArguments, + Map? namedArguments, + Object? typeArguments, + InvocationKind? kind, +}) { + var matcher = isA(); + + if (kind != null) { + switch (kind) { + case InvocationKind.method: + matcher = matcher.having((e) => e.isMethod, 'isMethod', true); + case InvocationKind.getter: + matcher = matcher.having((e) => e.isGetter, 'isGetter', true); + case InvocationKind.setter: + matcher = matcher.having((e) => e.isSetter, 'isSetter', true); + } + } + + if (typeArguments != null) { + matcher = matcher.having( + (e) => e.typeArguments, + 'typeArguments', + typeArguments, + ); + } + + if (memberName != null) { + matcher = matcher.having((e) => e.memberName, 'memberName', memberName); + } + + if (positionalArguments != null) { + matcher = matcher.having( + (e) => e.positionalArguments, + 'positionalArguments', + positionalArguments, + ); + } + + if (namedArguments != null) { + matcher = matcher.having( + (e) => e.namedArguments, + 'namedArguments', + namedArguments, + ); + } + + return matcher; +} + +class ObserverMock extends Mock implements ProviderObserver { + ObserverMock([this.label]); + + final String? label; + + @override + String toString() { + return label ?? super.toString(); + } + + @override + void didAddProvider( + ProviderObserverContext? context, + Object? value, + ); + + @override + void providerDidFail( + ProviderObserverContext? context, + Object? error, + StackTrace? stackTrace, + ); + + @override + void didUpdateProvider( + ProviderObserverContext? context, + Object? previousValue, + Object? newValue, + ); + + @override + void didDisposeProvider(ProviderObserverContext? context); + + @override + void mutationReset(ProviderObserverContext? context); + + @override + void mutationStart( + ProviderObserverContext? context, + MutationContext? mutation, + ); + + @override + void mutationError( + ProviderObserverContext? context, + MutationContext? mutation, + Object? error, + StackTrace? stackTrace, + ); + + @override + void mutationSuccess( + ProviderObserverContext? context, + MutationContext? mutation, + Object? result, + ); +} + +TypeMatcher isProviderObserverContext( + Object? provider, + Object? container, { + required Object? mutation, +}) { + var matcher = isA(); + + matcher = matcher.having((e) => e.provider, 'provider', provider); + matcher = matcher.having((e) => e.container, 'container', container); + matcher = matcher.having((e) => e.mutation, 'mutation', mutation); + + return matcher; +} + +TypeMatcher isMutationContext( + Object? invocation, { + Object? notifier, +}) { + var matcher = isA(); + + matcher = matcher.having((e) => e.invocation, 'invocation', invocation); + if (notifier != null) { + matcher = matcher.having((e) => e.notifier, 'notifier', notifier); + } + + return matcher; +} diff --git a/packages/riverpod_generator/test/mutation_test.dart b/packages/riverpod_generator/test/mutation_test.dart new file mode 100644 index 000000000..65d317563 --- /dev/null +++ b/packages/riverpod_generator/test/mutation_test.dart @@ -0,0 +1,528 @@ +import 'dart:async'; + +import 'package:mockito/mockito.dart'; +import 'package:riverpod/riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:test/test.dart'; + +import 'integration/mutation.dart'; +import 'mock.dart'; + +void main() { + test('Can listen a mutation', () async { + final container = ProviderContainer.test(); + final listener = ListenerMock(); + + final sub = container.listen( + simpleProvider.delegated, + listener.call, + fireImmediately: true, + ); + + verifyOnly( + listener, + listener(any, isMutationBase(state: isIdleMutationState())), + ); + + final future = sub.read().call(() async => 1); + + verifyOnly( + listener, + listener(any, isMutationBase(state: isPendingMutationState())), + ); + + expect(await future, 1); + + verifyOnly( + listener, + listener(any, isMutationBase(state: isSuccessMutationState(1))), + ); + + final future2 = sub.read().call(() => throw StateError('42')); + + await expectLater(future2, throwsA(isStateError)); + verifyInOrder([ + listener(any, isMutationBase(state: isPendingMutationState())), + listener( + any, + isMutationBase(state: isErrorMutationState(isStateError)), + ), + ]); + verifyNoMoreInteractions(listener); + }); + + test('Can listen a mutation with family', () async { + final container = ProviderContainer.test(); + final listener = ListenerMock(); + + final sub = + container.listen(simpleFamilyProvider('key').increment, listener.call); + + expect( + sub.read(), + isMutationBase(state: isIdleMutationState()), + ); + + final future = sub.read().call(2); + + expect( + sub.read(), + isMutationBase(state: isPendingMutationState()), + ); + + expect(await future, 2); + + expect( + sub.read(), + isMutationBase(state: isSuccessMutationState(2)), + ); + }); + + test('Supports generic mutations', () async { + final container = ProviderContainer.test(); + final listener = ListenerMock(); + + final sub = container.listen(genericMutProvider.increment, listener.call); + + expect( + sub.read(), + isMutationBase(state: isIdleMutationState()), + ); + + final future = sub.read().call(2.5); + + expect(await future, 3); + + expect( + sub.read(), + isMutationBase(state: isSuccessMutationState(3)), + ); + }); + + group('auto reset', () { + test('Automatically resets the state when all listeners are removed', + () async { + final container = ProviderContainer.test(); + + // The mutation should reset even if the provider is kept alive + container.listen(simpleProvider, (a, b) {}); + + final sub = container.listen(simpleProvider.increment, (a, b) {}); + final sub2 = container.listen(simpleProvider.increment, (a, b) {}); + + await sub.read().call(2); + + sub.close(); + await null; + + expect( + container.read(simpleProvider.increment), + isMutationBase(state: isSuccessMutationState(2)), + ); + + sub2.close(); + await null; + + expect( + container.read(simpleProvider.increment), + isMutationBase(state: isIdleMutationState()), + ); + }); + + test('is cancelled if a listener is added during the delay', () async { + final container = ProviderContainer.test(); + + final sub = container.listen(simpleProvider.increment, (a, b) {}); + + await sub.read().call(2); + sub.close(); + + container.listen(simpleProvider.increment, (a, b) {}); + await null; + + expect( + container.read(simpleProvider.increment), + isMutationBase(state: isSuccessMutationState(2)), + ); + }); + }); + + test('Maintains progress even if a provider is when the provider is reset', + () async { + final container = ProviderContainer.test(); + + final sub = container.listen(simpleProvider.increment, (a, b) {}); + + await sub.read().call(2); + container.invalidate(simpleProvider); + + expect( + sub.read(), + isMutationBase(state: isSuccessMutationState(2)), + ); + }); + + test('Supports getting called again while pending', () async { + final container = ProviderContainer.test(); + final sub = container.listen(simpleAsyncProvider.delegated, (a, b) {}); + + final completer = Completer(); + final completer2 = Completer(); + final completer3 = Completer(); + + final future = sub.read().call(() => completer.future); + final future2 = sub.read().call(() => completer2.future); + final future3 = sub.read().call(() => completer3.future); + + completer.complete(42); + + expect(await future, 42); + expect( + sub.read(), + isMutationBase(state: isPendingMutationState()), + ); + expect(container.read(simpleAsyncProvider), const AsyncData(42)); + + completer2.completeError(21); + await expectLater(future2, throwsA(21)); + expect( + sub.read(), + isMutationBase(state: isPendingMutationState()), + ); + expect(container.read(simpleAsyncProvider), const AsyncData(42)); + + completer3.complete(21); + expect(await future3, 21); + expect( + sub.read(), + isMutationBase(state: isSuccessMutationState(21)), + ); + expect(container.read(simpleAsyncProvider), const AsyncData(21)); + }); + + test('Listening to a mutation keeps the provider alive', () async { + final container = ProviderContainer.test(); + + final sub = container.listen(simpleProvider.increment, (a, b) {}); + + expect(container.read(simpleProvider), 0); + + await container.pump(); + expect(container.exists(simpleProvider), true); + + sub.close(); + + await container.pump(); + expect(container.exists(simpleProvider), false); + }); + + test('Listening a mutation lazily initializes the provider', () async { + final container = ProviderContainer.test(); + + final sub = container.listen(simpleProvider.increment, (a, b) {}); + + final element = container.readProviderElement(simpleProvider); + + expect(element.stateResult, null); + + await sub.read().call(2); + + expect(container.read(simpleProvider), 2); + }); + + test('If notifier constructor throws, the mutation immediately throws', + () async { + final observer = ObserverMock(); + final container = ProviderContainer.test(observers: [observer]); + + final sub = container.listen(failingCtorProvider.increment, (a, b) {}); + + expect(sub.read(), isMutationBase(state: isIdleMutationState())); + + expect(() => sub.read().call(2), throwsStateError); + + expect( + sub.read(), + isMutationBase(state: isIdleMutationState()), + ); + verifyNever(observer.mutationError(any, any, any, any)); + }); + + test('Typed providers and named parameters', () async { + final container = ProviderContainer.test(); + + final sub = container.listen(typedProvider.mutate, (a, b) {}); + + expect(container.read(typedProvider), 'typed'); + + await sub.read().call('five', two: 'six', three: 'seven'); + + expect( + sub.read(), + isMutationBase(state: isSuccessMutationState('five six seven')), + ); + }); + + group('reset', () { + test('Supports calling reset while pending', () async { + final container = ProviderContainer.test(); + final sub = container.listen(simpleProvider.delegated, (a, b) {}); + + final completer = Completer(); + final future = sub.read().call(() => completer.future); + + sub.read().reset(); + + completer.complete(42); + + expect(await future, 42); + expect( + sub.read(), + isMutationBase(state: isIdleMutationState()), + ); + }); + + test('sets the state back to idle', () async { + final container = ProviderContainer.test(); + final listener = ListenerMock(); + + final sub = container.listen(simpleProvider.increment, listener.call); + + await sub.read().call(2); + + sub.read().reset(); + + expect( + sub.read(), + isMutationBase(state: isIdleMutationState()), + ); + }); + }); + + group('Integration with ProviderObserver', () { + test('handles generic methods', () async { + final observer = ObserverMock(); + final container = ProviderContainer.test( + observers: [observer], + ); + + container.listen(genericMutProvider, (a, b) {}); + container.listen(genericMutProvider.increment, (a, b) {}); + await container.read(genericMutProvider.future); + + clearInteractions(observer); + + await container.read(genericMutProvider.increment).call(42); + + verify( + observer.didUpdateProvider( + argThat( + isProviderObserverContext( + genericMutProvider, + container, + mutation: isMutationContext( + isInvocation( + memberName: #increment, + positionalArguments: [42.0], + kind: InvocationKind.method, + typeArguments: [double], + ), + ), + ), + ), + const AsyncData(0), + const AsyncData(42), + ), + ); + }); + + test('sends current mutation to didUpdateProvider', () async { + final observer = ObserverMock(); + final container = ProviderContainer.test( + observers: [observer], + ); + + final sub = container.listen(simpleProvider.notifier, (a, b) {}); + container.listen(simpleProvider.delegated, (a, b) {}); + + clearInteractions(observer); + + Future fn() async { + sub.read().state = 1; + return 42; + } + + await container.read(simpleProvider.delegated).call(fn); + + verifyInOrder([ + observer.didUpdateProvider( + argThat( + isProviderObserverContext( + simpleProvider, + container, + mutation: isMutationContext( + isInvocation( + memberName: #delegated, + positionalArguments: [fn], + kind: InvocationKind.method, + typeArguments: isEmpty, + ), + ), + ), + ), + 0, + 1, + ), + observer.didUpdateProvider( + argThat( + isProviderObserverContext( + simpleProvider, + container, + mutation: isMutationContext( + isInvocation( + memberName: #delegated, + positionalArguments: [fn], + kind: InvocationKind.method, + typeArguments: isEmpty, + ), + ), + ), + ), + 1, + 42, + ), + ]); + }); + + test('handles mutationStart/Pending/Success/Error/Reset', () async { + final observer = ObserverMock(); + final container = ProviderContainer.test( + observers: [observer], + ); + + container.listen(simpleProvider, (a, b) {}); + clearInteractions(observer); + + final sub = container.listen(simpleProvider.delegated, (a, b) {}); + verifyNoMoreInteractions(observer); + + Future fn() async => 42; + final stack = StackTrace.current; + final err = StateError('foo'); + Future fn2() async => Error.throwWithStackTrace(err, stack); + + final future = sub.read().call(fn); + verifyOnly( + observer, + observer.mutationStart( + argThat( + isProviderObserverContext( + simpleProvider, + container, + mutation: isNotNull, + ), + ), + argThat( + isMutationContext( + isInvocation( + memberName: #delegated, + positionalArguments: [fn], + kind: InvocationKind.method, + typeArguments: isEmpty, + ), + ), + ), + ), + ); + + await future; + + verify( + observer.mutationSuccess( + argThat( + isProviderObserverContext( + simpleProvider, + container, + mutation: isNotNull, + ), + ), + argThat( + isMutationContext( + isInvocation( + memberName: #delegated, + positionalArguments: [fn], + kind: InvocationKind.method, + typeArguments: isEmpty, + ), + ), + ), + 42, + ), + ); + + final future2 = sub.read().call(fn2); + + verify( + observer.mutationStart( + argThat( + isProviderObserverContext( + simpleProvider, + container, + mutation: isNotNull, + ), + ), + argThat( + isMutationContext( + isInvocation( + memberName: #delegated, + positionalArguments: [fn2], + kind: InvocationKind.method, + typeArguments: isEmpty, + ), + ), + ), + ), + ); + + await expectLater(future2, throwsA(isStateError)); + + verify( + observer.mutationError( + argThat( + isProviderObserverContext( + simpleProvider, + container, + mutation: isNotNull, + ), + ), + argThat( + isMutationContext( + isInvocation( + memberName: #delegated, + positionalArguments: [fn2], + kind: InvocationKind.method, + typeArguments: isEmpty, + ), + ), + ), + err, + stack, + ), + ); + + sub.read().reset(); + + verify( + observer.mutationReset( + argThat( + isProviderObserverContext( + simpleProvider, + container, + mutation: isNull, + ), + ), + ), + ); + }); + }); +} diff --git a/packages/riverpod_generator/test/notifier_test.dart b/packages/riverpod_generator/test/notifier_test.dart index bdeaf0fd1..cad4957f6 100644 --- a/packages/riverpod_generator/test/notifier_test.dart +++ b/packages/riverpod_generator/test/notifier_test.dart @@ -1,18 +1,16 @@ // ignore_for_file: omit_local_variable_types, unused_local_variable -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart' show ProviderBase, ProviderContainer; import 'package:test/test.dart'; import 'integration/sync.dart'; -import 'utils.dart'; void main() { test('Creates a Provider if @riverpod is used on a synchronous function', () { - final container = createContainer(); + final container = ProviderContainer.test(); - final AutoDisposeNotifierProvider provider = - publicClassProvider; + const ProviderBase provider = publicClassProvider; final String result = container.read(publicProvider); expect(result, 'Hello world'); @@ -27,7 +25,7 @@ void main() { }); test('Supports overriding non-family notifiers', () { - final container = createContainer( + final container = ProviderContainer.test( overrides: [ publicClassProvider.overrideWith(() => PublicClass('Hello world')), ], @@ -36,12 +34,13 @@ void main() { final notifier = container.read(publicClassProvider.notifier); expect(notifier.param, 'Hello world'); + // ignore: invalid_use_of_protected_member expect(notifier.ref, isNotNull); expect(notifier.state, isNotNull); }); test('Supports overriding family notifiers', () { - final container = createContainer( + final container = ProviderContainer.test( overrides: [ familyClassProvider(42, third: .42) .overrideWith(() => FamilyClass('Hello world')), @@ -57,6 +56,7 @@ void main() { expect(notifier.fourth, true); expect(notifier.fifth, null); + // ignore: invalid_use_of_protected_member expect(notifier.ref, isNotNull); expect(notifier.state, isNotNull); }); @@ -64,7 +64,7 @@ void main() { test( 'Creates a NotifierProvider.family if @riverpod is used on a synchronous function with parameters', () { - final container = createContainer(); + final container = ProviderContainer.test(); const FamilyClassFamily family = familyClassProvider; @@ -89,8 +89,6 @@ void main() { familyClassProvider( 42, third: .42, - // ignore: avoid_redundant_argument_values - fourth: true, ), ); expect( @@ -98,8 +96,6 @@ void main() { familyClassProvider( 42, third: .42, - // ignore: avoid_redundant_argument_values - fourth: true, ).hashCode, ); @@ -111,14 +107,7 @@ void main() { fifth: ['x42'], ); // ignore: invalid_use_of_internal_member - final AutoDisposeNotifierProviderImpl futureProvider = - provider; - - expect(provider.first, 42); - expect(provider.second, 'x42'); - expect(provider.third, .42); - expect(provider.fourth, false); - expect(provider.fifth, ['x42']); + final ProviderBase futureProvider = provider; final String result = container.read( familyClassProvider( diff --git a/packages/riverpod_generator/test/retry_test.dart b/packages/riverpod_generator/test/retry_test.dart new file mode 100644 index 000000000..c8e31e166 --- /dev/null +++ b/packages/riverpod_generator/test/retry_test.dart @@ -0,0 +1,12 @@ +import 'package:test/test.dart'; + +import 'integration/hash/retry.dart'; + +void main() { + test('Passes retry', () { + expect(aProvider.retry, myRetry); + + expect(bProvider.retry, myRetry2); + expect(bProvider(42).retry, myRetry2); + }); +} diff --git a/packages/riverpod_generator/test/scoped_test.dart b/packages/riverpod_generator/test/scoped_test.dart index 4e64663d6..c597a5c86 100644 --- a/packages/riverpod_generator/test/scoped_test.dart +++ b/packages/riverpod_generator/test/scoped_test.dart @@ -1,25 +1,51 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:test/test.dart'; import 'integration/scopes.dart'; -import 'utils.dart'; void main() { test('throws UnsupportedError if accessed without an override', () { - final container = createContainer(); + final container = ProviderContainer.test(); expect( - () => container.read(scopedProvider), - throwsUnsupportedError, + () => container.read(scopedClassProvider), + throwsA( + isA().having( + (e) => e.toString(), + 'toString', + startsWith( + 'MissingScopeException: The provider scopedClassProvider is scoped, ', + ), + ), + ), ); }); test('Can be accessed without problem if the provider is overridden', () { - final container = createContainer( - overrides: [ - scopedProvider.overrideWith((ref) => 42), - ], + final container = ProviderContainer.test(); + + expect( + () => container.read(scopedClassFamilyProvider(42)), + throwsA( + isA().having( + (e) => e.toString(), + 'toString', + startsWith( + 'MissingScopeException: The provider scopedClassFamilyProvider(42) is scoped, ', + ), + ), + ), ); + }); - expect(container.read(scopedProvider), 42); + test('Marks the provider as scoped', () { + expect( + scopedClassFamilyProvider.allTransitiveDependencies, + same(const []), + ); + expect( + scopedClassProvider.allTransitiveDependencies, + same(const []), + ); }); } diff --git a/packages/riverpod_generator/test/stream_notifier_test.dart b/packages/riverpod_generator/test/stream_notifier_test.dart index d066c4a8d..283bc1dee 100644 --- a/packages/riverpod_generator/test/stream_notifier_test.dart +++ b/packages/riverpod_generator/test/stream_notifier_test.dart @@ -1,19 +1,18 @@ // ignore_for_file: omit_local_variable_types, unused_local_variable +import 'package:riverpod/riverpod.dart' show ProviderBase; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:test/test.dart'; import 'integration/stream.dart'; -import 'utils.dart'; void main() { test( 'Creates a StreamNotifierProvider if @riverpod is used on a Stream class', () async { - final container = createContainer(); + final container = ProviderContainer.test(); - final AutoDisposeStreamNotifierProvider provider = - publicClassProvider; + const ProviderBase> provider = publicClassProvider; expect( await container.listen(publicClassProvider.future, (_, __) {}).read(), @@ -30,7 +29,7 @@ void main() { }); test('Supports overriding non-family notifiers', () { - final container = createContainer( + final container = ProviderContainer.test( overrides: [ publicClassProvider.overrideWith(() => PublicClass('Hello world')), ], @@ -39,12 +38,13 @@ void main() { final notifier = container.read(publicClassProvider.notifier); expect(notifier.param, 'Hello world'); + // ignore: invalid_use_of_protected_member expect(notifier.ref, isNotNull); expect(notifier.state, isNotNull); }); test('Supports overriding family notifiers', () { - final container = createContainer( + final container = ProviderContainer.test( overrides: [ familyClassProvider(42, third: .42) .overrideWith(() => FamilyClass('Hello world')), @@ -60,6 +60,7 @@ void main() { expect(notifier.fourth, true); expect(notifier.fifth, null); + // ignore: invalid_use_of_protected_member expect(notifier.ref, isNotNull); expect(notifier.state, isNotNull); }); @@ -67,7 +68,7 @@ void main() { test( 'Creates a NotifierProvider.family if @riverpod is used on a synchronous function with parameters', () async { - final container = createContainer(); + final container = ProviderContainer.test(); const FamilyClassFamily family = familyClassProvider; @@ -92,8 +93,6 @@ void main() { familyClassProvider( 42, third: .42, - // ignore: avoid_redundant_argument_values - fourth: true, ), ); expect( @@ -101,8 +100,6 @@ void main() { familyClassProvider( 42, third: .42, - // ignore: avoid_redundant_argument_values - fourth: true, ).hashCode, ); @@ -113,15 +110,7 @@ void main() { fourth: false, fifth: ['x42'], ); - // ignore: invalid_use_of_internal_member - final AutoDisposeStreamNotifierProviderImpl - futureProvider = provider; - - expect(provider.first, 42); - expect(provider.second, 'x42'); - expect(provider.third, .42); - expect(provider.fourth, false); - expect(provider.fifth, ['x42']); + final ProviderBase> futureProvider = provider; expect( await container diff --git a/packages/riverpod_generator/test/stream_test.dart b/packages/riverpod_generator/test/stream_test.dart index fa6d54ee2..7a39f0eb9 100644 --- a/packages/riverpod_generator/test/stream_test.dart +++ b/packages/riverpod_generator/test/stream_test.dart @@ -1,17 +1,17 @@ // ignore_for_file: omit_local_variable_types, unused_local_variable +import 'package:riverpod/riverpod.dart' show ProviderBase; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:test/test.dart'; import 'integration/stream.dart'; -import 'utils.dart'; void main() { test('Creates a StreamProvider if @riverpod is used on a Stream function', () async { - final container = createContainer(); + final container = ProviderContainer.test(); - final AutoDisposeStreamProvider provider = publicProvider; + const ProviderBase> provider = publicProvider; expect( await container.listen(publicProvider.future, (_, __) {}).read(), @@ -28,36 +28,49 @@ void main() { }); test('Supports overriding non-family providers', () async { - final container = createContainer( + final container = ProviderContainer.test( overrides: [ publicProvider.overrideWith((ref) => Stream.value('Hello world')), ], ); - final result = container.read(publicProvider.future); + final result = container.listen(publicProvider.future, (a, b) {}).read(); expect(await result, 'Hello world'); }); test('Supports overriding family providers', () async { - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - familyProvider(42, third: .42).overrideWith( - (ref) => Stream.value( - 'Hello world ${ref.first} ${ref.second} ' - '${ref.third} ${ref.fourth} ${ref.fifth}', + familyProvider.overrideWith( + (ref, args) => Stream.value( + 'Hello world ${args.$1} ${args.second} ' + '${args.third} ${args.fourth} ${args.fifth}', ), ), + familyProvider(21, third: .21).overrideWith( + (ref) => Stream.value('Override'), + ), ], ); - final result = container.read(familyProvider(42, third: .42).future); - expect(await result, 'Hello world 42 null 0.42 true null'); + expect( + await container + .listen(familyProvider(42, third: .42).future, (a, b) {}) + .read(), + 'Hello world 42 null 0.42 true null', + ); + expect( + await container + .listen(familyProvider(21, third: .21).future, (a, b) {}) + .read(), + 'Override', + ); }); test( 'Creates a Provider.family if @riverpod is used on a synchronous function with parameters', () async { - final container = createContainer(); + final container = ProviderContainer.test(); const FamilyFamily family = familyProvider; @@ -82,8 +95,6 @@ void main() { familyProvider( 42, third: .42, - // ignore: avoid_redundant_argument_values - fourth: true, ), ); expect( @@ -91,8 +102,6 @@ void main() { familyProvider( 42, third: .42, - // ignore: avoid_redundant_argument_values - fourth: true, ).hashCode, ); @@ -103,13 +112,7 @@ void main() { fourth: false, fifth: ['x42'], ); - final AutoDisposeStreamProvider futureProvider = provider; - - expect(provider.first, 42); - expect(provider.second, 'x42'); - expect(provider.third, .42); - expect(provider.fourth, false); - expect(provider.fifth, ['x42']); + final ProviderBase> futureProvider = provider; expect( await container @@ -127,4 +130,53 @@ void main() { '(first: 42, second: x42, third: 0.42, fourth: false, fifth: [x42])', ); }); + + test('can overrideWith', () async { + final container = ProviderContainer.test( + overrides: [ + publicProvider.overrideWith((ref) => Stream.value('test')), + publicClassProvider.overrideWith(() => PublicClass(42)), + familyProvider.overrideWith( + (ref, args) { + return Stream.value( + 'test (first: ${args.$1}, second: ${args.second}, third: ${args.third}, fourth: ${args.fourth}, fifth: ${args.fifth})', + ); + }, + ), + familyClassProvider.overrideWith(FamilyClass.new), + ], + ); + + expect( + await container.listen(publicProvider.future, (a, b) {}).read(), + 'test', + ); + expect(container.read(publicClassProvider.notifier).param, 42); + expect( + await container + .listen( + familyProvider(42, second: '42', third: .42).future, + (a, b) {}, + ) + .read(), + 'test (first: 42, second: 42, third: 0.42, fourth: true, fifth: null)', + ); + expect( + container + .read(familyClassProvider(42, second: '42', third: .42).notifier) + .param, + (42, second: '42', third: 0.42, fourth: true, fifth: null), + ); + }); + + test('can overrideWithValue providers ', () { + final container = ProviderContainer.test( + overrides: [ + publicProvider.overrideWithValue(const AsyncData('test')), + ], + ); + + expect(container.read(publicProvider), const AsyncData('test')); + expect(container.read(publicProvider.future), completion('test')); + }); } diff --git a/packages/riverpod_generator/test/sync_test.dart b/packages/riverpod_generator/test/sync_test.dart index af406d70b..54b17509b 100644 --- a/packages/riverpod_generator/test/sync_test.dart +++ b/packages/riverpod_generator/test/sync_test.dart @@ -1,16 +1,78 @@ // ignore_for_file: omit_local_variable_types, unused_local_variable -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart' show ProviderBase, ProviderContainer; import 'package:test/test.dart'; import 'integration/sync.dart'; -import 'utils.dart'; void main() { - // TODO test that the generated providers contain the docs from the annotated element + group('Supports generics', () { + test('checks generics in hashCode', () { + expect( + genericProvider().hashCode, + genericProvider().hashCode, + ); + expect( + genericProvider().hashCode, + isNot(genericProvider().hashCode), + ); + expect( + genericProvider().hashCode, + isNot(genericProvider().hashCode), + ); + expect( + genericProvider().hashCode, + isNot(genericProvider().hashCode), + ); + expect( + genericProvider().hashCode, + genericProvider().hashCode, + ); + }); + + test('checks generics in ==', () { + expect( + genericProvider(), + genericProvider(), + ); + expect( + genericProvider(), + isNot(genericProvider()), + ); + expect( + genericProvider(), + isNot(genericProvider()), + ); + expect( + genericProvider(), + isNot(genericProvider()), + ); + expect( + genericProvider(), + genericProvider(), + ); + }); + + test('in simple scenarios', () { + final container = ProviderContainer.test(); + + expect( + container.listen(genericProvider(), (p, n) {}).read(), + [42], + ); + expect( + container.listen(genericProvider(), (p, n) {}).read(), + [3.14], + ); + expect( + container.listen(genericProvider(), (p, n) {}).read(), + [42, 3.14], + ); + }); + }); test('Supports Raw', () async { - final container = createContainer(); + final container = ProviderContainer.test(); expect( container.read(rawFutureProvider), @@ -65,8 +127,49 @@ void main() { ); }); + test('overrides toString', () { + expect(publicProvider.toString(), 'publicProvider'); + expect(familyProvider.toString(), 'familyProvider'); + expect( + familyProvider.call(42, second: 'foo', third: .3).toString(), + 'familyProvider(42, fifth: null, fourth: true, second: foo, third: 0.3)', + ); + + expect( + publicProvider.overrideWithValue('foo').toString(), + 'publicProvider.overrideWithValue(foo)', + ); + expect( + familyProvider.overrideWith((ref, _) => 'foo').toString(), + 'familyProvider.overrideWith(...)', + ); + expect( + familyProvider(42, second: 'foo', third: .3) + .overrideWith((ref) => '') + .toString(), + 'familyProvider(42, fifth: null, fourth: true, second: foo, third: 0.3).overrideWith(...)', + ); + + expect( + complexGenericProvider(param: 42).toString(), + 'complexGenericProvider(otherParam: null, param: 42)', + ); + expect( + rawFamilyStreamProvider(42).toString(), + 'rawFamilyStreamProvider(42)', + ); + expect( + supports$InFnNameProvider().toString(), + r'supports$InFnNameProvider()', + ); + expect( + supports$InClassNameProvider().toString(), + r'supports$InClassNameProvider()', + ); + }); + test('Supports overriding non-family providers', () { - final container = createContainer( + final container = ProviderContainer.test( overrides: [ publicProvider.overrideWith((ref) => 'Hello world'), ], @@ -77,25 +180,30 @@ void main() { }); test('Supports overriding family providers', () { - final container = createContainer( + final container = ProviderContainer.test( overrides: [ - familyProvider(42, third: .42).overrideWith( - (ref) => 'Hello world ${ref.first} ${ref.second} ' - '${ref.third} ${ref.fourth} ${ref.fifth}', + familyProvider.overrideWith( + (ref, args) => 'Hello world ${args.$1} ${args.second} ' + '${args.third} ${args.fourth} ${args.fifth}', ), + familyProvider(42, third: .42).overrideWith((ref) => 'Override'), ], ); - final result = container.read(familyProvider(42, third: .42)); - expect(result, 'Hello world 42 null 0.42 true null'); + expect( + container.read(familyProvider(21, third: .21)), + 'Hello world 21 null 0.21 true null', + ); + + expect(container.read(familyProvider(42, third: .42)), 'Override'); }); test( 'Creates a Provider if @riverpod is used on an stream function wrapped in Raw', () async { - final container = createContainer(); + final container = ProviderContainer.test(); - final AutoDisposeProvider> provider = rawStreamProvider; + const ProviderBase> provider = rawStreamProvider; final Stream result = container.read(rawStreamProvider); await expectLater(result, emits('Hello world')); @@ -103,9 +211,9 @@ void main() { test('Creates a Provider if @riverpod is used on a synchronous function', () { - final container = createContainer(); + final container = ProviderContainer.test(); - final AutoDisposeProvider provider = publicProvider; + const ProviderBase provider = publicProvider; final String result = container.read(publicProvider); expect(result, 'Hello world'); @@ -122,7 +230,7 @@ void main() { test( 'Creates a Provider.family if @riverpod is used on a synchronous function with parameters', () { - final container = createContainer(); + final container = ProviderContainer.test(); const FamilyFamily family = familyProvider; @@ -147,8 +255,6 @@ void main() { familyProvider( 42, third: .42, - // ignore: avoid_redundant_argument_values - fourth: true, ), ); expect( @@ -156,8 +262,6 @@ void main() { familyProvider( 42, third: .42, - // ignore: avoid_redundant_argument_values - fourth: true, ).hashCode, ); @@ -166,15 +270,29 @@ void main() { second: 'x42', third: .42, fourth: false, - fifth: ['x42'], + fifth: const ['x42'], ); - final AutoDisposeProvider futureProvider = provider; - expect(provider.first, 42); - expect(provider.second, 'x42'); - expect(provider.third, .42); - expect(provider.fourth, false); - expect(provider.fifth, ['x42']); + final ProviderBase futureProvider = provider; + + final argument = provider.argument! as ( + int, { + String? second, + double third, + bool fourth, + List? fifth, + }); + + expect( + argument, + ( + 42, + second: 'x42', + third: .42, + fourth: false, + fifth: const ['x42'], + ), + ); final String result = container.read( familyProvider( @@ -191,4 +309,54 @@ void main() { '(first: 42, second: x42, third: 0.42, fourth: false, fifth: [x42])', ); }); + + test('can override providers', () { + final container = ProviderContainer.test( + overrides: [ + publicProvider.overrideWith((ref) => 'test'), + publicClassProvider.overrideWith(() => PublicClass(42)), + familyProvider.overrideWith( + (ref, args) => + 'test (first: ${args.$1}, second: ${args.second}, third: ${args.third}, fourth: ${args.fourth}, fifth: ${args.fifth})', + ), + familyProvider(21, third: .21).overrideWithValue('Override'), + familyClassProvider.overrideWith(FamilyClass.new), + ], + ); + final container2 = ProviderContainer.test( + overrides: [ + publicClassProvider.overrideWithBuild((ref, notifier) => 'Hello world'), + familyClassProvider.overrideWithBuild((ref, notifier, args) { + return 'FamilyClass$args'; + }), + familyClassProvider(21, third: .21).overrideWithBuild((ref, notifier) { + return 'Override'; + }), + ], + ); + + expect(container.read(publicProvider), 'test'); + expect(container.read(publicClassProvider.notifier).param, 42); + expect( + container.read(familyProvider(42, second: '42', third: .42)), + 'test (first: 42, second: 42, third: 0.42, fourth: true, fifth: null)', + ); + expect(container.read(familyProvider(21, third: .21)), 'Override'); + expect( + container + .read(familyClassProvider(42, second: '42', third: .42).notifier) + .param, + (42, second: '42', third: 0.42, fourth: true, fifth: null), + ); + + expect(container2.read(publicClassProvider), 'Hello world'); + expect( + container2.read(familyClassProvider(42, third: .42)), + 'FamilyClass(42, fifth: null, fourth: true, second: null, third: 0.42)', + ); + expect( + container2.read(familyClassProvider(21, third: .21)), + 'Override', + ); + }); } diff --git a/packages/riverpod_generator/test/utils.dart b/packages/riverpod_generator/test/utils.dart deleted file mode 100644 index 57e3436da..000000000 --- a/packages/riverpod_generator/test/utils.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:riverpod/riverpod.dart'; -import 'package:test/test.dart'; - -ProviderContainer createContainer({ - ProviderContainer? parent, - List overrides = const [], - List? observers, -}) { - final container = ProviderContainer( - parent: parent, - overrides: overrides, - observers: observers, - ); - addTearDown(container.dispose); - return container; -} diff --git a/packages/riverpod_graph/pubspec.yaml b/packages/riverpod_graph/pubspec.yaml index eb8efbcbe..228dac230 100644 --- a/packages/riverpod_graph/pubspec.yaml +++ b/packages/riverpod_graph/pubspec.yaml @@ -8,7 +8,7 @@ funding: - https://github.com/sponsors/rrousselGit/ environment: - sdk: ">=2.15.0 <4.0.0" + sdk: ">=3.0.0<4.0.0" dependencies: analyzer: ^7.0.0 diff --git a/packages/riverpod_graph/test/integration/addition/golden/lib/constructor_providers.dart b/packages/riverpod_graph/test/integration/addition/golden/lib/constructor_providers.dart index dabe06388..c6988e5f2 100644 --- a/packages/riverpod_graph/test/integration/addition/golden/lib/constructor_providers.dart +++ b/packages/riverpod_graph/test/integration/addition/golden/lib/constructor_providers.dart @@ -1,40 +1,30 @@ import 'package:riverpod/riverpod.dart'; -/// standard example Counter class class Counter { - /// docs Counter({this.value = 0}); - /// counter value late final int value; - /// immutable increment Counter increment() { return Counter(value: value + 1); } } -/// -///Extract from example -/// class MarvelRepository { - // ignore: public_member_api_docs MarvelRepository( this.ref, { int Function()? getCurrentTimestamp, }) : _getCurrentTimestamp = getCurrentTimestamp ?? (() => DateTime.now().millisecondsSinceEpoch); - /// final Ref ref; - /// final int Function() _getCurrentTimestamp; } /// taken from the marvel example /// referenced in marvelTearOffConsumer -final marvelRefdProvider = Provider(MarvelRepository.new); +final marvelRefProvider = Provider(MarvelRepository.new); /// not ref'd anywhere final marvelLostProvider = Provider(MarvelRepository.new); @@ -43,7 +33,6 @@ final marvelLostProvider = Provider(MarvelRepository.new); /// /// analyze.dart docs say: Providers must be either top level element or static element of classes. class MarvelLostProviderContainer { - /// creating a reference static final marvelLostProviderInContainer = Provider(MarvelRepository.new); } @@ -53,6 +42,6 @@ final marvelTearOffConsumer = Provider((ref) { // ignore: unused_element void doSomething() { // ignore: unused_local_variable - final theTime = ref.read(marvelRefdProvider)._getCurrentTimestamp; + final theTime = ref.read(marvelRefProvider)._getCurrentTimestamp; } }); diff --git a/packages/riverpod_graph/test/integration/addition/golden/pubspec.yaml b/packages/riverpod_graph/test/integration/addition/golden/pubspec.yaml index cd00bb9ff..96e988906 100644 --- a/packages/riverpod_graph/test/integration/addition/golden/pubspec.yaml +++ b/packages/riverpod_graph/test/integration/addition/golden/pubspec.yaml @@ -2,7 +2,7 @@ name: riverpod_graph_golden_addition publish_to: none environment: - sdk: ">=2.15.0 <3.0.0" + sdk: ">=3.0.0<4.0.0" dependencies: riverpod: diff --git a/packages/riverpod_graph/test/integration/analysis_options.yaml b/packages/riverpod_graph/test/integration/analysis_options.yaml new file mode 100644 index 000000000..063188e3d --- /dev/null +++ b/packages/riverpod_graph/test/integration/analysis_options.yaml @@ -0,0 +1,5 @@ +include: ../../../../analysis_options.yaml + +linter: + rules: + public_member_api_docs: false \ No newline at end of file diff --git a/packages/riverpod_graph/test/integration/consumer_widget/golden/lib/main.dart b/packages/riverpod_graph/test/integration/consumer_widget/golden/lib/main.dart index 5f86352e3..31d508987 100644 --- a/packages/riverpod_graph/test/integration/consumer_widget/golden/lib/main.dart +++ b/packages/riverpod_graph/test/integration/consumer_widget/golden/lib/main.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /// Counter provider. final counterProvider = StateProvider((ref) => 0); diff --git a/packages/riverpod_graph/test/integration/consumer_widget/golden/pubspec.yaml b/packages/riverpod_graph/test/integration/consumer_widget/golden/pubspec.yaml index edcb0f190..d2dca93c0 100644 --- a/packages/riverpod_graph/test/integration/consumer_widget/golden/pubspec.yaml +++ b/packages/riverpod_graph/test/integration/consumer_widget/golden/pubspec.yaml @@ -4,7 +4,7 @@ description: A new Flutter project. publish_to: "none" environment: - sdk: ">=2.17.0 <3.0.0" + sdk: ">=3.0.0<4.0.0" flutter: ">=1.17.0" dependencies: diff --git a/packages/riverpod_graph/test/integration/generated/generated_test.dart b/packages/riverpod_graph/test/integration/generated/generated_test.dart index 90b6897fd..08327553a 100644 --- a/packages/riverpod_graph/test/integration/generated/generated_test.dart +++ b/packages/riverpod_graph/test/integration/generated/generated_test.dart @@ -3,7 +3,6 @@ import 'package:test_process/test_process.dart'; void main() { group('mermaid format', () { - // TODO(ValentinVignal): Support the generated families. test('It should log the structure of the generated project', () async { final process = await TestProcess.start( 'dart', diff --git a/packages/riverpod_graph/test/integration/generated/golden/lib/sync.dart b/packages/riverpod_graph/test/integration/generated/golden/lib/sync.dart index 6d68f2eb3..228a0eff2 100644 --- a/packages/riverpod_graph/test/integration/generated/golden/lib/sync.dart +++ b/packages/riverpod_graph/test/integration/generated/golden/lib/sync.dart @@ -1,4 +1,3 @@ -import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'sync.g.dart'; @@ -30,7 +29,7 @@ String family( } /// A private generated provider. -final AutoDisposeProvider privateProvider = _privateProvider; +const privateProvider = _privateProvider; @riverpod String _private(Ref ref) { @@ -47,7 +46,7 @@ class PublicClass extends _$PublicClass { } /// A private generate provider from a class. -final privateClassProvider = _privateClassProvider; +const privateClassProvider = _privateClassProvider; @riverpod class _PrivateClass extends _$PrivateClass { diff --git a/packages/riverpod_graph/test/integration/generated/golden/lib/sync.g.dart b/packages/riverpod_graph/test/integration/generated/golden/lib/sync.g.dart index 367e8bf5d..5eff2951a 100644 --- a/packages/riverpod_graph/test/integration/generated/golden/lib/sync.g.dart +++ b/packages/riverpod_graph/test/integration/generated/golden/lib/sync.g.dart @@ -6,575 +6,834 @@ part of 'sync.dart'; // RiverpodGenerator // ************************************************************************** -String _$publicHash() => r'94bee36125844f9fe521363bb228632b9f3bfbc7'; - /// A public generated provider. -/// -/// Copied from [public]. @ProviderFor(public) -final publicProvider = AutoDisposeProvider.internal( - public, - name: r'publicProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$publicHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef PublicRef = AutoDisposeProviderRef; -String _$supports$inNamesHash() => r'a883450ddca90a227631fe54d1d9ae305bc558d9'; +const publicProvider = PublicProvider._(); + +/// A public generated provider. +final class PublicProvider extends $FunctionalProvider + with $Provider { + /// A public generated provider. + const PublicProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'publicProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$publicHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + PublicProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return PublicProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? public; + return _$cb(ref); + } +} + +String _$publicHash() => r'94bee36125844f9fe521363bb228632b9f3bfbc7'; /// A generated provider with a '$' in its name. -/// -/// Copied from [supports$inNames]. @ProviderFor(supports$inNames) -final supports$inNamesProvider = AutoDisposeProvider.internal( - supports$inNames, - name: r'supports$inNamesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$supports$inNamesHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef Supports$inNamesRef = AutoDisposeProviderRef; -String _$familyHash() => r'062561e0cad8585939dc9adc23de6452be2c9788'; +const supports$inNamesProvider = Supports$inNamesProvider._(); + +/// A generated provider with a '$' in its name. +final class Supports$inNamesProvider extends $FunctionalProvider + with $Provider { + /// A generated provider with a '$' in its name. + const Supports$inNamesProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'supports$inNamesProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$supports$inNamesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); + @override + Supports$inNamesProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return Supports$inNamesProvider._(create: create); } - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + @override + String create(Ref ref) { + final _$cb = _createCb ?? supports$inNames; + return _$cb(ref); } } +String _$supports$inNamesHash() => r'a883450ddca90a227631fe54d1d9ae305bc558d9'; + /// A generated family provider. -/// -/// Copied from [family]. @ProviderFor(family) -const familyProvider = FamilyFamily(); +const familyProvider = FamilyFamily._(); /// A generated family provider. -/// -/// Copied from [family]. -class FamilyFamily extends Family { +final class FamilyProvider extends $FunctionalProvider + with $Provider { /// A generated family provider. - /// - /// Copied from [family]. - const FamilyFamily(); + const FamilyProvider._( + {required FamilyFamily super.from, + required ( + int, { + String? second, + double third, + bool forth, + List? fifth, + }) + super.argument, + String Function( + Ref ref, + int first, { + String? second, + required double third, + bool forth, + List? fifth, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'familyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// A generated family provider. - /// - /// Copied from [family]. - FamilyProvider call( + final String Function( + Ref ref, int first, { String? second, required double third, - bool forth = true, + bool forth, List? fifth, - }) { - return FamilyProvider( - first, - second: second, - third: third, - forth: forth, - fifth: fifth, - ); - } + })? _createCb; @override - FamilyProvider getProviderOverride( - covariant FamilyProvider provider, - ) { - return call( - provider.first, - second: provider.second, - third: provider.third, - forth: provider.forth, - fifth: provider.fifth, + String debugGetCreateSourceHash() => _$familyHash(); + + @override + String toString() { + return r'familyProvider' + '' + '$argument'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } - static const Iterable? _dependencies = null; + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - Iterable? get dependencies => _dependencies; + FamilyProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return FamilyProvider._( + argument: argument as ( + int, { + String? second, + double third, + bool forth, + List? fifth, + }), + from: from! as FamilyFamily, + create: ( + ref, + int first, { + String? second, + required double third, + bool forth = true, + List? fifth, + }) => + create(ref)); + } - static const Iterable? _allTransitiveDependencies = null; + @override + String create(Ref ref) { + final _$cb = _createCb ?? family; + final argument = this.argument as ( + int, { + String? second, + double third, + bool forth, + List? fifth, + }); + return _$cb( + ref, + argument.$1, + second: argument.second, + third: argument.third, + forth: argument.forth, + fifth: argument.fifth, + ); + } @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + bool operator ==(Object other) { + return other is FamilyProvider && other.argument == argument; + } @override - String? get name => r'familyProvider'; + int get hashCode { + return argument.hashCode; + } } +String _$familyHash() => r'062561e0cad8585939dc9adc23de6452be2c9788'; + /// A generated family provider. -/// -/// Copied from [family]. -class FamilyProvider extends AutoDisposeProvider { +final class FamilyFamily extends Family { + const FamilyFamily._() + : super( + retry: null, + name: r'familyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + /// A generated family provider. - /// - /// Copied from [family]. - FamilyProvider( + FamilyProvider call( int first, { String? second, required double third, bool forth = true, List? fifth, - }) : this._internal( - (ref) => family( - ref as FamilyRef, - first, - second: second, - third: third, - forth: forth, - fifth: fifth, - ), - from: familyProvider, - name: r'familyProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$familyHash, - dependencies: FamilyFamily._dependencies, - allTransitiveDependencies: FamilyFamily._allTransitiveDependencies, - first: first, - second: second, - third: third, - forth: forth, - fifth: fifth, - ); + }) => + FamilyProvider._(argument: ( + first, + second: second, + third: third, + forth: forth, + fifth: fifth, + ), from: this); - FamilyProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.first, - required this.second, - required this.third, - required this.forth, - required this.fifth, - }) : super.internal(); - - final int first; - final String? second; - final double third; - final bool forth; - final List? fifth; + @override + String debugGetCreateSourceHash() => _$familyHash(); @override + String toString() => r'familyProvider'; + + /// {@macro riverpod.override_with} Override overrideWith( - String Function(FamilyRef provider) create, + String Function( + Ref ref, + ( + int, { + String? second, + double third, + bool forth, + List? fifth, + }) args, + ) create, ) { - return ProviderOverride( - origin: this, - override: FamilyProvider._internal( - (ref) => create(ref as FamilyRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - first: first, - second: second, - third: third, - forth: forth, - fifth: fifth, - ), + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyProvider; + + final argument = provider.argument as ( + int, { + String? second, + double third, + bool forth, + List? fifth, + }); + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, ); } +} + +@ProviderFor(_private) +const _privateProvider = _PrivateProvider._(); + +final class _PrivateProvider extends $FunctionalProvider + with $Provider { + const _PrivateProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'_privateProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; @override - AutoDisposeProviderElement createElement() { - return _FamilyProviderElement(this); + String debugGetCreateSourceHash() => _$privateHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); } + @$internal @override - bool operator ==(Object other) { - return other is FamilyProvider && - other.first == first && - other.second == second && - other.third == third && - other.forth == forth && - other.fifth == fifth; - } + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, first.hashCode); - hash = _SystemHash.combine(hash, second.hashCode); - hash = _SystemHash.combine(hash, third.hashCode); - hash = _SystemHash.combine(hash, forth.hashCode); - hash = _SystemHash.combine(hash, fifth.hashCode); + _PrivateProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return _PrivateProvider._(create: create); + } - return _SystemHash.finish(hash); + @override + String create(Ref ref) { + final _$cb = _createCb ?? _private; + return _$cb(ref); } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FamilyRef on AutoDisposeProviderRef { - /// The parameter `first` of this provider. - int get first; +String _$privateHash() => r'4f7b825ffa8a674f01dc8453cb480060a6a7bf5f'; - /// The parameter `second` of this provider. - String? get second; +/// A generated public provider from a class +@ProviderFor(PublicClass) +const publicClassProvider = PublicClassProvider._(); - /// The parameter `third` of this provider. - double get third; +/// A generated public provider from a class +final class PublicClassProvider extends $NotifierProvider { + /// A generated public provider from a class + const PublicClassProvider._( + {super.runNotifierBuildOverride, PublicClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'publicClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// The parameter `forth` of this provider. - bool get forth; + final PublicClass Function()? _createCb; - /// The parameter `fifth` of this provider. - List? get fifth; -} + @override + String debugGetCreateSourceHash() => _$publicClassHash(); -class _FamilyProviderElement extends AutoDisposeProviderElement - with FamilyRef { - _FamilyProviderElement(super.provider); + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + @$internal @override - int get first => (origin as FamilyProvider).first; - @override - String? get second => (origin as FamilyProvider).second; + PublicClass create() => _createCb?.call() ?? PublicClass(); + + @$internal @override - double get third => (origin as FamilyProvider).third; + PublicClassProvider $copyWithCreate( + PublicClass Function() create, + ) { + return PublicClassProvider._(create: create); + } + + @$internal @override - bool get forth => (origin as FamilyProvider).forth; + PublicClassProvider $copyWithBuild( + String Function( + Ref, + PublicClass, + ) build, + ) { + return PublicClassProvider._(runNotifierBuildOverride: build); + } + + @$internal @override - List? get fifth => (origin as FamilyProvider).fifth; + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); } -String _$privateHash() => r'4f7b825ffa8a674f01dc8453cb480060a6a7bf5f'; - -/// See also [_private]. -@ProviderFor(_private) -final _privateProvider = AutoDisposeProvider.internal( - _private, - name: r'_privateProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$privateHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef _PrivateRef = AutoDisposeProviderRef; String _$publicClassHash() => r'c27eae39f455b986e570abb84f1471de7445ef3b'; -/// A generated public provider from a class -/// -/// Copied from [PublicClass]. -@ProviderFor(PublicClass) -final publicClassProvider = - AutoDisposeNotifierProvider.internal( - PublicClass.new, - name: r'publicClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$publicClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$PublicClass = AutoDisposeNotifier; -String _$privateClassHash() => r'3b08af72c6d4f24aed264efcf181572525b75603'; +abstract class _$PublicClass extends $Notifier { + String build(); + @$internal + @override + String runBuild() => build(); +} -/// See also [_PrivateClass]. @ProviderFor(_PrivateClass) -final _privateClassProvider = - AutoDisposeNotifierProvider<_PrivateClass, String>.internal( - _PrivateClass.new, - name: r'_privateClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$privateClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$PrivateClass = AutoDisposeNotifier; -String _$familyClassHash() => r'721bdd2f1ca0d7cee1a0ae476d7bfe93f9ce6875'; +const _privateClassProvider = _PrivateClassProvider._(); + +final class _PrivateClassProvider + extends $NotifierProvider<_PrivateClass, String> { + const _PrivateClassProvider._( + {super.runNotifierBuildOverride, _PrivateClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'_privateClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -abstract class _$FamilyClass extends BuildlessAutoDisposeNotifier { - late final int first; - late final String? second; - late final double third; - late final bool forth; - late final List? fifth; + final _PrivateClass Function()? _createCb; - String build( - int first, { - String? second, - required double third, - bool forth = true, - List? fifth, - }); + @override + String debugGetCreateSourceHash() => _$privateClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + _PrivateClass create() => _createCb?.call() ?? _PrivateClass(); + + @$internal + @override + _PrivateClassProvider $copyWithCreate( + _PrivateClass Function() create, + ) { + return _PrivateClassProvider._(create: create); + } + + @$internal + @override + _PrivateClassProvider $copyWithBuild( + String Function( + Ref, + _PrivateClass, + ) build, + ) { + return _PrivateClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement<_PrivateClass, String> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$privateClassHash() => r'3b08af72c6d4f24aed264efcf181572525b75603'; + +abstract class _$PrivateClass extends $Notifier { + String build(); + @$internal + @override + String runBuild() => build(); } /// A generated family provider from a class. -/// -/// Copied from [FamilyClass]. @ProviderFor(FamilyClass) -const familyClassProvider = FamilyClassFamily(); +const familyClassProvider = FamilyClassFamily._(); /// A generated family provider from a class. -/// -/// Copied from [FamilyClass]. -class FamilyClassFamily extends Family { +final class FamilyClassProvider extends $NotifierProvider { /// A generated family provider from a class. - /// - /// Copied from [FamilyClass]. - const FamilyClassFamily(); + const FamilyClassProvider._( + {required FamilyClassFamily super.from, + required ( + int, { + String? second, + double third, + bool forth, + List? fifth, + }) + super.argument, + super.runNotifierBuildOverride, + FamilyClass Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'familyClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// A generated family provider from a class. - /// - /// Copied from [FamilyClass]. - FamilyClassProvider call( - int first, { - String? second, - required double third, - bool forth = true, - List? fifth, - }) { - return FamilyClassProvider( - first, - second: second, - third: third, - forth: forth, - fifth: fifth, + final FamilyClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$familyClassHash(); + + @override + String toString() { + return r'familyClassProvider' + '' + '$argument'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } + @$internal + @override + FamilyClass create() => _createCb?.call() ?? FamilyClass(); + + @$internal @override - FamilyClassProvider getProviderOverride( - covariant FamilyClassProvider provider, + FamilyClassProvider $copyWithCreate( + FamilyClass Function() create, ) { - return call( - provider.first, - second: provider.second, - third: provider.third, - forth: provider.forth, - fifth: provider.fifth, - ); + return FamilyClassProvider._( + argument: argument as ( + int, { + String? second, + double third, + bool forth, + List? fifth, + }), + from: from! as FamilyClassFamily, + create: create); } - static const Iterable? _dependencies = null; - + @$internal @override - Iterable? get dependencies => _dependencies; + FamilyClassProvider $copyWithBuild( + String Function( + Ref, + FamilyClass, + ) build, + ) { + return FamilyClassProvider._( + argument: argument as ( + int, { + String? second, + double third, + bool forth, + List? fifth, + }), + from: from! as FamilyClassFamily, + runNotifierBuildOverride: build); + } - static const Iterable? _allTransitiveDependencies = null; + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + bool operator ==(Object other) { + return other is FamilyClassProvider && other.argument == argument; + } @override - String? get name => r'familyClassProvider'; + int get hashCode { + return argument.hashCode; + } } +String _$familyClassHash() => r'721bdd2f1ca0d7cee1a0ae476d7bfe93f9ce6875'; + /// A generated family provider from a class. -/// -/// Copied from [FamilyClass]. -class FamilyClassProvider - extends AutoDisposeNotifierProviderImpl { +final class FamilyClassFamily extends Family { + const FamilyClassFamily._() + : super( + retry: null, + name: r'familyClassProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + /// A generated family provider from a class. - /// - /// Copied from [FamilyClass]. - FamilyClassProvider( + FamilyClassProvider call( int first, { String? second, required double third, bool forth = true, List? fifth, - }) : this._internal( - () => FamilyClass() - ..first = first - ..second = second - ..third = third - ..forth = forth - ..fifth = fifth, - from: familyClassProvider, - name: r'familyClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$familyClassHash, - dependencies: FamilyClassFamily._dependencies, - allTransitiveDependencies: - FamilyClassFamily._allTransitiveDependencies, - first: first, - second: second, - third: third, - forth: forth, - fifth: fifth, - ); - - FamilyClassProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.first, - required this.second, - required this.third, - required this.forth, - required this.fifth, - }) : super.internal(); - - final int first; - final String? second; - final double third; - final bool forth; - final List? fifth; - - @override - String runNotifierBuild( - covariant FamilyClass notifier, - ) { - return notifier.build( - first, - second: second, - third: third, - forth: forth, - fifth: fifth, - ); - } - - @override - Override overrideWith(FamilyClass Function() create) { - return ProviderOverride( - origin: this, - override: FamilyClassProvider._internal( - () => create() - ..first = first - ..second = second - ..third = third - ..forth = forth - ..fifth = fifth, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - first: first, + }) => + FamilyClassProvider._(argument: ( + first, second: second, third: third, forth: forth, fifth: fifth, - ), - ); - } + ), from: this); @override - AutoDisposeNotifierProviderElement createElement() { - return _FamilyClassProviderElement(this); - } + String debugGetCreateSourceHash() => _$familyClassHash(); @override - bool operator ==(Object other) { - return other is FamilyClassProvider && - other.first == first && - other.second == second && - other.third == third && - other.forth == forth && - other.fifth == fifth; - } + String toString() => r'familyClassProvider'; - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, first.hashCode); - hash = _SystemHash.combine(hash, second.hashCode); - hash = _SystemHash.combine(hash, third.hashCode); - hash = _SystemHash.combine(hash, forth.hashCode); - hash = _SystemHash.combine(hash, fifth.hashCode); + /// {@macro riverpod.override_with} + Override overrideWith( + FamilyClass Function( + ( + int, { + String? second, + double third, + bool forth, + List? fifth, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyClassProvider; + + final argument = provider.argument as ( + int, { + String? second, + double third, + bool forth, + List? fifth, + }); + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } - return _SystemHash.finish(hash); + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + String Function( + Ref ref, + FamilyClass notifier, + ( + int, { + String? second, + double third, + bool forth, + List? fifth, + }) argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyClassProvider; + + final argument = provider.argument as ( + int, { + String? second, + double third, + bool forth, + List? fifth, + }); + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FamilyClassRef on AutoDisposeNotifierProviderRef { - /// The parameter `first` of this provider. - int get first; - - /// The parameter `second` of this provider. - String? get second; +abstract class _$FamilyClass extends $Notifier { + late final _$args = ref.$arg as ( + int, { + String? second, + double third, + bool forth, + List? fifth, + }); + int get first => _$args.$1; + String? get second => _$args.second; + double get third => _$args.third; + bool get forth => _$args.forth; + List? get fifth => _$args.fifth; - /// The parameter `third` of this provider. - double get third; + String build( + int first, { + String? second, + required double third, + bool forth = true, + List? fifth, + }); + @$internal + @override + String runBuild() => build( + _$args.$1, + second: _$args.second, + third: _$args.third, + forth: _$args.forth, + fifth: _$args.fifth, + ); +} - /// The parameter `forth` of this provider. - bool get forth; +/// A generated provider from a class with a '$' in its name. +@ProviderFor(Supports$InClassName) +const supports$InClassNameProvider = Supports$InClassNameProvider._(); - /// The parameter `fifth` of this provider. - List? get fifth; -} +/// A generated provider from a class with a '$' in its name. +final class Supports$InClassNameProvider + extends $NotifierProvider { + /// A generated provider from a class with a '$' in its name. + const Supports$InClassNameProvider._( + {super.runNotifierBuildOverride, Supports$InClassName Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'supports$InClassNameProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -class _FamilyClassProviderElement - extends AutoDisposeNotifierProviderElement - with FamilyClassRef { - _FamilyClassProviderElement(super.provider); + final Supports$InClassName Function()? _createCb; @override - int get first => (origin as FamilyClassProvider).first; + String debugGetCreateSourceHash() => _$supports$InClassNameHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal @override - String? get second => (origin as FamilyClassProvider).second; + Supports$InClassName create() => _createCb?.call() ?? Supports$InClassName(); + + @$internal @override - double get third => (origin as FamilyClassProvider).third; + Supports$InClassNameProvider $copyWithCreate( + Supports$InClassName Function() create, + ) { + return Supports$InClassNameProvider._(create: create); + } + + @$internal @override - bool get forth => (origin as FamilyClassProvider).forth; + Supports$InClassNameProvider $copyWithBuild( + String Function( + Ref, + Supports$InClassName, + ) build, + ) { + return Supports$InClassNameProvider._(runNotifierBuildOverride: build); + } + + @$internal @override - List? get fifth => (origin as FamilyClassProvider).fifth; + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); } String _$supports$InClassNameHash() => r'dd23b01994664e5a2c22ba3a61f3b23d2128861b'; -/// A generated provider from a class with a '$' in its name. -/// -/// Copied from [Supports$InClassName]. -@ProviderFor(Supports$InClassName) -final supports$InClassNameProvider = - AutoDisposeNotifierProvider.internal( - Supports$InClassName.new, - name: r'supports$InClassNameProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$supports$InClassNameHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Supports$InClassName = AutoDisposeNotifier; +abstract class _$Supports$InClassName extends $Notifier { + String build(); + @$internal + @override + String runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_graph/test/integration/generated/golden/pubspec_overrides.yaml b/packages/riverpod_graph/test/integration/generated/golden/pubspec_overrides.yaml index 8406dd743..396e50934 100644 --- a/packages/riverpod_graph/test/integration/generated/golden/pubspec_overrides.yaml +++ b/packages/riverpod_graph/test/integration/generated/golden/pubspec_overrides.yaml @@ -1,12 +1,12 @@ # melos_managed_dependency_overrides: flutter_riverpod,riverpod,riverpod_annotation,riverpod_generator dependency_overrides: - flutter_riverpod: + flutter_riverpod: path: ../../../../../flutter_riverpod - riverpod: + riverpod: path: ../../../../../riverpod riverpod_analyzer_utils: path: ../../../../../../packages/riverpod_analyzer_utils - riverpod_annotation: + riverpod_annotation: path: ../../../../../riverpod_annotation - riverpod_generator: + riverpod_generator: path: ../../../../../riverpod_generator diff --git a/packages/riverpod_lint/CHANGELOG.md b/packages/riverpod_lint/CHANGELOG.md index 9d9294c27..8d2cf1cfa 100644 --- a/packages/riverpod_lint/CHANGELOG.md +++ b/packages/riverpod_lint/CHANGELOG.md @@ -1,3 +1,49 @@ +## Unreleased build + +- All lints/assists now automatically add the relevant imports when + updating code. +- Updated `provider_dependencies` to support `@Dependencies` +- added `riverpod_syntax_error`, for reporting errors when the generator would throw. +- added `avoid_keep_alive_dependency_inside_auto_dispose` +- added `unknown_scoped_usage`, for reporting when a scoped provider is used but the ref could not be found. +- added automatic migration to import `package:riverpod/legacy.dart` for corresponding providers. +- Fix `provider_parameters` for objects using mixins. + +- **Breaking**: No-longer exports various providers + from `package:riverpod`. + +Various lints had their severity changed: + +- `avoid_build_context_in_providers` is now an INFO +- `avoid_ref_inside_state_dispose` is now a WARNING +- `functional_ref` is now a WARNING +- `notifier_build` is now an error. +- `missing_provider_scope` is now a WARNING +- `provider_dependencies` is now a WARNING +- `scoped_providers_should_specify_dependencies` is now a WARNING +- `notifier_extends` is now a WARNING +- `provider_parameters` is now a WARNING + +## 3.0.0-dev.4 - 2023-11-27 + +- `riverpod` upgraded to `3.0.0-dev.3` + +## 3.0.0-dev.3 - 2023-11-20 + +- `riverpod` upgraded to `3.0.0-dev.2` + +## 3.0.0-dev.2 - 2023-11-20 + +- Fix crash when encountering classes with a `ProviderBase` field. + +## 3.0.0-dev.1 - 2023-10-30 + +- `riverpod_analyzer_utils` upgraded to `1.0.0-dev.0` + +## 3.0.0-dev.0 - 2023-10-29 + +- `riverpod` upgraded to `3.0.0-dev.0` + ## 2.6.4 - 2025-01-08 Support latest analyzer @@ -68,7 +114,7 @@ Bump custom_lint ## 2.3.4 - 2023-11-13 -- Updated `scoped_providers_should_specify_dependencies` to ignore instances of using pumpWidget in tests (thanks to [lockieRichter](https://github.com/lockieRichter)) +- Updated `scoped_providers_should_specify_dependencies` to ignore instances of using pumpWidget in tests (thanks to @lockieRichter) ## 2.3.3 - 2023-10-28 @@ -85,13 +131,15 @@ Bump custom_lint ## 2.3.0 - 2023-10-06 -- Added `async_value_nullable_patttern` lint, to warn against using `AsyncValue(:final value?)` in pattern match when `value` is possibly nullable. +- Added `async_value_nullable_pattern` lint, to warn against using `AsyncValue(:final value?)` in pattern match when `value` is possibly nullable. - Added `protected_notifier_state` lint, which warns against using the `Notifier.state` property of a notifier different than the current one. Aka a Notifier "A" should not directly access the `state` if a Notifier "B". ## 2.2.1 - 2023-10-02 +- Updated `functional_ref` and `generator_class_extends` to support providers + with generic parameters. - Fixed `functional_ref` throwing if a provider specifies arguments but incorrectly did not specify a Ref @@ -242,3 +290,5 @@ Fix quick-fix for provider_dependencies ## 1.0.0 Initial release + + diff --git a/packages/riverpod_lint/README.md b/packages/riverpod_lint/README.md index b71f329b7..3a23b0dbb 100644 --- a/packages/riverpod_lint/README.md +++ b/packages/riverpod_lint/README.md @@ -51,7 +51,9 @@ Riverpod_lint adds various warnings with quick fixes and refactoring options, su - [functional\_ref (riverpod\_generator only)](#functional_ref-riverpod_generator-only) - [notifier\_extends (riverpod\_generator only)](#notifier_extends-riverpod_generator-only) - [avoid\_ref\_inside\_state\_dispose](#avoid_ref_inside_state_dispose) + - [avoid\_keep\_alive\_dependency\_inside\_auto\_dispose (riverpod\_generator only)](#avoid_keep_alive_dependency_inside_auto_dispose-riverpod_generator-only) - [notifier\_build (riverpod\_generator only)](#notifier_build-riverpod_generator-only) +- [riverpod\_syntax\_error (riverpod\_generator only)](#riverpod_syntax_error-riverpod_generator-only) - [async\_value\_nullable\_patttern](#async_value_nullable_patttern) - [protected\_notifier\_properties](#protected_notifier_properties) - [All assists](#all-assists) @@ -61,7 +63,8 @@ Riverpod_lint adds various warnings with quick fixes and refactoring options, su - [Convert widget to `ConsumerStatefulWidget`](#convert-widget-to-consumerstatefulwidget) - [Convert functional `@riverpod` to class variant](#convert-functional-riverpod-to-class-variant) - [Convert class `@riverpod` to functional variant](#convert-class-riverpod-to-functional-variant) -- [Upcoming content:](#upcoming-content) +- [Migrations](#migrations) + - [missing\_legacy\_import](#missing_legacy_import) ## Installing riverpod_lint @@ -229,6 +232,28 @@ void example(Ref ref) { // scopedProvider is scoped and as such specifying "dependencies" is required. ref.watch(scopedProvider); } + +// For non-provider objects that use scoped providers, we can use `@Dependencies` +// for similar purposes. +@Dependencies([scoped]) +class BookView extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + final selectedBookID = ref.watch(scopedProvider); + return Text(selectedBookID.toString()); + } +} + +// Alternatively, widgets specifically can opt to override scoped providers +// using `ProviderScope`: +ProviderScope( + overrides: [ + scopedProvider.overrideWithValue(42), + ], + // Even though BookView uses "scopedProvider", the linter won't complain + // as we override the provider. + child: BookView(), +) ``` **Bad**: @@ -249,6 +274,15 @@ void example(Ref ref) { // rootProvider is not a scoped provider. As such it shouldn't be listed in "dependencies" ref.watch(rootProvider); } + +class BookView extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + // If a function/class uses a scoped provider, they must specify `@Dependencies` + final selectedBookID = ref.watch(scopedProvider); + return Text(selectedBookID.toString()); + } +} ``` ### scoped_providers_should_specify_dependencies (generator only) @@ -588,6 +622,26 @@ class _MyWidgetState extends ConsumerState { } ``` +### avoid_keep_alive_dependency_inside_auto_dispose (riverpod_generator only) + +Warn when a `keepAlive` provider tries to use a non-`keepAlive` provider. + +This is discouraged because such relationship would cause the non-`keepAlive` provider +to behave as a `keepAlive`, even though it isn't marked as such. + +**Bad**: + +```dart +@riverpod +int nonKeepAlive(Ref ref) => 0; + +@Riverpod(keepAlive: true) +int fn(Ref ref) { + // `keepAlive` providers should only depend on keepAlive providers. + ref.watch(nonKeepAliveProvider); +} +``` + ### notifier_build (riverpod_generator only) Classes annotated by `@riverpod` must have the `build` method. @@ -611,6 +665,33 @@ class Example extends _$Example { class Example extends _$Example {} ``` +## riverpod_syntax_error (riverpod_generator only) + +A general purpose lint, for when an exception is detected +in `riverpod_generator`. This reports the error in the relevant file. + +For example, an error will be shown if a "notifier" is abstract: + +**Good**: + +```dart +@riverpod +class Example extends _$Example { + @override + int build() => 0; +} +``` + +**Bad**: + +```dart +@riverpod +abstract class Example extends _$Example { + @override + int build() => 0; +} +``` + ### async_value_nullable_patttern Warn if the pattern `AsyncValue(:final value?)` is used when the data @@ -722,12 +803,24 @@ class B extends _$B { ![Convert provider to functional variant sample](https://raw.githubusercontent.com/rrousselGit/riverpod/master/packages/riverpod_lint/resources/convert_to_functional_provider.gif) -## Upcoming content: +## Migrations + +Migrations are a list of warnings with an automatic quick-fix, to help +upgrading to higher Riverpod versions. +They are designed to be used only once. + +As a general rule, it is recommended to apply migration by running the following +in your terminal: + +```sh +dart run custom_lint --fix +``` + +### missing_legacy_import -- Warn if a provider's `dependencies` parameter doesn't match the `ref.watch/read/listen` usages. -- Refactoring to convert AsyncNotifier<>Notifier + autoDispose/family variants -- Warn if an `AsyncNotifierProvider.autoDispose` doesn't use an `AutoDisposeAsyncNotifier` +As part of Riverpod 3.0, `StateProvider`, `StateNotifierProvider`, `StateNotifier` and `ChangeNotifierProvider` are moved out of `package:riverpod/riverpod.dart` to +`package:riverpod/legacy.dart`. -and much more +This migration will automatically adds the missing import. [custom_lint]: https://pub.dev/packages/custom_lint diff --git a/packages/riverpod_lint/lib/riverpod_lint.dart b/packages/riverpod_lint/lib/riverpod_lint.dart index f4e65de63..1703b1e3b 100644 --- a/packages/riverpod_lint/lib/riverpod_lint.dart +++ b/packages/riverpod_lint/lib/riverpod_lint.dart @@ -16,31 +16,42 @@ import 'src/lints/functional_ref.dart'; import 'src/lints/missing_provider_scope.dart'; import 'src/lints/notifier_build.dart'; import 'src/lints/notifier_extends.dart'; +import 'src/lints/only_use_keep_alive_inside_keep_alive.dart'; import 'src/lints/protected_notifier_properties.dart'; import 'src/lints/provider_dependencies.dart'; import 'src/lints/provider_parameters.dart'; +import 'src/lints/riverpod_syntax_error.dart'; import 'src/lints/scoped_providers_should_specify_dependencies.dart'; +import 'src/lints/unknown_scoped_usage.dart'; import 'src/lints/unsupported_provider_value.dart'; +import 'src/migration/missing_legacy_import.dart'; +import 'src/riverpod_custom_lint.dart'; PluginBase createPlugin() => _RiverpodPlugin(); class _RiverpodPlugin extends PluginBase { @override - List getLintRules(CustomLintConfigs configs) => [ + List getLintRules(CustomLintConfigs configs) => [ + const AsyncValueNullablePattern(), const AvoidBuildContextInProviders(), + const OnlyUseKeepAliveInsideKeepAlive(), + const AvoidManualProvidersAsGeneratedProviderDependency(), const AvoidPublicNotifierProperties(), + const AvoidRefInsideStateDispose(), const FunctionalRef(), const MissingProviderScope(), - const ProviderParameters(), + const NotifierBuild(), const NotifierExtends(), + const ProtectedNotifierProperties(), const ProviderDependencies(), - const AvoidManualProvidersAsGeneratedProviderDependency(), + const ProviderParameters(), + const RiverpodSyntaxError(), const ScopedProvidersShouldSpecifyDependencies(), + const UnknownScopedUsage(), const UnsupportedProviderValue(), - const AvoidRefInsideStateDispose(), - const NotifierBuild(), - const AsyncValueNullablePattern(), - const ProtectedNotifierProperties(), + + // Migrations + const MissingLegacyImport(), ]; @override diff --git a/packages/riverpod_lint/lib/src/assists/class_based_to_functional_provider.dart b/packages/riverpod_lint/lib/src/assists/class_based_to_functional_provider.dart index 900915161..6edb18d30 100644 --- a/packages/riverpod_lint/lib/src/assists/class_based_to_functional_provider.dart +++ b/packages/riverpod_lint/lib/src/assists/class_based_to_functional_provider.dart @@ -50,12 +50,28 @@ class ClassBasedToFunctionalProvider extends RiverpodAssist { declaration.node.name.lexeme.lowerFirst, ); + var typeParametersSource = ''; + final typeParameters = declaration.node.typeParameters; + if (typeParameters != null) { + // Obtain the source of type parameters + typeParametersSource = resolver.source.contents.data.substring( + typeParameters.offset, + typeParameters.end, + ); + + // Make the function generic if the class was generic + builder.addSimpleInsertion( + declaration.buildMethod.name.end, + typeParametersSource, + ); + } + final parameters = declaration.buildMethod.parameters!; final trailingRefParameter = parameters.parameters.isEmpty ? '' : ', '; // Add ref parameter to the build method builder.addSimpleInsertion( parameters.leftParenthesis.end, - '${refNameFor(declaration)} ref$trailingRefParameter', + '${refNameFor(declaration)}$typeParametersSource ref$trailingRefParameter', ); // Remove anything after the build method diff --git a/packages/riverpod_lint/lib/src/assists/convert_to_stateful_base_widget.dart b/packages/riverpod_lint/lib/src/assists/convert_to_stateful_base_widget.dart index 8647482b9..93c81e4bc 100644 --- a/packages/riverpod_lint/lib/src/assists/convert_to_stateful_base_widget.dart +++ b/packages/riverpod_lint/lib/src/assists/convert_to_stateful_base_widget.dart @@ -8,6 +8,7 @@ import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dar import 'package:collection/collection.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; +import '../imports.dart'; import '../riverpod_custom_lint.dart'; import 'convert_to_widget_utils.dart'; @@ -72,7 +73,7 @@ class ConvertToStatefulBaseWidget extends RiverpodAssist { ExtendsClause node, ) { final changeBuilder = reporter.createChangeBuilder( - message: 'Convert to ${targetWidget.widgetName}', + message: 'Convert to ${targetWidget.widgetAssistName}', priority: targetWidget.priority, ); @@ -80,7 +81,7 @@ class ConvertToStatefulBaseWidget extends RiverpodAssist { // Change the extended base class builder.addSimpleReplacement( node.superclass.sourceRange, - targetWidget.widgetName, + targetWidget.widgetName(builder), ); final widgetClass = node.thisOrAncestorOfType(); @@ -143,12 +144,10 @@ class ConvertToStatefulBaseWidget extends RiverpodAssist { switch (targetWidget) { case StatefulBaseWidgetType.consumerStatefulWidget: case StatefulBaseWidgetType.statefulHookConsumerWidget: - baseStateName = 'ConsumerState'; - break; + baseStateName = builder.importConsumerState(); case StatefulBaseWidgetType.statefulHookWidget: case StatefulBaseWidgetType.statefulWidget: - baseStateName = 'State'; - break; + baseStateName = builder.importState(); } // Split the class into two classes right before the build method @@ -180,7 +179,7 @@ class $createdStateClassName extends $baseStateName<${widgetClass.name}> { required int priorityAdjustment, }) { final changeBuilder = reporter.createChangeBuilder( - message: 'Convert to ${targetWidget.widgetName}', + message: 'Convert to ${targetWidget.widgetAssistName}', priority: targetWidget.priority + priorityAdjustment, ); @@ -188,7 +187,7 @@ class $createdStateClassName extends $baseStateName<${widgetClass.name}> { // Change the extended base class builder.addSimpleReplacement( node.superclass.sourceRange, - targetWidget.widgetName, + targetWidget.widgetName(builder), ); final widgetClass = node.thisOrAncestorOfType(); @@ -201,12 +200,10 @@ class $createdStateClassName extends $baseStateName<${widgetClass.name}> { switch (targetWidget) { case StatefulBaseWidgetType.consumerStatefulWidget: case StatefulBaseWidgetType.statefulHookConsumerWidget: - baseStateName = 'ConsumerState'; - break; + baseStateName = builder.importConsumerState(); case StatefulBaseWidgetType.statefulHookWidget: case StatefulBaseWidgetType.statefulWidget: - baseStateName = 'State'; - break; + baseStateName = builder.importState(); } final createStateMethod = widgetClass.members @@ -235,7 +232,7 @@ class $createdStateClassName extends $baseStateName<${widgetClass.name}> { } } -// Original implemenation in +// Original implementation in // package:analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateful_widget.dart class _FieldFinder extends RecursiveAstVisitor { Set fieldsAssignedInConstructors = {}; diff --git a/packages/riverpod_lint/lib/src/assists/convert_to_stateless_base_widget.dart b/packages/riverpod_lint/lib/src/assists/convert_to_stateless_base_widget.dart index 372d0e0d1..a0aaddc5e 100644 --- a/packages/riverpod_lint/lib/src/assists/convert_to_stateless_base_widget.dart +++ b/packages/riverpod_lint/lib/src/assists/convert_to_stateless_base_widget.dart @@ -7,6 +7,7 @@ import 'package:analyzer/source/source_range.dart'; import 'package:collection/collection.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; +import '../imports.dart'; import '../riverpod_custom_lint.dart'; import 'convert_to_widget_utils.dart'; @@ -74,7 +75,7 @@ class ConvertToStatelessBaseWidget extends RiverpodAssist { ExtendsClause node, ) { final changeBuilder = reporter.createChangeBuilder( - message: 'Convert to ${targetWidget.widgetName}', + message: 'Convert to ${targetWidget.assistName}', priority: targetWidget.priority, ); @@ -82,7 +83,7 @@ class ConvertToStatelessBaseWidget extends RiverpodAssist { // Change the extended base class builder.addSimpleReplacement( node.superclass.sourceRange, - targetWidget.widgetName, + targetWidget.widgetName(builder), ); final buildMethod = node @@ -99,14 +100,15 @@ class ConvertToStatelessBaseWidget extends RiverpodAssist { switch (targetWidget) { case StatelessBaseWidgetType.consumerWidget: case StatelessBaseWidgetType.hookConsumerWidget: + final widgetRef = builder.importWidgetRef(); + // If the build method has not a ref, add it if (buildParams.parameters.length == 1) { builder.addSimpleInsertion( buildParams.parameters.last.end, - ', WidgetRef ref', + ', $widgetRef ref', ); } - break; case StatelessBaseWidgetType.hookWidget: case StatelessBaseWidgetType.statelessWidget: // If the build method has a ref, remove it @@ -118,7 +120,6 @@ class ConvertToStatelessBaseWidget extends RiverpodAssist { ), ); } - break; } }); } @@ -130,7 +131,7 @@ class ConvertToStatelessBaseWidget extends RiverpodAssist { required int priorityAdjustment, }) { final changeBuilder = reporter.createChangeBuilder( - message: 'Convert to ${targetWidget.widgetName}', + message: 'Convert to ${targetWidget.assistName}', priority: targetWidget.priority + priorityAdjustment, ); @@ -138,7 +139,7 @@ class ConvertToStatelessBaseWidget extends RiverpodAssist { // Change the extended base class builder.addSimpleReplacement( node.superclass.sourceRange, - targetWidget.widgetName, + targetWidget.widgetName(builder), ); final widgetClass = node.thisOrAncestorOfType(); @@ -254,18 +255,17 @@ class ConvertToStatelessBaseWidget extends RiverpodAssist { switch (targetWidget) { case StatelessBaseWidgetType.consumerWidget: case StatelessBaseWidgetType.hookConsumerWidget: + final widgetRef = builder.importWidgetRef(); builder.addSimpleReplacement( parameterRange, - 'BuildContext context, WidgetRef ref', + 'BuildContext context, $widgetRef ref', ); - break; case StatelessBaseWidgetType.hookWidget: case StatelessBaseWidgetType.statelessWidget: builder.addSimpleReplacement( parameterRange, 'BuildContext context', ); - break; } }); } @@ -279,7 +279,7 @@ class ConvertToStatelessBaseWidget extends RiverpodAssist { } } -// Original implemenation in +// Original implementation in // package:analysis_server/lib/src/services/correction/dart/flutter_convert_to_stateless_widget.dart class _FieldFinder extends RecursiveAstVisitor { final fieldsAssignedInConstructors = {}; diff --git a/packages/riverpod_lint/lib/src/assists/convert_to_widget_utils.dart b/packages/riverpod_lint/lib/src/assists/convert_to_widget_utils.dart index 9f0093eb5..a75c299c9 100644 --- a/packages/riverpod_lint/lib/src/assists/convert_to_widget_utils.dart +++ b/packages/riverpod_lint/lib/src/assists/convert_to_widget_utils.dart @@ -1,14 +1,14 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; -// ignore: implementation_imports, somehow not exported by analyzer +import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart'; import 'package:collection/collection.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; +import '../imports.dart'; import '../object_utils.dart'; enum StatelessBaseWidgetType { hookConsumerWidget( - widgetName: 'HookConsumerWidget', priority: 37, typeChecker: TypeChecker.fromName( 'HookConsumerWidget', @@ -17,7 +17,6 @@ enum StatelessBaseWidgetType { requiredPackage: 'hooks_riverpod', ), hookWidget( - widgetName: 'HookWidget', priority: 36, typeChecker: TypeChecker.fromName( 'HookWidget', @@ -26,7 +25,6 @@ enum StatelessBaseWidgetType { requiredPackage: 'flutter_hooks', ), consumerWidget( - widgetName: 'ConsumerWidget', priority: 35, typeChecker: TypeChecker.fromName( 'ConsumerWidget', @@ -34,7 +32,6 @@ enum StatelessBaseWidgetType { ), ), statelessWidget( - widgetName: 'StatelessWidget', priority: 34, typeChecker: TypeChecker.fromName( 'StatelessWidget', @@ -44,20 +41,39 @@ enum StatelessBaseWidgetType { ; const StatelessBaseWidgetType({ - required this.widgetName, required this.priority, required this.typeChecker, this.requiredPackage, }); - final String widgetName; + final int priority; final TypeChecker typeChecker; final String? requiredPackage; + + String widgetName(DartFileEditBuilder builder) { + return switch (this) { + StatelessBaseWidgetType.hookConsumerWidget => + builder.importHookConsumerWidget(), + StatelessBaseWidgetType.hookWidget => builder.importHookWidget(), + StatelessBaseWidgetType.consumerWidget => builder.importConsumerWidget(), + StatelessBaseWidgetType.statelessWidget => + builder.importStatelessWidget(), + }; + } + + String get assistName { + return switch (this) { + StatelessBaseWidgetType.hookConsumerWidget => 'HookConsumerWidget', + StatelessBaseWidgetType.hookWidget => 'HookWidget', + StatelessBaseWidgetType.consumerWidget => 'ConsumerWidget', + StatelessBaseWidgetType.statelessWidget => 'StatelessWidget', + }; + } } enum StatefulBaseWidgetType { statefulHookConsumerWidget( - widgetName: 'StatefulHookConsumerWidget', + widgetAssistName: 'StatefulHookConsumerWidget', priority: 33, typeChecker: TypeChecker.fromName( 'StatefulHookConsumerWidget', @@ -66,7 +82,7 @@ enum StatefulBaseWidgetType { requiredPackage: 'hooks_riverpod', ), statefulHookWidget( - widgetName: 'StatefulHookWidget', + widgetAssistName: 'StatefulHookWidget', priority: 32, typeChecker: TypeChecker.fromName( 'StatefulHookWidget', @@ -75,7 +91,7 @@ enum StatefulBaseWidgetType { requiredPackage: 'flutter_hooks', ), consumerStatefulWidget( - widgetName: 'ConsumerStatefulWidget', + widgetAssistName: 'ConsumerStatefulWidget', priority: 31, typeChecker: TypeChecker.fromName( 'ConsumerStatefulWidget', @@ -83,7 +99,7 @@ enum StatefulBaseWidgetType { ), ), statefulWidget( - widgetName: 'StatefulWidget', + widgetAssistName: 'StatefulWidget', priority: 30, typeChecker: TypeChecker.fromName( 'StatefulWidget', @@ -93,15 +109,28 @@ enum StatefulBaseWidgetType { ; const StatefulBaseWidgetType({ - required this.widgetName, + required this.widgetAssistName, required this.priority, required this.typeChecker, this.requiredPackage, }); - final String widgetName; + + final String widgetAssistName; final int priority; final TypeChecker typeChecker; final String? requiredPackage; + + String widgetName(DartFileEditBuilder builder) { + return switch (this) { + StatefulBaseWidgetType.statefulHookConsumerWidget => + builder.importStatefulHookConsumerWidget(), + StatefulBaseWidgetType.statefulHookWidget => + builder.importStatefulHookWidget(), + StatefulBaseWidgetType.consumerStatefulWidget => + builder.importConsumerStatefulWidget(), + StatefulBaseWidgetType.statefulWidget => builder.importStatefulWidget(), + }; + } } TypeChecker getStatelessBaseType({ diff --git a/packages/riverpod_lint/lib/src/assists/functional_to_class_based_provider.dart b/packages/riverpod_lint/lib/src/assists/functional_to_class_based_provider.dart index 95c82bf14..606345f64 100644 --- a/packages/riverpod_lint/lib/src/assists/functional_to_class_based_provider.dart +++ b/packages/riverpod_lint/lib/src/assists/functional_to_class_based_provider.dart @@ -33,11 +33,24 @@ class FunctionalToClassBasedProvider extends RiverpodAssist { ); changeBuilder.addDartFileEdit((builder) { + var typeParametersSource = ''; + final typeParameters = + declaration.node.functionExpression.typeParameters; + if (typeParameters != null) { + // Remove type arguments, if any + builder.addDeletion(typeParameters.sourceRange); + + typeParametersSource = resolver.source.contents.data.substring( + typeParameters.offset, + typeParameters.end, + ); + } + // Add the class name builder.addSimpleInsertion( functionStartOffset, ''' -class ${classNameFor(declaration)} extends ${generatedClassNameFor(declaration)} { +class ${classNameFor(declaration)}$typeParametersSource extends ${generatedClassNameFor(declaration)}$typeParametersSource { @override ''', ); diff --git a/packages/riverpod_lint/lib/src/assists/wrap_with_consumer.dart b/packages/riverpod_lint/lib/src/assists/wrap_with_consumer.dart index 6ac8fb383..823bea5d5 100644 --- a/packages/riverpod_lint/lib/src/assists/wrap_with_consumer.dart +++ b/packages/riverpod_lint/lib/src/assists/wrap_with_consumer.dart @@ -2,6 +2,7 @@ import 'package:analyzer/source/source_range.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; +import '../imports.dart'; import '../riverpod_custom_lint.dart'; /// Right above "wrap in builder" @@ -33,9 +34,11 @@ class WrapWithConsumer extends RiverpodAssist { ); changeBuilder.addDartFileEdit((builder) { + final consumer = builder.importConsumer(); + builder.addSimpleInsertion( node.offset, - 'Consumer(builder: (context, ref, child) { return ', + '$consumer(builder: (context, ref, child) { return ', ); builder.addSimpleInsertion(node.end, '; },)'); }); diff --git a/packages/riverpod_lint/lib/src/assists/wrap_with_provider_scope.dart b/packages/riverpod_lint/lib/src/assists/wrap_with_provider_scope.dart index 2c44bb97b..a95fd4bde 100644 --- a/packages/riverpod_lint/lib/src/assists/wrap_with_provider_scope.dart +++ b/packages/riverpod_lint/lib/src/assists/wrap_with_provider_scope.dart @@ -2,6 +2,7 @@ import 'package:analyzer/source/source_range.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; +import '../imports.dart'; import '../riverpod_custom_lint.dart'; import 'wrap_with_consumer.dart'; @@ -31,10 +32,8 @@ class WrapWithProviderScope extends RiverpodAssist { ); changeBuilder.addDartFileEdit((builder) { - builder.addSimpleInsertion( - node.offset, - 'ProviderScope(child: ', - ); + final providerScope = builder.importProviderScope(); + builder.addSimpleInsertion(node.offset, '$providerScope(child: '); builder.addSimpleInsertion(node.end, ',)'); }); }); diff --git a/packages/riverpod_lint/lib/src/imports.dart b/packages/riverpod_lint/lib/src/imports.dart new file mode 100644 index 000000000..6a5eff658 --- /dev/null +++ b/packages/riverpod_lint/lib/src/imports.dart @@ -0,0 +1,128 @@ +import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart'; +import 'package:meta/meta.dart'; + +extension ImportFix on DartFileEditBuilder { + // hybrid + @useResult + String importRef() => _importHybridRiverpod('Ref'); + + // flutter_riverpod + @useResult + String importWidgetRef() => _importFlutterRiverpod('WidgetRef'); + @useResult + String importConsumerStatefulWidget() => + _importFlutterRiverpod('ConsumerStatefulWidget'); + @useResult + String importConsumerWidget() => _importFlutterRiverpod('ConsumerWidget'); + @useResult + String importConsumerState() => _importFlutterRiverpod('ConsumerState'); + @useResult + String importConsumer() => _importFlutterRiverpod('Consumer'); + @useResult + String importProviderScope() => _importFlutterRiverpod('ProviderScope'); + + // flutter_hooks + @useResult + String importHookWidget() => _importFlutterHooks('HookWidget'); + +// hooks_riverpod + @useResult + String importHookConsumerWidget() => + _importHooksRiverpod('HookConsumerWidget'); + @useResult + String importStatefulHookConsumerWidget() => + _importHooksRiverpod('StatefulHookConsumerWidget'); + + // riverpod_annotation + @useResult + String importDependenciesClass() => _importRiverpodAnnotation('Dependencies'); + @useResult + String importRiverpod() => _importRiverpodAnnotation('riverpod'); + @useResult + String importRiverpodClass() => _importRiverpodAnnotation('Riverpod'); + + // Flutter widgets: + @useResult + String importState() => _importFlutterWidgets('State'); + @useResult + String importStatelessWidget() => _importFlutterWidgets('StatelessWidget'); + @useResult + String importStatefulHookWidget() => + _importFlutterWidgets('StatefulHookWidget'); + @useResult + String importStatefulWidget() => _importFlutterWidgets('StatefulWidget'); + + @useResult + String _importWithPrefix(String name, List uris) { + for (var i = 0; i < uris.length - 1; i++) { + final uri = uris[i]; + if (importsLibrary(uri)) return _buildImport(uri, name); + } + + final lastUri = uris.last; + return _buildImport(lastUri, name); + } + + @useResult + String _importHybridRiverpod(String name) { + return _importWithPrefix(name, [ + Uri(scheme: 'package', path: 'hooks_riverpod/hooks_riverpod.dart'), + Uri(scheme: 'package', path: 'flutter_riverpod/flutter_riverpod.dart'), + Uri( + scheme: 'package', + path: 'riverpod_annotation/riverpod_annotation.dart', + ), + Uri(scheme: 'package', path: 'riverpod/riverpod.dart'), + ]); + } + + @useResult + String _importHooksRiverpod(String name) { + return _importWithPrefix(name, [ + Uri(scheme: 'package', path: 'hooks_riverpod/hooks_riverpod.dart'), + ]); + } + + @useResult + String _importFlutterRiverpod(String name) { + return _importWithPrefix(name, [ + Uri(scheme: 'package', path: 'hooks_riverpod/hooks_riverpod.dart'), + Uri(scheme: 'package', path: 'flutter_riverpod/flutter_riverpod.dart'), + ]); + } + + @useResult + String _importRiverpodAnnotation(String name) { + return _importWithPrefix(name, [ + Uri( + scheme: 'package', + path: 'riverpod_annotation/riverpod_annotation.dart', + ), + ]); + } + + @useResult + String _importFlutterHooks(String name) { + return _importWithPrefix(name, [ + Uri(scheme: 'package', path: 'flutter_hooks/flutter_hooks.dart'), + ]); + } + + @useResult + String _importFlutterWidgets(String name) { + return _importWithPrefix(name, [ + Uri(scheme: 'package', path: 'flutter/cupertino.dart'), + Uri(scheme: 'package', path: 'flutter/material.dart'), + Uri(scheme: 'package', path: 'flutter/widgets.dart'), + ]); + } + + String _buildImport(Uri uri, String name) { + final import = importLibraryElement(uri); + + final prefix = import.prefix; + if (prefix != null) return '$prefix.$name'; + + return name; + } +} diff --git a/packages/riverpod_lint/lib/src/legacy_backup/avoid_dynamic_providers.dart b/packages/riverpod_lint/lib/src/legacy_backup/avoid_dynamic_providers.dart deleted file mode 100644 index 14ebef5e4..000000000 --- a/packages/riverpod_lint/lib/src/legacy_backup/avoid_dynamic_providers.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:analyzer/error/listener.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; - -class AvoidDynamicProviders extends DartLintRule { - const AvoidDynamicProviders() : super(code: _code); - - static const _code = LintCode( - name: 'avoid_dynamic_providers', - problemMessage: - 'Providers should only be created statically. They should not be ' - 'created inside functions or as properties of an object.', - ); - - @override - void run( - CustomLintResolver resolver, - ErrorReporter reporter, - CustomLintContext context, - ) { - // TODO: implement run - } -} diff --git a/packages/riverpod_lint/lib/src/legacy_backup/avoid_exposing_provider_ref.dart b/packages/riverpod_lint/lib/src/legacy_backup/avoid_exposing_provider_ref.dart deleted file mode 100644 index b4d6f9992..000000000 --- a/packages/riverpod_lint/lib/src/legacy_backup/avoid_exposing_provider_ref.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:analyzer/error/listener.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; - -class AvoidExposingProviderRef extends DartLintRule { - const AvoidExposingProviderRef() : super(code: _code); - - static const _code = LintCode( - name: 'avoid_exposing_provider_ref', - problemMessage: - 'The "ref" of a provider should not be accessible from outside of the ' - 'provider and its internals.', - ); - - @override - void run( - CustomLintResolver resolver, - ErrorReporter reporter, - CustomLintContext context, - ) { - // TODO: implement run - } -} diff --git a/packages/riverpod_lint/lib/src/legacy_backup/avoid_exposing_widget_ref.dart b/packages/riverpod_lint/lib/src/legacy_backup/avoid_exposing_widget_ref.dart deleted file mode 100644 index 7bdb2f1f3..000000000 --- a/packages/riverpod_lint/lib/src/legacy_backup/avoid_exposing_widget_ref.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:analyzer/error/listener.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; - -class AvoidExposingWidgetRef extends DartLintRule { - const AvoidExposingWidgetRef() : super(code: _code); - - static const _code = LintCode( - name: 'avoid_exposing_widget_ref', - problemMessage: - 'The "ref" of a widget should not be accessible outside of that widget.', - ); - - @override - void run( - CustomLintResolver resolver, - ErrorReporter reporter, - CustomLintContext context, - ) { - // TODO: implement run - } -} diff --git a/packages/riverpod_lint/lib/src/legacy_backup/avoid_read_inside_build.dart b/packages/riverpod_lint/lib/src/legacy_backup/avoid_read_inside_build.dart deleted file mode 100644 index b0596e479..000000000 --- a/packages/riverpod_lint/lib/src/legacy_backup/avoid_read_inside_build.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:analyzer/error/listener.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; - -import '../riverpod_custom_lint.dart'; - -class AvoidReadInsideBuild extends RiverpodLintRule { - const AvoidReadInsideBuild() : super(code: _code); - - static const _code = LintCode( - name: 'riverpod_avoid_read_inside_build', - problemMessage: - 'Avoid using ref.read inside the build method of widgets/providers.', - ); - - @override - void run( - CustomLintResolver resolver, - ErrorReporter reporter, - CustomLintContext context, - ) {} -} diff --git a/packages/riverpod_lint/lib/src/legacy_backup/avoid_watch_outside_build.dart b/packages/riverpod_lint/lib/src/legacy_backup/avoid_watch_outside_build.dart deleted file mode 100644 index b4f04bdcf..000000000 --- a/packages/riverpod_lint/lib/src/legacy_backup/avoid_watch_outside_build.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:analyzer/error/listener.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; - -import '../riverpod_custom_lint.dart'; - -class AvoidWatchOutsideBuild extends RiverpodLintRule { - const AvoidWatchOutsideBuild() : super(code: _code); - - static const _code = LintCode( - name: 'riverpod_avoid_watch_outside_build', - problemMessage: - 'Avoid using ref.watch outside the build method of widgets/providers.', - ); - - @override - void run( - CustomLintResolver resolver, - ErrorReporter reporter, - CustomLintContext context, - ) { - riverpodRegistry(context).addRefWatchInvocation((watch) {}); - } -} diff --git a/packages/riverpod_lint/lib/src/legacy_backup/dont_modify_providers_during_init.dart b/packages/riverpod_lint/lib/src/legacy_backup/dont_modify_providers_during_init.dart deleted file mode 100644 index c2673ad9c..000000000 --- a/packages/riverpod_lint/lib/src/legacy_backup/dont_modify_providers_during_init.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:analyzer/error/listener.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; - -class DontModifyProvidersDuringInit extends DartLintRule { - const DontModifyProvidersDuringInit() : super(code: _code); - - static const _code = LintCode( - name: 'dont_modify_providers_during_init', - problemMessage: - 'During its initialization, a provider should not modify other providers.', - ); - - @override - void run( - CustomLintResolver resolver, - ErrorReporter reporter, - CustomLintContext context, - ) { - // TODO: implement run - } -} diff --git a/packages/riverpod_lint/lib/src/legacy_backup/dont_modify_providers_inside_widget_lifecycles.dart b/packages/riverpod_lint/lib/src/legacy_backup/dont_modify_providers_inside_widget_lifecycles.dart deleted file mode 100644 index 9f0c0ea3c..000000000 --- a/packages/riverpod_lint/lib/src/legacy_backup/dont_modify_providers_inside_widget_lifecycles.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:analyzer/error/listener.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; - -class DontModifyProvidersInsideWidgetLifecycles extends DartLintRule { - const DontModifyProvidersInsideWidgetLifecycles() : super(code: _code); - - static const _code = LintCode( - name: 'dont_modify_providers_inside_widget_lifecycles', - problemMessage: 'Widget life-cycles are not allowed to modify providers, ' - 'and this would lead to an exception.', - ); - - @override - void run( - CustomLintResolver resolver, - ErrorReporter reporter, - CustomLintContext context, - ) { - // TODO: implement run - } -} diff --git a/packages/riverpod_lint/lib/src/legacy_backup/riverpod_lint.copy b/packages/riverpod_lint/lib/src/legacy_backup/riverpod_lint.copy deleted file mode 100644 index 3c62b8cfa..000000000 --- a/packages/riverpod_lint/lib/src/legacy_backup/riverpod_lint.copy +++ /dev/null @@ -1,1437 +0,0 @@ -import 'dart:async'; - -import 'package:analyzer/dart/analysis/results.dart'; -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/ast/syntactic_entity.dart'; -import 'package:analyzer/dart/ast/token.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/source/source_range.dart'; -import 'package:analyzer_plugin/protocol/protocol_generated.dart'; -import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; -import 'package:collection/collection.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; -import 'package:meta/meta.dart'; -import 'package:riverpod/riverpod.dart'; -import 'src/analyzer_utils.dart'; - -PluginBase createPlugin() => _RiverpodPlugin(); - -extension on String { - String get titled { - return replaceFirstMapped( - RegExp('[a-zA-Z]'), - (match) => match.group(0)!.toUpperCase(), - ); - } - - String get lowerFirst { - return replaceFirstMapped( - RegExp('[a-zA-Z]'), - (match) => match.group(0)!.toLowerCase(), - ); - } -} - -class _RiverpodPlugin extends PluginBase { - @override - Stream getLints(ResolvedUnitResult resolvedUnitResult) { - final visitor = RiverpodVisitor(resolvedUnitResult); - return resolvedUnitResult.unit.accept(visitor) ?? - const Stream.empty(); - } - - @override - Future handleGetAssists( - ResolvedUnitResult resolvedUnitResult, { - required int offset, - required int length, - }) async { - return EditGetAssistsResult( - await _computeAssistChanges( - resolvedUnitResult, - offset: offset, - length: length, - ).toList(), - ); - } - - Stream _computeAssistChanges( - ResolvedUnitResult resolvedUnitResult, { - required int offset, - required int length, - }) async* { - final unit = SourceRange( - resolvedUnitResult.unit.offset, - resolvedUnitResult.unit.length, - ); - - print( - 'Asked for $offset($length), got ${resolvedUnitResult.unit.offset} ' - '// ${resolvedUnitResult.unit.length} ' - '// ${unit.contains(offset)}', - ); - final child = resolvedUnitResult.unit.findAstNodeAtOffset(offset); - print('Found item $child ${child.runtimeType}'); - - if (child is FunctionDeclaration) { - final element = child.declaredElement!; - final annotation = _riverpod.firstAnnotationOf(element); - if (annotation == null) return; - - print('function at offset'); - final changeBuilder = ChangeBuilder(session: resolvedUnitResult.session); - - // The function offset after the annotations/comments. - final functionStartOffset = child.returnType?.offset ?? child.name.offset; - final functionLength = child.end - functionStartOffset; - - final newClassName = child.name.lexeme.titled; - - await changeBuilder.addDartFileEdit( - resolvedUnitResult.path, - (builder) { - builder.addReplacement( - SourceRange(functionStartOffset, functionLength), - (builder) { - builder.write(''' -class $newClassName extends _\$$newClassName { - ${child.returnType?.resolveSource(resolvedUnitResult) ?? ''} build() ${child.functionExpression.body.resolveSource(resolvedUnitResult)} -} -'''); - }, - ); - }, - ); - - yield PrioritizedSourceChange( - 1, - changeBuilder.sourceChange..message = 'Refactor provider as class', - ); - } - - if (child is ClassDeclaration) { - final element = child.declaredElement!; - final annotation = _riverpod.firstAnnotationOf(element); - if (annotation == null) return; - - final changeBuilder = ChangeBuilder(session: resolvedUnitResult.session); - - final buildMethod = child.members - .whereType() - .firstWhereOrNull((element) => element.name.lexeme == 'build'); - if (buildMethod == null) return; - - final newFunctionName = child.name.lexeme.lowerFirst; - final newFunctionRefName = '${child.name.lexeme.titled}Ref'; - - await changeBuilder.addDartFileEdit( - resolvedUnitResult.path, - (builder) { - builder.addReplacement( - SourceRange( - child.classKeyword.offset, - child.end - child.classKeyword.offset, - ), - (builder) { - builder.write(''' -${buildMethod.returnType?.resolveSource(resolvedUnitResult) ?? ''} $newFunctionName($newFunctionRefName ref) ${buildMethod.body.resolveSource(resolvedUnitResult)} -'''); - }, - ); - }, - ); - - yield PrioritizedSourceChange( - 1, - changeBuilder.sourceChange..message = 'Refactor provider as function', - ); - } - } -} - -bool _isOffsetInSourceRange( - int targetOffset, { - required int rangeOffset, - required int rangeLength, -}) { - return SourceRange(rangeOffset, rangeLength).contains(targetOffset); -} - -extension on AstNode { - String resolveSource(ResolvedUnitResult resolvedUnitResult) { - return resolvedUnitResult.content.substring(offset, end); - } - - AstNode? findAstNodeAtOffset(int targetOffset) { - final containsTarget = _isOffsetInSourceRange( - targetOffset, - rangeOffset: offset, - rangeLength: length, - ); - if (!containsTarget) { - return null; - } - - for (final child in childEntities) { - final match = child.when( - token: (_) { - // Ignoring tokens as we only care about returning AstNodes - }, - node: (node) => node.findAstNodeAtOffset(targetOffset), - ); - if (match != null) return match; - } - - return this; - } -} - -extension on SyntacticEntity { - R when({ - required R Function(Token token) token, - required R Function(AstNode node) node, - }) { - final that = this; - if (that is Token) { - return token(that); - } else if (that is AstNode) { - return node(that); - } else { - throw UnsupportedError('Unknown object $runtimeType'); - } - } - - // SyntacticEntity? findChildAtOffset(int targetOffset) { - // final containsTarget = _isOffsetInSourceRange( - // targetOffset, - // rangeOffset: offset, - // rangeLength: length, - // ); - // if (!containsTarget) { - // return null; - // } - - // return when( - // token: (token) { - // var result = token; - // for (Token? tokenItem = token; - // tokenItem != null; - // tokenItem = tokenItem == tokenItem.next ? null : tokenItem.next) { - // if (tokenItem.length >= result.length) continue; - // final containsOffset = _isOffsetInSourceRange( - // targetOffset, - // rangeOffset: tokenItem.offset, - // rangeLength: tokenItem.length, - // ); - // if (containsOffset) { - // result = tokenItem; - // } - // } - - // return result; - // }, - // node: (node) { - // for (final child in node.childEntities) { - // final match = child.findChildAtOffset(targetOffset); - // if (match != null) return match; - // } - // return this; - // }, - // ); - // } -} - -mixin _ProviderCreationVisitor on AsyncRecursiveVisitor { - /// The initialization of a provider was found - Stream? visitProviderCreation(AstNode node); - - @override - Stream? visitFunctionExpressionInvocation( - FunctionExpressionInvocation node, - ) async* { - final superStream = super.visitFunctionExpressionInvocation(node); - if (superStream != null) yield* superStream; - - if (node.isProviderCreation ?? false) { - final stream = visitProviderCreation(node); - if (stream != null) yield* stream; - } - } - - @override - Stream? visitInstanceCreationExpression( - InstanceCreationExpression node, - ) async* { - final superStream = super.visitInstanceCreationExpression(node); - if (superStream != null) yield* superStream; - - final createdType = node.staticType?.element; - if (createdType != null && - _providerOrFamily.isAssignableFrom(createdType)) { - final stream = visitProviderCreation(node); - if (stream != null) yield* stream; - } - } - - @override - Stream? visitClassDeclaration(ClassDeclaration node) async* { - final superStream = super.visitClassDeclaration(node); - if (superStream != null) yield* superStream; - if (node.isProviderCreation ?? false) { - final stream = visitProviderCreation(node); - if (stream != null) yield* stream; - } - } - - @override - Stream? visitFunctionDeclaration(FunctionDeclaration node) async* { - final superStream = super.visitFunctionDeclaration(node); - if (superStream != null) yield* superStream; - if (node.isProviderCreation ?? false) { - final stream = visitProviderCreation(node); - if (stream != null) yield* stream; - } - } -} - -class RefLifecycleInvocation { - RefLifecycleInvocation._(this.invocation); - - final MethodInvocation invocation; - - late final bool? isWithinBuild = invocation.inBuild; -} - -enum AsyncContext { - // Synchronous context in build of widget / initState or Provider - buildSync, - // Synchronous context in callback function - callbackSync, - // In callback, but after await / await_for - callbackAfterAsync; - - bool get isSync => this == buildSync || this == callbackSync; - bool get isAsync => this == callbackAfterAsync; -} - -mixin _AsyncContextVisitor on AsyncRecursiveVisitor { - AsyncContext context = AsyncContext.buildSync; - @override - Stream? visitMethodDeclaration(MethodDeclaration node) async* { - final last = context; - context = AsyncContext.buildSync; - yield* super.visitMethodDeclaration(node) ?? const Stream.empty(); - context = last; - } - - @override - Stream? visitFunctionExpression(FunctionExpression node) async* { - final last = context; - if (node.isBuild() ?? false) { - context = AsyncContext.buildSync; - } else { - context = AsyncContext.callbackSync; - } - yield* super.visitFunctionExpression(node) ?? const Stream.empty(); - context = last; - } - - @override - Stream? visitAwaitExpression(AwaitExpression node) async* { - yield* super.visitAwaitExpression(node) ?? const Stream.empty(); - context = AsyncContext.callbackAfterAsync; - } - - @override - Stream? visitForStatement(ForStatement node) async* { - // First time through the loop could be synchronous, so wait till the loop is done - yield* super.visitForStatement(node) ?? const Stream.empty(); - if (node.awaitKeyword != null) { - context = AsyncContext.callbackAfterAsync; - } - } -} -mixin _RefLifecycleVisitor on AsyncRecursiveVisitor { - /// A Ref/WidgetRef method was invoked. It isn't guaranteed to be watch/listen/read - Stream? visitRefInvocation(RefLifecycleInvocation node); - - @override - Stream? visitMethodInvocation(MethodInvocation node) async* { - final superStream = super.visitMethodInvocation(node); - if (superStream != null) yield* superStream; - - final targetType = node.target?.staticType?.element; - if (targetType == null) { - return; - } - - if (_ref.isAssignableFrom(targetType) || - _widgetRef.isAssignableFrom(targetType) || - _container.isAssignableFrom(targetType)) { - final refInvocStream = visitRefInvocation(RefLifecycleInvocation._(node)); - if (refInvocStream != null) yield* refInvocStream; - } - } -} - -mixin _InvocationVisitor - on AsyncRecursiveVisitor, _AsyncContextVisitor { - // Whether the method is forced async - bool forceAsync = false; - bool get asyncBad; - Stream visitCalledFunction(AstNode node, {required AstNode callingNode}); - @override - Stream? visitPropertyAccess(PropertyAccess node) async* { - final method = node.propertyName.staticElement; - if (method != null) { - final ast = await findAstNodeForElement(method); - if (ast != null && ast.refPassed) { - yield* visitCalledFunction(ast, callingNode: node); - } - } - yield* super.visitPropertyAccess(node) ?? const Stream.empty(); - } - - @override - Stream? visitPrefixedIdentifier(PrefixedIdentifier node) async* { - final method = node.identifier.staticElement; - if (method != null) { - final ast = await findAstNodeForElement(method); - if (ast != null && ast.refPassed) { - yield* visitCalledFunction(ast, callingNode: node); - } - } - yield* super.visitPrefixedIdentifier(node) ?? const Stream.empty(); - } - - @override - Stream? visitFunctionExpressionInvocation( - FunctionExpressionInvocation node, - ) async* { - final method = node.staticElement; - if (method != null) { - final ast = await findAstNodeForElement(method); - if (ast != null && ast.refPassed) { - yield* visitCalledFunction( - ast as FunctionDeclaration, - callingNode: node, - ); - } - } - yield* super.visitFunctionExpressionInvocation(node) ?? - const Stream.empty(); - } - - @override - Stream? visitMethodInvocation(MethodInvocation node) async* { - final method = node.methodName.staticElement; - if (method != null) { - final ast = await findAstNodeForElement(method.declaration!); - if (ast != null && ast.refPassed) { - yield* visitCalledFunction(ast, callingNode: node); - } - } - - yield* super.visitMethodInvocation(node) ?? const Stream.empty(); - } - - @override - Stream? visitInstanceCreationExpression( - InstanceCreationExpression node, - ) async* { - final constructor = node.constructorName.staticElement; - if (constructor != null) { - if (_futureOrStream.isAssignableFrom(constructor.enclosingElement)) { - if (!asyncBad) { - return; - } else { - for (final arg in node.argumentList.arguments) { - // The arguments to a future or stream could be called after a widget is disposed - final last = context; - final lastAsync = forceAsync; - forceAsync = true; - context = AsyncContext.callbackAfterAsync; - yield* visitExpression(arg) ?? const Stream.empty(); - context = last; - forceAsync = lastAsync; - } - } - } else { - final ast = await findAstNodeForElement(constructor.declaration); - - if (ast != null) { - yield* visitConstructorDeclaration(ast as ConstructorDeclaration) ?? - const Stream.empty(); - } - for (final arg in node.argumentList.arguments) { - yield* visitExpression(arg) ?? const Stream.empty(); - } - } - } - } -} - -/// Recursively search all the places where a Provider's `ref` is used -// TODO handle Ref fn() => ref; -class ProviderRefUsageVisitor extends AsyncRecursiveVisitor - with _RefLifecycleVisitor { - @override - Stream? visitArgumentList(ArgumentList node) async* { - final superStream = super.visitArgumentList(node); - if (superStream != null) yield* superStream; - final providerBody = node.arguments.firstOrNull; - if (providerBody is ConstructorReference) { - final element = providerBody - .constructorName.staticElement?.declaration.enclosingElement; - if (element != null) { - final ast = await findAstNodeForElement(element); - - final createdObjectStream = ast?.accept(this); - if (createdObjectStream != null) yield* createdObjectStream; - } - } - - argumentsLoop: - for (final arg in node.arguments) { - final type = arg.staticType?.element; - if (type != null && _ref.isAssignableFrom(type)) { - // "ref" was passed as argument to a constructor/function. - // We now will search for the constructor/function invoked, to see how - // it uses ref. - - for (final parent in node.parents) { - if (parent is MethodInvocation) { - final functionExpression = parent.function; - final functionElement = functionExpression is Identifier - ? functionExpression.staticElement - : null; - - if (functionElement != null) { - final declaration = await findAstNodeForElement(functionElement); - final createdObjectStream = declaration?.accept(this); - if (createdObjectStream != null) yield* createdObjectStream; - } - - continue argumentsLoop; - } else if (parent is InstanceCreationExpression) { - final createdObject = parent.staticType?.element; - - if (createdObject != null) { - final ast = await findAstNodeForElement(createdObject); - final createdObjectStream = ast?.accept(this); - if (createdObjectStream != null) yield* createdObjectStream; - } - - continue argumentsLoop; - } - } - } - } - } - - @override - Stream? visitAssignmentExpression( - AssignmentExpression node, - ) async* { - final superStream = super.visitAssignmentExpression(node); - if (superStream != null) yield* superStream; - final rightType = node.rightHandSide.staticType?.element; - - if (rightType != null && _ref.isAssignableFrom(rightType)) { - // "ref" was assigned to a variable or field - // We now will see if it is a field, to see how the class the field is assigned to uses ref. - for (final parent in node.parents) { - if (parent is CascadeExpression) { - final classElement = parent.target.staticType?.element; - if (classElement != null) { - final declaration = await findAstNodeForElement(classElement); - final objectStream = declaration?.accept(this); - if (objectStream != null) yield* objectStream; - } - } else if (parent is ExpressionStatement) { - final assignee = node.leftHandSide; - if (assignee is PrefixedIdentifier) { - final target = assignee.prefix.staticType?.element; - if (target is InterfaceElement) { - final declaration = await findAstNodeForElement(target); - final objectStream = declaration?.accept(this); - if (objectStream != null) yield* objectStream; - } - } - } - } - } - } - - @override - Stream? visitRefInvocation( - RefLifecycleInvocation node, - ) async* { - if (_refBinders.contains(node.invocation.methodName.name)) { - final providerExpression = node.invocation.argumentList.arguments.first; - final providerOrigin = - await ProviderDeclaration.tryParse(providerExpression); - - if (providerOrigin != null) yield providerOrigin; - } - } -} - -class RefAsyncUsageVisitor extends AsyncRecursiveVisitor - with _AsyncContextVisitor, _InvocationVisitor, _RefLifecycleVisitor { - RefAsyncUsageVisitor(this.unit); - final ResolvedUnitResult unit; - - @override - Stream visitCalledFunction( - AstNode node, { - required AstNode callingNode, - }) async* { - final results = node is MethodDeclaration - ? visitMethodDeclaration(node) - : node is FunctionDeclaration - ? visitFunctionDeclaration(node) - : null; - if (results != null) { - await for (final _ in results) { - yield Lint( - code: 'riverpod_no_ref_after_async', - message: - 'Do not use ref after async gaps in flutter widgets, a function was called which uses ref after a widget could be disposed', - location: unit.lintLocationFromOffset( - callingNode.offset, - length: callingNode.length, - ), - ); - // Only need to report the function once - return; - } - } - } - - @override - Stream? visitRefInvocation(RefLifecycleInvocation node) async* { - if (context == AsyncContext.callbackAfterAsync) { - // TODO Allow checking mounted in stateful widgets and checking mounted in StateNotifiers - yield Lint( - code: 'riverpod_no_ref_after_async', - message: 'Do not use ref after async gaps in flutter widgets.', - location: unit.lintLocationFromOffset( - node.invocation.offset, - length: node.invocation.length, - ), - ); - } - } - - @override - bool get asyncBad => true; -} - -extension on FormalParameterList { - bool get hasRefParameter { - return parameters.any((p) { - final type = p.declaredElement?.type; - return type != null && _anyRef.isAssignableFromType(type); - }); - } -} - -extension on InterfaceOrAugmentationElement { - bool get hasRefField => - fields.any((f) => _anyRef.isAssignableFromType(f.type)); -} - -extension RefPassed on AstNode { - bool get refPassed { - final node = this; - if (node is MethodDeclaration) { - if (!(node.parameters?.hasRefParameter ?? false)) { - final enclosingElement = node.declaredElement?.enclosingElement; - if (enclosingElement is ExtensionElement && - !_anyRef.isAssignableFromType(enclosingElement.extendedType)) { - return false; // If ref is not passed in, there is no way the ref could be used within the the called function - } else if (enclosingElement is InterfaceOrAugmentationElement && - !enclosingElement.hasRefField) { - return false; // If ref is not passed in and not a field, there is no way the ref could be used within the the called function - } - } - } else if (node is FunctionDeclaration) { - if (!(node.functionExpression.parameters?.hasRefParameter ?? false)) { - return false; // If ref is not passed in, there is no way the ref could be used within the the called function - } - } - return true; - } -} - -class ProviderSyncMutationVisitor extends AsyncRecursiveVisitor - with _AsyncContextVisitor, _InvocationVisitor { - ProviderSyncMutationVisitor(this.unit); - - final ResolvedUnitResult unit; - - @override - Stream visitCalledFunction( - AstNode node, { - required AstNode callingNode, - }) async* { - final results = node is MethodDeclaration - ? visitMethodDeclaration(node) - : node is FunctionDeclaration - ? visitFunctionDeclaration(node) - : null; - if (results != null) { - await for (final _ in results) { - yield Lint( - code: 'riverpod_no_mutate_sync', - message: - 'Do not mutate a provider synchronously, a function was called which mutates a provider synchronously', - location: unit.lintLocationFromOffset( - callingNode.offset, - length: callingNode.length, - ), - ); - // Only need to report the function once - return; - } - } - } - - @override - Stream? visitExpression(Expression node) async* { - final lints = super.visitExpression(node); - yield* lints ?? const Stream.empty(); - final mutate = node.isMutation; - if (!forceAsync && context == AsyncContext.buildSync && (mutate ?? false)) { - yield Lint( - code: 'riverpod_no_mutate_sync', - message: 'Do not mutate a provider synchronously', - location: unit.lintLocationFromOffset( - node.offset, - length: node.length, - ), - ); - } - } - - @override - Stream? visitNode(AstNode node) { - if (!forceAsync && context == AsyncContext.buildSync) { - return super.visitNode(node); - } - return null; - } - - @override - bool get asyncBad => false; -} - -class _FindProviderCallbackVisitor - extends CombiningRecursiveVisitor { - @override - Iterable? visitArgumentList(ArgumentList node) { - if (node.arguments.isEmpty) return null; - - return [node.arguments.first as FunctionExpression]; - } -} - -class RiverpodVisitor extends AsyncRecursiveVisitor - with _RefLifecycleVisitor, _ProviderCreationVisitor { - RiverpodVisitor(this.unit); - - final ResolvedUnitResult unit; - - @override - Stream visitRefInvocation(RefLifecycleInvocation node) async* { - switch (node.invocation.methodName.name) { - case 'read': - if (node.isWithinBuild ?? false) { - yield Lint( - code: 'riverpod_avoid_read_inside_build', - message: - 'Avoid using ref.read inside the build method of widgets/providers.', - location: unit.lintLocationFromOffset( - node.invocation.offset, - length: node.invocation.length, - ), - ); - } - final arg = - node.invocation.argumentList.arguments.firstOrNull?.staticType; - - if (arg != null && - !_alwaysAliveProviderListenable.isAssignableFromType(arg)) { - yield Lint( - code: 'riverpod_avoid_read_auto_dispose', - message: 'Avoid using ref.read on an autoDispose provider', - correction: ''' -Instead use: - final listener = ref.listen(${node.invocation.argumentList.arguments.first}, (_, __){}); - final currentValue = listener.read(); -Then dispose of the listener when you no longer need the autoDispose provider to be kept alive.''', - location: unit.lintLocationFromOffset( - node.invocation.offset, - length: node.invocation.length, - ), - ); - } - break; - case 'watch': - if (node.isWithinBuild == false) { - yield Lint( - code: 'riverpod_avoid_watch_outside_build', - message: - 'Avoid using ref.watch outside the build method of widgets/providers.', - location: unit.lintLocationFromOffset( - node.invocation.offset, - length: node.invocation.length, - ), - ); - } - break; - default: - } - } - - @override - Stream visitProviderCreation(AstNode node) async* { - if (node is ClassDeclaration || node is FunctionDeclaration) { - yield* ProviderSyncMutationVisitor(unit).visitNode(node) ?? - const Stream.empty(); - return; - } - final variableDeclaration = - node.parents.whereType().firstOrNull; - - final providerDeclaration = variableDeclaration?.declaredElement != null - ? ProviderDeclaration._( - variableDeclaration!, - variableDeclaration.declaredElement!, - ) - : null; - yield* _checkValidProviderDeclaration(node); - - if (providerDeclaration != null) { - yield* _checkProviderDependencies(providerDeclaration); - - yield* ProviderSyncMutationVisitor(unit).visitNode(node) ?? - const Stream.empty(); - } - } - - @override - Stream? visitExpression(Expression node) async* { - final superStream = super.visitExpression(node); - if (superStream != null) yield* superStream; - final st = node.staticType?.element; - if (st != null && - _ref.isAssignableFrom(st) && - (node.parent is ExpressionFunctionBody || - node.parent is ReturnStatement)) { - yield Lint( - code: 'riverpod_ref_escape_scope', - message: 'Ref escaped the scope via a function or return expression.', - correction: - 'Pass ref to the function or constructor that needs it instead', - severity: LintSeverity.warning, - location: unit.lintLocationFromOffset( - node.offset, - length: node.length, - ), - ); - } - } - - @override - Stream? visitConstructorDeclaration( - ConstructorDeclaration node, - ) async* { - final superStream = super.visitConstructorDeclaration(node); - if (superStream != null) yield* superStream; - for (final param in node.parameters.parameters) { - final type = param.declaredElement?.type.element; - - if (type != null && _widgetRef.isAssignableFrom(type)) { - final klass = node.returnType.staticElement; - if (klass != null && - (_widget.isAssignableFrom(klass) || - _widgetState.isAssignableFrom(klass))) { - yield Lint( - code: 'riverpod_ref_escape_scope', - message: 'Ref escaped its scope to another widget.', - correction: 'Use a Consumer widget instead', - severity: LintSeverity.warning, - location: unit.lintLocationFromOffset( - param.offset, - length: param.length, - ), - ); - } - } - } - } - - Stream _checkProviderDependencies(ProviderDeclaration provider) async* { - final providerDependencies = await provider.dependencies; - if (providerDependencies == null) return; - final expectedDependencies = providerDependencies.value; - final visitor = ProviderRefUsageVisitor(); - final actualDependencies = await provider.node.accept(visitor)!.toList(); - - if (expectedDependencies == null) { - // no `dependencies` specified - - for (final actualDependency in actualDependencies) { - if (await actualDependency.isScoped) { - yield Lint( - code: 'riverpod_unspecified_dependencies', - message: 'This provider does not specify `dependencies`, ' - 'yet depends on "${actualDependency.name}" which did specify its dependencies.', - correction: 'Add `dependencies` to this provider ' - 'or remove `dependencies` from "${actualDependency.name}".', - severity: LintSeverity.warning, - location: unit.lintLocationFromOffset( - provider.node.offset, - length: provider.name.length, - ), - getAnalysisErrorFixes: (lint) async* { - final changeBuilder = ChangeBuilder(session: unit.session); - - final providerCallback = - provider.node.accept(_FindProviderCallbackVisitor())!.single; - - await changeBuilder.addDartFileEdit(unit.path, (builder) { - builder.addSimpleInsertion( - providerCallback.end, - ', dependencies: [${actualDependencies.map((e) => e.name).join(', ')}]', - ); - }); - - yield AnalysisErrorFixes( - lint.asAnalysisError(), - fixes: [ - PrioritizedSourceChange( - 0, - changeBuilder.sourceChange - ..message = 'List all dependencies', - ) - ], - ); - }, - ); - } - } - } else { - for (final actualDependency in actualDependencies) { - if (!expectedDependencies.any((e) => e.origin == actualDependency)) { - yield Lint( - code: 'riverpod_missing_dependency', - message: 'This provider depends on "${actualDependency.name}" ' - 'yet "${actualDependency.name}" isn\'t listed in the dependencies.', - correction: - 'Add "${actualDependency.name}" to the list of dependencies ' - 'of this provider.', - severity: LintSeverity.warning, - location: unit.lintLocationFromOffset( - // TODO is there a better way to do this? - provider.dependenciesExpression!.value!.offset, - length: 'dependencies'.length, - ), - ); - } - } - - for (final expectedDependency in expectedDependencies) { - if (!actualDependencies.contains(expectedDependency.origin)) { - yield Lint( - code: 'riverpod_unused_dependency', - message: - 'This provider specifies that it depends on "${expectedDependency.origin.name}" ' - 'yet it never uses that provider.', - correction: - 'Remove "${expectedDependency.origin.name}" from the list of dependencies.', - severity: LintSeverity.warning, - location: unit.lintLocationFromOffset( - // TODO is there a better way to do this? - expectedDependency.node.offset, - length: expectedDependency.node.length, - ), - ); - } - } - } - - // print( - // 'Provider\n - origin: $node\n - expected: ${dependencies.value}\n - dependencies: ${visitor.dependencies}'); - } - - @override - Stream visitTopLevelVariableDeclaration( - TopLevelVariableDeclaration node, - ) async* { - yield* super.visitTopLevelVariableDeclaration(node) ?? const Stream.empty(); - for (final variable in node.variables.variables) { - final type = variable.declaredElement?.type; - - if (type != null && _container.isAssignableFromType(type)) { - yield Lint( - code: 'riverpod_global_container', - message: 'This container is global', - correction: - 'Make the container non-global by creating it in your main and assigning it to a UncontrolledProviderScope.', - severity: LintSeverity.warning, - location: unit.lintLocationFromOffset( - variable.offset, - length: variable.length, - ), - ); - } - } - } - - @override - Stream visitNode(AstNode node) async* { - yield* super.visitNode(node) ?? const Stream.empty(); - if (node.isWidgetBuild ?? false) { - final syncMutationDetector = ProviderSyncMutationVisitor(unit); - final results = node is MethodDeclaration - ? syncMutationDetector.visitMethodDeclaration(node) - : node is FunctionDeclaration - ? syncMutationDetector.visitFunctionDeclaration(node) - : null; - yield* results ?? const Stream.empty(); - final refAfterAsyncGaps = RefAsyncUsageVisitor(unit); - final results2 = node is MethodDeclaration - ? refAfterAsyncGaps.visitMethodDeclaration(node) - : node is FunctionDeclaration - ? refAfterAsyncGaps.visitFunctionDeclaration(node) - : null; - yield* results2 ?? const Stream.empty(); - } else if (node.isInitState ?? false) { - final syncMutationDetector = ProviderSyncMutationVisitor(unit); - final results = node is MethodDeclaration - ? syncMutationDetector.visitMethodDeclaration(node) - : node is FunctionDeclaration - ? syncMutationDetector.visitFunctionDeclaration(node) - : null; - yield* results ?? const Stream.empty(); - } - } - - Stream _checkValidProviderDeclaration(AstNode providerNode) async* { - if (providerNode is ClassDeclaration || - providerNode is FunctionDeclaration) { - // Codegen provider - return; - } - final declaration = - providerNode.parents.whereType().firstOrNull; - final variable = declaration?.declaredElement; - - if (variable == null) { - yield Lint( - code: 'riverpod_final_provider', - message: 'Providers should always be declared as final', - location: unit.lintLocationFromOffset( - providerNode.offset, - length: providerNode.length, - ), - ); - return; - } - - final isGlobal = declaration!.parents.any( - (element) => - element is TopLevelVariableDeclaration || - (element is FieldDeclaration && element.isStatic), - ); - - if (!isGlobal) { - yield Lint( - code: 'riverpod_avoid_dynamic_provider', - message: - 'Providers should be either top level variables or static properties', - location: unit.lintLocationFromOffset( - variable.nameOffset, - length: variable.nameLength, - ), - ); - } - - if (!variable.isFinal) { - yield Lint( - code: 'riverpod_final_provider', - message: 'Providers should always be declared as final', - location: unit.lintLocationFromOffset( - variable.nameOffset, - length: variable.nameLength, - ), - ); - } - } -} - -extension on AstNode { - Iterable get parents sync* { - for (var node = parent; node != null; node = node.parent) { - yield node; - } - } -} - -class Result { - const Result(this.value); - final T value; - - @override - String toString() => 'Result($value)'; -} - -@immutable -class ProviderDeclaration { - ProviderDeclaration._(this.node, this.element); - - /// Decode a provider expression to extract the provider listened. - /// - /// For example, for: - /// - /// ```dart - /// family(42).select(...) - /// ``` - /// - /// this will return the variable definition of `family`. - /// - /// Returns `null` if failed to decode the expression. - // TODO fuse with riverpod_graph - static FutureOr tryParse(AstNode providerExpression) { - if (providerExpression is PropertyAccess) { - // watch(family(0).modifier) - final target = providerExpression.target; - if (target != null) return ProviderDeclaration.tryParse(target); - } else if (providerExpression is PrefixedIdentifier) { - // watch(provider.modifier) - return ProviderDeclaration.tryParse(providerExpression.prefix); - } else if (providerExpression is Identifier) { - // watch(variable) - final Object? staticElement = providerExpression.staticElement; - if (staticElement is PropertyAccessorElement) { - // TODO can this be removed? - return findAstNodeForElement(staticElement.declaration.variable) - .then((ast) { - if (ast is VariableDeclaration) { - return ProviderDeclaration._( - ast, - staticElement.declaration.variable, - ); - } - return null; - }); - } - } else if (providerExpression is FunctionExpressionInvocation) { - // watch(family(id)) - return ProviderDeclaration.tryParse(providerExpression.function); - } else if (providerExpression is MethodInvocation) { - // watch(variable.select(...)) or watch(family(id).select(...)) - final target = providerExpression.target; - if (target != null) return ProviderDeclaration.tryParse(target); - } - - return null; - } - - /// Finds the "dependencies:" expression in a provider creation - /// - /// Returns null if failed to parse. - /// Returns Result(null) if successfully passed but no dependencies was specified - static Result? _findDependenciesExpression( - VariableDeclaration node, - ) { - final initializer = node.initializer; - ArgumentList argumentList; - - if (initializer is InstanceCreationExpression) { - argumentList = initializer.argumentList; - } else if (initializer is FunctionExpressionInvocation) { - argumentList = initializer.argumentList; - } else { - return null; - } - - return Result( - argumentList.arguments - .whereType() - .firstWhereOrNull((e) => e.name.label.name == 'dependencies'), - ); - } - - /// Decode the parameter "dependencies" from a provider - /// - /// Returns null if failed to decode. - /// Returns a [Result] with `value` as null if the parameter "dependencies" was - /// not specified. - static Future?>?> _findDependencies( - Result? dependenciesExpressionResult, - ) async { - if (dependenciesExpressionResult == null) return null; - final namedExpression = dependenciesExpressionResult.value; - if (namedExpression == null) return const Result(null); - final value = namedExpression.expression; - if (value is! ListLiteral) return null; - - return Result( - await Stream.fromIterable(value.elements) - .asyncMap((node) async { - final origin = await ProviderDeclaration.tryParse(node); - if (origin == null) return null; - return ProviderDependency(origin, node); - }) - .where((e) => e != null) - .cast() - .toList(), - ); - } - - final VariableDeclaration node; - final VariableElement element; - - /// The [AstNode] that points to the `dependencies` parameter of a provider - late final dependenciesExpression = _findDependenciesExpression(node); - - /// The decoded `dependencies` of a provider - late final dependencies = _findDependencies(dependenciesExpression); - - late final Future isScoped = dependencies.then((e) => e?.value != null); - - String get name => node.name.lexeme; - - @override - String toString() => node.toString(); - - @override - bool operator ==(Object other) { - return other is ProviderDeclaration && element == other.element; - } - - @override - int get hashCode => element.hashCode; -} - -@immutable -class ProviderDependency { - const ProviderDependency(this.origin, this.node); - - /// The provider that is depended on - final ProviderDeclaration origin; - - /// The [AstNode] associated with this dependency - final AstNode node; -} - -extension on FunctionExpressionInvocation { - /// Null if unknown - bool? get isProviderCreation { - final returnType = staticType?.element; - if (returnType == null) return null; - - final function = this.function; - - return _providerOrFamily.isAssignableFrom(returnType) && - function is PropertyAccess && - (function.propertyName.name == 'autoDispose' || - function.propertyName.name == 'family'); - } -} - -extension on MethodDeclaration { - bool? get isProviderCreation { - final isBuildMethod = name.lexeme == 'build'; - final interface = declaredElement?.enclosingElement; - if (interface == null) { - return null; - } - return isBuildMethod && _codegenNotifier.isAssignableFrom(interface); - } -} - -extension on ClassDeclaration { - bool? get isProviderCreation { - final interface = declaredElement; - if (interface == null) { - return null; - } - return _codegenNotifier.isAssignableFrom(interface); - } -} - -extension on FunctionDeclaration { - bool? get isProviderCreation { - final annotation = metadata.firstWhereOrNull( - (a) => a.name.name == 'riverpod' || a.constructorName?.name == 'Riverpod', - ); - return annotation == null ? null : true; - } -} - -extension on AstNode { - bool? get isWidgetBuild { - final expr = this; - if (expr is FunctionExpression) { - return (this as FunctionExpression).isWidgetBuilder; - } - if (expr is MethodDeclaration) { - if (expr.name.lexeme != 'build') { - return false; - } - - final classElement = expr.parents - .whereType() - .firstOrNull - ?.declaredElement; - - if (classElement == null) return null; - return _consumerWidget.isAssignableFrom(classElement) || - _consumerState.isAssignableFrom(classElement); - } - return null; - } - - bool? isBuild({bool hasFoundFunctionExpression = false}) { - final node = this; - - if (node is FunctionExpression) { - if (hasFoundFunctionExpression) { - return false; - } - if (node.isWidgetBuilder ?? false) { - return true; - } - - return parent?.isBuild(hasFoundFunctionExpression: true); - } - if (node is InstanceCreationExpression) { - return node.isProviderCreation; - } - if (node is FunctionExpressionInvocation) { - return node.isProviderCreation; - } - if (node is FunctionDeclaration) { - return node.isProviderCreation; - } - if (node is MethodDeclaration) { - if (node.isProviderCreation ?? false) { - return true; - } - if (hasFoundFunctionExpression || node.name.lexeme != 'build') { - return false; - } - - final classElement = node.parents - .whereType() - .firstOrNull - ?.declaredElement; - - if (classElement == null) return null; - return _consumerWidget.isAssignableFrom(classElement) || - _consumerState.isAssignableFrom(classElement); - } - return parent?.isBuild( - hasFoundFunctionExpression: hasFoundFunctionExpression, - ); - } - - bool? get inBuild { - final inBuild = parent?.isBuild(); - if (inBuild ?? false) { - return inBuild; - } - return inBuild; - } - - bool? get isInitState { - final expr = this; - if (expr is MethodDeclaration) { - if (expr.name.lexeme != 'initState') { - return false; - } - - final classElement = expr.parents - .whereType() - .firstOrNull - ?.declaredElement; - - if (classElement == null) return null; - return _consumerState.isAssignableFrom(classElement); - } - return null; - } -} - -extension on FunctionExpression { - /// Null if unknown - bool? get isWidgetBuilder { - final returnType = declaredElement?.returnType.element; - if (returnType == null) return null; - - return _widget.isAssignableFrom(returnType); - } -} - -extension on InstanceCreationExpression { - /// Null if unknown - bool? get isProviderCreation { - final type = staticType?.element; - if (type == null) return null; - - return _providerOrFamily.isAssignableFrom(type); - } -} - -extension on Expression { - // ref.watch(a.notifier).state = ''; - bool? get isMutation { - final expr = this; - if (expr is AssignmentExpression) { - final left = expr.leftHandSide; - if (left is! PropertyAccess || left.propertyName.name != 'state') { - return null; - } - final targ = left.target; - if (targ is! MethodInvocation) { - return null; - } - final methodTarget = targ.methodName.staticElement?.enclosingElement; - if (methodTarget == null || methodTarget is! InterfaceElement) { - return null; - } - if (_ref.isAssignableFromType(methodTarget.thisType) || - _widgetRef.isAssignableFromType(methodTarget.thisType)) { - // TODO: Synchronous listen - if (targ.methodName.name == 'watch' || targ.methodName.name == 'read') { - return true; - } - } - return false; - } else if (expr is MethodInvocation) { - if (expr.methodName.name == 'refresh' || - expr.methodName.name == 'invalidate' || - expr.methodName.name == 'invalidateSelf') { - final methodTarget = expr.methodName.staticElement?.enclosingElement; - if (methodTarget == null || methodTarget is! InterfaceElement) { - return null; - } - if (_ref.isAssignableFromType(methodTarget.thisType) || - _widgetRef.isAssignableFromType(methodTarget.thisType)) { - return true; - } else { - return false; - } - } else { - return false; - } - } - return null; - } -} diff --git a/packages/riverpod_lint/lib/src/lints/avoid_build_context_in_providers.dart b/packages/riverpod_lint/lib/src/lints/avoid_build_context_in_providers.dart index d87c645dc..7f8a97130 100644 --- a/packages/riverpod_lint/lib/src/lints/avoid_build_context_in_providers.dart +++ b/packages/riverpod_lint/lib/src/lints/avoid_build_context_in_providers.dart @@ -1,8 +1,4 @@ import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/error/error.dart' - hide - // ignore: undefined_hidden_name, necessary to support lower analyzer version - LintCode; import 'package:analyzer/error/listener.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; @@ -20,7 +16,6 @@ class AvoidBuildContextInProviders extends RiverpodLintRule { name: 'avoid_build_context_in_providers', problemMessage: 'Passing BuildContext to providers indicates mixing UI with the business logic.', - errorSeverity: ErrorSeverity.WARNING, ); @override diff --git a/packages/riverpod_lint/lib/src/lints/avoid_global_provider_container.dart b/packages/riverpod_lint/lib/src/lints/avoid_global_provider_container.dart deleted file mode 100644 index 870acd593..000000000 --- a/packages/riverpod_lint/lib/src/lints/avoid_global_provider_container.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:analyzer/error/listener.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; -import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; - -class AvoidGlobalProviderContainer extends DartLintRule { - const AvoidGlobalProviderContainer() : super(code: _code); - - static const _code = LintCode( - name: 'avoid_global_provider_container', - problemMessage: - 'ProviderContainer instances should not be accessible globally.', - ); - - @override - void run( - CustomLintResolver resolver, - ErrorReporter reporter, - CustomLintContext context, - ) { - context.registry.addInstanceCreationExpression((node) { - // If there is a parameterElement it means we are not declaring a variable - if (node.staticParameterElement != null) return; - - // Check that the object created is indeed a ProviderContainer - final type = node.staticType; - if (type == null || !providerContainerType.isExactlyType(type)) { - return; - } - }); - } -} diff --git a/packages/riverpod_lint/lib/src/lints/avoid_manual_providers_as_generated_provider_dependency.dart b/packages/riverpod_lint/lib/src/lints/avoid_manual_providers_as_generated_provider_dependency.dart index 575b9f0d1..b863db0cf 100644 --- a/packages/riverpod_lint/lib/src/lints/avoid_manual_providers_as_generated_provider_dependency.dart +++ b/packages/riverpod_lint/lib/src/lints/avoid_manual_providers_as_generated_provider_dependency.dart @@ -1,3 +1,4 @@ +import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/error/listener.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; @@ -22,24 +23,19 @@ class AvoidManualProvidersAsGeneratedProviderDependency ErrorReporter reporter, CustomLintContext context, ) { - void checkDependency( - GeneratorProviderDeclaration provider, - RefDependencyInvocation dependency, - ) { - final dependencyElement = dependency.provider.providerElement; - if (dependencyElement is! GeneratorProviderDeclarationElement) { - reporter.atNode( - dependency.provider.provider ?? dependency.provider.node, - _code, - ); + riverpodRegistry(context).addProviderIdentifier((dependency) { + // The dependency is a generated provider, no need to check + if (dependency.providerElement is GeneratorProviderDeclarationElement) { + return; } - } + // We're depending on a non-generated provider. Let's check if the + // associated provider is a generated provider + final enclosingProvider = dependency.node + .thisOrAncestorOfType() + ?.provider; - riverpodRegistry(context).addGeneratorProviderDeclaration((declaration) { - for (final invocation in declaration.refInvocations) { - if (invocation is RefDependencyInvocation) { - checkDependency(declaration, invocation); - } + if (enclosingProvider != null) { + reporter.atNode(dependency.node, code); } }); } diff --git a/packages/riverpod_lint/lib/src/lints/avoid_public_notifier_properties.dart b/packages/riverpod_lint/lib/src/lints/avoid_public_notifier_properties.dart index d75395ac6..17ab0a808 100644 --- a/packages/riverpod_lint/lib/src/lints/avoid_public_notifier_properties.dart +++ b/packages/riverpod_lint/lib/src/lints/avoid_public_notifier_properties.dart @@ -4,7 +4,9 @@ import 'package:analyzer/error/listener.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; -class AvoidPublicNotifierProperties extends DartLintRule { +import '../riverpod_custom_lint.dart'; + +class AvoidPublicNotifierProperties extends RiverpodLintRule { const AvoidPublicNotifierProperties() : super(code: _code); static const _code = LintCode( diff --git a/packages/riverpod_lint/lib/src/lints/avoid_read_auto_dispose.dart b/packages/riverpod_lint/lib/src/lints/avoid_read_auto_dispose.dart deleted file mode 100644 index 858f4b5de..000000000 --- a/packages/riverpod_lint/lib/src/lints/avoid_read_auto_dispose.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:analyzer/error/listener.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; - -import '../riverpod_custom_lint.dart'; - -class AvoidReadAutoDispose extends RiverpodLintRule { - const AvoidReadAutoDispose() : super(code: _code); - - static const _code = LintCode( - name: 'riverpod_avoid_read_auto_dispose', - problemMessage: 'Avoid using ref.read on an autoDispose provider', - correctionMessage: ''' -Instead use: - final listener = ref.listen({0}, (_, __){}); - final currentValue = listener.read(); -Then dispose of the listener when you no longer need the autoDispose provider to be kept alive.''', - ); - - @override - void run( - CustomLintResolver resolver, - ErrorReporter reporter, - CustomLintContext context, - ) { - riverpodRegistry(context).addRefReadInvocation((read) { - if (read.provider.providerElement?.isAutoDispose ?? false) { - reporter.atNode( - read.node, - _code, - arguments: [read.provider.provider!], - ); - } - }); - } -} diff --git a/packages/riverpod_lint/lib/src/lints/avoid_ref_inside_state_dispose.dart b/packages/riverpod_lint/lib/src/lints/avoid_ref_inside_state_dispose.dart index d5d9a32ae..c85f2a5dc 100644 --- a/packages/riverpod_lint/lib/src/lints/avoid_ref_inside_state_dispose.dart +++ b/packages/riverpod_lint/lib/src/lints/avoid_ref_inside_state_dispose.dart @@ -1,4 +1,8 @@ import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/error/error.dart' + hide + // ignore: undefined_hidden_name, necessary to support broad analyzer versions + LintCode; import 'package:analyzer/error/listener.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; @@ -13,6 +17,7 @@ class AvoidRefInsideStateDispose extends RiverpodLintRule { static const _code = LintCode( name: 'avoid_ref_inside_state_dispose', problemMessage: "Avoid using 'Ref' inside State.dispose.", + errorSeverity: ErrorSeverity.WARNING, ); @override diff --git a/packages/riverpod_lint/lib/src/lints/functional_ref.dart b/packages/riverpod_lint/lib/src/lints/functional_ref.dart index 0d6498e72..35c46f1e5 100644 --- a/packages/riverpod_lint/lib/src/lints/functional_ref.dart +++ b/packages/riverpod_lint/lib/src/lints/functional_ref.dart @@ -5,11 +5,10 @@ import 'package:analyzer/error/error.dart' // ignore: undefined_hidden_name, necessary to support lower analyzer version LintCode; import 'package:analyzer/error/listener.dart'; -import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart'; import 'package:collection/collection.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; -import 'package:meta/meta.dart'; +import '../imports.dart'; import '../riverpod_custom_lint.dart'; class FunctionalRef extends RiverpodLintRule { @@ -19,6 +18,7 @@ class FunctionalRef extends RiverpodLintRule { name: 'functional_ref', problemMessage: 'Functional providers must receive a ref matching the provider name as their first positional parameter.', + errorSeverity: ErrorSeverity.WARNING, ); @override @@ -28,9 +28,6 @@ class FunctionalRef extends RiverpodLintRule { CustomLintContext context, ) { riverpodRegistry(context).addFunctionalProviderDeclaration((declaration) { - // Scoped providers don't need a ref - if (declaration.needsOverride) return; - final parameters = declaration.node.functionExpression.parameters!; final refNode = parameters.parameters.firstOrNull; @@ -92,7 +89,6 @@ class FunctionalRefFix extends RiverpodFix { changeBuilder.addDartFileEdit((builder) { final ref = builder.importRef(); - var toInsert = '$ref ref'; if (refNode != null) { toInsert = '$toInsert, '; @@ -106,6 +102,8 @@ class FunctionalRefFix extends RiverpodFix { return; } + if (refNode is! SimpleFormalParameter) return; + // No type specified, adding it. final changeBuilder = reporter.createChangeBuilder( message: 'Type as Ref', @@ -114,7 +112,6 @@ class FunctionalRefFix extends RiverpodFix { changeBuilder.addDartFileEdit((builder) { final ref = builder.importRef(); - if (!refNode.isExplicitlyTyped) { builder.addSimpleInsertion(refNode.name!.offset, '$ref '); return; @@ -137,41 +134,6 @@ extension LibraryForNode on AstNode { LibraryElement get library => (root as CompilationUnit).library; } -extension ImportFix on DartFileEditBuilder { - @useResult - String importRef() { - return _importWithPrefix('Ref'); - } - - @useResult - String _importWithPrefix(String name) { - final hooksRiverpodUri = - Uri(scheme: 'package', path: 'hooks_riverpod/hooks_riverpod.dart'); - final flutterRiverpodUri = - Uri(scheme: 'package', path: 'flutter_riverpod/flutter_riverpod.dart'); - final riverpodUri = Uri(scheme: 'package', path: 'riverpod/riverpod.dart'); - - if (importsLibrary(hooksRiverpodUri)) { - return _buildImport(hooksRiverpodUri, name); - } - - if (importsLibrary(flutterRiverpodUri)) { - return _buildImport(flutterRiverpodUri, name); - } - - return _buildImport(riverpodUri, name); - } - - String _buildImport(Uri uri, String name) { - final import = importLibraryElement(uri); - - final prefix = import.prefix; - if (prefix != null) return '$prefix.$name'; - - return name; - } -} - TypeAnnotation typeAnnotationFor(FormalParameter param) { if (param is DefaultFormalParameter) { return typeAnnotationFor(param.parameter); diff --git a/packages/riverpod_lint/lib/src/lints/missing_provider_scope.dart b/packages/riverpod_lint/lib/src/lints/missing_provider_scope.dart index eb1fba1fb..09eefd6e6 100644 --- a/packages/riverpod_lint/lib/src/lints/missing_provider_scope.dart +++ b/packages/riverpod_lint/lib/src/lints/missing_provider_scope.dart @@ -9,15 +9,18 @@ import 'package:collection/collection.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; +import '../imports.dart'; +import '../riverpod_custom_lint.dart'; import 'scoped_providers_should_specify_dependencies.dart'; -class MissingProviderScope extends DartLintRule { +class MissingProviderScope extends RiverpodLintRule { const MissingProviderScope() : super(code: _code); static const _code = LintCode( name: 'missing_provider_scope', problemMessage: 'Flutter applications should have a ProviderScope widget ' 'at the top of the widget tree.', + errorSeverity: ErrorSeverity.WARNING, ); @override @@ -48,7 +51,7 @@ class MissingProviderScope extends DartLintRule { } @override - List getFixes() => [AddProviderScope()]; + List getFixes() => [AddProviderScope()]; } class AddProviderScope extends DartFix { @@ -70,12 +73,13 @@ class AddProviderScope extends DartFix { ); changeBuilder.addDartFileEdit((builder) { + final providerScope = builder.importProviderScope(); final firstArgument = node.argumentList.arguments.firstOrNull; if (firstArgument == null) return; builder.addSimpleInsertion( firstArgument.offset, - 'ProviderScope(child: ', + '$providerScope(child: ', ); builder.addSimpleInsertion(firstArgument.end, ')'); }); diff --git a/packages/riverpod_lint/lib/src/lints/notifier_build.dart b/packages/riverpod_lint/lib/src/lints/notifier_build.dart index c878a9f15..e233f229e 100644 --- a/packages/riverpod_lint/lib/src/lints/notifier_build.dart +++ b/packages/riverpod_lint/lib/src/lints/notifier_build.dart @@ -18,6 +18,7 @@ class NotifierBuild extends RiverpodLintRule { name: 'notifier_build', problemMessage: 'Classes annotated by `@riverpod` must have the `build` method', + errorSeverity: ErrorSeverity.ERROR, ); @override diff --git a/packages/riverpod_lint/lib/src/lints/notifier_extends.dart b/packages/riverpod_lint/lib/src/lints/notifier_extends.dart index 5dff21726..0a8bcf156 100644 --- a/packages/riverpod_lint/lib/src/lints/notifier_extends.dart +++ b/packages/riverpod_lint/lib/src/lints/notifier_extends.dart @@ -1,6 +1,7 @@ +import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/error/error.dart' hide - // ignore: undefined_hidden_name, necessary to support lower analyzer version + // ignore: undefined_hidden_name, necessary to support broad analyzer versions LintCode; import 'package:analyzer/error/listener.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; @@ -12,12 +13,50 @@ String _generatedClassName(ProviderDeclaration declaration) { return '_\$${declaration.name.lexeme.public}'; } +/// Check that a generic type definition matches with a generic type usage. +/// +/// This is a strict check based on names, such that `` will +/// match with `` but not `` or `` or cases with extra/fewer +/// type arguments. +bool areGenericTypeArgumentsMatching( + List expectedTypeArguments, + List actualTypeArguments, +) { + // Are type arguments specified in the correct order? + var i = 0; + for (; + i < expectedTypeArguments.length && i < actualTypeArguments.length; + i++) { + final expectedType = expectedTypeArguments[i].name.lexeme; + final actualType = actualTypeArguments[i].toSource(); + + if (expectedType != actualType) { + return false; + } + } + + // Is a type argument missing? + if (i != expectedTypeArguments.length || i != actualTypeArguments.length) { + return false; + } + + return true; +} + +/// Convert a [TypeParameterList] to a string. +String genericsDisplayStringFor(TypeParameterList? typeParameters) { + if (typeParameters == null) return ''; + + return '<${typeParameters.typeParameters.map((e) => e.name).join(', ')}>'; +} + class NotifierExtends extends RiverpodLintRule { const NotifierExtends() : super(code: _code); static const _code = LintCode( name: 'notifier_extends', problemMessage: r'Classes annotated by @riverpod must extend _$ClassName', + errorSeverity: ErrorSeverity.WARNING, ); @override @@ -41,11 +80,26 @@ class NotifierExtends extends RiverpodLintRule { reporter.atNode(extendsClause.superclass, _code); return; } + + final expectedTypeArguments = + declaration.node.typeParameters?.typeParameters ?? + const []; + final actualTypeArguments = + extendsClause.superclass.typeArguments?.arguments ?? + const []; + if (!areGenericTypeArgumentsMatching( + expectedTypeArguments, + actualTypeArguments, + )) { + // No type specified. Underlining the ref name + reporter.atNode(extendsClause.superclass, _code); + return; + } }); } @override - List getFixes() => [NotifierExtendsFix()]; + List getFixes() => [NotifierExtendsFix()]; } class NotifierExtendsFix extends RiverpodFix { @@ -63,10 +117,15 @@ class NotifierExtendsFix extends RiverpodFix { return; } + final expectedGenerics = genericsDisplayStringFor( + declaration.node.typeParameters, + ); final expectedClassName = _generatedClassName(declaration); + final expectedExtends = '$expectedClassName$expectedGenerics'; + final extendsClause = declaration.node.extendsClause; final changeBuilder = reporter.createChangeBuilder( - message: 'Extend $expectedClassName', + message: 'Extend $expectedExtends', priority: 90, ); @@ -75,7 +134,7 @@ class NotifierExtendsFix extends RiverpodFix { // No "extends" clause builder.addSimpleInsertion( declaration.name.end, - ' extends $expectedClassName', + ' extends $expectedExtends', ); return; } @@ -83,7 +142,7 @@ class NotifierExtendsFix extends RiverpodFix { // There is an "extends" clause but the extended type is wrong builder.addSimpleReplacement( extendsClause.superclass.sourceRange, - expectedClassName, + expectedExtends, ); }); }); diff --git a/packages/riverpod_lint/lib/src/lints/only_use_keep_alive_inside_keep_alive.dart b/packages/riverpod_lint/lib/src/lints/only_use_keep_alive_inside_keep_alive.dart new file mode 100644 index 000000000..eb60dc017 --- /dev/null +++ b/packages/riverpod_lint/lib/src/lints/only_use_keep_alive_inside_keep_alive.dart @@ -0,0 +1,48 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/error/error.dart' + hide + // ignore: undefined_hidden_name, necessary to support broad analyzer versions + LintCode; +import 'package:analyzer/error/listener.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; + +import '../riverpod_custom_lint.dart'; + +class OnlyUseKeepAliveInsideKeepAlive extends RiverpodLintRule { + const OnlyUseKeepAliveInsideKeepAlive() : super(code: _code); + + static const _code = LintCode( + name: 'only_use_keep_alive_inside_keep_alive', + problemMessage: 'If a provider is declared as `keepAlive`, ' + 'it can only use providers that are also declared as `keepAlive.', + correctionMessage: 'Either stop marking this provider as `keepAlive` or ' + 'remove `keepAlive` from the used provider.', + errorSeverity: ErrorSeverity.WARNING, + ); + + @override + void run( + CustomLintResolver resolver, + ErrorReporter reporter, + CustomLintContext context, + ) { + riverpodRegistry(context).addRefInvocation((node) { + if (node is! RefDependencyInvocation) return; + final dependencyElement = node.listenable.provider?.providerElement; + // This only applies if the watched provider is a generated one. + if (dependencyElement is! GeneratorProviderDeclarationElement) return; + if (!dependencyElement.isAutoDispose) return; + + final provider = node.node + .thisOrAncestorOfType() + ?.provider; + if (provider == null) return; + + // The enclosing provider is "autoDispose", so it is allowed to use other "autoDispose" providers + if (provider.providerElement.isAutoDispose) return; + + reporter.atNode(node.node, _code); + }); + } +} diff --git a/packages/riverpod_lint/lib/src/lints/prefer_final_provider.dart b/packages/riverpod_lint/lib/src/lints/prefer_final_provider.dart deleted file mode 100644 index bb087e476..000000000 --- a/packages/riverpod_lint/lib/src/lints/prefer_final_provider.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:analyzer/error/listener.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; - -import '../riverpod_custom_lint.dart'; - -class PreferFinalProvider extends RiverpodLintRule { - const PreferFinalProvider() : super(code: _code); - - static const _code = LintCode( - name: 'prefer_final_provider', - problemMessage: 'Providers should be declared as final variables', - ); - - @override - void run( - CustomLintResolver resolver, - ErrorReporter reporter, - CustomLintContext context, - ) { - riverpodRegistry(context).addLegacyProviderDeclaration((provider) { - if (!provider.node.isFinal) { - reporter.atNode(provider.node, _code); - } - }); - } -} diff --git a/packages/riverpod_lint/lib/src/lints/protected_notifier_properties.dart b/packages/riverpod_lint/lib/src/lints/protected_notifier_properties.dart index 528ce685e..8543689bd 100644 --- a/packages/riverpod_lint/lib/src/lints/protected_notifier_properties.dart +++ b/packages/riverpod_lint/lib/src/lints/protected_notifier_properties.dart @@ -3,7 +3,9 @@ import 'package:analyzer/error/listener.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; -class ProtectedNotifierProperties extends DartLintRule { +import '../riverpod_custom_lint.dart'; + +class ProtectedNotifierProperties extends RiverpodLintRule { const ProtectedNotifierProperties() : super(code: _code); static const _code = LintCode( @@ -30,18 +32,16 @@ class ProtectedNotifierProperties extends DartLintRule { return; } - final enclosingClass = propertyAccess - .thisOrAncestorOfType() - ?.declaredElement; - if (enclosingClass == null) return; + final enclosingClass = + propertyAccess.thisOrAncestorOfType(); + final enclosingClassElement = enclosingClass?.declaredElement; + if (enclosingClass == null || enclosingClassElement == null) return; - final isAnnotatedWithRiverpod = - riverpodType.hasAnnotationOfExact(enclosingClass); - if (!isAnnotatedWithRiverpod) return; + if (enclosingClass.riverpod == null) return; final targetType = propertyAccess.target?.staticType; if (targetType == null) return; - if (targetType == enclosingClass.thisType) return; + if (targetType == enclosingClassElement.thisType) return; if (!anyNotifierType.isAssignableFromType(targetType)) return; reporter.atNode(propertyAccess.propertyName, _code); diff --git a/packages/riverpod_lint/lib/src/lints/provider_dependencies.dart b/packages/riverpod_lint/lib/src/lints/provider_dependencies.dart index 49eeff65c..defd93d77 100644 --- a/packages/riverpod_lint/lib/src/lints/provider_dependencies.dart +++ b/packages/riverpod_lint/lib/src/lints/provider_dependencies.dart @@ -1,77 +1,209 @@ -import 'dart:math'; - +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/diagnostic/diagnostic.dart'; import 'package:analyzer/error/error.dart' hide // ignore: undefined_hidden_name, necessary to support lower analyzer version LintCode; import 'package:analyzer/error/listener.dart'; -import 'package:collection/collection.dart'; +import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart'; +import 'package:analyzer_plugin/utilities/range_factory.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; +import '../imports.dart'; +import '../object_utils.dart'; import '../riverpod_custom_lint.dart'; const _fixDependenciesPriority = 100; -class _ExtraAndMissingDependencies { - final List extra = []; - final List missing = []; +class _LocatedProvider { + _LocatedProvider(this.provider, this.node); + + final ProviderDeclarationElement provider; + final AstNode node; } -extension on GeneratorProviderDeclaration { - Iterable findScopedDependencies() sync* { - for (final dependency - in refInvocations.whereType()) { - final dependencyElement = dependency.provider.providerElement; - if (dependencyElement is! GeneratorProviderDeclarationElement) { - // If we cannot statically determine the dependencies of the dependency, - // we cannot check if the provider is missing a dependency. - return; - } - if (dependencyElement.isScoped) { - yield dependency; - } +class _MyDiagnostic implements DiagnosticMessage { + _MyDiagnostic({ + required this.message, + required this.filePath, + required this.length, + required this.offset, + }); + + @override + String? get url => null; + + final String message; + + @override + final String filePath; + + @override + final int length; + + @override + final int offset; + + @override + String messageText({required bool includeUrl}) => message; +} + +class _FindNestedDependency extends RecursiveRiverpodAstVisitor { + _FindNestedDependency( + this.accumulatedDependencyList, { + required this.onProvider, + this.visitStates = false, + }); + + final AccumulatedDependencyList accumulatedDependencyList; + final bool visitStates; + final void Function( + _LocatedProvider provider, + AccumulatedDependencyList list, { + required bool checkOverrides, + }) onProvider; + + _FindNestedDependency copyWith({ + AccumulatedDependencyList? accumulatedDependencyList, + bool? visitStates, + void Function(AccumulatedDependencyList child)? parentAddChild, + }) { + return _FindNestedDependency( + accumulatedDependencyList ?? this.accumulatedDependencyList, + onProvider: onProvider, + visitStates: visitStates ?? this.visitStates, + ); + } + + @override + void visitComment(Comment node) { + // Identifiers in comments shouldn't count. + } + + @override + void visitWidgetDeclaration(WidgetDeclaration node) { + super.visitWidgetDeclaration(node); + + if (node is! StatefulWidgetDeclaration) return; + + final stateAst = node.findStateAst(); + + // If targeting a StatefulWidget, we also need to check the state class. + if (stateAst != null) { + stateAst.node.accept(copyWith(visitStates: true)); + } + } + + @override + void visitStateDeclaration(StateDeclaration node) { + if (!visitStates) return; + + super.visitStateDeclaration(node); + } + + @override + void visitProviderOverrideExpression(ProviderOverrideExpression node) { + // Disable the lint for overrides. But only if the override isn't + // `overrides: [provider]`. + if (node.node.safeCast()?.providerListenable != null) { + super.visitProviderOverrideExpression(node); + return; } } - _ExtraAndMissingDependencies findExtraAndMissingDependencies() { - final result = _ExtraAndMissingDependencies(); + @override + void visitProviderIdentifier(ProviderIdentifier node) { + super.visitProviderIdentifier(node); - final dependencies = annotation.dependencies?.dependencies; - final scopedInvocations = findScopedDependencies().toList(); + onProvider( + _LocatedProvider(node.providerElement, node.node), + accumulatedDependencyList, + checkOverrides: false, + ); + } - for (final scopedDependency in scopedInvocations) { - final dependencyName = scopedDependency.provider.providerElement?.name; + @override + void visitAccumulatedDependencyList(AccumulatedDependencyList node) { + node.node.visitChildren( + copyWith( + accumulatedDependencyList: node, + ), + ); + } - if (dependencies == null || - !dependencies.any((e) => e.provider.name == dependencyName)) { - result.missing.add(scopedDependency); + @override + void visitIdentifierDependencies(IdentifierDependencies node) { + super.visitIdentifierDependencies(node); + + if (_isSelfReference(node.dependencies)) return; + + if (node.dependencies.dependencies case final deps?) { + for (final dep in deps) { + onProvider( + _LocatedProvider(dep, node.node), + accumulatedDependencyList, + checkOverrides: false, + ); } } + } + + /// If an object references itself, so we don't count those dependencies + /// as "used". + bool _isSelfReference(DependenciesAnnotationElement node) { + return node == accumulatedDependencyList.dependencies?.element; + } + + @override + void visitNamedTypeDependencies(NamedTypeDependencies node) { + super.visitNamedTypeDependencies(node); + + if (_isSelfReference(node.dependencies)) return; - if (dependencies != null) { - for (final dependency in dependencies) { - final isDependencyUsed = scopedInvocations.any( - (e) => e.provider.providerElement?.name == dependency.provider.name, + final type = node.node.type; + if (type == null) return; + late final isWidget = widgetType.isAssignableFromType(type); + + if (node.dependencies.dependencies case final deps?) { + for (final dep in deps) { + onProvider( + _LocatedProvider(dep, node.node), + accumulatedDependencyList, + // We check overrides only for Widget instances, as we can't guarantee + // that non-widget instances use a "ref" that's a child of the overrides. + checkOverrides: isWidget, ); - if (!isDependencyUsed) { - result.extra.add(dependency); - } } } - - return result; } } +class _Data { + _Data({ + required this.list, + required this.usedDependencies, + }); + + final AccumulatedDependencyList list; + final List<_LocatedProvider> usedDependencies; +} + +extension on AccumulatedDependencyList { + AstNode get target => + riverpod?.annotation.dependencyList?.node ?? + riverpod?.annotation.node ?? + dependencies?.dependencies.node ?? + node; +} + class ProviderDependencies extends RiverpodLintRule { const ProviderDependencies() : super(code: _code); static const _code = LintCode( name: 'provider_dependencies', - problemMessage: - 'If a provider depends on providers which specify "dependencies", ' - 'they should themselves specify "dependencies" and include all the scoped providers. ', + problemMessage: '{0}', + errorSeverity: ErrorSeverity.WARNING, ); @override @@ -80,24 +212,94 @@ class ProviderDependencies extends RiverpodLintRule { ErrorReporter reporter, CustomLintContext context, ) { - riverpodRegistry(context).addGeneratorProviderDeclaration((declaration) { - final extraAndMissing = declaration.findExtraAndMissingDependencies(); + riverpodRegistry(context).addAccumulatedDependencyList((list) { + // Ignore ProviderScopes. We only check annotations + if (list.overrides != null) return; - for (final extra in extraAndMissing.extra) { - reporter.atNode(extra.node, _code); + // If the State has an associated widget, we don't visit it. + // The widget will already visit the state. + if (list.node.safeCast()?.state?.findWidgetAst() != + null) { + return; } - if (extraAndMissing.missing.isNotEmpty) { - reporter.atNode( - declaration.annotation.dependencies?.node ?? - declaration.annotation.annotation, - _code, - ); + + final usedDependencies = <_LocatedProvider>[]; + + final visitor = _FindNestedDependency( + list, + onProvider: (locatedProvider, list, {required checkOverrides}) { + final provider = locatedProvider.provider; + if (provider is! GeneratorProviderDeclarationElement) return; + if (!provider.isScoped) return; + + // Check if the provider is overridden. If it is, the provider doesn't + // count towards the unused/missing dependencies + if (checkOverrides && + list.isSafelyAccessibleAfterOverrides(provider)) { + return; + } + + usedDependencies.add(locatedProvider); + }, + ); + + list.node.accept(visitor); + + var unusedDependencies = list.allDependencies + ?.where( + (dependency) => + !usedDependencies.any((e) => e.provider == dependency.provider), + ) + .toList(); + final missingDependencies = usedDependencies + .where( + (dependency) => + list.allDependencies + ?.every((e) => e.provider != dependency.provider) ?? + true, + ) + .toSet(); + + unusedDependencies ??= const []; + if (unusedDependencies.isEmpty && missingDependencies.isEmpty) return; + + final message = StringBuffer(); + if (unusedDependencies.isNotEmpty) { + message.write('Unused dependencies: '); + message.writeAll(unusedDependencies.map((e) => e.provider.name), ', '); } + if (missingDependencies.isNotEmpty) { + if (unusedDependencies.isNotEmpty) { + message.write(' | '); + } + message.write('Missing dependencies: '); + message.writeAll(missingDependencies.map((e) => e.provider.name), ', '); + } + + late final unit = list.node.thisOrAncestorOfType(); + late final source = unit?.declaredElement?.source; + + reporter.atNode( + list.target, + _code, + arguments: [message.toString()], + contextMessages: [ + for (final dependency in missingDependencies) + if (source != null) + _MyDiagnostic( + message: dependency.provider.name, + filePath: source.fullName, + offset: dependency.node.offset, + length: dependency.node.length, + ), + ], + data: _Data(usedDependencies: usedDependencies, list: list), + ); }); } @override - List getFixes() => [_ProviderDependenciesFix()]; + List getFixes() => [_ProviderDependenciesFix()]; } class _ProviderDependenciesFix extends RiverpodFix { @@ -109,104 +311,139 @@ class _ProviderDependenciesFix extends RiverpodFix { AnalysisError analysisError, List others, ) { - riverpodRegistry(context).addGeneratorProviderDeclaration((declaration) { - if (!declaration.node.sourceRange.intersects(analysisError.sourceRange)) { - return; - } - - final scopedDependencies = declaration.findScopedDependencies().toList(); - final dependenciesNode = declaration.annotation.dependencies?.node; + final data = analysisError.data; + if (data is! _Data) return; - final newDependencies = scopedDependencies.isEmpty - ? null - : '[${scopedDependencies.map((e) => e.provider.providerElement?.name).join(', ')}]'; + final scopedDependencies = + data.usedDependencies.map((e) => e.provider).toSet(); + final newDependencies = scopedDependencies.isEmpty + ? null + : '[${scopedDependencies.map((e) => e.name).join(', ')}]'; - if (newDependencies == null) { - // Should never be null, but just in case - if (dependenciesNode == null) return; - - final changeBuilder = reporter.createChangeBuilder( - message: 'Remove "dependencies"', - priority: _fixDependenciesPriority, - ); - changeBuilder.addDartFileEdit((builder) { - if (declaration.annotation.keepAliveNode == null) { - // Only "dependencies" is specified in the annotation. - // So instead of @Riverpod(dependencies: []) -> @Riverpod(), - // we can do @Riverpod(dependencies: []) -> @riverpod - builder.addSimpleReplacement( - declaration.annotation.annotation.sourceRange, - '@riverpod', - ); - } else { - // Some parameters are specified in the annotation, so we remove - // only the "dependencies" parameter. - - final end = min( - // End before the closing parenthesis or before the next parameter - declaration - .annotation.annotation.arguments!.rightParenthesis.offset, - dependenciesNode.endToken.next!.end, - ); - - final start = max( - // Start after the opening parenthesis or after the next parameter - declaration.annotation.annotation.arguments!.leftParenthesis.end, - dependenciesNode.beginToken.previous!.end, - ); - - builder.addDeletion(sourceRangeFrom(start: start, end: end)); - } - }); + final riverpodAnnotation = data.list.riverpod?.annotation; + final dependencies = data.list.dependencies; + if (newDependencies == null) { + if (riverpodAnnotation == null && dependencies == null) { + // No annotation found, so we can't fix anything. + // This shouldn't happen but prevents errors in case of bad states. return; } - if (dependenciesNode == null) { - final changeBuilder = reporter.createChangeBuilder( - message: 'Specify "dependencies"', - priority: _fixDependenciesPriority, - ); - changeBuilder.addDartFileEdit((builder) { - final annotationArguments = - declaration.annotation.annotation.arguments; - if (annotationArguments == null) { - // No argument list found. We are using the @riverpod annotation. - builder.addSimpleReplacement( - declaration.annotation.annotation.sourceRange, - '@Riverpod(dependencies: $newDependencies)', - ); - } else { - // Argument list found, we are using the @Riverpod() annotation - - // We want to insert the "dependencies" parameter after the last - final insertOffset = - annotationArguments.arguments.lastOrNull?.end ?? - annotationArguments.leftParenthesis.end; - - builder.addSimpleInsertion( - insertOffset, - ', dependencies: $newDependencies', - ); - } - }); + final changeBuilder = reporter.createChangeBuilder( + message: 'Remove "dependencies"', + priority: _fixDependenciesPriority, + ); + changeBuilder.addDartFileEdit((builder) { + if (riverpodAnnotation case final riverpod?) { + _riverpodRemoveDependencies(builder, riverpod); + } else if (dependencies != null) { + builder.addDeletion(data.list.dependencies!.node.sourceRange); + } + }); - return; - } + return; + } + + final dependencyList = data.list.riverpod?.annotation.dependencyList ?? + data.list.dependencies?.dependencies; + if (dependencyList == null) { final changeBuilder = reporter.createChangeBuilder( - message: 'Update "dependencies"', + message: 'Specify "dependencies"', priority: _fixDependenciesPriority, ); changeBuilder.addDartFileEdit((builder) { - final dependencies = scopedDependencies - .map((e) => e.provider.providerElement?.name) - .join(', '); + if (riverpodAnnotation case final riverpod?) { + _riverpodSpecifyDependencies(builder, riverpod, newDependencies); + } else { + final dep = builder.importDependenciesClass(); + builder.addSimpleInsertion( + data.list.node.offset, + '@$dep($newDependencies)\n', + ); + } + }); + + return; + } + + if (riverpodAnnotation == null && dependencies == null) { + // No annotation found, so we can't fix anything. + // This shouldn't happen but prevents errors in case of bad states. + return; + } + final changeBuilder = reporter.createChangeBuilder( + message: 'Update "dependencies"', + priority: _fixDependenciesPriority, + ); + changeBuilder.addDartFileEdit((builder) { + if (riverpodAnnotation != null) { + final dependencies = scopedDependencies.map((e) => e.name).join(', '); builder.addSimpleReplacement( - dependenciesNode.expression.sourceRange, + dependencyList.node!.sourceRange, '[$dependencies]', ); - }); + } else { + final dep = builder.importDependenciesClass(); + builder.addSimpleReplacement( + data.list.dependencies!.node.sourceRange, + '@$dep($newDependencies)', + ); + } }); } + + void _riverpodRemoveDependencies( + DartFileEditBuilder builder, + RiverpodAnnotation riverpod, + ) { + if (riverpod.keepAliveNode == null) { + final _riverpod = builder.importRiverpod(); + // Only "dependencies" is specified in the annotation. + // So instead of @Riverpod(dependencies: []) -> @Riverpod(), + // we can do @Riverpod(dependencies: []) -> @riverpod + builder.addSimpleReplacement( + riverpod.node.sourceRange, + '@$_riverpod', + ); + return; + } + + // Some parameters are specified in the annotation, so we remove + // only the "dependencies" parameter. + final dependenciesNode = riverpod.dependenciesNode!; + final argumentList = riverpod.node.arguments!; + + builder.addDeletion( + range.nodeInList(argumentList.arguments, dependenciesNode), + ); + } + + void _riverpodSpecifyDependencies( + DartFileEditBuilder builder, + RiverpodAnnotation riverpod, + String newDependencies, + ) { + final annotationArguments = riverpod.node.arguments; + if (annotationArguments == null) { + final _riverpod = builder.importRiverpodClass(); + // No argument list found. We are using the @riverpod annotation. + builder.addSimpleReplacement( + riverpod.node.sourceRange, + '@$_riverpod(dependencies: $newDependencies)', + ); + } else { + // Argument list found, we are using the @Riverpod() annotation + + // We want to insert the "dependencies" parameter after the last + final insertOffset = annotationArguments.arguments.lastOrNull?.end ?? + annotationArguments.leftParenthesis.end; + + builder.addSimpleInsertion( + insertOffset, + ', dependencies: $newDependencies', + ); + } + } } diff --git a/packages/riverpod_lint/lib/src/lints/provider_parameters.dart b/packages/riverpod_lint/lib/src/lints/provider_parameters.dart index c16911a5b..431c12b34 100644 --- a/packages/riverpod_lint/lib/src/lints/provider_parameters.dart +++ b/packages/riverpod_lint/lib/src/lints/provider_parameters.dart @@ -1,9 +1,13 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/error/error.dart' + hide + // ignore: undefined_hidden_name, necessary to support broad analyzer versions + LintCode; import 'package:analyzer/error/listener.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; -import '../object_utils.dart'; import '../riverpod_custom_lint.dart'; class ProviderParameters extends RiverpodLintRule { @@ -15,6 +19,7 @@ class ProviderParameters extends RiverpodLintRule { 'Meaning either the values should be cached, or the parameters should override ==', url: 'https://riverpod.dev/docs/concepts/modifiers/family#passing-multiple-parameters-to-a-family', + errorSeverity: ErrorSeverity.WARNING, ); @override @@ -23,7 +28,10 @@ class ProviderParameters extends RiverpodLintRule { ErrorReporter reporter, CustomLintContext context, ) { - riverpodRegistry(context).addProviderListenableExpression((expression) { + context.registry.addExpression((node) { + final expression = node.providerListenable; + if (expression == null) return; + final arguments = expression.familyArguments; if (arguments == null) return; @@ -48,12 +56,7 @@ class ProviderParameters extends RiverpodLintRule { final operatorEqual = instantiatedObject?.enclosingElement3.recursiveGetMethod('=='); - final isEqualFromObjectMethod = operatorEqual?.enclosingElement3 - .safeCast() - ?.thisType - .isDartCoreObject; - - if (operatorEqual == null || (isEqualFromObjectMethod ?? true)) { + if (operatorEqual == null) { // Doing `provider(new Class())` is bad if the class does not override == reporter.atNode(value, _code); } @@ -73,6 +76,8 @@ extension on ConstructorElement { extension on InterfaceElement { MethodElement? recursiveGetMethod(String name) { + if (thisType.isDartCoreObject) return null; + final thisMethod = getMethod(name); if (thisMethod != null) return thisMethod; diff --git a/packages/riverpod_lint/lib/src/lints/riverpod_syntax_error.dart b/packages/riverpod_lint/lib/src/lints/riverpod_syntax_error.dart new file mode 100644 index 000000000..ca5f279cf --- /dev/null +++ b/packages/riverpod_lint/lib/src/lints/riverpod_syntax_error.dart @@ -0,0 +1,49 @@ +import 'package:analyzer/error/error.dart' + hide + // ignore: undefined_hidden_name, necessary to support broad analyzer versions + LintCode; +import 'package:analyzer/error/listener.dart'; +import 'package:analyzer/source/source_range.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; + +import '../riverpod_custom_lint.dart'; + +class RiverpodSyntaxError extends RiverpodLintRule { + const RiverpodSyntaxError() : super(code: _code); + + static const _code = LintCode( + name: 'riverpod_syntax_error', + problemMessage: '{0}', + errorSeverity: ErrorSeverity.ERROR, + ); + + @override + void run( + CustomLintResolver resolver, + ErrorReporter reporter, + CustomLintContext context, + ) { + riverpodRegistry(context).addRiverpodAnalysisError((error) { + if (error.code == RiverpodAnalysisErrorCode.missingNotifierBuild) { + return; + } + + final location = switch (error) { + RiverpodAnalysisError(:final targetElement?) => + SourceRange(targetElement.nameOffset, targetElement.nameLength), + RiverpodAnalysisError(:final targetNode?) => targetNode.sourceRange, + _ => null, + }; + + if (location == null) return; + + reporter.atOffset( + errorCode: _code, + offset: location.offset, + length: location.length, + arguments: [error.message], + ); + }); + } +} diff --git a/packages/riverpod_lint/lib/src/lints/scoped_providers_should_specify_dependencies.dart b/packages/riverpod_lint/lib/src/lints/scoped_providers_should_specify_dependencies.dart index 2386f90f6..4eb8868bf 100644 --- a/packages/riverpod_lint/lib/src/lints/scoped_providers_should_specify_dependencies.dart +++ b/packages/riverpod_lint/lib/src/lints/scoped_providers_should_specify_dependencies.dart @@ -1,4 +1,8 @@ import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/error/error.dart' + hide + // ignore: undefined_hidden_name, necessary to support broad analyzer versions + LintCode; import 'package:analyzer/error/listener.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; @@ -38,6 +42,7 @@ class ScopedProvidersShouldSpecifyDependencies extends RiverpodLintRule { name: 'scoped_providers_should_specify_dependencies', problemMessage: 'Providers which are overridden in a non-root ProviderContainer/ProviderScope should specify dependencies.', + errorSeverity: ErrorSeverity.WARNING, ); @override @@ -46,56 +51,60 @@ class ScopedProvidersShouldSpecifyDependencies extends RiverpodLintRule { ErrorReporter reporter, CustomLintContext context, ) { - void checkScopedOverrideList( - ProviderOverrideList? overrideList, - ) { - final overrides = overrideList?.overrides; - if (overrides == null) return; - - for (final override in overrides) { - final provider = override.providerElement; - // We can only know statically if a provider is scoped on generator providers - if (provider is! GeneratorProviderDeclarationElement) continue; - - if (!provider.isScoped) { - reporter.atNode(override.expression, _code); - } + riverpodRegistry(context) + ..addProviderContainerInstanceCreationExpression((node) { + handleProviderContainerInstanceCreation(node, reporter); + }) + ..addProviderScopeInstanceCreationExpression((node) { + handleProviderScopeInstanceCreation(node, reporter); + }); + } + + void checkScopedOverrideList( + ProviderOverrideList? overrideList, + ErrorReporter reporter, + ) { + final overrides = overrideList?.overrides; + if (overrides == null) return; + + for (final override in overrides) { + final provider = override.provider?.providerElement; + + // We can only know statically if a provider is scoped on generator providers + if (provider is! GeneratorProviderDeclarationElement) continue; + if (!provider.isScoped) { + reporter.atNode(override.node, code); } } + } - riverpodRegistry(context) - ..addProviderScopeInstanceCreationExpression((expression) { - final isScoped = isProviderScopeScoped(expression); - if (!isScoped) return; + void handleProviderScopeInstanceCreation( + ProviderScopeInstanceCreationExpression expression, + ErrorReporter reporter, + ) { + final isScoped = isProviderScopeScoped(expression); + if (!isScoped) return; - checkScopedOverrideList(expression.overrides); - }) - ..addProviderContainerInstanceCreationExpression((expression) { - final hasParent = expression.node.argumentList.arguments - .whereType() - // TODO handle parent:null. - // This might be doable by checking that the expression's - // static type is non-nullable - .any((e) => e.name.label.name == 'parent'); - - // No parent: parameter found, therefore ProviderContainer is never scoped - if (!hasParent) return; - - checkScopedOverrideList(expression.overrides); - }); + checkScopedOverrideList(expression.overrides, reporter); + } + + void handleProviderContainerInstanceCreation( + ProviderContainerInstanceCreationExpression expression, + ErrorReporter reporter, + ) { + // This might be doable by checking that the expression's + // static type is non-nullable + final hasParent = expression.parent != null; + + // No parent: parameter found, therefore ProviderContainer is never scoped + if (!hasParent) return; + + checkScopedOverrideList(expression.overrides, reporter); } bool isProviderScopeScoped( ProviderScopeInstanceCreationExpression expression, ) { - final hasParentParameter = expression.node.argumentList.arguments - .whereType() - // TODO handle parent:null. - // This might be doable by checking that the expression's - // static type is non-nullable - .any((e) => e.name.label.name == 'parent'); - if (hasParentParameter) return true; - // in runApp(ProviderScope(..)) the direct parent of the ProviderScope // is an ArgumentList. final enclosingExpression = expression.node.parent?.parent; diff --git a/packages/riverpod_lint/lib/src/lints/unknown_scoped_usage.dart b/packages/riverpod_lint/lib/src/lints/unknown_scoped_usage.dart new file mode 100644 index 000000000..b3541907a --- /dev/null +++ b/packages/riverpod_lint/lib/src/lints/unknown_scoped_usage.dart @@ -0,0 +1,78 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/error/error.dart' + hide + // ignore: undefined_hidden_name, necessary to support broad analyzer versions + LintCode; +import 'package:analyzer/error/listener.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; +import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; + +import '../object_utils.dart'; +import '../riverpod_custom_lint.dart'; + +class UnknownScopedUsage extends RiverpodLintRule { + const UnknownScopedUsage() : super(code: _code); + + static const _code = LintCode( + name: 'unknown_scoped_usage', + problemMessage: + 'A provider was used, but could not find the associated `ref`.', + errorSeverity: ErrorSeverity.WARNING, + ); + + @override + void run( + CustomLintResolver resolver, + ErrorReporter reporter, + CustomLintContext context, + ) { + riverpodRegistry(context).addProviderIdentifier((identifier) { + // Ignore [provider] identifiers in comments. + if (identifier.node.parent is CommentReference) return; + + final providerElement = identifier.providerElement; + if (providerElement is! GeneratorProviderDeclarationElement) return; + if (!providerElement.isScoped) return; + + final enclosingMethodInvocation = + identifier.node.thisOrAncestorOfType(); + final refInvocation = enclosingMethodInvocation?.refInvocation + .safeCast(); + final widgetRefInvocation = enclosingMethodInvocation?.widgetRefInvocation + .safeCast(); + + // If in a ref expression, and the associated ref is the checked provider, + // then it's fine. + // This is to reject cases like `ref.watch(something(provider))`. + if (refInvocation?.listenable.provider == identifier || + widgetRefInvocation?.listenable.provider == identifier) { + return; + } + + // .parent is used because providers count as overrides. + // We don't want to count "provider" as an override, and want to focus + // on "provider.overrideX". + final override = identifier.node.parent + ?.thisOrAncestorOfType() + ?.providerOverride; + // The identifier is in override, so it's fine. + if (override?.provider == identifier) return; + + final enclosingConstructorType = identifier + .node.staticParameterElement?.enclosingElement3 + .safeCast() + ?.returnType; + // Silence the warning if passed to a widget constructor. + if (enclosingConstructorType != null && + widgetType.isAssignableFromType(enclosingConstructorType)) { + return; + } + + reporter.atNode( + identifier.node, + code, + ); + }); + } +} diff --git a/packages/riverpod_lint/lib/src/migration/missing_legacy_import.dart b/packages/riverpod_lint/lib/src/migration/missing_legacy_import.dart new file mode 100644 index 000000000..e0be586d6 --- /dev/null +++ b/packages/riverpod_lint/lib/src/migration/missing_legacy_import.dart @@ -0,0 +1,108 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:analyzer/error/error.dart' + hide + // ignore: undefined_hidden_name, necessary to support broad analyzer versions + LintCode; +import 'package:analyzer/error/listener.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; + +import '../riverpod_custom_lint.dart'; + +class MissingLegacyImport extends RiverpodLintRule { + const MissingLegacyImport() : super(code: _code); + + static const _code = LintCode( + name: 'missing_legacy_import', + problemMessage: + 'StateProvider/StateNotifierProvider/ChangeNotifierProvider/StateNotifier were used ' + 'without importing `package:flutter_riverpod/legacy.dart`.', + ); + + @override + void run( + CustomLintResolver resolver, + ErrorReporter reporter, + CustomLintContext context, + ) { + void handleType(AstNode node, DartType? type, String? name) { + // Skip resolved types. Even if the import is missing, + // chances are the import was indirectly imported. + if (type != null && type is! InvalidType) { + return; + } + + const legacyIdentifiers = [ + 'StateProvider', + 'StateNotifierProvider', + 'StateNotifier', + 'ChangeNotifierProvider', + ]; + + if (!legacyIdentifiers.contains(name)) return; + + final unit = node.thisOrAncestorOfType()!; + + final imports = unit.directives + .whereType() + .map((e) => e.uri.stringValue); + + final compatibleImports = [ + 'package:flutter_riverpod/legacy.dart', + 'package:hooks_riverpod/legacy.dart', + if (name != 'ChangeNotifierProvider') 'package:riverpod/legacy.dart', + ]; + + if (compatibleImports.any(imports.contains)) return; + + reporter.atNode(node, code); + } + + context.registry.addNamedType((node) { + handleType(node, node.type, node.name2.lexeme); + }); + + context.registry.addIdentifier((node) { + handleType(node, node.staticType, node.name); + }); + } + + @override + List getFixes() => [_AddMissingLegacyImport()]; +} + +class _AddMissingLegacyImport extends DartFix { + @override + void run( + CustomLintResolver resolver, + ChangeReporter reporter, + CustomLintContext context, + AnalysisError analysisError, + List others, + ) { + void handleType(AstNode node, String? name) { + if (!node.sourceRange.intersects(analysisError.sourceRange)) return; + + final toImport = name == 'ChangeNotifierProvider' + ? 'package:flutter_riverpod/legacy.dart' + : 'package:riverpod/legacy.dart'; + + final changeBuilder = reporter.createChangeBuilder( + message: 'Import "$toImport"', + priority: 100, + ); + + changeBuilder.addDartFileEdit((builder) { + builder.addSimpleInsertion(0, "import '$toImport';\n"); + }); + } + + context.registry.addIdentifier((node) { + handleType(node, node.name); + }); + + context.registry.addNamedType((node) { + handleType(node, node.name2.lexeme); + }); + } +} diff --git a/packages/riverpod_lint/lib/src/object_utils.dart b/packages/riverpod_lint/lib/src/object_utils.dart index 1a932ca44..1b14ef467 100644 --- a/packages/riverpod_lint/lib/src/object_utils.dart +++ b/packages/riverpod_lint/lib/src/object_utils.dart @@ -1,3 +1,5 @@ +import 'package:analyzer/dart/ast/ast.dart'; + extension ObjectUtils on T? { R? safeCast() { final that = this; @@ -11,3 +13,13 @@ extension ObjectUtils on T? { return cb?.call(that); } } + +extension AstUtils on AstNode { + Iterable get ancestors sync* { + var parent = this.parent; + while (parent != null) { + yield parent; + parent = parent.parent; + } + } +} diff --git a/packages/riverpod_lint/lib/src/riverpod_custom_lint.dart b/packages/riverpod_lint/lib/src/riverpod_custom_lint.dart index d1efe4798..892770e75 100644 --- a/packages/riverpod_lint/lib/src/riverpod_custom_lint.dart +++ b/packages/riverpod_lint/lib/src/riverpod_custom_lint.dart @@ -60,6 +60,9 @@ abstract class RiverpodLintRule extends DartLintRule with _ParseRiverpod { await _setupRiverpod(resolver, context); await super.startUp(resolver, context); } + + @override + List getFixes() => []; } abstract class RiverpodFix extends DartFix with _ParseRiverpod { @@ -83,11 +86,9 @@ mixin _ParseRiverpod { if (context.sharedState.containsKey(_contextKey)) return; // Only run the riverpod parsing logic once final registry = context.sharedState[_contextKey] = RiverpodAstRegistry(); - final unit = await resolver.getResolvedUnitResult(); - final result = ResolvedRiverpodLibraryResult.from([unit.unit]); - context.addPostRunCallback(() => registry.run(result)); + context.addPostRunCallback(() => registry.run(unit.unit)); } RiverpodAstRegistry riverpodRegistry(CustomLintContext context) { diff --git a/packages/riverpod_lint/pubspec.yaml b/packages/riverpod_lint/pubspec.yaml index 612e09e3b..f0905305a 100644 --- a/packages/riverpod_lint/pubspec.yaml +++ b/packages/riverpod_lint/pubspec.yaml @@ -1,6 +1,6 @@ name: riverpod_lint description: Riverpod_lint is a developer tool for users of Riverpod, designed to help stop common issues and simplify repetitive tasks. -version: 2.6.4 +version: 3.0.0-dev.4 homepage: https://riverpod.dev repository: https://github.com/rrousselGit/river_pod issue_tracker: https://github.com/rrousselGit/riverpod/issues @@ -8,7 +8,7 @@ funding: - https://github.com/sponsors/rrousselGit/ environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0<4.0.0" dependencies: analyzer: ^7.0.0 @@ -17,8 +17,8 @@ dependencies: custom_lint_builder: ^0.7.0 meta: ^1.7.0 path: ^1.8.1 - riverpod: 2.6.1 - riverpod_analyzer_utils: 0.5.9 + riverpod: 3.0.0-dev.3 + riverpod_analyzer_utils: 1.0.0-dev.1 source_span: ^1.8.0 yaml: ^3.1.1 diff --git a/packages/riverpod_lint/pubspec_overrides.yaml b/packages/riverpod_lint/pubspec_overrides.yaml index 82a782285..06c1d5f2b 100644 --- a/packages/riverpod_lint/pubspec_overrides.yaml +++ b/packages/riverpod_lint/pubspec_overrides.yaml @@ -1,6 +1,6 @@ # melos_managed_dependency_overrides: riverpod dependency_overrides: - riverpod: - path: ../riverpod riverpod_analyzer_utils: path: ../riverpod_analyzer_utils + riverpod: + path: ../riverpod diff --git a/packages/riverpod_lint_flutter_test/README.md b/packages/riverpod_lint_flutter_test/README.md new file mode 100644 index 000000000..101a28fb1 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/README.md @@ -0,0 +1,8 @@ +## Updating golden tests + +This folder uses snapshot testing. +To update the "snapshots"/"goldens", run: + +```sh +goldens=true dart test +``` \ No newline at end of file diff --git a/packages/riverpod_lint_flutter_test/analysis_options.yaml b/packages/riverpod_lint_flutter_test/analysis_options.yaml index ed2f7b35b..b3d19c02e 100644 --- a/packages/riverpod_lint_flutter_test/analysis_options.yaml +++ b/packages/riverpod_lint_flutter_test/analysis_options.yaml @@ -7,3 +7,6 @@ analyzer: - test/lints/functional_ref/functional_ref.g.dart - test/lints/functional_ref/fix/auto_import.g.dart - test/lints/functional_ref/fix/use_prefix.g.dart + +custom_lint: + debug: true diff --git a/packages/riverpod_lint_flutter_test/build.yaml b/packages/riverpod_lint_flutter_test/build.yaml index 354797122..43ef69596 100644 --- a/packages/riverpod_lint_flutter_test/build.yaml +++ b/packages/riverpod_lint_flutter_test/build.yaml @@ -5,5 +5,7 @@ targets: generate_for: exclude: # Those files voluntarily have the generation fail - - test/lints/notifier_build/notifier_build.dart - - test/lints/notifier_build/fix/notifier_build.dart + - test/lints/notifier_build.dart + - test/lints/riverpod_syntax_error.dart + - test/lints/functional_ref/failing_functional_ref.dart + - test/lints/notifier_extends/failing_notifier_extends.dart \ No newline at end of file diff --git a/packages/riverpod_lint_flutter_test/pubspec.yaml b/packages/riverpod_lint_flutter_test/pubspec.yaml index 321b161d2..a21d60005 100644 --- a/packages/riverpod_lint_flutter_test/pubspec.yaml +++ b/packages/riverpod_lint_flutter_test/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: sdk: flutter freezed_annotation: ^2.2.0 json_annotation: ^4.8.0 + pubspec_parse: ^1.3.0 dev_dependencies: build_runner: ^2.4.6 @@ -25,6 +26,11 @@ dev_dependencies: test: ^1.24.9 flutter_test: sdk: flutter + glob: ^2.1.2 + analyzer: ^7.0.0 + path: ^1.9.0 + source_span: ^1.10.0 + analyzer_plugin: ^0.12.0 dependency_overrides: riverpod_generator: diff --git a/packages/riverpod_lint_flutter_test/pubspec_overrides.yaml b/packages/riverpod_lint_flutter_test/pubspec_overrides.yaml index 46fe485d4..8aa9357fd 100644 --- a/packages/riverpod_lint_flutter_test/pubspec_overrides.yaml +++ b/packages/riverpod_lint_flutter_test/pubspec_overrides.yaml @@ -1,17 +1,17 @@ -# melos_managed_dependency_overrides: riverpod_generator,riverpod,flutter_riverpod,hooks_riverpod,riverpod_annotation,riverpod_lint +# melos_managed_dependency_overrides: flutter_riverpod,hooks_riverpod,riverpod,riverpod_annotation,riverpod_generator,riverpod_lint dependency_overrides: - riverpod_generator: - path: ../riverpod_generator - riverpod: - path: ../riverpod flutter_riverpod: path: ../flutter_riverpod hooks_riverpod: path: ../hooks_riverpod + riverpod: + path: ../riverpod riverpod_analyzer_utils: path: ../riverpod_analyzer_utils riverpod_annotation: path: ../riverpod_annotation + riverpod_generator: + path: ../riverpod_generator riverpod_lint: path: ../riverpod_lint diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.dart b/packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.dart index 9f52ebe5d..ffcf5db4e 100644 --- a/packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.dart +++ b/packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.dart @@ -18,3 +18,9 @@ class ExampleFamily extends _$ExampleFamily { return 0; } } + +@riverpod +class Generic extends _$Generic { + @override + int build() => 0; +} diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.g.dart b/packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.g.dart index 3ba8663ed..5c978d16b 100644 --- a/packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.g.dart +++ b/packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.g.dart @@ -6,218 +6,435 @@ part of 'convert_class_based_provider_to_functional.dart'; // RiverpodGenerator // ************************************************************************** -String _$exampleHash() => r'081776126bafed3e1583bba9c1fadef798215ad7'; - /// Some comment -/// -/// Copied from [Example]. @ProviderFor(Example) -final exampleProvider = AutoDisposeNotifierProvider.internal( - Example.new, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Example = AutoDisposeNotifier; -String _$exampleFamilyHash() => r'37d4a4fd66999562cd92051f91266270d5a1e5ea'; +const exampleProvider = ExampleProvider._(); + +/// Some comment +final class ExampleProvider extends $NotifierProvider { + /// Some comment + const ExampleProvider._( + {super.runNotifierBuildOverride, Example Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Example Function()? _createCb; -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Example create() => _createCb?.call() ?? Example(); - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); + @$internal + @override + ExampleProvider $copyWithCreate( + Example Function() create, + ) { + return ExampleProvider._(create: create); } - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + @$internal + @override + ExampleProvider $copyWithBuild( + int Function( + Ref, + Example, + ) build, + ) { + return ExampleProvider._(runNotifierBuildOverride: build); } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); } -abstract class _$ExampleFamily extends BuildlessAutoDisposeNotifier { - late final int a; - late final String b; +String _$exampleHash() => r'081776126bafed3e1583bba9c1fadef798215ad7'; - int build({ - required int a, - String b = '42', - }); +abstract class _$Example extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); } /// Some comment -/// -/// Copied from [ExampleFamily]. @ProviderFor(ExampleFamily) -const exampleFamilyProvider = ExampleFamilyFamily(); +const exampleFamilyProvider = ExampleFamilyFamily._(); /// Some comment -/// -/// Copied from [ExampleFamily]. -class ExampleFamilyFamily extends Family { +final class ExampleFamilyProvider + extends $NotifierProvider { /// Some comment - /// - /// Copied from [ExampleFamily]. - const ExampleFamilyFamily(); + const ExampleFamilyProvider._( + {required ExampleFamilyFamily super.from, + required ({ + int a, + String b, + }) + super.argument, + super.runNotifierBuildOverride, + ExampleFamily Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'exampleFamilyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// Some comment - /// - /// Copied from [ExampleFamily]. - ExampleFamilyProvider call({ - required int a, - String b = '42', - }) { - return ExampleFamilyProvider( - a: a, - b: b, + final ExampleFamily Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleFamilyHash(); + + @override + String toString() { + return r'exampleFamilyProvider' + '' + '$argument'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } + @$internal + @override + ExampleFamily create() => _createCb?.call() ?? ExampleFamily(); + + @$internal @override - ExampleFamilyProvider getProviderOverride( - covariant ExampleFamilyProvider provider, + ExampleFamilyProvider $copyWithCreate( + ExampleFamily Function() create, ) { - return call( - a: provider.a, - b: provider.b, - ); + return ExampleFamilyProvider._( + argument: argument as ({ + int a, + String b, + }), + from: from! as ExampleFamilyFamily, + create: create); } - static const Iterable? _dependencies = null; - + @$internal @override - Iterable? get dependencies => _dependencies; + ExampleFamilyProvider $copyWithBuild( + int Function( + Ref, + ExampleFamily, + ) build, + ) { + return ExampleFamilyProvider._( + argument: argument as ({ + int a, + String b, + }), + from: from! as ExampleFamilyFamily, + runNotifierBuildOverride: build); + } - static const Iterable? _allTransitiveDependencies = null; + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + bool operator ==(Object other) { + return other is ExampleFamilyProvider && other.argument == argument; + } @override - String? get name => r'exampleFamilyProvider'; + int get hashCode { + return argument.hashCode; + } } +String _$exampleFamilyHash() => r'37d4a4fd66999562cd92051f91266270d5a1e5ea'; + /// Some comment -/// -/// Copied from [ExampleFamily]. -class ExampleFamilyProvider - extends AutoDisposeNotifierProviderImpl { - /// Some comment - /// - /// Copied from [ExampleFamily]. - ExampleFamilyProvider({ - required int a, - String b = '42', - }) : this._internal( - () => ExampleFamily() - ..a = a - ..b = b, - from: exampleFamilyProvider, +final class ExampleFamilyFamily extends Family { + const ExampleFamilyFamily._() + : super( + retry: null, name: r'exampleFamilyProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$exampleFamilyHash, - dependencies: ExampleFamilyFamily._dependencies, - allTransitiveDependencies: - ExampleFamilyFamily._allTransitiveDependencies, - a: a, - b: b, + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, ); - ExampleFamilyProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.a, - required this.b, - }) : super.internal(); + /// Some comment + ExampleFamilyProvider call({ + required int a, + String b = '42', + }) => + ExampleFamilyProvider._(argument: ( + a: a, + b: b, + ), from: this); - final int a; - final String b; + @override + String debugGetCreateSourceHash() => _$exampleFamilyHash(); @override - int runNotifierBuild( - covariant ExampleFamily notifier, + String toString() => r'exampleFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + ExampleFamily Function( + ({ + int a, + String b, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ExampleFamilyProvider; + + final argument = provider.argument as ({ + int a, + String b, + }); + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function( + Ref ref, + ExampleFamily notifier, + ({ + int a, + String b, + }) argument) + build, ) { - return notifier.build( - a: a, - b: b, + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ExampleFamilyProvider; + + final argument = provider.argument as ({ + int a, + String b, + }); + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, ); } +} + +abstract class _$ExampleFamily extends $Notifier { + late final _$args = ref.$arg as ({ + int a, + String b, + }); + int get a => _$args.a; + String get b => _$args.b; + + int build({ + required int a, + String b = '42', + }); + @$internal + @override + int runBuild() => build( + a: _$args.a, + b: _$args.b, + ); +} + +@ProviderFor(Generic) +const genericProvider = GenericFamily._(); + +final class GenericProvider + extends $NotifierProvider, int> { + const GenericProvider._( + {required GenericFamily super.from, + super.runNotifierBuildOverride, + Generic Function()? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'genericProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Generic Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$genericHash(); + + GenericProvider _copyWithCreate( + Generic Function() create, + ) { + return GenericProvider._( + from: from! as GenericFamily, create: create); + } + + GenericProvider _copyWithBuild( + int Function( + Ref, + Generic, + ) build, + ) { + return GenericProvider._( + from: from! as GenericFamily, runNotifierBuildOverride: build); + } @override - Override overrideWith(ExampleFamily Function() create) { - return ProviderOverride( + String toString() { + return r'genericProvider' + '<${A}, ${B}>' + '()'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( origin: this, - override: ExampleFamilyProvider._internal( - () => create() - ..a = a - ..b = b, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - a: a, - b: b, - ), + providerOverride: $ValueProvider(value), ); } + @$internal + @override + Generic create() => _createCb?.call() ?? Generic(); + + @$internal @override - AutoDisposeNotifierProviderElement createElement() { - return _ExampleFamilyProviderElement(this); + GenericProvider $copyWithCreate( + Generic Function() create, + ) { + return GenericProvider._( + from: from! as GenericFamily, create: create); } + @$internal + @override + GenericProvider $copyWithBuild( + int Function( + Ref, + Generic, + ) build, + ) { + return GenericProvider._( + from: from! as GenericFamily, runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement, int> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + @override bool operator ==(Object other) { - return other is ExampleFamilyProvider && other.a == a && other.b == b; + return other is GenericProvider && + other.runtimeType == runtimeType && + other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, a.hashCode); - hash = _SystemHash.combine(hash, b.hashCode); - - return _SystemHash.finish(hash); + return Object.hash(runtimeType, argument); } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin ExampleFamilyRef on AutoDisposeNotifierProviderRef { - /// The parameter `a` of this provider. - int get a; +String _$genericHash() => r'0a3792d7b59723aebd92715eef2c74d2f267cbd2'; - /// The parameter `b` of this provider. - String get b; -} +final class GenericFamily extends Family { + const GenericFamily._() + : super( + retry: null, + name: r'genericProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); -class _ExampleFamilyProviderElement - extends AutoDisposeNotifierProviderElement - with ExampleFamilyRef { - _ExampleFamilyProviderElement(super.provider); + GenericProvider call() => GenericProvider._(from: this); @override - int get a => (origin as ExampleFamilyProvider).a; + String debugGetCreateSourceHash() => _$genericHash(); + @override - String get b => (origin as ExampleFamilyProvider).b; + String toString() => r'genericProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Generic Function() create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericProvider; + + return provider._copyWithCreate(create).$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function(Ref ref, Generic notifier) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericProvider; + + return provider._copyWithBuild(build).$createElement(pointer); + }, + ); + } } + +abstract class _$Generic extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional_test.dart b/packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional_test.dart index ef3a77426..049d20692 100644 --- a/packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional_test.dart +++ b/packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional_test.dart @@ -1,11 +1,11 @@ import 'package:riverpod_lint/src/assists/class_based_to_functional_provider.dart'; -import '../../golden.dart'; +import '../../test_lint.dart'; void main() { testGolden( 'Convert plain class provider to functional provider', - 'assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.diff', + 'test/assists/goldens/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.diff', sourcePath: 'test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.dart', (result, helper) async { @@ -23,7 +23,9 @@ class <>Example e<>xtends _$<>Example { class Exampl<>eFamily extends _$ExampleFamily { '''); - return helper.runAssist(assist, result, cursors); + final cursors2 = helper.rangesForString('class Gen<>eric'); + + return helper.runAssist(assist, result, [...cursors, ...cursors2]); }, ); } diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.dart b/packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.dart index 894b5db24..f7f660062 100644 --- a/packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.dart +++ b/packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.dart @@ -1,4 +1,3 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'convert_functional_provider_to_class_based.g.dart'; diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.g.dart b/packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.g.dart index 09cb5a20a..0190818a7 100644 --- a/packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.g.dart +++ b/packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.g.dart @@ -6,200 +6,225 @@ part of 'convert_functional_provider_to_class_based.dart'; // RiverpodGenerator // ************************************************************************** -String _$exampleHash() => r'67898608b444d39a000852f647ca6d3326740c98'; - /// Some comment -/// -/// Copied from [example]. @ProviderFor(example) -final exampleProvider = AutoDisposeProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeProviderRef; -String _$exampleFamilyHash() => r'70dfc6f4b2d7d251edbc3a66c3ac0f2c56aebf8b'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} +const exampleProvider = ExampleProvider._(); /// Some comment -/// -/// Copied from [exampleFamily]. -@ProviderFor(exampleFamily) -const exampleFamilyProvider = ExampleFamilyFamily(); - -/// Some comment -/// -/// Copied from [exampleFamily]. -class ExampleFamilyFamily extends Family { +final class ExampleProvider extends $FunctionalProvider + with $Provider { /// Some comment - /// - /// Copied from [exampleFamily]. - const ExampleFamilyFamily(); + const ExampleProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// Some comment - /// - /// Copied from [exampleFamily]. - ExampleFamilyProvider call({ - required int a, - String b = '42', - }) { - return ExampleFamilyProvider( - a: a, - b: b, - ); - } + final int Function( + Ref ref, + )? _createCb; @override - ExampleFamilyProvider getProviderOverride( - covariant ExampleFamilyProvider provider, - ) { - return call( - a: provider.a, - b: provider.b, + String debugGetCreateSourceHash() => _$exampleHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } - static const Iterable? _dependencies = null; - + @$internal @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + ExampleProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } @override - String? get name => r'exampleFamilyProvider'; + int create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } } +String _$exampleHash() => r'67898608b444d39a000852f647ca6d3326740c98'; + +/// Some comment +@ProviderFor(exampleFamily) +const exampleFamilyProvider = ExampleFamilyFamily._(); + /// Some comment -/// -/// Copied from [exampleFamily]. -class ExampleFamilyProvider extends AutoDisposeProvider { +final class ExampleFamilyProvider extends $FunctionalProvider + with $Provider { /// Some comment - /// - /// Copied from [exampleFamily]. - ExampleFamilyProvider({ - required int a, - String b = '42', - }) : this._internal( - (ref) => exampleFamily( - ref as ExampleFamilyRef, - a: a, - b: b, - ), - from: exampleFamilyProvider, + const ExampleFamilyProvider._( + {required ExampleFamilyFamily super.from, + required ({ + int a, + String b, + }) + super.argument, + int Function( + Ref ref, { + required int a, + String b, + })? create}) + : _createCb = create, + super( + retry: null, name: r'exampleFamilyProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$exampleFamilyHash, - dependencies: ExampleFamilyFamily._dependencies, - allTransitiveDependencies: - ExampleFamilyFamily._allTransitiveDependencies, - a: a, - b: b, + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - ExampleFamilyProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.a, - required this.b, - }) : super.internal(); + final int Function( + Ref ref, { + required int a, + String b, + })? _createCb; - final int a; - final String b; + @override + String debugGetCreateSourceHash() => _$exampleFamilyHash(); @override - Override overrideWith( - int Function(ExampleFamilyRef provider) create, - ) { - return ProviderOverride( + String toString() { + return r'exampleFamilyProvider' + '' + '$argument'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( origin: this, - override: ExampleFamilyProvider._internal( - (ref) => create(ref as ExampleFamilyRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - a: a, - b: b, - ), + providerOverride: $ValueProvider(value), ); } + @$internal @override - AutoDisposeProviderElement createElement() { - return _ExampleFamilyProviderElement(this); + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ExampleFamilyProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return ExampleFamilyProvider._( + argument: argument as ({ + int a, + String b, + }), + from: from! as ExampleFamilyFamily, + create: ( + ref, { + required int a, + String b = '42', + }) => + create(ref)); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? exampleFamily; + final argument = this.argument as ({ + int a, + String b, + }); + return _$cb( + ref, + a: argument.a, + b: argument.b, + ); } @override bool operator ==(Object other) { - return other is ExampleFamilyProvider && other.a == a && other.b == b; + return other is ExampleFamilyProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, a.hashCode); - hash = _SystemHash.combine(hash, b.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin ExampleFamilyRef on AutoDisposeProviderRef { - /// The parameter `a` of this provider. - int get a; +String _$exampleFamilyHash() => r'70dfc6f4b2d7d251edbc3a66c3ac0f2c56aebf8b'; - /// The parameter `b` of this provider. - String get b; -} +/// Some comment +final class ExampleFamilyFamily extends Family { + const ExampleFamilyFamily._() + : super( + retry: null, + name: r'exampleFamilyProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); -class _ExampleFamilyProviderElement extends AutoDisposeProviderElement - with ExampleFamilyRef { - _ExampleFamilyProviderElement(super.provider); + /// Some comment + ExampleFamilyProvider call({ + required int a, + String b = '42', + }) => + ExampleFamilyProvider._(argument: ( + a: a, + b: b, + ), from: this); @override - int get a => (origin as ExampleFamilyProvider).a; + String debugGetCreateSourceHash() => _$exampleFamilyHash(); + @override - String get b => (origin as ExampleFamilyProvider).b; + String toString() => r'exampleFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + int Function( + Ref ref, + ({ + int a, + String b, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ExampleFamilyProvider; + + final argument = provider.argument as ({ + int a, + String b, + }); + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based_test.dart b/packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based_test.dart index 5fe1d9ba9..bbc09b52c 100644 --- a/packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based_test.dart +++ b/packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based_test.dart @@ -1,17 +1,16 @@ import 'package:riverpod_lint/src/assists/functional_to_class_based_provider.dart'; -import '../../golden.dart'; +import '../../test_lint.dart'; void main() { - testGolden( - 'Convert functional providers to class-based providers', - 'assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.diff', - sourcePath: - 'test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.dart', - (result, helper) async { - final assist = FunctionalToClassBasedProvider(); + testGolden('Convert functional providers to class-based providers', + 'test/assists/goldens/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.diff', + sourcePath: + 'test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.dart', + (result, helper) async { + final assist = FunctionalToClassBasedProvider(); - final cursors = helper.rangesForString(''' + final cursors = helper.rangesForString(''' @rive<>rpo<>d int ex<>ample(R<>ef ref) =><> 0; @@ -20,7 +19,6 @@ int ex<>ample(R<>ef ref) =><> 0; int exampleF<>amily(Ref ref, {required int a, String b = '42'}) { '''); - return helper.runAssist(assist, result, cursors); - }, - ); + return helper.runAssist(assist, result, cursors); + }); } diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_widget_test.dart b/packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_widget_test.dart index 748c514d2..16a12986a 100644 --- a/packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_widget_test.dart +++ b/packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_widget_test.dart @@ -5,7 +5,7 @@ import 'package:riverpod_lint/src/assists/convert_to_widget_utils.dart'; import 'package:riverpod_lint/src/riverpod_custom_lint.dart'; import 'package:test/test.dart'; -import '../../golden.dart'; +import '../../test_lint.dart'; void main() { final pubspecWithDependencies = Pubspec( @@ -36,7 +36,7 @@ void main() { targetWidget: targetWidget, ), 'Convert widgets to ${targetWidget.name}s with hooks_riverpod and flutter_hooks dependency', - 'assists/convert_to_widget/convert_to_${targetWidget.name.toSnakeCase()}.diff', + 'test/assists/goldens/convert_to_widget/convert_to_${targetWidget.name.toSnakeCase()}.diff', pubspecWithDependencies, expectedChangeCount, ); @@ -63,7 +63,7 @@ void main() { targetWidget: targetWidget, ), 'Convert widgets to ${targetWidget.name}s with hooks_riverpod and flutter_hooks dependency', - 'assists/convert_to_widget/convert_to_${targetWidget.name.toSnakeCase()}.diff', + 'test/assists/goldens/convert_to_widget/convert_to_${targetWidget.name.toSnakeCase()}.diff', pubspecWithDependencies, expectedChangeCount, ); @@ -93,12 +93,12 @@ void main() { switch (targetWidget) { case StatelessBaseWidgetType.hookWidget: case StatelessBaseWidgetType.hookConsumerWidget: - goldenFilePath = 'assists/empty.diff'; + goldenFilePath = 'test/assists/goldens/empty.diff'; break; case StatelessBaseWidgetType.consumerWidget: case StatelessBaseWidgetType.statelessWidget: goldenFilePath = - 'assists/convert_to_widget/convert_to_${targetWidget.name.toSnakeCase()}.diff'; + 'test/assists/goldens/convert_to_widget/convert_to_${targetWidget.name.toSnakeCase()}.diff'; break; } @@ -133,12 +133,12 @@ void main() { switch (targetWidget) { case StatefulBaseWidgetType.statefulHookWidget: case StatefulBaseWidgetType.statefulHookConsumerWidget: - goldenFilePath = 'assists/empty.diff'; + goldenFilePath = 'test/assists/goldens/empty.diff'; break; case StatefulBaseWidgetType.consumerStatefulWidget: case StatefulBaseWidgetType.statefulWidget: goldenFilePath = - 'assists/convert_to_widget/convert_to_${targetWidget.name.toSnakeCase()}.diff'; + 'test/assists/goldens/convert_to_widget/convert_to_${targetWidget.name.toSnakeCase()}.diff'; break; } diff --git a/packages/riverpod_lint_flutter_test/test/assists/empty.diff b/packages/riverpod_lint_flutter_test/test/assists/empty.diff deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.diff b/packages/riverpod_lint_flutter_test/test/assists/goldens/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.diff similarity index 79% rename from packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.diff rename to packages/riverpod_lint_flutter_test/test/assists/goldens/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.diff index d0cf0ff76..4a35375eb 100644 --- a/packages/riverpod_lint_flutter_test/test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.diff +++ b/packages/riverpod_lint_flutter_test/test/assists/goldens/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.diff @@ -59,5 +59,20 @@ Diff for file `test/assists/convert_class_based_provider_to_functional/convert_c + // Hello world + return 0; + } + +@riverpod +``` +--- +Message: `Convert to functional provider` +Priority: 100 +Diff for file `test/assists/convert_class_based_provider_to_functional/convert_class_based_provider_to_functional.dart:23`: +``` + +@riverpod +- class Generic extends _$Generic { +- @override +- int build() => 0; +- } ++ int generic(Ref ref) => 0; ``` --- diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.diff b/packages/riverpod_lint_flutter_test/test/assists/goldens/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.diff similarity index 85% rename from packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.diff rename to packages/riverpod_lint_flutter_test/test/assists/goldens/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.diff index 729b5335f..0980c5b6b 100644 --- a/packages/riverpod_lint_flutter_test/test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.diff +++ b/packages/riverpod_lint_flutter_test/test/assists/goldens/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.diff @@ -1,6 +1,6 @@ Message: `Convert to class-based provider` Priority: 100 -Diff for file `test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.dart:8`: +Diff for file `test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.dart:7`: ``` /// Some comment @riverpod @@ -15,7 +15,7 @@ Diff for file `test/assists/convert_functional_provider_to_class_based/convert_f --- Message: `Convert to class-based provider` Priority: 100 -Diff for file `test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.dart:12`: +Diff for file `test/assists/convert_functional_provider_to_class_based/convert_functional_provider_to_class_based.dart:11`: ``` /// Some comment @riverpod diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_consumer_stateful_widget.diff b/packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_consumer_stateful_widget.diff similarity index 100% rename from packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_consumer_stateful_widget.diff rename to packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_consumer_stateful_widget.diff diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_consumer_widget.diff b/packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_consumer_widget.diff similarity index 100% rename from packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_consumer_widget.diff rename to packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_consumer_widget.diff diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_hook_consumer_widget.diff b/packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_hook_consumer_widget.diff similarity index 100% rename from packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_hook_consumer_widget.diff rename to packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_hook_consumer_widget.diff diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_hook_widget.diff b/packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_hook_widget.diff similarity index 100% rename from packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_hook_widget.diff rename to packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_hook_widget.diff diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_stateful_hook_consumer_widget.diff b/packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_stateful_hook_consumer_widget.diff similarity index 100% rename from packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_stateful_hook_consumer_widget.diff rename to packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_stateful_hook_consumer_widget.diff diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_stateful_hook_widget.diff b/packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_stateful_hook_widget.diff similarity index 100% rename from packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_stateful_hook_widget.diff rename to packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_stateful_hook_widget.diff diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_stateful_widget.diff b/packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_stateful_widget.diff similarity index 100% rename from packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_stateful_widget.diff rename to packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_stateful_widget.diff diff --git a/packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_stateless_widget.diff b/packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_stateless_widget.diff similarity index 100% rename from packages/riverpod_lint_flutter_test/test/assists/convert_to_widget/convert_to_stateless_widget.diff rename to packages/riverpod_lint_flutter_test/test/assists/goldens/convert_to_widget/convert_to_stateless_widget.diff diff --git a/packages/riverpod_lint_flutter_test/test/assists/wrap_widget/wrap_with_consumer.diff b/packages/riverpod_lint_flutter_test/test/assists/goldens/wrap_widget/wrap_with_consumer.diff similarity index 100% rename from packages/riverpod_lint_flutter_test/test/assists/wrap_widget/wrap_with_consumer.diff rename to packages/riverpod_lint_flutter_test/test/assists/goldens/wrap_widget/wrap_with_consumer.diff diff --git a/packages/riverpod_lint_flutter_test/test/assists/wrap_widget/wrap_with_provider_scope.diff b/packages/riverpod_lint_flutter_test/test/assists/goldens/wrap_widget/wrap_with_provider_scope.diff similarity index 100% rename from packages/riverpod_lint_flutter_test/test/assists/wrap_widget/wrap_with_provider_scope.diff rename to packages/riverpod_lint_flutter_test/test/assists/goldens/wrap_widget/wrap_with_provider_scope.diff diff --git a/packages/riverpod_lint_flutter_test/test/assists/wrap_widget/wrap_widget_test.dart b/packages/riverpod_lint_flutter_test/test/assists/wrap_widget/wrap_widget_test.dart index 7469ba17c..29c2d8212 100644 --- a/packages/riverpod_lint_flutter_test/test/assists/wrap_widget/wrap_widget_test.dart +++ b/packages/riverpod_lint_flutter_test/test/assists/wrap_widget/wrap_widget_test.dart @@ -2,12 +2,12 @@ import 'package:test/test.dart'; import 'package:riverpod_lint/src/assists/wrap_with_consumer.dart'; import 'package:riverpod_lint/src/assists/wrap_with_provider_scope.dart'; -import '../../golden.dart'; +import '../../test_lint.dart'; void main() { testGolden( 'Wrap with consumer', - 'assists/wrap_widget/wrap_with_consumer.diff', + 'test/assists/goldens/wrap_widget/wrap_with_consumer.diff', sourcePath: 'test/assists/wrap_widget/wrap_widget.dart', (result, helper) async { final assist = WrapWithConsumer(); @@ -31,7 +31,7 @@ void main() { testGolden( 'Wrap with ProviderScope', - 'assists/wrap_widget/wrap_with_provider_scope.diff', + 'test/assists/goldens/wrap_widget/wrap_with_provider_scope.diff', sourcePath: 'test/assists/wrap_widget/wrap_widget.dart', (result, helper) async { final assist = WrapWithProviderScope(); diff --git a/packages/riverpod_lint_flutter_test/test/encoders.dart b/packages/riverpod_lint_flutter_test/test/encoders.dart new file mode 100644 index 000000000..1905b2c81 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/encoders.dart @@ -0,0 +1,199 @@ +import 'dart:io'; +import 'dart:math'; + +import 'package:analyzer/diagnostic/diagnostic.dart'; +import 'package:analyzer/error/error.dart'; +import 'package:analyzer/source/line_info.dart'; +import 'package:analyzer_plugin/protocol/protocol_generated.dart'; +import 'package:custom_lint_core/custom_lint_core.dart'; +import 'package:matcher/matcher.dart'; +import 'package:path/path.dart' as p; +import 'package:test/test.dart'; + +import 'golden.dart'; + +Matcher matchesPrioritizedSourceChangesGolden( + File file, { + required String source, + required String sourcePath, +}) { + return matchersGoldenFile>( + file, + isEmpty: (value) => value.isEmpty, + encode: (changes) { + return encodePrioritizedSourceChanges( + changes, + sources: {'**': source}, + relativePath: Directory.current.path, + ); + }, + ); +} + +Matcher matchesAnalysisErrorGoldens(String fileName) { + return matchersGoldenFile>( + File(fileName), + isEmpty: (value) => value.isEmpty, + encode: (changes) { + return _encodeAnalysisErrors( + changes, + relativePath: Directory.current.path, + ); + }, + ); +} + +String _encodeAnalysisErrors( + Iterable errors, { + required String relativePath, +}) { + final buffer = StringBuffer(); + + for (final (index, error) in errors.indexed) { + if (index != 0) buffer.writeln('\n=======\n'); + + _writeAnalysisError( + buffer, + error, + relativePath: relativePath, + ); + } + + return buffer.toString(); +} + +void _writeAnalysisError( + StringBuffer buffer, + AnalysisError error, { + String indent = '', + required String relativePath, +}) { + buffer.writeln('${indent}code: ${error.errorCode.name}'); + _writeDiagnostic( + buffer, + error, + indent: indent, + relativePath: Directory.current.path, + ); +} + +void _writeDiagnostic( + StringBuffer buffer, + Diagnostic diagnostic, { + String indent = '', + required String relativePath, +}) { + buffer.writeln('${indent}severity: ${diagnostic.severity}'); + if (diagnostic.correctionMessage case final correctionMessage?) { + buffer.writeln('${indent}correctionMessage: ${correctionMessage}'); + } + if (diagnostic.contextMessages.isNotEmpty) { + buffer.writeln('${indent}contextMessages:'); + for (final (index, message) in diagnostic.contextMessages.indexed) { + if (index != 0) buffer.writeln(); + + _writeDiagnosticMessage( + buffer, + message, + indent: indent + ' ', + relativePath: relativePath, + ); + } + } + + _writeDiagnosticMessage( + buffer, + diagnostic.problemMessage, + indent: indent, + relativePath: relativePath, + ); +} + +void _writeDiagnosticMessage( + StringBuffer buffer, + DiagnosticMessage error, { + String indent = '', + required String relativePath, +}) { + buffer.writeln( + '${indent}message: ${error.messageText(includeUrl: false)}', + ); + + if (error.url case final url?) { + buffer.writeln('${indent}url: ${url}'); + } + + _highlight( + buffer, + File(error.filePath), + offset: error.offset, + length: error.length, + indent: indent, + ); +} + +void _highlight( + StringBuffer buffer, + File file, { + required int offset, + required int length, + String indent = '', +}) { + const leadingCount = 2; + const trailingCount = 2; + + final source = file.readAsStringSync(); + final lineInfo = LineInfo.fromContent(source); + + final start = lineInfo.getLocation(offset); + final end = lineInfo.getLocation(offset + length); + + buffer.writeln( + '${indent}${p.normalize(p.relative(file.path))}:${start.lineNumber}:${start.columnNumber}', + ); + buffer.writeln(); + + buffer.writeln('$indent```${p.extension(file.path).substring(1)}'); + final firstChangedLine = start.lineNumber - 1; + final lastChangedLine = end.lineNumber - 1; + + final endLine = min(lastChangedLine + trailingCount, lineInfo.lineCount - 1); + + for (var line = max(0, firstChangedLine - leadingCount); + line <= endLine; + line++) { + final endOfSource = !(line + 1 < lineInfo.lineCount); + + final lineContent = source.substring( + lineInfo.getOffsetOfLine(line), + endOfSource ? null : lineInfo.getOffsetOfLine(line + 1) - 1, + ); + buffer.write(indent); + + var startCol = 0; + if (line == firstChangedLine) { + startCol = max(0, start.columnNumber - 1); + } + + buffer.write(lineContent.substring(0, startCol)); + if (line == firstChangedLine) buffer.write('>>>'); + + var endCol = max(lineContent.length, 0); + if (line == lastChangedLine) { + endCol = max( + min(end.columnNumber - 1, lineContent.length), + startCol, + ); + } + + buffer.write(lineContent.substring(startCol, endCol)); + if (line == lastChangedLine) buffer.write('<<<'); + + if (endCol < lineContent.length) { + buffer.write(lineContent.substring(endCol)); + } + + if (!endOfSource) buffer.writeln(); + } + buffer.writeln('$indent```'); +} diff --git a/packages/riverpod_lint_flutter_test/test/golden.dart b/packages/riverpod_lint_flutter_test/test/golden.dart index 95e4810b1..a368db2d1 100644 --- a/packages/riverpod_lint_flutter_test/test/golden.dart +++ b/packages/riverpod_lint_flutter_test/test/golden.dart @@ -1,18 +1,123 @@ import 'dart:convert'; import 'dart:io'; -import 'package:test/test.dart'; -import 'package:path/path.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; -import 'package:custom_lint_core/custom_lint_core.dart'; -import 'package:analyzer_plugin/protocol/protocol_generated.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/analysis/utilities.dart'; import 'package:analyzer/source/source_range.dart'; +import 'package:analyzer_plugin/protocol/protocol_generated.dart'; +import 'package:custom_lint_core/custom_lint_core.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:path/path.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; +import 'package:test/test.dart'; import 'package:riverpod_lint/src/riverpod_custom_lint.dart'; -@Deprecated('Do not commit') -var goldenWrite = false; +final _goldenWrite = bool.parse(Platform.environment[r'goldens'] ?? 'false'); + +/// Expects that a value matches a golden file. +@visibleForTesting +Matcher matchersGoldenFile( + File file, { + required String Function(T value) encode, + required bool Function(T value) isEmpty, +}) { + return _MatchesGoldenFile( + file: file, + encode: encode, + isEmpty: isEmpty, + ); +} + +class _MatchesGoldenFile extends Matcher { + _MatchesGoldenFile({ + required this.encode, + required this.file, + required this.isEmpty, + }); + + final File file; + final String Function(T) encode; + final bool Function(T) isEmpty; + + static final Object _mismatchedValueKey = Object(); + static final Object _expectedKey = Object(); + + @override + bool matches( + Object? object, + Map matchState, + ) { + if (object is! T) { + matchState[_mismatchedValueKey] = 'Expected a ${T.toString()}'; + return false; + } + + late final actual = encode(object); + + if (!_goldenWrite) { + if (isEmpty(object)) { + if (file.existsSync()) { + matchState[_mismatchedValueKey] = + 'Expected to have no file, but found: ${file.path}'; + return false; + } + return true; + } + + if (!file.existsSync()) { + matchState[_mismatchedValueKey] = 'File not found: ${file.path}'; + return false; + } + + final expected = file.readAsStringSync(); + if (actual != expected) { + matchState[_mismatchedValueKey] = actual; + matchState[_expectedKey] = expected; + return false; + } + } else if (isEmpty(object)) { + try { + file.deleteSync(recursive: true); + } catch (_) {} + } else { + file + ..createSync(recursive: true) + ..writeAsStringSync(actual); + } + + return true; + } + + @override + Description describe(Description description) { + return description.add('to match snapshot at ${file.path}'); + } + + @override + Description describeMismatch( + Object? item, + Description mismatchDescription, + Map matchState, + bool verbose, + ) { + final actualValue = matchState[_mismatchedValueKey] as String?; + if (actualValue != null) { + final expected = matchState[_expectedKey] as String?; + + if (expected != null) { + return mismatchDescription + .add('Expected to match snapshot at ${file.path}:\n') + .addDescriptionOf(expected) + .add('\n\nbut was:\n') + .addDescriptionOf(actualValue); + } else { + return mismatchDescription.add(actualValue); + } + } + + return mismatchDescription.add('Unknown mismatch'); + } +} File writeToTemporaryFile(String content) { final tempDir = Directory.systemTemp.createTempSync(); @@ -169,8 +274,7 @@ void testGolden( ), ); } on TestFailure { - // ignore: deprecated_member_use_from_same_package - if (!goldenWrite) rethrow; + if (!_goldenWrite) rethrow; final source = File(sourcePath).readAsStringSync(); final result = encodePrioritizedSourceChanges( diff --git a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/auto_dispose_read.back b/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/auto_dispose_read.back deleted file mode 100644 index cd73eaf57..000000000 --- a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/auto_dispose_read.back +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; - -part 'auto_dispose_read.g.dart'; - -final a = Provider.autoDispose((ref) {}); - -class B extends ConsumerWidget { - const B({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return ElevatedButton( - onPressed: () { - // expect_lint: riverpod_avoid_read_auto_dispose - ref.read(a); - }, - ); - } -} - -@riverpod -class Foo extends _$Foo { - @override - int build() => 0; - - void onChange() { - // expect_lint: riverpod_avoid_read_auto_dispose - ref.read(a); - } -} - -void main() { - // Example of test usage - final container = ProviderContainer(); - // Lint - // expect_lint: riverpod_avoid_read_auto_dispose - container.read(a); - container.dispose(); -} diff --git a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/avoid_dynamic_provider.back b/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/avoid_dynamic_provider.back deleted file mode 100644 index 13885b314..000000000 --- a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/avoid_dynamic_provider.back +++ /dev/null @@ -1,21 +0,0 @@ -// ignore_for_file: unused_element, unused_local_variable - -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -final global = Provider((ref) => 0); - -class Class { - static final topLevel = Provider((ref) => 0); - - // expect_lint: riverpod_avoid_dynamic_provider - final local = Provider((ref) => 0); -} - -void fn() { - // expect_lint: riverpod_avoid_dynamic_provider - final shouldBeTopLevel = Provider((ref) => 0); - // expect_lint: riverpod_avoid_dynamic_provider - final shouldBeTopLevelFamily = Provider.family((ref, id) => 0); - // expect_lint: riverpod_avoid_dynamic_provider - final shouldBeTopLevelAutoDispose = Provider.autoDispose((ref) => 0); -} diff --git a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/dependencies.back b/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/dependencies.back deleted file mode 100644 index 4eb0f74c3..000000000 --- a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/dependencies.back +++ /dev/null @@ -1,159 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; - -final a = StateProvider((ref) { - return 0; -}); - -final b = StateProvider((ref) { - ref.watch(a); - return 0; -}); - -final c = StateProvider((ref) { - ref.watch(a); - return 0; -}, dependencies: [a]); - -final d = StateProvider( - (ref) { - ref.watch(a); - return 0; - }, - // expect_lint: riverpod_missing_dependency - dependencies: [], -); - -final e = StateProvider((ref) { - ref.watch(a); - return 0; -}, dependencies: [ - a, - // expect_lint: riverpod_unused_dependency - b, -]); - -final f = StateProvider((ref) { - ref.watch(a.notifier); - return 0; -}, dependencies: [a]); - -final g = StateProvider((ref) { - ref.watch(b); - return 0; -}, dependencies: [b]); - -final h = StateProvider((ref) { - ref.watch(c); - return 0; -}, dependencies: [c]); - -// expect_lint: riverpod_unspecified_dependencies -final i = StateProvider((ref) { - ref.watch(c); - return 0; -}); - -// Notifier without specified dependencies -final j = StateNotifierProvider((ref) { - return J(ref); -}); - -class J extends StateNotifier { - J(this.ref) : super(0); - final Ref ref; - - void fn() { - ref.read(a); - } -} - -// Notifier with dependency -final k = StateNotifierProvider((ref) { - return K(ref); -// expect_lint: riverpod_missing_dependency -}, dependencies: []); - -class K extends StateNotifier { - K(this.ref) : super(0); - final Ref ref; - - void fn() { - ref.read(a); - } -} - -// Provider variants (autoDispose / family) -final l = StateProvider.autoDispose((ref) { - return 0; -}, dependencies: []); - -final m = StateProvider.autoDispose.family((ref, param) { - return 0; -// expect_lint: riverpod_unused_dependency -}, dependencies: [a]); - -// Passing ref to a function -final n = StateProvider((ref) { - fn(ref); - return 0; -}, dependencies: [a]); - -final o = StateProvider((ref) { - fn(ref); - return 0; -// expect_lint: riverpod_missing_dependency -}, dependencies: []); - -void fn(Ref ref) { - ref.watch(a); -} - -final p = StateNotifierProvider(P.new); - -class P extends StateNotifier { - P(this.ref) : super(0); - final Ref ref; - - void fn() { - ref.read(a); - } -} - -final q = StateNotifierProvider( - Q.new, - // expect_lint: riverpod_missing_dependency - dependencies: [], -); - -class Q extends StateNotifier { - Q(this.ref) : super(0); - final Ref ref; - - void fn() { - ref.read(a); - } -} - -final r1 = StateNotifierProvider( - (ref) => R()..ref = ref, - // expect_lint: riverpod_missing_dependency - dependencies: [], -); - -final r2 = StateNotifierProvider((ref) { - final r = R(); - r.ref = ref; - return r; - // expect_lint: riverpod_missing_dependency -}, dependencies: []); - -class R extends StateNotifier { - R() : super(0); - late final Ref ref; - - void fn() { - ref.read(a); - } -} - -// TODO check dynamic ref invocation diff --git a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/final_provider.back b/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/final_provider.back deleted file mode 100644 index 4b79fcbfc..000000000 --- a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/final_provider.back +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class Class { - static final ok = Provider((ref) => 0); - // expect_lint: prefer_final_provider - static var staticShouldBeFinal = Provider((ref) => 0); - // expect_lint: prefer_final_provider - static Provider get shouldBeFinalGetter => Provider((ref) => 0); -} - -final ok = StateProvider((ref) => 0); - -// expect_lint: prefer_final_provider -var shouldBeFinal = StateProvider.autoDispose((ref) => 0); - -// expect_lint: prefer_final_provider -var shouldBeFinalFamily = StateProvider.autoDispose.family((ref, value) => 0); - -// expect_lint: prefer_final_provider -Provider get shouldBeFinalGetter => Provider((ref) => 0); diff --git a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/flutter_test.back b/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/flutter_test.back deleted file mode 100644 index 280a4d063..000000000 --- a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/flutter_test.back +++ /dev/null @@ -1,110 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter_test/flutter_test.dart'; - -Future main() async { - test('goldens', timeout: Timeout(Duration(seconds: 60)), () async { - final result = await Process.run( - 'flutter', - ['pub', 'run', 'custom_lint'], - stdoutEncoding: utf8, - ); - - expect(result.stdout, ''' - test/goldens/auto_dispose_read.dart:11:5 • Avoid using ref.read inside the build method of widgets/providers. • riverpod_avoid_read_inside_build - test/goldens/auto_dispose_read.dart:11:5 • Avoid using ref.read on an autoDispose provider • riverpod_avoid_read_auto_dispose - test/goldens/auto_dispose_read.dart:20:3 • Avoid using ref.read on an autoDispose provider • riverpod_avoid_read_auto_dispose - test/goldens/avoid_dynamic_provider.dart:10:9 • Providers should be either top level variables or static properties • riverpod_avoid_dynamic_provider - test/goldens/avoid_dynamic_provider.dart:14:9 • Providers should be either top level variables or static properties • riverpod_avoid_dynamic_provider - test/goldens/avoid_dynamic_provider.dart:15:9 • Providers should be either top level variables or static properties • riverpod_avoid_dynamic_provider - test/goldens/avoid_dynamic_provider.dart:16:9 • Providers should be either top level variables or static properties • riverpod_avoid_dynamic_provider - test/goldens/dependencies.dart:20:4 • This provider depends on "a" yet "a" isn't listed in the dependencies. • riverpod_missing_dependency - test/goldens/dependencies.dart:25:22 • This provider specifies that it depends on "b" yet it never uses that provider. • riverpod_unused_dependency - test/goldens/dependencies.dart:42:7 • This provider does not specify `dependencies`, yet depends on "c" which did specify its dependencies. • riverpod_unspecified_dependencies - test/goldens/dependencies.dart:64:4 • This provider depends on "a" yet "a" isn't listed in the dependencies. • riverpod_missing_dependency - test/goldens/dependencies.dart:82:19 • This provider specifies that it depends on "a" yet it never uses that provider. • riverpod_unused_dependency - test/goldens/dependencies.dart:93:4 • This provider depends on "a" yet "a" isn't listed in the dependencies. • riverpod_missing_dependency - test/goldens/dependencies.dart:110:48 • This provider depends on "a" yet "a" isn't listed in the dependencies. • riverpod_missing_dependency - test/goldens/dependencies.dart:122:60 • This provider depends on "a" yet "a" isn't listed in the dependencies. • riverpod_missing_dependency - test/goldens/dependencies.dart:127:4 • This provider depends on "a" yet "a" isn't listed in the dependencies. • riverpod_missing_dependency - test/goldens/final_provider.dart:5:14 • Providers should always be declared as final • riverpod_final_provider - test/goldens/final_provider.dart:6:51 • Providers should always be declared as final • riverpod_final_provider - test/goldens/final_provider.dart:11:5 • Providers should always be declared as final • riverpod_final_provider - test/goldens/final_provider.dart:13:5 • Providers should always be declared as final • riverpod_final_provider - test/goldens/final_provider.dart:15:42 • Providers should always be declared as final • riverpod_final_provider - test/goldens/global_providers.dart:3:7 • This container is global • riverpod_global_container - test/goldens/global_providers.dart:4:7 • This container is global • riverpod_global_container - test/goldens/global_providers.dart:4:41 • This container is global • riverpod_global_container - test/goldens/mutate_in_create.dart:10:3 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:11:3 • Do not mutate a provider synchronously, a function was called which mutates a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:21:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:25:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:26:5 • Do not mutate a provider synchronously, a function was called which mutates a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:27:5 • Do not mutate a provider synchronously, a function was called which mutates a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:54:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:55:5 • Do not mutate a provider synchronously, a function was called which mutates a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:56:5 • Do not mutate a provider synchronously, a function was called which mutates a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:57:5 • Do not use ref after async gaps in flutter widgets, a function was called which uses ref after a widget could be disposed • riverpod_no_ref_after_async - test/goldens/mutate_in_create.dart:58:5 • Do not mutate a provider synchronously, a function was called which mutates a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:58:5 • Do not use ref after async gaps in flutter widgets. • riverpod_no_ref_after_async - test/goldens/mutate_in_create.dart:59:5 • Do not mutate a provider synchronously, a function was called which mutates a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:60:5 • Do not mutate a provider synchronously, a function was called which mutates a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:108:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:112:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:113:5 • Do not mutate a provider synchronously, a function was called which mutates a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:114:5 • Do not mutate a provider synchronously, a function was called which mutates a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:115:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:116:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:140:3 • Do not mutate a provider synchronously, a function was called which mutates a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:154:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:155:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:156:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:168:3 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:169:3 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:170:3 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:180:3 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:181:3 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:182:3 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:195:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:196:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:197:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:210:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:211:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.dart:212:5 • Do not mutate a provider synchronously • riverpod_no_mutate_sync - test/goldens/mutate_in_create.g.dart:92:12 • Providers should always be declared as final • riverpod_final_provider - test/goldens/mutate_in_create.g.dart:195:12 • Providers should always be declared as final • riverpod_final_provider - test/goldens/mutate_in_create.g.dart:291:12 • Providers should always be declared as final • riverpod_final_provider - test/goldens/mutate_in_create.g.dart:377:12 • Providers should always be declared as final • riverpod_final_provider - test/goldens/read_vs_watch.dart:12:3 • Avoid using ref.read inside the build method of widgets/providers. • riverpod_avoid_read_inside_build - test/goldens/read_vs_watch.dart:20:5 • Avoid using ref.watch outside the build method of widgets/providers. • riverpod_avoid_watch_outside_build - test/goldens/read_vs_watch.dart:28:5 • Avoid using ref.read inside the build method of widgets/providers. • riverpod_avoid_read_inside_build - test/goldens/read_vs_watch.dart:36:7 • Avoid using ref.watch outside the build method of widgets/providers. • riverpod_avoid_watch_outside_build - test/goldens/read_vs_watch.dart:47:5 • Avoid using ref.read inside the build method of widgets/providers. • riverpod_avoid_read_inside_build - test/goldens/read_vs_watch.dart:55:7 • Avoid using ref.watch outside the build method of widgets/providers. • riverpod_avoid_watch_outside_build - test/goldens/read_vs_watch.dart:67:5 • Avoid using ref.read inside the build method of widgets/providers. • riverpod_avoid_read_inside_build - test/goldens/read_vs_watch.dart:71:7 • Avoid using ref.read inside the build method of widgets/providers. • riverpod_avoid_read_inside_build - test/goldens/read_vs_watch.dart:77:9 • Avoid using ref.watch outside the build method of widgets/providers. • riverpod_avoid_watch_outside_build - test/goldens/read_vs_watch.dart:87:5 • Avoid using ref.watch outside the build method of widgets/providers. • riverpod_avoid_watch_outside_build - test/goldens/read_vs_watch.dart:96:5 • Avoid using ref.read inside the build method of widgets/providers. • riverpod_avoid_read_inside_build - test/goldens/read_vs_watch.dart:100:7 • Avoid using ref.read inside the build method of widgets/providers. • riverpod_avoid_read_inside_build - test/goldens/read_vs_watch.dart:106:9 • Avoid using ref.watch outside the build method of widgets/providers. • riverpod_avoid_watch_outside_build - test/goldens/read_vs_watch.dart:116:5 • Avoid using ref.watch outside the build method of widgets/providers. • riverpod_avoid_watch_outside_build - test/goldens/read_vs_watch.dart:130:5 • Avoid using ref.watch outside the build method of widgets/providers. • riverpod_avoid_watch_outside_build - test/goldens/read_vs_watch.dart:137:3 • Avoid using ref.read inside the build method of widgets/providers. • riverpod_avoid_read_inside_build - test/goldens/read_vs_watch.dart:146:5 • Avoid using ref.read inside the build method of widgets/providers. • riverpod_avoid_read_inside_build - test/goldens/read_vs_watch.dart:153:5 • Avoid using ref.watch outside the build method of widgets/providers. • riverpod_avoid_watch_outside_build - test/goldens/read_vs_watch.g.dart:92:12 • Providers should always be declared as final • riverpod_final_provider - test/goldens/read_vs_watch.g.dart:188:12 • Providers should always be declared as final • riverpod_final_provider - test/goldens/ref_escape_scope.dart:7:12 • Ref escaped the scope via a function or return expression. • riverpod_ref_escape_scope - test/goldens/ref_escape_scope.dart:37:32 • Ref escaped its scope to another widget. • riverpod_ref_escape_scope - test/goldens/ref_escape_scope.dart:46:32 • Ref escaped its scope to another widget. • riverpod_ref_escape_scope - test/goldens/use_ref_before_async_gaps.dart:48:11 • Do not use ref after async gaps in flutter widgets. • riverpod_no_ref_after_async - test/goldens/use_ref_before_async_gaps.dart:51:9 • Do not use ref after async gaps in flutter widgets, a function was called which uses ref after a widget could be disposed • riverpod_no_ref_after_async - test/goldens/use_ref_before_async_gaps.dart:53:9 • Do not use ref after async gaps in flutter widgets, a function was called which uses ref after a widget could be disposed • riverpod_no_ref_after_async - test/goldens/use_ref_before_async_gaps.dart:55:9 • Do not use ref after async gaps in flutter widgets, a function was called which uses ref after a widget could be disposed • riverpod_no_ref_after_async - test/goldens/use_ref_before_async_gaps.dart:57:15 • Do not use ref after async gaps in flutter widgets, a function was called which uses ref after a widget could be disposed • riverpod_no_ref_after_async - test/goldens/use_ref_before_async_gaps.dart:59:9 • Do not use ref after async gaps in flutter widgets. • riverpod_no_ref_after_async -'''); - }, skip: 'TODO flaky'); -} diff --git a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/global_providers.back b/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/global_providers.back deleted file mode 100644 index 9948f0308..000000000 --- a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/global_providers.back +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; - -// expect_lint: riverpod_global_container -final container1 = ProviderContainer(); - -final - // expect_lint: riverpod_global_container - container2 = ProviderContainer(), - // expect_lint: riverpod_global_container - container3 = ProviderContainer(); diff --git a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/mutate_in_create.back b/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/mutate_in_create.back deleted file mode 100644 index f5341efec..000000000 --- a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/mutate_in_create.back +++ /dev/null @@ -1,256 +0,0 @@ -// ignore_for_file: unused_result - -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:flutter/material.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; - -part 'mutate_in_create.g.dart'; - -final a = StateProvider((ref) => 'String'); - -final b = Provider((ref) { - // expect_lint: riverpod_no_mutate_sync - ref.watch(a.notifier).state = ''; - // expect_lint: riverpod_no_mutate_sync - ref.watch(c.notifier).fn(); - return false; -}); - -final c = StateNotifierProvider((ref) { - return C(ref); -}); - -class C extends StateNotifier { - C(this.ref) : super(0) { - // expect_lint: riverpod_no_mutate_sync - ref.read(a.notifier).state = ''; - Future.delayed(Duration(milliseconds: 10), () { - ref.read(a.notifier).state = ''; - }); - // expect_lint: riverpod_no_mutate_sync - ref.read(a.notifier).state = ''; - // expect_lint: riverpod_no_mutate_sync - fn(); - // expect_lint: riverpod_no_mutate_sync - fn2(); - fn3(); - } - final Ref ref; - - void fn() { - ref.read(a.notifier).state = ''; - } - - // Not okay - Future fn2() async { - ref.read(a.notifier).state = ''; - await Future.delayed(Duration(seconds: 1)); - } - - // Okay - Future fn3() async { - await Future.delayed(Duration(seconds: 1)); - ref.read(a.notifier).state = ''; - } -} - -class D extends ConsumerWidget { - const D({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context, WidgetRef ref) { - // expect_lint: riverpod_no_mutate_sync - ref.watch(a.notifier).state = ''; - // expect_lint: riverpod_no_mutate_sync - fn(ref); - // expect_lint: riverpod_no_mutate_sync - fn2(ref); - fn3(ref); - // expect_lint: riverpod_no_mutate_sync - ref.watch(c.notifier).fn(); - // expect_lint: riverpod_no_mutate_sync - ref.fn_g; - // expect_lint: riverpod_no_mutate_sync - ref.fn2_g; - ref.fn3_g; - return Container(); - } - - void fn(WidgetRef ref) { - ref.read(a.notifier).state = ''; - } - - // Not okay - Future fn2(WidgetRef ref) async { - ref.read(a.notifier).state = ''; - await Future.delayed(Duration(seconds: 1)); - } - - // Okay, for synchronous, but bad for async usage - Future fn3(WidgetRef ref) async { - await Future.delayed(Duration(seconds: 1)); - ref.read(a.notifier).state = ''; - } -} - -extension on WidgetRef { - void fn() { - read(a.notifier).state = ''; - } - - void get fn_g => fn(); - - // Not okay - Future fn2() async { - read(a.notifier).state = ''; - await Future.delayed(Duration(seconds: 1)); - } - - Future get fn2_g => fn2(); - - // Okay - Future fn3() async { - await Future.delayed(Duration(seconds: 1)); - read(a.notifier).state = ''; - } - - Future get fn3_g => fn3(); -} - -class E extends ChangeNotifier { - E(this.ref) { - // expect_lint: riverpod_no_mutate_sync - ref.read(a.notifier).state = ''; - Future.delayed(Duration(milliseconds: 10), () { - ref.read(a.notifier).state = ''; - }); - // expect_lint: riverpod_no_mutate_sync - ref.read(a.notifier).state = ''; - // expect_lint: riverpod_no_mutate_sync - fn(); - // expect_lint: riverpod_no_mutate_sync - fn2(); - // expect_lint: riverpod_no_mutate_sync - ref.invalidate(a); - // expect_lint: riverpod_no_mutate_sync - ref.invalidateSelf(); - fn3(); - } - final Ref ref; - - void fn() { - ref.read(a.notifier).state = ''; - } - - // Not okay - Future fn2() async { - ref.read(a.notifier).state = ''; - await Future.delayed(Duration(seconds: 1)); - } - - // Okay - Future fn3() async { - await Future.delayed(Duration(seconds: 1)); - ref.read(a.notifier).state = ''; - } -} - -final f = ChangeNotifierProvider((ref) { - final e = E(ref); - // expect_lint: riverpod_no_mutate_sync - e.fn(); - return e; -}); - -class G extends ConsumerStatefulWidget { - const G({Key? key}) : super(key: key); - - @override - ConsumerState createState() => _GState(); -} - -class _GState extends ConsumerState { - @override - void initState() { - // expect_lint: riverpod_no_mutate_sync - ref.read(a.notifier).state = ''; - // expect_lint: riverpod_no_mutate_sync - ref.invalidate(a); - // expect_lint: riverpod_no_mutate_sync - ref.refresh(a); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Container(); - } -} - -@riverpod -Future generated(GeneratedRef ref, String value, int otherValue) async { - // expect_lint: riverpod_no_mutate_sync - ref.watch(a.notifier).state = 'Other'; - // expect_lint: riverpod_no_mutate_sync - ref.invalidate(a); - // expect_lint: riverpod_no_mutate_sync - ref.refresh(a); - await Future.delayed(Duration(seconds: 1)); - ref.watch(a.notifier).state = 'Other'; - ref.invalidate(a); - ref.refresh(a); - return ''; -} - -@riverpod -String generatedSync(GeneratedSyncRef ref, String value, int otherValue) { - // expect_lint: riverpod_no_mutate_sync - ref.watch(a.notifier).state = 'Other'; - // expect_lint: riverpod_no_mutate_sync - ref.invalidate(a); - // expect_lint: riverpod_no_mutate_sync - ref.refresh(a); - Future.delayed(Duration(seconds: 1), () { - ref.read(a.notifier).state = 'Other'; - ref.invalidate(a); - ref.refresh(a); - }); - return ''; -} - -@riverpod -class MyNotifier extends _$MyNotifier { - @override - Future build(int i, String b) async { - // expect_lint: riverpod_no_mutate_sync - ref.watch(a.notifier).state = 'Other'; - // expect_lint: riverpod_no_mutate_sync - ref.invalidate(a); - // expect_lint: riverpod_no_mutate_sync - ref.refresh(a); - await Future.delayed(Duration(seconds: 1)); - ref.watch(a.notifier).state = 'Other'; - ref.invalidate(a); - ref.refresh(a); - return ''; - } -} - -@riverpod -class MyNotifier2 extends _$MyNotifier2 { - @override - String build(int i, String b) { - // expect_lint: riverpod_no_mutate_sync - ref.watch(a.notifier).state = 'Other'; - // expect_lint: riverpod_no_mutate_sync - ref.invalidate(a); - // expect_lint: riverpod_no_mutate_sync - ref.refresh(a); - Future.delayed(Duration(seconds: 1), () { - ref.read(a.notifier).state = 'Other'; - ref.invalidate(a); - ref.refresh(a); - }); - return ''; - } -} diff --git a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/read_vs_watch.back b/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/read_vs_watch.back deleted file mode 100644 index 607ecebc3..000000000 --- a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/read_vs_watch.back +++ /dev/null @@ -1,174 +0,0 @@ -// ignore_for_file: unused_element, unused_local_variable - -import 'package:flutter/material.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; -part 'read_vs_watch.g.dart'; - -final provider = Provider((ref) => 0); - -final another = Provider((ref) { - ref.watch(provider); - // expect_lint: riverpod_avoid_read_inside_build - ref.read(provider); - - void fn() { - // expect_lint: riverpod_avoid_watch_outside_build - ref.watch(provider); - ref.read(provider); - } - - final fn2 = () { - // expect_lint: riverpod_avoid_watch_outside_build - ref.watch(provider); - ref.read(provider); - }; -}); - -final foo = Consumer( - builder: (context, ref, child) { - ref.watch(provider); - // expect_lint: riverpod_avoid_read_inside_build - ref.read(provider); - - void fn() { - ref.watch(provider); - ref.read(provider); - } - - final fn2 = () { - // expect_lint: riverpod_avoid_watch_outside_build - ref.watch(provider); - ref.read(provider); - }; - - return Container(); - }, -); - -final bar = HookConsumer( - builder: (context, ref, child) { - ref.watch(provider); - // expect_lint: riverpod_avoid_read_inside_build - ref.read(provider); - - void fn() { - ref.watch(provider); - ref.read(provider); - } - - final fn2 = () { - // expect_lint: riverpod_avoid_watch_outside_build - ref.watch(provider); - ref.read(provider); - }; - - return Container(); - }, -); - -class Home extends ConsumerWidget { - @override - Widget build(BuildContext context, WidgetRef ref) { - ref.watch(provider); - // expect_lint: riverpod_avoid_read_inside_build - ref.read(provider); - - Builder(builder: (context) { - ref.watch(provider); - // expect_lint: riverpod_avoid_read_inside_build - ref.read(provider); - return Container(); - }); - - FloatingActionButton( - onPressed: () { - // expect_lint: riverpod_avoid_watch_outside_build - ref.watch(provider); - ref.read(provider); - }, - child: const Icon(Icons.add), - ); - - return Container(); - } - - void fn(WidgetRef ref) { - // expect_lint: riverpod_avoid_watch_outside_build - ref.watch(provider); - ref.read(provider); - } -} - -class HookHome extends HookConsumerWidget { - @override - Widget build(BuildContext context, WidgetRef ref) { - ref.watch(provider); - // expect_lint: riverpod_avoid_read_inside_build - ref.read(provider); - - Builder(builder: (context) { - ref.watch(provider); - // expect_lint: riverpod_avoid_read_inside_build - ref.read(provider); - return Container(); - }); - - FloatingActionButton( - onPressed: () { - // expect_lint: riverpod_avoid_watch_outside_build - ref.watch(provider); - ref.read(provider); - }, - child: const Icon(Icons.add), - ); - - return Container(); - } - - void fn(WidgetRef ref) { - // expect_lint: riverpod_avoid_watch_outside_build - ref.watch(provider); - ref.read(provider); - } -} - -class Counter extends StateNotifier { - Counter(this.ref) : super(0) { - ref.watch(provider); - ref.read(provider); - } - - final Ref ref; - - void increment() { - // expect_lint: riverpod_avoid_watch_outside_build - ref.watch(provider); - ref.read(provider); - } -} - -@riverpod -Future generated(GeneratedRef ref, String value, int otherValue) async { - // expect_lint: riverpod_avoid_read_inside_build - ref.read(provider); - ref.watch(provider); - return ''; -} - -@riverpod -class MyNotifier extends _$MyNotifier { - @override - Future build(int i, String b) async { - // expect_lint: riverpod_avoid_read_inside_build - ref.read(provider); - ref.watch(provider); - return ''; - } - - void fn() { - ref.read(provider); - // expect_lint: riverpod_avoid_watch_outside_build - ref.watch(provider); - } -} diff --git a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/ref_escape_scope.back b/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/ref_escape_scope.back deleted file mode 100644 index ec121d8f8..000000000 --- a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/ref_escape_scope.back +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:flutter/material.dart'; - -final a = StateProvider((ref) => ''); - -final b = StateNotifierProvider((ref) { - // expect_lint: riverpod_ref_escape_scope - get() => ref; - return B(get); -}); - -class B extends StateNotifier { - B(this.get) : super(0); - final Ref Function() get; - - void fn() { - get().read(a); - } -} - -class C extends ConsumerWidget { - const C({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return Column(children: [ - BadWidgetA(ref: ref), - BadWidgetB(ref: ref), - ]); - } - - void fn(WidgetRef ref) { - ref.read(a); - } -} - -class BadWidgetA extends StatelessWidget { - const BadWidgetA({ - super.key, - // expect_lint: riverpod_ref_escape_scope - this.ref, - }); - - final WidgetRef? ref; - - @override - Widget build(BuildContext context) { - throw UnimplementedError(); - } -} - -class BadWidgetB extends StatefulWidget { - const BadWidgetB({ - super.key, - // expect_lint: riverpod_ref_escape_scope - this.ref, - }); - - final WidgetRef? ref; - - @override - State createState() { - return BadWidgetBState(); - } -} - -class BadWidgetBState extends State { - @override - Widget build(BuildContext context) { - throw UnimplementedError(); - } -} diff --git a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/use_ref_before_async_gaps.back b/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/use_ref_before_async_gaps.back deleted file mode 100644 index 18601c513..000000000 --- a/packages/riverpod_lint_flutter_test/test/goldens/legacy_backup/use_ref_before_async_gaps.back +++ /dev/null @@ -1,105 +0,0 @@ -// Tests that the ref is used before async gaps in widget methods - -// ignore_for_file: unused_result - -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:flutter/material.dart'; - -final a = Provider((ref) => ''); -final b = StateProvider((ref) => ''); - -/// GOOD -class C extends ConsumerWidget { - @override - Widget build(BuildContext context, WidgetRef ref) { - final bn = ref.watch(b.notifier); - return ElevatedButton( - onPressed: () async { - ref.read(a); - final n = ref.read(b.notifier); - fn2(ref); - await Future.delayed(Duration(seconds: 1)); - n.state = ''; - bn.state = ''; - }, - child: Text('Hi'), - ); - } - - Future fn2(WidgetRef ref) async { - ref.read(a); - ref.invalidate(b); - ref.listen(b, (_, __) {}); - ref.refresh(b); - } -} - -/// BAD -class D extends ConsumerWidget { - @override - Widget build(BuildContext context, WidgetRef ref) { - return ElevatedButton( - onPressed: () async { - // Okay - ref.read(a); - ref.invalidate(b); - ref.listen(b, (_, __) {}); - ref.refresh(b); - Future.delayed(Duration(milliseconds: 10), () { - // Bad ref is used in future callback - // expect_lint: riverpod_no_ref_after_async - ref.read(b.notifier).state = ''; - }); - // Bad (ref is used after async in fn) - // expect_lint: riverpod_no_ref_after_async - fn(ref); - // Bad (ref is used after async in fn2) - // expect_lint: riverpod_no_ref_after_async - fn2(ref); - // Bad (ref is used after async in fn3) - // expect_lint: riverpod_no_ref_after_async - fn3(ref); - // Bad (ref is used after async in fn4) - // expect_lint: riverpod_no_ref_after_async - await fn4(ref); - // Bad - // expect_lint: riverpod_no_ref_after_async - ref.read(b.notifier).state = ''; - }, - child: Text('Hi'), - ); - } - - Future fn(WidgetRef ref) async { - await Future.delayed(Duration(seconds: 1)); - ref.read(b.notifier).state = ''; - } - - Future fn2(WidgetRef ref) async { - await Future.delayed(Duration(seconds: 1)); - ref.invalidate(b); - } - - Future fn3(WidgetRef ref) async { - await Future.delayed(Duration(seconds: 1)); - - ref.listen(b, (_, __) {}); - } - - Future fn4(WidgetRef ref) async { - await Future.delayed(Duration(seconds: 1)); - ref.refresh(b); - } -} - -final stream = StreamProvider((ref) async* { - // No lint after async - await Future.delayed(Duration(seconds: 1)); - ref.watch(a); -}); - -final future = FutureProvider((ref) async* { - await Future.delayed(Duration(seconds: 1)); - // No lint after async - ref.watch(a); -}); diff --git a/packages/riverpod_lint_flutter_test/test/io_utils.dart b/packages/riverpod_lint_flutter_test/test/io_utils.dart new file mode 100644 index 000000000..76407db15 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/io_utils.dart @@ -0,0 +1,15 @@ +import 'dart:io'; + +import 'package:test/test.dart'; +import 'package:path/path.dart'; + +File writeToTemporaryFile(String content) { + final tempDir = Directory.systemTemp.createTempSync(); + addTearDown(() => tempDir.deleteSync(recursive: true)); + + final file = File(join(tempDir.path, 'file.dart')) + ..createSync(recursive: true) + ..writeAsStringSync(content); + + return file; +} diff --git a/packages/riverpod_lint_flutter_test/test/lints/another.g.dart b/packages/riverpod_lint_flutter_test/test/lints/another.g.dart deleted file mode 100644 index bee45802c..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/another.g.dart +++ /dev/null @@ -1,26 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'another.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$bHash() => r'31624e9aa10c9cd7941c9626e841c6df3468723b'; - -/// See also [b]. -@ProviderFor(b) -final bProvider = AutoDisposeProvider.internal( - b, - name: r'bProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$bHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef BRef = AutoDisposeProviderRef; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/async_value_nullable_pattern/async_value_nullable_pattern.dart b/packages/riverpod_lint_flutter_test/test/lints/async_value_nullable_pattern.dart similarity index 100% rename from packages/riverpod_lint_flutter_test/test/lints/async_value_nullable_pattern/async_value_nullable_pattern.dart rename to packages/riverpod_lint_flutter_test/test/lints/async_value_nullable_pattern.dart diff --git a/packages/riverpod_lint_flutter_test/test/lints/async_value_nullable_pattern/fix/async_value_nullable_pattern.dart b/packages/riverpod_lint_flutter_test/test/lints/async_value_nullable_pattern/fix/async_value_nullable_pattern.dart deleted file mode 100644 index bc7f5a411..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/async_value_nullable_pattern/fix/async_value_nullable_pattern.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:riverpod_annotation/riverpod_annotation.dart'; - -void main() { - switch (Object()) { - // T is nullable, therefore we should check hasData - case AsyncValue( - // expect_lint: async_value_nullable_pattern - :final value?, - ): - print(value); - } - - switch (Object()) { - // T is nullable, therefore we should check hasData - case AsyncValue( - // expect_lint: async_value_nullable_pattern - :final value? - ): - print(value); - } -} diff --git a/packages/riverpod_lint_flutter_test/test/lints/async_value_nullable_pattern/fix/async_value_nullable_pattern.diff b/packages/riverpod_lint_flutter_test/test/lints/async_value_nullable_pattern/fix/async_value_nullable_pattern.diff deleted file mode 100644 index 77c08f60a..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/async_value_nullable_pattern/fix/async_value_nullable_pattern.diff +++ /dev/null @@ -1,24 +0,0 @@ -Message: `Use "hasValue: true" instead` -Priority: 100 -Diff for file `test/lints/async_value_nullable_pattern/fix/async_value_nullable_pattern.dart:8`: -``` - case AsyncValue( - // expect_lint: async_value_nullable_pattern -- :final value?, -+ :final value, hasValue: true, - ): - print(value); -``` ---- -Message: `Use "hasValue: true" instead` -Priority: 100 -Diff for file `test/lints/async_value_nullable_pattern/fix/async_value_nullable_pattern.dart:17`: -``` - case AsyncValue( - // expect_lint: async_value_nullable_pattern -- :final value? -+ :final value, hasValue: true - ): - print(value); -``` ---- diff --git a/packages/riverpod_lint_flutter_test/test/lints/async_value_nullable_pattern/fix/async_value_nullable_pattern_test.dart b/packages/riverpod_lint_flutter_test/test/lints/async_value_nullable_pattern/fix/async_value_nullable_pattern_test.dart deleted file mode 100644 index b0351a624..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/async_value_nullable_pattern/fix/async_value_nullable_pattern_test.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:riverpod_lint/src/lints/async_value_nullable_pattern.dart'; - -import '../../../golden.dart'; - -void main() { - testGolden( - 'Verify that @riverpod classes has the build method', - 'lints/async_value_nullable_pattern/fix/async_value_nullable_pattern.diff', - sourcePath: - 'test/lints/async_value_nullable_pattern/fix/async_value_nullable_pattern.dart', - (result, helper) async { - const lint = AsyncValueNullablePattern(); - final fix = lint.getFixes().single; - - final errors = await lint.testRun(result); - - final changes = await Future.wait([ - for (final error in errors) fix.testRun(result, error, errors), - ]); - - return changes.flattened; - }, - ); -} diff --git a/packages/riverpod_lint_flutter_test/test/lints/avoid_build_context_in_providers.dart b/packages/riverpod_lint_flutter_test/test/lints/avoid_build_context_in_providers.dart index faa14343c..e3ca4231b 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/avoid_build_context_in_providers.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/avoid_build_context_in_providers.dart @@ -1,7 +1,6 @@ // ignore_for_file: unused_element import 'package:flutter/widgets.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'avoid_build_context_in_providers.g.dart'; diff --git a/packages/riverpod_lint_flutter_test/test/lints/avoid_build_context_in_providers.g.dart b/packages/riverpod_lint_flutter_test/test/lints/avoid_build_context_in_providers.g.dart index 8a21b41f1..1254e1c65 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/avoid_build_context_in_providers.g.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/avoid_build_context_in_providers.g.dart @@ -6,352 +6,427 @@ part of 'avoid_build_context_in_providers.dart'; // RiverpodGenerator // ************************************************************************** -String _$fnHash() => r'8a726da6104b38a55782e44062757e6771b19de3'; +@ProviderFor(fn) +const fnProvider = FnFamily._(); + +final class FnProvider extends $FunctionalProvider + with $Provider { + const FnProvider._( + {required FnFamily super.from, + required ( + BuildContext, { + BuildContext context2, + }) + super.argument, + int Function( + Ref ref, + BuildContext context1, { + required BuildContext context2, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'fnProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); + final int Function( + Ref ref, + BuildContext context1, { + required BuildContext context2, + })? _createCb; - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } + @override + String debugGetCreateSourceHash() => _$fnHash(); - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + @override + String toString() { + return r'fnProvider' + '' + '$argument'; } -} - -/// See also [fn]. -@ProviderFor(fn) -const fnProvider = FnFamily(); -/// See also [fn]. -class FnFamily extends Family { - /// See also [fn]. - const FnFamily(); - - /// See also [fn]. - FnProvider call( - BuildContext context1, { - required BuildContext context2, - }) { - return FnProvider( - context1, - context2: context2, + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + @override - FnProvider getProviderOverride( - covariant FnProvider provider, + FnProvider $copyWithCreate( + int Function( + Ref ref, + ) create, ) { - return call( - provider.context1, - context2: provider.context2, - ); + return FnProvider._( + argument: argument as ( + BuildContext, { + BuildContext context2, + }), + from: from! as FnFamily, + create: ( + ref, + BuildContext context1, { + required BuildContext context2, + }) => + create(ref)); } - static const Iterable? _dependencies = null; - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; + int create(Ref ref) { + final _$cb = _createCb ?? fn; + final argument = this.argument as ( + BuildContext, { + BuildContext context2, + }); + return _$cb( + ref, + argument.$1, + context2: argument.context2, + ); + } @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + bool operator ==(Object other) { + return other is FnProvider && other.argument == argument; + } @override - String? get name => r'fnProvider'; + int get hashCode { + return argument.hashCode; + } } -/// See also [fn]. -class FnProvider extends AutoDisposeProvider { - /// See also [fn]. - FnProvider( - BuildContext context1, { - required BuildContext context2, - }) : this._internal( - (ref) => fn( - ref as FnRef, - context1, - context2: context2, - ), - from: fnProvider, +String _$fnHash() => r'8a726da6104b38a55782e44062757e6771b19de3'; + +final class FnFamily extends Family { + const FnFamily._() + : super( + retry: null, name: r'fnProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$fnHash, - dependencies: FnFamily._dependencies, - allTransitiveDependencies: FnFamily._allTransitiveDependencies, - context1: context1, - context2: context2, + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, ); - FnProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.context1, - required this.context2, - }) : super.internal(); + FnProvider call( + BuildContext context1, { + required BuildContext context2, + }) => + FnProvider._(argument: ( + context1, + context2: context2, + ), from: this); - final BuildContext context1; - final BuildContext context2; + @override + String debugGetCreateSourceHash() => _$fnHash(); @override + String toString() => r'fnProvider'; + + /// {@macro riverpod.override_with} Override overrideWith( - int Function(FnRef provider) create, + int Function( + Ref ref, + ( + BuildContext, { + BuildContext context2, + }) args, + ) create, ) { - return ProviderOverride( - origin: this, - override: FnProvider._internal( - (ref) => create(ref as FnRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - context1: context1, - context2: context2, - ), + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FnProvider; + + final argument = provider.argument as ( + BuildContext, { + BuildContext context2, + }); + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, ); } +} + +@ProviderFor(MyNotifier) +const myNotifierProvider = MyNotifierFamily._(); + +final class MyNotifierProvider extends $NotifierProvider { + const MyNotifierProvider._( + {required MyNotifierFamily super.from, + required ( + BuildContext, { + BuildContext context2, + }) + super.argument, + super.runNotifierBuildOverride, + MyNotifier Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'myNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyNotifier Function()? _createCb; @override - AutoDisposeProviderElement createElement() { - return _FnProviderElement(this); - } + String debugGetCreateSourceHash() => _$myNotifierHash(); @override - bool operator ==(Object other) { - return other is FnProvider && - other.context1 == context1 && - other.context2 == context2; + String toString() { + return r'myNotifierProvider' + '' + '$argument'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); } + @$internal @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, context1.hashCode); - hash = _SystemHash.combine(hash, context2.hashCode); + MyNotifier create() => _createCb?.call() ?? MyNotifier(); - return _SystemHash.finish(hash); + @$internal + @override + MyNotifierProvider $copyWithCreate( + MyNotifier Function() create, + ) { + return MyNotifierProvider._( + argument: argument as ( + BuildContext, { + BuildContext context2, + }), + from: from! as MyNotifierFamily, + create: create); } -} -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FnRef on AutoDisposeProviderRef { - /// The parameter `context1` of this provider. - BuildContext get context1; - - /// The parameter `context2` of this provider. - BuildContext get context2; -} + @$internal + @override + MyNotifierProvider $copyWithBuild( + int Function( + Ref, + MyNotifier, + ) build, + ) { + return MyNotifierProvider._( + argument: argument as ( + BuildContext, { + BuildContext context2, + }), + from: from! as MyNotifierFamily, + runNotifierBuildOverride: build); + } -class _FnProviderElement extends AutoDisposeProviderElement with FnRef { - _FnProviderElement(super.provider); + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); @override - BuildContext get context1 => (origin as FnProvider).context1; + bool operator ==(Object other) { + return other is MyNotifierProvider && other.argument == argument; + } + @override - BuildContext get context2 => (origin as FnProvider).context2; + int get hashCode { + return argument.hashCode; + } } String _$myNotifierHash() => r'04a0cf33dbda80e3fa80748fe46546b1c968da22'; -abstract class _$MyNotifier extends BuildlessAutoDisposeNotifier { - late final BuildContext context1; - late final BuildContext context2; +final class MyNotifierFamily extends Family { + const MyNotifierFamily._() + : super( + retry: null, + name: r'myNotifierProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); - int build( + MyNotifierProvider call( BuildContext context1, { required BuildContext context2, - }); -} + }) => + MyNotifierProvider._(argument: ( + context1, + context2: context2, + ), from: this); -/// See also [MyNotifier]. -@ProviderFor(MyNotifier) -const myNotifierProvider = MyNotifierFamily(); + @override + String debugGetCreateSourceHash() => _$myNotifierHash(); -/// See also [MyNotifier]. -class MyNotifierFamily extends Family { - /// See also [MyNotifier]. - const MyNotifierFamily(); + @override + String toString() => r'myNotifierProvider'; - /// See also [MyNotifier]. - MyNotifierProvider call( - BuildContext context1, { - required BuildContext context2, - }) { - return MyNotifierProvider( - context1, - context2: context2, + /// {@macro riverpod.override_with} + Override overrideWith( + MyNotifier Function( + ( + BuildContext, { + BuildContext context2, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as MyNotifierProvider; + + final argument = provider.argument as ( + BuildContext, { + BuildContext context2, + }); + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, ); } - @override - MyNotifierProvider getProviderOverride( - covariant MyNotifierProvider provider, + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function( + Ref ref, + MyNotifier notifier, + ( + BuildContext, { + BuildContext context2, + }) argument) + build, ) { - return call( - provider.context1, - context2: provider.context2, + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as MyNotifierProvider; + + final argument = provider.argument as ( + BuildContext, { + BuildContext context2, + }); + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, ); } +} - static const Iterable? _dependencies = null; - - @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; +abstract class _$MyNotifier extends $Notifier { + late final _$args = ref.$arg as ( + BuildContext, { + BuildContext context2, + }); + BuildContext get context1 => + _$args.$1; // expect_lint: avoid_build_context_in_providers + BuildContext get context2 => _$args.context2; + int build( + BuildContext context1, { + required BuildContext context2, + }); + @$internal @override - String? get name => r'myNotifierProvider'; + int runBuild() => build( + _$args.$1, + context2: _$args.context2, + ); } -/// See also [MyNotifier]. -class MyNotifierProvider - extends AutoDisposeNotifierProviderImpl { - /// See also [MyNotifier]. - MyNotifierProvider( - BuildContext context1, { - required BuildContext context2, - }) : this._internal( - () => MyNotifier() - ..context1 = context1 - ..context2 = context2, - from: myNotifierProvider, - name: r'myNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$myNotifierHash, - dependencies: MyNotifierFamily._dependencies, - allTransitiveDependencies: - MyNotifierFamily._allTransitiveDependencies, - context1: context1, - context2: context2, +@ProviderFor(Regression2959) +const regression2959Provider = Regression2959Provider._(); + +final class Regression2959Provider + extends $NotifierProvider { + const Regression2959Provider._( + {super.runNotifierBuildOverride, Regression2959 Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'regression2959Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - MyNotifierProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.context1, - required this.context2, - }) : super.internal(); - - final BuildContext context1; - final BuildContext context2; + final Regression2959 Function()? _createCb; @override - int runNotifierBuild( - covariant MyNotifier notifier, - ) { - return notifier.build( - context1, - context2: context2, - ); - } + String debugGetCreateSourceHash() => _$regression2959Hash(); - @override - Override overrideWith(MyNotifier Function() create) { - return ProviderOverride( + /// {@macro riverpod.override_with_value} + Override overrideWithValue(void value) { + return $ProviderOverride( origin: this, - override: MyNotifierProvider._internal( - () => create() - ..context1 = context1 - ..context2 = context2, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - context1: context1, - context2: context2, - ), + providerOverride: $ValueProvider(value), ); } + @$internal @override - AutoDisposeNotifierProviderElement createElement() { - return _MyNotifierProviderElement(this); - } + Regression2959 create() => _createCb?.call() ?? Regression2959(); + @$internal @override - bool operator ==(Object other) { - return other is MyNotifierProvider && - other.context1 == context1 && - other.context2 == context2; + Regression2959Provider $copyWithCreate( + Regression2959 Function() create, + ) { + return Regression2959Provider._(create: create); } + @$internal @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, context1.hashCode); - hash = _SystemHash.combine(hash, context2.hashCode); - - return _SystemHash.finish(hash); + Regression2959Provider $copyWithBuild( + void Function( + Ref, + Regression2959, + ) build, + ) { + return Regression2959Provider._(runNotifierBuildOverride: build); } -} -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin MyNotifierRef on AutoDisposeNotifierProviderRef { - /// The parameter `context1` of this provider. - BuildContext get context1; - - /// The parameter `context2` of this provider. - BuildContext get context2; + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); } -class _MyNotifierProviderElement - extends AutoDisposeNotifierProviderElement - with MyNotifierRef { - _MyNotifierProviderElement(super.provider); +String _$regression2959Hash() => r'e58855125577a855d642da1ef85f35178ad95afd'; +abstract class _$Regression2959 extends $Notifier { + void build(); + @$internal @override - BuildContext get context1 => (origin as MyNotifierProvider).context1; - @override - BuildContext get context2 => (origin as MyNotifierProvider).context2; + void runBuild() => build(); } -String _$regression2959Hash() => r'e58855125577a855d642da1ef85f35178ad95afd'; - -/// See also [Regression2959]. -@ProviderFor(Regression2959) -final regression2959Provider = - AutoDisposeNotifierProvider.internal( - Regression2959.new, - name: r'regression2959Provider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$regression2959Hash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Regression2959 = AutoDisposeNotifier; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/avoid_public_notifier_properties.dart b/packages/riverpod_lint_flutter_test/test/lints/avoid_public_notifier_properties.dart index c212b93f9..bda98cfd4 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/avoid_public_notifier_properties.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/avoid_public_notifier_properties.dart @@ -43,7 +43,7 @@ class MyNotifier extends Notifier { } } -class MyAutoDisposeNotifier extends AutoDisposeNotifier { +class MyAutoDisposeNotifier extends Notifier { int get _privateGetter => 0; // expect_lint: avoid_public_notifier_properties @@ -53,7 +53,7 @@ class MyAutoDisposeNotifier extends AutoDisposeNotifier { int build() => 0; } -class MyAutoDisposeFamilyNotifier extends AutoDisposeFamilyNotifier { +class MyAutoDisposeFamilyNotifier extends FamilyNotifier { int get _privateGetter => 0; // expect_lint: avoid_public_notifier_properties @@ -73,7 +73,7 @@ class MyAsyncNotifier extends AsyncNotifier { Future build() async => 0; } -class MyAutoDisposeAsyncNotifier extends AutoDisposeAsyncNotifier { +class MyAutoDisposeAsyncNotifier extends AsyncNotifier { int get _privateGetter => 0; // expect_lint: avoid_public_notifier_properties @@ -83,8 +83,7 @@ class MyAutoDisposeAsyncNotifier extends AutoDisposeAsyncNotifier { Future build() async => 0; } -class MyAutoDisposeFamilyAsyncNotifier - extends AutoDisposeFamilyAsyncNotifier { +class MyAutoDisposeFamilyAsyncNotifier extends FamilyAsyncNotifier { int get _privateGetter => 0; // expect_lint: avoid_public_notifier_properties diff --git a/packages/riverpod_lint_flutter_test/test/lints/avoid_public_notifier_properties.g.dart b/packages/riverpod_lint_flutter_test/test/lints/avoid_public_notifier_properties.g.dart index 367ea9d29..6de5b8755 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/avoid_public_notifier_properties.g.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/avoid_public_notifier_properties.g.dart @@ -6,169 +6,166 @@ part of 'avoid_public_notifier_properties.dart'; // RiverpodGenerator // ************************************************************************** -String _$generatedNotifierHash() => r'2b7f4fba816b6e8ccd0e8b7d11fcd207bbb79828'; +@ProviderFor(GeneratedNotifier) +const generatedNotifierProvider = GeneratedNotifierFamily._(); + +final class GeneratedNotifierProvider + extends $NotifierProvider { + const GeneratedNotifierProvider._( + {required GeneratedNotifierFamily super.from, + required int super.argument, + super.runNotifierBuildOverride, + GeneratedNotifier Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'generatedNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); + final GeneratedNotifier Function()? _createCb; - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } + @override + String debugGetCreateSourceHash() => _$generatedNotifierHash(); - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + @override + String toString() { + return r'generatedNotifierProvider' + '' + '($argument)'; } -} -abstract class _$GeneratedNotifier extends BuildlessAutoDisposeNotifier { - late final int param; - - int build( - int param, - ); -} - -/// See also [GeneratedNotifier]. -@ProviderFor(GeneratedNotifier) -const generatedNotifierProvider = GeneratedNotifierFamily(); + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } -/// See also [GeneratedNotifier]. -class GeneratedNotifierFamily extends Family { - /// See also [GeneratedNotifier]. - const GeneratedNotifierFamily(); + @$internal + @override + GeneratedNotifier create() => _createCb?.call() ?? GeneratedNotifier(); - /// See also [GeneratedNotifier]. - GeneratedNotifierProvider call( - int param, + @$internal + @override + GeneratedNotifierProvider $copyWithCreate( + GeneratedNotifier Function() create, ) { - return GeneratedNotifierProvider( - param, - ); + return GeneratedNotifierProvider._( + argument: argument as int, + from: from! as GeneratedNotifierFamily, + create: create); } + @$internal @override - GeneratedNotifierProvider getProviderOverride( - covariant GeneratedNotifierProvider provider, + GeneratedNotifierProvider $copyWithBuild( + int Function( + Ref, + GeneratedNotifier, + ) build, ) { - return call( - provider.param, - ); + return GeneratedNotifierProvider._( + argument: argument as int, + from: from! as GeneratedNotifierFamily, + runNotifierBuildOverride: build); } - static const Iterable? _dependencies = null; - + @$internal @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + bool operator ==(Object other) { + return other is GeneratedNotifierProvider && other.argument == argument; + } @override - String? get name => r'generatedNotifierProvider'; + int get hashCode { + return argument.hashCode; + } } -/// See also [GeneratedNotifier]. -class GeneratedNotifierProvider - extends AutoDisposeNotifierProviderImpl { - /// See also [GeneratedNotifier]. - GeneratedNotifierProvider( - int param, - ) : this._internal( - () => GeneratedNotifier()..param = param, - from: generatedNotifierProvider, +String _$generatedNotifierHash() => r'2b7f4fba816b6e8ccd0e8b7d11fcd207bbb79828'; + +final class GeneratedNotifierFamily extends Family { + const GeneratedNotifierFamily._() + : super( + retry: null, name: r'generatedNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$generatedNotifierHash, - dependencies: GeneratedNotifierFamily._dependencies, - allTransitiveDependencies: - GeneratedNotifierFamily._allTransitiveDependencies, - param: param, + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, ); - GeneratedNotifierProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.param, - }) : super.internal(); + GeneratedNotifierProvider call( + int param, + ) => + GeneratedNotifierProvider._(argument: param, from: this); - final int param; + @override + String debugGetCreateSourceHash() => _$generatedNotifierHash(); @override - int runNotifierBuild( - covariant GeneratedNotifier notifier, + String toString() => r'generatedNotifierProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + GeneratedNotifier Function( + int args, + ) create, ) { - return notifier.build( - param, - ); - } + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GeneratedNotifierProvider; - @override - Override overrideWith(GeneratedNotifier Function() create) { - return ProviderOverride( - origin: this, - override: GeneratedNotifierProvider._internal( - () => create()..param = param, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - param: param, - ), - ); - } + final argument = provider.argument as int; - @override - AutoDisposeNotifierProviderElement createElement() { - return _GeneratedNotifierProviderElement(this); + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); } - @override - bool operator ==(Object other) { - return other is GeneratedNotifierProvider && other.param == param; - } + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function(Ref ref, GeneratedNotifier notifier, int argument) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GeneratedNotifierProvider; - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, param.hashCode); + final argument = provider.argument as int; - return _SystemHash.finish(hash); + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin GeneratedNotifierRef on AutoDisposeNotifierProviderRef { - /// The parameter `param` of this provider. - int get param; -} - -class _GeneratedNotifierProviderElement - extends AutoDisposeNotifierProviderElement - with GeneratedNotifierRef { - _GeneratedNotifierProviderElement(super.provider); +abstract class _$GeneratedNotifier extends $Notifier { + late final _$args = ref.$arg as int; + int get param => _$args; + int build( + int param, + ); + @$internal @override - int get param => (origin as GeneratedNotifierProvider).param; + int runBuild() => build( + _$args, + ); } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/dependencies.g.dart b/packages/riverpod_lint_flutter_test/test/lints/dependencies.g.dart deleted file mode 100644 index f4e96403a..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/dependencies.g.dart +++ /dev/null @@ -1,810 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'dependencies.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$unimplementedScopedHash() => - r'5f32fc56f4157238612d62ef54038fe92b7cdfe8'; - -/// See also [unimplementedScoped]. -@ProviderFor(unimplementedScoped) -final unimplementedScopedProvider = AutoDisposeProvider.internal( - (_) => throw UnsupportedError( - 'The provider "unimplementedScopedProvider" is expected to get overridden/scoped, ' - 'but was accessed without an override.', - ), - name: r'unimplementedScopedProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$unimplementedScopedHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef UnimplementedScopedRef = AutoDisposeProviderRef; -String _$depHash() => r'578a350a40cda46444ecd9fa3ea2fd7bd0994692'; - -/// See also [dep]. -@ProviderFor(dep) -final depProvider = AutoDisposeProvider.internal( - dep, - name: r'depProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$depHash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef DepRef = AutoDisposeProviderRef; -String _$generatedScopedHash() => r'f8e5b6926ce13765c83dbb7f8c8458c9c5fe7d69'; - -/// See also [generatedScoped]. -@ProviderFor(generatedScoped) -final generatedScopedProvider = AutoDisposeProvider.internal( - generatedScoped, - name: r'generatedScopedProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$generatedScopedHash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef GeneratedScopedRef = AutoDisposeProviderRef; -String _$generatedRootHash() => r'179253a56503f28bb616c602d8af9ad3b23d438f'; - -/// See also [generatedRoot]. -@ProviderFor(generatedRoot) -final generatedRootProvider = AutoDisposeProvider.internal( - generatedRoot, - name: r'generatedRootProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$generatedRootHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef GeneratedRootRef = AutoDisposeProviderRef; -String _$watchScopedButNoDependenciesHash() => - r'e326226fdc19ea7a4430900154c071f5a1a98e40'; - -/// See also [watchScopedButNoDependencies]. -@ProviderFor(watchScopedButNoDependencies) -final watchScopedButNoDependenciesProvider = AutoDisposeProvider.internal( - watchScopedButNoDependencies, - name: r'watchScopedButNoDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchScopedButNoDependenciesHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchScopedButNoDependenciesRef = AutoDisposeProviderRef; -String _$watchExternalButNoDependenciesHash() => - r'2ed9c528aa61dbb5d1cf274d41b527d761c3d522'; - -/// See also [watchExternalButNoDependencies]. -@ProviderFor(watchExternalButNoDependencies) -final watchExternalButNoDependenciesProvider = - AutoDisposeProvider.internal( - watchExternalButNoDependencies, - name: r'watchExternalButNoDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchExternalButNoDependenciesHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchExternalButNoDependenciesRef = AutoDisposeProviderRef; -String _$watchGeneratedScopedButNoDependenciesHash() => - r'2109f8ccbc13632e45f18ccb93bc3059c431eba1'; - -/// See also [watchGeneratedScopedButNoDependencies]. -@ProviderFor(watchGeneratedScopedButNoDependencies) -final watchGeneratedScopedButNoDependenciesProvider = - AutoDisposeProvider.internal( - watchGeneratedScopedButNoDependencies, - name: r'watchGeneratedScopedButNoDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchGeneratedScopedButNoDependenciesHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchGeneratedScopedButNoDependenciesRef = AutoDisposeProviderRef; -String _$watchRootButNoDependenciesHash() => - r'cfecc8aeb539e82c46276f9e4dd78c323b4bef12'; - -/// See also [watchRootButNoDependencies]. -@ProviderFor(watchRootButNoDependencies) -final watchRootButNoDependenciesProvider = AutoDisposeProvider.internal( - watchRootButNoDependencies, - name: r'watchRootButNoDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchRootButNoDependenciesHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchRootButNoDependenciesRef = AutoDisposeProviderRef; -String _$watchGeneratedRootButNoDependenciesHash() => - r'c839dab901f606c11c78f9c8761931027d3db1d1'; - -/// See also [watchGeneratedRootButNoDependencies]. -@ProviderFor(watchGeneratedRootButNoDependencies) -final watchGeneratedRootButNoDependenciesProvider = - AutoDisposeProvider.internal( - watchGeneratedRootButNoDependencies, - name: r'watchGeneratedRootButNoDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchGeneratedRootButNoDependenciesHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchGeneratedRootButNoDependenciesRef = AutoDisposeProviderRef; -String _$watchScopedButEmptyDependenciesHash() => - r'a194f52730f635e9c92b3467b33b8c302c93b1ab'; - -/// See also [watchScopedButEmptyDependencies]. -@ProviderFor(watchScopedButEmptyDependencies) -final watchScopedButEmptyDependenciesProvider = - AutoDisposeProvider.internal( - watchScopedButEmptyDependencies, - name: r'watchScopedButEmptyDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchScopedButEmptyDependenciesHash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchScopedButEmptyDependenciesRef = AutoDisposeProviderRef; -String _$watchGeneratedScopedButEmptyDependenciesHash() => - r'fa4cb564341e7b3f0dd10f70e17381c67859c643'; - -/// See also [watchGeneratedScopedButEmptyDependencies]. -@ProviderFor(watchGeneratedScopedButEmptyDependencies) -final watchGeneratedScopedButEmptyDependenciesProvider = - AutoDisposeProvider.internal( - watchGeneratedScopedButEmptyDependencies, - name: r'watchGeneratedScopedButEmptyDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchGeneratedScopedButEmptyDependenciesHash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchGeneratedScopedButEmptyDependenciesRef - = AutoDisposeProviderRef; -String _$watchRootButEmptyDependenciesHash() => - r'8669a421efcd8caadc0d070f0c88043668610bbb'; - -/// See also [watchRootButEmptyDependencies]. -@ProviderFor(watchRootButEmptyDependencies) -final watchRootButEmptyDependenciesProvider = AutoDisposeProvider.internal( - watchRootButEmptyDependencies, - name: r'watchRootButEmptyDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchRootButEmptyDependenciesHash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchRootButEmptyDependenciesRef = AutoDisposeProviderRef; -String _$watchGeneratedRootButEmptyDependenciesHash() => - r'80581ac491e25ae8c6ee7b7f25dff9939f8de37c'; - -/// See also [watchGeneratedRootButEmptyDependencies]. -@ProviderFor(watchGeneratedRootButEmptyDependencies) -final watchGeneratedRootButEmptyDependenciesProvider = - AutoDisposeProvider.internal( - watchGeneratedRootButEmptyDependencies, - name: r'watchGeneratedRootButEmptyDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchGeneratedRootButEmptyDependenciesHash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchGeneratedRootButEmptyDependenciesRef = AutoDisposeProviderRef; -String _$watchScopedButMissingDependenciesHash() => - r'c890e4845b1fca73ee02442eb7a203734605173c'; - -/// See also [watchScopedButMissingDependencies]. -@ProviderFor(watchScopedButMissingDependencies) -final watchScopedButMissingDependenciesProvider = - AutoDisposeProvider.internal( - watchScopedButMissingDependencies, - name: r'watchScopedButMissingDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchScopedButMissingDependenciesHash, - dependencies: [depProvider], - allTransitiveDependencies: { - depProvider, - ...?depProvider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchScopedButMissingDependenciesRef = AutoDisposeProviderRef; -String _$watchGeneratedScopedButMissingDependenciesHash() => - r'fbbb5f1ea3725a7554dc05073f47a6b9ce5d913d'; - -/// See also [watchGeneratedScopedButMissingDependencies]. -@ProviderFor(watchGeneratedScopedButMissingDependencies) -final watchGeneratedScopedButMissingDependenciesProvider = - AutoDisposeProvider.internal( - watchGeneratedScopedButMissingDependencies, - name: r'watchGeneratedScopedButMissingDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchGeneratedScopedButMissingDependenciesHash, - dependencies: [depProvider], - allTransitiveDependencies: { - depProvider, - ...?depProvider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchGeneratedScopedButMissingDependenciesRef - = AutoDisposeProviderRef; -String _$watchRootButMissingDependenciesHash() => - r'cc9c5e6c3a1c34e291a63c429fb031e0cc701499'; - -/// See also [watchRootButMissingDependencies]. -@ProviderFor(watchRootButMissingDependencies) -final watchRootButMissingDependenciesProvider = - AutoDisposeProvider.internal( - watchRootButMissingDependencies, - name: r'watchRootButMissingDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchRootButMissingDependenciesHash, - dependencies: [depProvider], - allTransitiveDependencies: { - depProvider, - ...?depProvider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchRootButMissingDependenciesRef = AutoDisposeProviderRef; -String _$watchGeneratedRootButMissingDependenciesHash() => - r'10d01aea2b6b0772e98172f410bdbfce85786243'; - -/// See also [watchGeneratedRootButMissingDependencies]. -@ProviderFor(watchGeneratedRootButMissingDependencies) -final watchGeneratedRootButMissingDependenciesProvider = - AutoDisposeProvider.internal( - watchGeneratedRootButMissingDependencies, - name: r'watchGeneratedRootButMissingDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchGeneratedRootButMissingDependenciesHash, - dependencies: [depProvider], - allTransitiveDependencies: { - depProvider, - ...?depProvider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchGeneratedRootButMissingDependenciesRef - = AutoDisposeProviderRef; -String _$watchGeneratedScopedAndContainsDependencyHash() => - r'948e75e097500b33ee2fdbd3dc9fdecafa5f3d10'; - -/// See also [watchGeneratedScopedAndContainsDependency]. -@ProviderFor(watchGeneratedScopedAndContainsDependency) -final watchGeneratedScopedAndContainsDependencyProvider = - AutoDisposeProvider.internal( - watchGeneratedScopedAndContainsDependency, - name: r'watchGeneratedScopedAndContainsDependencyProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchGeneratedScopedAndContainsDependencyHash, - dependencies: [generatedScopedProvider], - allTransitiveDependencies: { - generatedScopedProvider, - ...?generatedScopedProvider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchGeneratedScopedAndContainsDependencyRef - = AutoDisposeProviderRef; -String _$watchGeneratedRootAndContainsDependencyHash() => - r'780392b647f1606186ee0f70c81dd5b03f506284'; - -/// See also [watchGeneratedRootAndContainsDependency]. -@ProviderFor(watchGeneratedRootAndContainsDependency) -final watchGeneratedRootAndContainsDependencyProvider = - AutoDisposeProvider.internal( - watchGeneratedRootAndContainsDependency, - name: r'watchGeneratedRootAndContainsDependencyProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$watchGeneratedRootAndContainsDependencyHash, - dependencies: [generatedRootProvider], - allTransitiveDependencies: { - generatedRootProvider, - ...?generatedRootProvider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WatchGeneratedRootAndContainsDependencyRef - = AutoDisposeProviderRef; -String _$specifiedDependencyButNeverUsedHash() => - r'cca97d259bcacbff290f0d459e0de3a9b5b6a510'; - -/// See also [specifiedDependencyButNeverUsed]. -@ProviderFor(specifiedDependencyButNeverUsed) -final specifiedDependencyButNeverUsedProvider = - AutoDisposeProvider.internal( - specifiedDependencyButNeverUsed, - name: r'specifiedDependencyButNeverUsedProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$specifiedDependencyButNeverUsedHash, - dependencies: [depProvider, generatedRootProvider], - allTransitiveDependencies: { - depProvider, - ...?depProvider.allTransitiveDependencies, - generatedRootProvider, - ...?generatedRootProvider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef SpecifiedDependencyButNeverUsedRef = AutoDisposeProviderRef; -String _$regression2348Hash() => r'6ad005595ee202c8b0188562ed8c4a33d01260e2'; - -/// See also [regression2348]. -@ProviderFor(regression2348) -final regression2348Provider = AutoDisposeProvider.internal( - regression2348, - name: r'regression2348Provider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$regression2348Hash, - dependencies: [generatedScopedProvider], - allTransitiveDependencies: { - generatedScopedProvider, - ...?generatedScopedProvider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef Regression2348Ref = AutoDisposeProviderRef; -String _$familyDepHash() => r'ed674a44492b3871b72b4fbc68180ea0839723e5'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// See also [familyDep]. -@ProviderFor(familyDep) -const familyDepProvider = FamilyDepFamily(); - -/// See also [familyDep]. -class FamilyDepFamily extends Family { - /// See also [familyDep]. - const FamilyDepFamily(); - - /// See also [familyDep]. - FamilyDepProvider call( - int p, - ) { - return FamilyDepProvider( - p, - ); - } - - @override - FamilyDepProvider getProviderOverride( - covariant FamilyDepProvider provider, - ) { - return call( - provider.p, - ); - } - - static final Iterable _dependencies = [ - depProvider - ]; - - @override - Iterable? get dependencies => _dependencies; - - static final Iterable _allTransitiveDependencies = - { - depProvider, - ...?depProvider.allTransitiveDependencies - }; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'familyDepProvider'; -} - -/// See also [familyDep]. -class FamilyDepProvider extends AutoDisposeProvider { - /// See also [familyDep]. - FamilyDepProvider( - int p, - ) : this._internal( - (ref) => familyDep( - ref as FamilyDepRef, - p, - ), - from: familyDepProvider, - name: r'familyDepProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$familyDepHash, - dependencies: FamilyDepFamily._dependencies, - allTransitiveDependencies: FamilyDepFamily._allTransitiveDependencies, - p: p, - ); - - FamilyDepProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.p, - }) : super.internal(); - - final int p; - - @override - Override overrideWith( - int Function(FamilyDepRef provider) create, - ) { - return ProviderOverride( - origin: this, - override: FamilyDepProvider._internal( - (ref) => create(ref as FamilyDepRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - p: p, - ), - ); - } - - @override - AutoDisposeProviderElement createElement() { - return _FamilyDepProviderElement(this); - } - - @override - bool operator ==(Object other) { - return other is FamilyDepProvider && other.p == p; - } - - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, p.hashCode); - - return _SystemHash.finish(hash); - } -} - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FamilyDepRef on AutoDisposeProviderRef { - /// The parameter `p` of this provider. - int get p; -} - -class _FamilyDepProviderElement extends AutoDisposeProviderElement - with FamilyDepRef { - _FamilyDepProviderElement(super.provider); - - @override - int get p => (origin as FamilyDepProvider).p; -} - -String _$familyDep2Hash() => r'ee9c96f7a1d65e1b66c29aa8d8c030146995504c'; - -/// See also [familyDep2]. -@ProviderFor(familyDep2) -const familyDep2Provider = FamilyDep2Family(); - -/// See also [familyDep2]. -class FamilyDep2Family extends Family { - /// See also [familyDep2]. - const FamilyDep2Family(); - - /// See also [familyDep2]. - FamilyDep2Provider call( - int p, - ) { - return FamilyDep2Provider( - p, - ); - } - - @override - FamilyDep2Provider getProviderOverride( - covariant FamilyDep2Provider provider, - ) { - return call( - provider.p, - ); - } - - static final Iterable _dependencies = [ - familyDepProvider - ]; - - @override - Iterable? get dependencies => _dependencies; - - static final Iterable _allTransitiveDependencies = - { - familyDepProvider, - ...?familyDepProvider.allTransitiveDependencies - }; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'familyDep2Provider'; -} - -/// See also [familyDep2]. -class FamilyDep2Provider extends AutoDisposeProvider { - /// See also [familyDep2]. - FamilyDep2Provider( - int p, - ) : this._internal( - (ref) => familyDep2( - ref as FamilyDep2Ref, - p, - ), - from: familyDep2Provider, - name: r'familyDep2Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$familyDep2Hash, - dependencies: FamilyDep2Family._dependencies, - allTransitiveDependencies: - FamilyDep2Family._allTransitiveDependencies, - p: p, - ); - - FamilyDep2Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.p, - }) : super.internal(); - - final int p; - - @override - Override overrideWith( - int Function(FamilyDep2Ref provider) create, - ) { - return ProviderOverride( - origin: this, - override: FamilyDep2Provider._internal( - (ref) => create(ref as FamilyDep2Ref), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - p: p, - ), - ); - } - - @override - AutoDisposeProviderElement createElement() { - return _FamilyDep2ProviderElement(this); - } - - @override - bool operator ==(Object other) { - return other is FamilyDep2Provider && other.p == p; - } - - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, p.hashCode); - - return _SystemHash.finish(hash); - } -} - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FamilyDep2Ref on AutoDisposeProviderRef { - /// The parameter `p` of this provider. - int get p; -} - -class _FamilyDep2ProviderElement extends AutoDisposeProviderElement - with FamilyDep2Ref { - _FamilyDep2ProviderElement(super.provider); - - @override - int get p => (origin as FamilyDep2Provider).p; -} - -String _$aliasHash() => r'b410585ad56c66160898a05647e09e1a606aa9d2'; - -/// See also [alias]. -@ProviderFor(alias) -final aliasProvider = AutoDisposeProvider.internal( - alias, - name: r'aliasProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$aliasHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef AliasRef = AutoDisposeProviderRef; -String _$classWatchGeneratedRootButMissingDependenciesHash() => - r'e36d7126a86ea9ded6dc66a6f33eabb2724455a9'; - -/// See also [ClassWatchGeneratedRootButMissingDependencies]. -@ProviderFor(ClassWatchGeneratedRootButMissingDependencies) -final classWatchGeneratedRootButMissingDependenciesProvider = - AutoDisposeNotifierProvider.internal( - ClassWatchGeneratedRootButMissingDependencies.new, - name: r'classWatchGeneratedRootButMissingDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$classWatchGeneratedRootButMissingDependenciesHash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -typedef _$ClassWatchGeneratedRootButMissingDependencies - = AutoDisposeNotifier; -String _$classWatchGeneratedScopedButMissingDependenciesHash() => - r'f5a5ba5f572ee2d0654c89de9e991cef9f15b936'; - -/// See also [ClassWatchGeneratedScopedButMissingDependencies]. -@ProviderFor(ClassWatchGeneratedScopedButMissingDependencies) -final classWatchGeneratedScopedButMissingDependenciesProvider = - AutoDisposeNotifierProvider.internal( - ClassWatchGeneratedScopedButMissingDependencies.new, - name: r'classWatchGeneratedScopedButMissingDependenciesProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$classWatchGeneratedScopedButMissingDependenciesHash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -typedef _$ClassWatchGeneratedScopedButMissingDependencies - = AutoDisposeNotifier; -String _$regression2417Hash() => r'c9ac0ba44e849ea1460c79c1f676feba1b5400da'; - -/// See also [Regression2417]. -@ProviderFor(Regression2417) -final regression2417Provider = - AutoDisposeNotifierProvider.internal( - Regression2417.new, - name: r'regression2417Provider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$regression2417Hash, - dependencies: [generatedScopedProvider], - allTransitiveDependencies: { - generatedScopedProvider, - ...?generatedScopedProvider.allTransitiveDependencies - }, -); - -typedef _$Regression2417 = AutoDisposeNotifier; -String _$aliasClassHash() => r'f5c1f43e7541638274ca7dc334a713763c9c8071'; - -/// See also [AliasClass]. -@ProviderFor(AliasClass) -final aliasClassProvider = - AutoDisposeNotifierProvider.internal( - AliasClass.new, - name: r'aliasClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$aliasClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$AliasClass = AutoDisposeNotifier; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/failing_functional_ref.dart b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/failing_functional_ref.dart new file mode 100644 index 000000000..393aba95d --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/failing_functional_ref.dart @@ -0,0 +1,25 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +@riverpod +// expect_lint: functional_ref +int refless() { + return 0; +} + +@riverpod +int incorrectlyTyped( + // expect_lint: functional_ref + int ref, +) { + return 0; +} + +// Regression test for https://github.com/rrousselGit/riverpod/issues/2689 +@riverpod +// expect_lint: functional_ref +int noRefButArgs({int a = 42}) { + return 0; +} + +@riverpod +int valid(Ref ref) => 0; diff --git a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/auto_import.dart b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/auto_import.dart deleted file mode 100644 index f9da55bfe..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/auto_import.dart +++ /dev/null @@ -1,17 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package -import 'package:riverpod_annotation/riverpod_annotation.dart'; - -part 'auto_import.g.dart'; - -// No riverpod imported. Should add it automatically -@riverpod -// expect_lint: functional_ref -int example(ExampleRef ref) => 0; - -@riverpod -// expect_lint: functional_ref -int empty() => 0; - -@riverpod -// expect_lint: functional_ref -int untyped(ref) => 0; diff --git a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/auto_import.diff b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/auto_import.diff deleted file mode 100644 index 8467bc041..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/auto_import.diff +++ /dev/null @@ -1,100 +0,0 @@ -Message: `Type as Ref` -Priority: 90 -Diff for file `test/lints/functional_ref/fix/auto_import.dart:2`: -``` -// ignore_for_file: deprecated_member_use_from_same_package -- import 'package:riverpod_annotation/riverpod_annotation.dart'; -- -- part 'auto_import.g.dart'; -- -- // No riverpod imported. Should add it automatically -- @riverpod -- // expect_lint: functional_ref -- int example(ExampleRef ref) => 0; -+ import 'package:riverpod/riverpod.dart'; -+ import 'package:riverpod_annotation/riverpod_annotation.dart'; -+ -+ part 'auto_import.g.dart'; -+ -+ // No riverpod imported. Should add it automatically -+ @riverpod -+ // expect_lint: functional_ref -+ int example(Ref ref) => 0; - -@riverpod -``` ---- -Message: `Add ref parameter` -Priority: 90 -Diff for file `test/lints/functional_ref/fix/auto_import.dart:2`: -``` -// ignore_for_file: deprecated_member_use_from_same_package -- import 'package:riverpod_annotation/riverpod_annotation.dart'; -- -- part 'auto_import.g.dart'; -- -- // No riverpod imported. Should add it automatically -- @riverpod -- // expect_lint: functional_ref -- int example(ExampleRef ref) => 0; -- -- @riverpod -- // expect_lint: functional_ref -- int empty() => 0; -+ import 'package:riverpod/riverpod.dart'; -+ import 'package:riverpod_annotation/riverpod_annotation.dart'; -+ -+ part 'auto_import.g.dart'; -+ -+ // No riverpod imported. Should add it automatically -+ @riverpod -+ // expect_lint: functional_ref -+ int example(ExampleRef ref) => 0; -+ -+ @riverpod -+ // expect_lint: functional_ref -+ int empty(Ref ref) => 0; - -@riverpod -``` ---- -Message: `Type as Ref` -Priority: 90 -Diff for file `test/lints/functional_ref/fix/auto_import.dart:2`: -``` -// ignore_for_file: deprecated_member_use_from_same_package -- import 'package:riverpod_annotation/riverpod_annotation.dart'; -- -- part 'auto_import.g.dart'; -- -- // No riverpod imported. Should add it automatically -- @riverpod -- // expect_lint: functional_ref -- int example(ExampleRef ref) => 0; -- -- @riverpod -- // expect_lint: functional_ref -- int empty() => 0; -- -- @riverpod -- // expect_lint: functional_ref -- int untyped(ref) => 0; -+ import 'package:riverpod/riverpod.dart'; -+ import 'package:riverpod_annotation/riverpod_annotation.dart'; -+ -+ part 'auto_import.g.dart'; -+ -+ // No riverpod imported. Should add it automatically -+ @riverpod -+ // expect_lint: functional_ref -+ int example(ExampleRef ref) => 0; -+ -+ @riverpod -+ // expect_lint: functional_ref -+ int empty() => 0; -+ -+ @riverpod -+ // expect_lint: functional_ref -+ int untyped(Ref ref) => 0; -``` ---- diff --git a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/auto_import.g.dart b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/auto_import.g.dart deleted file mode 100644 index ad144b68b..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/auto_import.g.dart +++ /dev/null @@ -1,58 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'auto_import.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$exampleHash() => r'638d7db2be22eaad0f51ea0b3ae38e0483d43725'; - -/// See also [example]. -@ProviderFor(example) -final exampleProvider = AutoDisposeProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeProviderRef; -String _$emptyHash() => r'eaec2981c894019fafd068e09478ffe961a8d188'; - -/// See also [empty]. -@ProviderFor(empty) -final emptyProvider = AutoDisposeProvider.internal( - empty, - name: r'emptyProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$emptyHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef EmptyRef = AutoDisposeProviderRef; -String _$untypedHash() => r'5dd4815f63ed35a15a2e027b4e0cb496693f07f4'; - -/// See also [untyped]. -@ProviderFor(untyped) -final untypedProvider = AutoDisposeProvider.internal( - untyped, - name: r'untypedProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$untypedHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef UntypedRef = AutoDisposeProviderRef; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/failing_functional_ref.diff b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/failing_functional_ref.diff new file mode 100644 index 000000000..81cb4b72f --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/failing_functional_ref.diff @@ -0,0 +1,36 @@ +Message: `Add ref parameter` +Priority: 90 +Diff for file `test/lints/functional_ref/failing_functional_ref.dart:5`: +``` +@riverpod +// expect_lint: functional_ref +- int refless() { ++ int refless(Ref ref) { + return 0; +} +``` +--- +Message: `Type as Ref` +Priority: 90 +Diff for file `test/lints/functional_ref/failing_functional_ref.dart:12`: +``` +int incorrectlyTyped( + // expect_lint: functional_ref +- int ref, ++ Ref ref, +) { + return 0; +``` +--- +Message: `Add ref parameter` +Priority: 90 +Diff for file `test/lints/functional_ref/failing_functional_ref.dart:20`: +``` +@riverpod +// expect_lint: functional_ref +- int noRefButArgs({int a = 42}) { ++ int noRefButArgs(Ref ref, {int a = 42}) { + return 0; +} +``` +--- diff --git a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/functional_ref.diff b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/functional_ref.diff index 804b040f1..69204fe4d 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/functional_ref.diff +++ b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/functional_ref.diff @@ -1,18 +1,6 @@ -Message: `Add ref parameter` -Priority: 90 -Diff for file `test/lints/functional_ref/functional_ref.dart:8`: -``` -@riverpod -// expect_lint: functional_ref -- int refless() { -+ int refless(Ref ref) { - return 0; -} -``` ---- Message: `Type as Ref` Priority: 90 -Diff for file `test/lints/functional_ref/functional_ref.dart:15`: +Diff for file `test/lints/functional_ref/functional_ref.dart:8`: ``` int nameless( // expect_lint: functional_ref @@ -22,27 +10,3 @@ int nameless( return 0; ``` --- -Message: `Type as Ref` -Priority: 90 -Diff for file `test/lints/functional_ref/functional_ref.dart:23`: -``` -int incorrectlyTyped( - // expect_lint: functional_ref -- int ref, -+ Ref ref, -) { - return 0; -``` ---- -Message: `Add ref parameter` -Priority: 90 -Diff for file `test/lints/functional_ref/functional_ref.dart:34`: -``` -@riverpod -// expect_lint: functional_ref -- int noRefButArgs({int a = 42}) { -+ int noRefButArgs(Ref ref, {int a = 42}) { - return 0; -} -``` ---- diff --git a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/functional_ref_test.dart b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/functional_ref_test.dart index ceff0f6bd..a28b4cdd1 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/functional_ref_test.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/functional_ref_test.dart @@ -1,12 +1,12 @@ import 'package:collection/collection.dart'; import 'package:riverpod_lint/src/lints/functional_ref.dart'; -import '../../../golden.dart'; +import '../../../test_lint.dart'; void main() { testGolden( 'Verify that @riverpod functions have a Ref', - 'lints/functional_ref/fix/functional_ref.diff', + 'test/lints/functional_ref/fix/functional_ref.diff', sourcePath: 'test/lints/functional_ref/functional_ref.dart', (result, helper) async { const lint = FunctionalRef(); @@ -21,40 +21,4 @@ void main() { return changes.flattened; }, ); - - testGolden( - 'The Ref fix should add imports', - 'lints/functional_ref/fix/auto_import.diff', - sourcePath: 'test/lints/functional_ref/fix/auto_import.dart', - (result, helper) async { - const lint = FunctionalRef(); - final fix = lint.getFixes().single; - - final errors = await lint.testRun(result); - - final changes = await Future.wait([ - for (final error in errors) fix.testRun(result, error, errors), - ]); - - return changes.flattened; - }, - ); - - testGolden( - 'Supports prefixes', - 'lints/functional_ref/fix/use_prefix.diff', - sourcePath: 'test/lints/functional_ref/fix/use_prefix.dart', - (result, helper) async { - const lint = FunctionalRef(); - final fix = lint.getFixes().single; - - final errors = await lint.testRun(result); - - final changes = await Future.wait([ - for (final error in errors) fix.testRun(result, error, errors), - ]); - - return changes.flattened; - }, - ); } diff --git a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/use_prefix.dart b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/use_prefix.dart deleted file mode 100644 index da23acc3e..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/use_prefix.dart +++ /dev/null @@ -1,19 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package -// ignore: unused_import, used for the fix -import 'package:riverpod/riverpod.dart' as prefix; -import 'package:riverpod_annotation/riverpod_annotation.dart'; - -part 'use_prefix.g.dart'; - -// No riverpod imported. Should add it automatically -@riverpod -// expect_lint: functional_ref -int example(ExampleRef ref) => 0; - -@riverpod -// expect_lint: functional_ref -int empty() => 0; - -@riverpod -// expect_lint: functional_ref -int untyped(ref) => 0; diff --git a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/use_prefix.diff b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/use_prefix.diff deleted file mode 100644 index c403a677f..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/use_prefix.diff +++ /dev/null @@ -1,34 +0,0 @@ -Message: `Type as Ref` -Priority: 90 -Diff for file `test/lints/functional_ref/fix/use_prefix.dart:11`: -``` -@riverpod -// expect_lint: functional_ref -- int example(ExampleRef ref) => 0; -+ int example(prefix.Ref ref) => 0; - -@riverpod -``` ---- -Message: `Add ref parameter` -Priority: 90 -Diff for file `test/lints/functional_ref/fix/use_prefix.dart:15`: -``` -@riverpod -// expect_lint: functional_ref -- int empty() => 0; -+ int empty(prefix.Ref ref) => 0; - -@riverpod -``` ---- -Message: `Type as Ref` -Priority: 90 -Diff for file `test/lints/functional_ref/fix/use_prefix.dart:19`: -``` -@riverpod -// expect_lint: functional_ref -- int untyped(ref) => 0; -+ int untyped(prefix.Ref ref) => 0; -``` ---- diff --git a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/use_prefix.g.dart b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/use_prefix.g.dart deleted file mode 100644 index 331f33241..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/fix/use_prefix.g.dart +++ /dev/null @@ -1,58 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'use_prefix.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$exampleHash() => r'638d7db2be22eaad0f51ea0b3ae38e0483d43725'; - -/// See also [example]. -@ProviderFor(example) -final exampleProvider = AutoDisposeProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeProviderRef; -String _$emptyHash() => r'eaec2981c894019fafd068e09478ffe961a8d188'; - -/// See also [empty]. -@ProviderFor(empty) -final emptyProvider = AutoDisposeProvider.internal( - empty, - name: r'emptyProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$emptyHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef EmptyRef = AutoDisposeProviderRef; -String _$untypedHash() => r'5dd4815f63ed35a15a2e027b4e0cb496693f07f4'; - -/// See also [untyped]. -@ProviderFor(untyped) -final untypedProvider = AutoDisposeProvider.internal( - untyped, - name: r'untypedProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$untypedHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef UntypedRef = AutoDisposeProviderRef; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/functional_ref.dart b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/functional_ref.dart index 5eda643c0..351164386 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/functional_ref.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/functional_ref.dart @@ -1,14 +1,7 @@ -import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'functional_ref.g.dart'; -@riverpod -// expect_lint: functional_ref -int refless() { - return 0; -} - @riverpod int nameless( // expect_lint: functional_ref @@ -18,22 +11,7 @@ int nameless( } @riverpod -int incorrectlyTyped( - // expect_lint: functional_ref - int ref, -) { - return 0; -} - -@riverpod -external int scoped(); - -// Regression test for https://github.com/rrousselGit/riverpod/issues/2689 -@riverpod -// expect_lint: functional_ref -int noRefButArgs({int a = 42}) { - return 0; -} +int generics(Ref ref) => 0; @riverpod int valid(Ref ref) => 0; diff --git a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/functional_ref.diff b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/functional_ref.diff new file mode 100644 index 000000000..b3dab2390 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/functional_ref.diff @@ -0,0 +1,46 @@ +Message: `Type as NamelessRef` +Priority: 90 +Diff for file `test/lints/functional_ref/functional_ref.dart:8`: +``` +int nameless( + // expect_lint: functional_ref +- ref, ++ NamelessRef ref, +) { + return 0; +``` +--- +Message: `Type as NoGenericsRef` +Priority: 90 +Diff for file `test/lints/functional_ref/functional_ref.dart:18`: +``` +@riverpod +// expect_lint: functional_ref +- int noGenerics(NoGenericsRef ref) => 0; ++ int noGenerics(NoGenericsRef ref) => 0; + +@riverpod +``` +--- +Message: `Type as MissingGenericsRef` +Priority: 90 +Diff for file `test/lints/functional_ref/functional_ref.dart:22`: +``` +@riverpod +// expect_lint: functional_ref +- int missingGenerics(MissingGenericsRef ref) => 0; ++ int missingGenerics(MissingGenericsRef ref) => 0; + +@riverpod +``` +--- +Message: `Type as WrongOrderRef` +Priority: 90 +Diff for file `test/lints/functional_ref/functional_ref.dart:26`: +``` +@riverpod +// expect_lint: functional_ref +- int wrongOrder(WrongOrderRef ref) => 0; ++ int wrongOrder(WrongOrderRef ref) => 0; +``` +--- diff --git a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/functional_ref.g.dart b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/functional_ref.g.dart index 8773a0812..16f8991e7 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/functional_ref/functional_ref.g.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/functional_ref/functional_ref.g.dart @@ -6,105 +6,242 @@ part of 'functional_ref.dart'; // RiverpodGenerator // ************************************************************************** -String _$reflessHash() => r'45894f451ae8fab59805fce9bd8292f2b5db1618'; - -/// See also [refless]. -@ProviderFor(refless) -final reflessProvider = AutoDisposeProvider.internal( - refless, - name: r'reflessProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$reflessHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ReflessRef = AutoDisposeProviderRef; +@ProviderFor(nameless) +const namelessProvider = NamelessProvider._(); + +final class NamelessProvider extends $FunctionalProvider + with $Provider { + const NamelessProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'namelessProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$namelessHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + NamelessProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return NamelessProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? nameless; + return _$cb(ref); + } +} + String _$namelessHash() => r'1a2aa61445a64c65301051820b159c5998195606'; -/// See also [nameless]. -@ProviderFor(nameless) -final namelessProvider = AutoDisposeProvider.internal( - nameless, - name: r'namelessProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$namelessHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef NamelessRef = AutoDisposeProviderRef; -String _$incorrectlyTypedHash() => r'36b38a6d23ff56629e8d18e1764a957495953ac0'; - -/// See also [incorrectlyTyped]. -@ProviderFor(incorrectlyTyped) -final incorrectlyTypedProvider = AutoDisposeProvider.internal( - incorrectlyTyped, - name: r'incorrectlyTypedProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$incorrectlyTypedHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef IncorrectlyTypedRef = AutoDisposeProviderRef; -String _$scopedHash() => r'590f1a203323105e732397a2616fbd7dac65f0cc'; - -/// See also [scoped]. -@ProviderFor(scoped) -final scopedProvider = AutoDisposeProvider.internal( - (_) => throw UnsupportedError( - 'The provider "scopedProvider" is expected to get overridden/scoped, ' - 'but was accessed without an override.', - ), - name: r'scopedProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$scopedHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ScopedRef = AutoDisposeProviderRef; -String _$noRefButArgsHash() => r'462ab15f4053f3e9592557cc8a698fbb2352bd40'; - -/// See also [noRefButArgs]. -@ProviderFor(noRefButArgs) -final noRefButArgsProvider = AutoDisposeProvider.internal( - noRefButArgs, - name: r'noRefButArgsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$noRefButArgsHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef NoRefButArgsRef = AutoDisposeProviderRef; -String _$validHash() => r'f33913278e3b1615927fe05b3e6e1f781da7729a'; +@ProviderFor(generics) +const genericsProvider = GenericsFamily._(); + +final class GenericsProvider + extends $FunctionalProvider with $Provider { + const GenericsProvider._( + {required GenericsFamily super.from, + int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'genericsProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$genericsHash(); + + GenericsProvider _copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return GenericsProvider._( + from: from! as GenericsFamily, create: create); + } + + @override + String toString() { + return r'genericsProvider' + '<${A}, ${B}>' + '()'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + GenericsProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return GenericsProvider._( + from: from! as GenericsFamily, create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? generics; + return _$cb(ref); + } + + @override + bool operator ==(Object other) { + return other is GenericsProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } + + @override + int get hashCode { + return Object.hash(runtimeType, argument); + } +} + +String _$genericsHash() => r'dddbd6460e73b1f20343bbadee6666311c5ac0ea'; + +final class GenericsFamily extends Family { + const GenericsFamily._() + : super( + retry: null, + name: r'genericsProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + GenericsProvider call() => + GenericsProvider._(from: this); + + @override + String debugGetCreateSourceHash() => _$genericsHash(); + + @override + String toString() => r'genericsProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + int Function(Ref ref) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericsProvider; + + return provider._copyWithCreate(create).$createElement(pointer); + }, + ); + } +} -/// See also [valid]. @ProviderFor(valid) -final validProvider = AutoDisposeProvider.internal( - valid, - name: r'validProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$validHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ValidRef = AutoDisposeProviderRef; +const validProvider = ValidProvider._(); + +final class ValidProvider extends $FunctionalProvider + with $Provider { + const ValidProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'validProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$validHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ValidProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return ValidProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? valid; + return _$cb(ref); + } +} + +String _$validHash() => r'f33913278e3b1615927fe05b3e6e1f781da7729a'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/async_value_nullable_pattern/async_value_nullable_pattern_fix.diff b/packages/riverpod_lint_flutter_test/test/lints/goldens/async_value_nullable_pattern/async_value_nullable_pattern_fix.diff new file mode 100644 index 000000000..70f4d28e8 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/async_value_nullable_pattern/async_value_nullable_pattern_fix.diff @@ -0,0 +1,60 @@ +Message: `Use "hasValue: true" instead` +Priority: 100 +Diff for file `test/lints/async_value_nullable_pattern.dart:8`: +``` + case AsyncValue( + // expect_lint: async_value_nullable_pattern +- :final value?, ++ :final value, hasValue: true, + ): + print(value); +``` +--- +Message: `Use "hasValue: true" instead` +Priority: 100 +Diff for file `test/lints/async_value_nullable_pattern.dart:32`: +``` + case AsyncError( + // expect_lint: async_value_nullable_pattern +- :final value?, ++ :final value, hasValue: true, + ): + print(value); +``` +--- +Message: `Use "hasValue: true" instead` +Priority: 100 +Diff for file `test/lints/async_value_nullable_pattern.dart:37`: +``` + case AsyncLoading( + // expect_lint: async_value_nullable_pattern +- :final value?, ++ :final value, hasValue: true, + ): + print(value); +``` +--- +Message: `Use "hasValue: true" instead` +Priority: 100 +Diff for file `test/lints/async_value_nullable_pattern.dart:62`: +``` + switch (obj) { + // expect_lint: async_value_nullable_pattern +- case AsyncValue(:final value?): ++ case AsyncValue(:final value, hasValue: true): + print(value); + } +``` +--- +Message: `Use "hasValue: true" instead` +Priority: 100 +Diff for file `test/lints/async_value_nullable_pattern.dart:77`: +``` + switch (obj) { + // expect_lint: async_value_nullable_pattern +- case AsyncValue(:final value?): ++ case AsyncValue(:final value, hasValue: true): + print(value); + } +``` +--- diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/async_value_nullable_pattern/async_value_nullable_pattern_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/async_value_nullable_pattern/async_value_nullable_pattern_lint.md new file mode 100644 index 000000000..cc4f4dfbc --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/async_value_nullable_pattern/async_value_nullable_pattern_lint.md @@ -0,0 +1,72 @@ +code: async_value_nullable_pattern +severity: Severity.warning +message: Using AsyncValue(:final value?) on possibly nullable value is unsafe. Use AsyncValue(:final value, hasValue: true) instead. +test/lints/async_value_nullable_pattern.dart:8:10 + +```dart + case AsyncValue( + // expect_lint: async_value_nullable_pattern + :>>>final value?<<<, + ): + print(value); +``` + +======= + +code: async_value_nullable_pattern +severity: Severity.warning +message: Using AsyncValue(:final value?) on possibly nullable value is unsafe. Use AsyncValue(:final value, hasValue: true) instead. +test/lints/async_value_nullable_pattern.dart:32:10 + +```dart + case AsyncError( + // expect_lint: async_value_nullable_pattern + :>>>final value?<<<, + ): + print(value); +``` + +======= + +code: async_value_nullable_pattern +severity: Severity.warning +message: Using AsyncValue(:final value?) on possibly nullable value is unsafe. Use AsyncValue(:final value, hasValue: true) instead. +test/lints/async_value_nullable_pattern.dart:37:10 + +```dart + case AsyncLoading( + // expect_lint: async_value_nullable_pattern + :>>>final value?<<<, + ): + print(value); +``` + +======= + +code: async_value_nullable_pattern +severity: Severity.warning +message: Using AsyncValue(:final value?) on possibly nullable value is unsafe. Use AsyncValue(:final value, hasValue: true) instead. +test/lints/async_value_nullable_pattern.dart:62:25 + +```dart + switch (obj) { + // expect_lint: async_value_nullable_pattern + case AsyncValue(:>>>final value?<<<): + print(value); + } +``` + +======= + +code: async_value_nullable_pattern +severity: Severity.warning +message: Using AsyncValue(:final value?) on possibly nullable value is unsafe. Use AsyncValue(:final value, hasValue: true) instead. +test/lints/async_value_nullable_pattern.dart:77:25 + +```dart + switch (obj) { + // expect_lint: async_value_nullable_pattern + case AsyncValue(:>>>final value?<<<): + print(value); + } +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/avoid_build_context_in_providers/avoid_build_context_in_providers_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/avoid_build_context_in_providers/avoid_build_context_in_providers_lint.md new file mode 100644 index 000000000..589c4dde1 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/avoid_build_context_in_providers/avoid_build_context_in_providers_lint.md @@ -0,0 +1,87 @@ +code: avoid_build_context_in_providers +severity: Severity.info +message: Passing BuildContext to providers indicates mixing UI with the business logic. +test/lints/avoid_build_context_in_providers.dart:12:3 + +```dart + Ref ref, + // expect_lint: avoid_build_context_in_providers + >>>BuildContext context1<<<, { + // expect_lint: avoid_build_context_in_providers + required BuildContext context2, +``` + +======= + +code: avoid_build_context_in_providers +severity: Severity.info +message: Passing BuildContext to providers indicates mixing UI with the business logic. +test/lints/avoid_build_context_in_providers.dart:14:3 + +```dart + BuildContext context1, { + // expect_lint: avoid_build_context_in_providers + >>>required BuildContext context2<<<, +}) => + 0; +``` + +======= + +code: avoid_build_context_in_providers +severity: Severity.info +message: Passing BuildContext to providers indicates mixing UI with the business logic. +test/lints/avoid_build_context_in_providers.dart:22:5 + +```dart + int build( + // expect_lint: avoid_build_context_in_providers + >>>BuildContext context1<<<, { + // expect_lint: avoid_build_context_in_providers + required BuildContext context2, +``` + +======= + +code: avoid_build_context_in_providers +severity: Severity.info +message: Passing BuildContext to providers indicates mixing UI with the business logic. +test/lints/avoid_build_context_in_providers.dart:24:5 + +```dart + BuildContext context1, { + // expect_lint: avoid_build_context_in_providers + >>>required BuildContext context2<<<, + }) => + 0; +``` + +======= + +code: avoid_build_context_in_providers +severity: Severity.info +message: Passing BuildContext to providers indicates mixing UI with the business logic. +test/lints/avoid_build_context_in_providers.dart:30:5 + +```dart + void event( + // expect_lint: avoid_build_context_in_providers + >>>BuildContext context3<<<, { + // expect_lint: avoid_build_context_in_providers + required BuildContext context4, +``` + +======= + +code: avoid_build_context_in_providers +severity: Severity.info +message: Passing BuildContext to providers indicates mixing UI with the business logic. +test/lints/avoid_build_context_in_providers.dart:32:5 + +```dart + BuildContext context3, { + // expect_lint: avoid_build_context_in_providers + >>>required BuildContext context4<<<, + }) {} +} +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/avoid_public_notifier_properties/avoid_public_notifier_properties_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/avoid_public_notifier_properties/avoid_public_notifier_properties_lint.md new file mode 100644 index 000000000..5b28c0007 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/avoid_public_notifier_properties/avoid_public_notifier_properties_lint.md @@ -0,0 +1,102 @@ +code: avoid_public_notifier_properties +severity: Severity.info +message: Notifiers should not have public properties/getters. Instead, all their public API should be exposed through the `state` property. +test/lints/avoid_public_notifier_properties.dart:16:3 + +```dart + + // expect_lint: avoid_public_notifier_properties + >>>int get publicGetter => _privateGetter;<<< + + // Public setters are OK +``` + +======= + +code: avoid_public_notifier_properties +severity: Severity.info +message: Notifiers should not have public properties/getters. Instead, all their public API should be exposed through the `state` property. +test/lints/avoid_public_notifier_properties.dart:24:3 + +```dart + + // expect_lint: avoid_public_notifier_properties + >>>int publicProperty = 0;<<< + + @protected +``` + +======= + +code: avoid_public_notifier_properties +severity: Severity.info +message: Notifiers should not have public properties/getters. Instead, all their public API should be exposed through the `state` property. +test/lints/avoid_public_notifier_properties.dart:50:3 + +```dart + + // expect_lint: avoid_public_notifier_properties + >>>int get publicGetter => _privateGetter;<<< + + @override +``` + +======= + +code: avoid_public_notifier_properties +severity: Severity.info +message: Notifiers should not have public properties/getters. Instead, all their public API should be exposed through the `state` property. +test/lints/avoid_public_notifier_properties.dart:60:3 + +```dart + + // expect_lint: avoid_public_notifier_properties + >>>int get publicGetter => _privateGetter;<<< + + @override +``` + +======= + +code: avoid_public_notifier_properties +severity: Severity.info +message: Notifiers should not have public properties/getters. Instead, all their public API should be exposed through the `state` property. +test/lints/avoid_public_notifier_properties.dart:70:3 + +```dart + + // expect_lint: avoid_public_notifier_properties + >>>int get publicGetter => _privateGetter;<<< + + @override +``` + +======= + +code: avoid_public_notifier_properties +severity: Severity.info +message: Notifiers should not have public properties/getters. Instead, all their public API should be exposed through the `state` property. +test/lints/avoid_public_notifier_properties.dart:80:3 + +```dart + + // expect_lint: avoid_public_notifier_properties + >>>int get publicGetter => _privateGetter;<<< + + @override +``` + +======= + +code: avoid_public_notifier_properties +severity: Severity.info +message: Notifiers should not have public properties/getters. Instead, all their public API should be exposed through the `state` property. +test/lints/avoid_public_notifier_properties.dart:90:3 + +```dart + + // expect_lint: avoid_public_notifier_properties + >>>int get publicGetter => _privateGetter;<<< + + @override +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/avoid_ref_inside_state_dispose/avoid_ref_inside_state_dispose_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/avoid_ref_inside_state_dispose/avoid_ref_inside_state_dispose_lint.md new file mode 100644 index 000000000..68b058df5 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/avoid_ref_inside_state_dispose/avoid_ref_inside_state_dispose_lint.md @@ -0,0 +1,27 @@ +code: avoid_ref_inside_state_dispose +severity: Severity.warning +message: Avoid using 'Ref' inside State.dispose. +test/lints/avoid_ref_inside_state_dispose.dart:17:5 + +```dart + void dispose() { + // expect_lint: avoid_ref_inside_state_dispose + >>>ref.read(provider)<<<; + // expect_lint: avoid_ref_inside_state_dispose + ref.watch(provider); +``` + +======= + +code: avoid_ref_inside_state_dispose +severity: Severity.warning +message: Avoid using 'Ref' inside State.dispose. +test/lints/avoid_ref_inside_state_dispose.dart:19:5 + +```dart + ref.read(provider); + // expect_lint: avoid_ref_inside_state_dispose + >>>ref.watch(provider)<<<; + + super.dispose(); +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/functional_ref/failing_functional_ref_fix.diff b/packages/riverpod_lint_flutter_test/test/lints/goldens/functional_ref/failing_functional_ref_fix.diff new file mode 100644 index 000000000..81cb4b72f --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/functional_ref/failing_functional_ref_fix.diff @@ -0,0 +1,36 @@ +Message: `Add ref parameter` +Priority: 90 +Diff for file `test/lints/functional_ref/failing_functional_ref.dart:5`: +``` +@riverpod +// expect_lint: functional_ref +- int refless() { ++ int refless(Ref ref) { + return 0; +} +``` +--- +Message: `Type as Ref` +Priority: 90 +Diff for file `test/lints/functional_ref/failing_functional_ref.dart:12`: +``` +int incorrectlyTyped( + // expect_lint: functional_ref +- int ref, ++ Ref ref, +) { + return 0; +``` +--- +Message: `Add ref parameter` +Priority: 90 +Diff for file `test/lints/functional_ref/failing_functional_ref.dart:20`: +``` +@riverpod +// expect_lint: functional_ref +- int noRefButArgs({int a = 42}) { ++ int noRefButArgs(Ref ref, {int a = 42}) { + return 0; +} +``` +--- diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/functional_ref/failing_functional_ref_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/functional_ref/failing_functional_ref_lint.md new file mode 100644 index 000000000..5cd4da160 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/functional_ref/failing_functional_ref_lint.md @@ -0,0 +1,42 @@ +code: functional_ref +severity: Severity.warning +message: Functional providers must receive a ref matching the provider name as their first positional parameter. +test/lints/functional_ref/failing_functional_ref.dart:5:5 + +```dart +@riverpod +// expect_lint: functional_ref +int >>>refless<<<() { + return 0; +} +``` + +======= + +code: functional_ref +severity: Severity.warning +message: Functional providers must receive a ref matching the provider name as their first positional parameter. +test/lints/functional_ref/failing_functional_ref.dart:12:3 + +```dart +int incorrectlyTyped( + // expect_lint: functional_ref + >>>int<<< ref, +) { + return 0; +``` + +======= + +code: functional_ref +severity: Severity.warning +message: Functional providers must receive a ref matching the provider name as their first positional parameter. +test/lints/functional_ref/failing_functional_ref.dart:20:23 + +```dart +@riverpod +// expect_lint: functional_ref +int noRefButArgs({int >>>a<<< = 42}) { + return 0; +} +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/functional_ref/functional_ref_fix.diff b/packages/riverpod_lint_flutter_test/test/lints/goldens/functional_ref/functional_ref_fix.diff new file mode 100644 index 000000000..69204fe4d --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/functional_ref/functional_ref_fix.diff @@ -0,0 +1,12 @@ +Message: `Type as Ref` +Priority: 90 +Diff for file `test/lints/functional_ref/functional_ref.dart:8`: +``` +int nameless( + // expect_lint: functional_ref +- ref, ++ Ref ref, +) { + return 0; +``` +--- diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/functional_ref/functional_ref_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/functional_ref/functional_ref_lint.md new file mode 100644 index 000000000..9e76f32a7 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/functional_ref/functional_ref_lint.md @@ -0,0 +1,12 @@ +code: functional_ref +severity: Severity.warning +message: Functional providers must receive a ref matching the provider name as their first positional parameter. +test/lints/functional_ref/functional_ref.dart:8:3 + +```dart +int nameless( + // expect_lint: functional_ref + >>>ref<<<, +) { + return 0; +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_legacy_import/missing_legacy_import_fix.diff b/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_legacy_import/missing_legacy_import_fix.diff new file mode 100644 index 000000000..823cdb8f7 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_legacy_import/missing_legacy_import_fix.diff @@ -0,0 +1,66 @@ +Message: `Import "package:riverpod/legacy.dart"` +Priority: 100 +Diff for file `test/lints/missing_legacy_import/missing_legacy_import.dart:1`: +``` +- // ignore_for_file: unused_import ++ import 'package:riverpod/legacy.dart'; ++ // ignore_for_file: unused_import + +import 'package:riverpod/riverpod.dart'; +``` +--- +Message: `Import "package:riverpod/legacy.dart"` +Priority: 100 +Diff for file `test/lints/missing_legacy_import/missing_legacy_import.dart:1`: +``` +- // ignore_for_file: unused_import ++ import 'package:riverpod/legacy.dart'; ++ // ignore_for_file: unused_import + +import 'package:riverpod/riverpod.dart'; +``` +--- +Message: `Import "package:flutter_riverpod/legacy.dart"` +Priority: 100 +Diff for file `test/lints/missing_legacy_import/missing_legacy_import.dart:1`: +``` +- // ignore_for_file: unused_import ++ import 'package:flutter_riverpod/legacy.dart'; ++ // ignore_for_file: unused_import + +import 'package:riverpod/riverpod.dart'; +``` +--- +Message: `Import "package:riverpod/legacy.dart"` +Priority: 100 +Diff for file `test/lints/missing_legacy_import/missing_legacy_import.dart:1`: +``` +- // ignore_for_file: unused_import ++ import 'package:riverpod/legacy.dart'; ++ // ignore_for_file: unused_import + +import 'package:riverpod/riverpod.dart'; +``` +--- +Message: `Import "package:riverpod/legacy.dart"` +Priority: 100 +Diff for file `test/lints/missing_legacy_import/missing_legacy_import.dart:1`: +``` +- // ignore_for_file: unused_import ++ import 'package:riverpod/legacy.dart'; ++ // ignore_for_file: unused_import + +import 'package:riverpod/riverpod.dart'; +``` +--- +Message: `Import "package:riverpod/legacy.dart"` +Priority: 100 +Diff for file `test/lints/missing_legacy_import/missing_legacy_import.dart:1`: +``` +- // ignore_for_file: unused_import ++ import 'package:riverpod/legacy.dart'; ++ // ignore_for_file: unused_import + +import 'package:riverpod/riverpod.dart'; +``` +--- diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_legacy_import/missing_legacy_import_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_legacy_import/missing_legacy_import_lint.md new file mode 100644 index 000000000..48ca6549f --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_legacy_import/missing_legacy_import_lint.md @@ -0,0 +1,72 @@ +code: missing_legacy_import +severity: Severity.info +message: StateProvider/StateNotifierProvider/ChangeNotifierProvider/StateNotifier were used without importing `package:flutter_riverpod/legacy.dart`. +test/lints/missing_legacy_import/missing_legacy_import.dart:10:11 + +```dart + +// expect_lint: missing_legacy_import +final p = >>>StateProvider<<<((ref) => 0); + +// expect_lint: missing_legacy_import +``` + +======= + +code: missing_legacy_import +severity: Severity.info +message: StateProvider/StateNotifierProvider/ChangeNotifierProvider/StateNotifier were used without importing `package:flutter_riverpod/legacy.dart`. +test/lints/missing_legacy_import/missing_legacy_import.dart:13:12 + +```dart + +// expect_lint: missing_legacy_import +final p2 = >>>StateProvider<<<.autoDispose((ref) => 0); + +// expect_lint: missing_legacy_import +``` + +======= + +code: missing_legacy_import +severity: Severity.info +message: StateProvider/StateNotifierProvider/ChangeNotifierProvider/StateNotifier were used without importing `package:flutter_riverpod/legacy.dart`. +test/lints/missing_legacy_import/missing_legacy_import.dart:16:12 + +```dart + +// expect_lint: missing_legacy_import +final p3 = >>>ChangeNotifierProvider<<<((ref) => 0); + +// expect_lint: missing_legacy_import +``` + +======= + +code: missing_legacy_import +severity: Severity.info +message: StateProvider/StateNotifierProvider/ChangeNotifierProvider/StateNotifier were used without importing `package:flutter_riverpod/legacy.dart`. +test/lints/missing_legacy_import/missing_legacy_import.dart:19:12 + +```dart + +// expect_lint: missing_legacy_import +final p4 = >>>StateNotifierProvider<<<((ref) => 0); + +// expect_lint: missing_legacy_import +``` + +======= + +code: missing_legacy_import +severity: Severity.info +message: StateProvider/StateNotifierProvider/ChangeNotifierProvider/StateNotifier were used without importing `package:flutter_riverpod/legacy.dart`. +test/lints/missing_legacy_import/missing_legacy_import.dart:22:26 + +```dart + +// expect_lint: missing_legacy_import +class MyNotifier extends >>>StateNotifier<<< { + MyNotifier() : super(0); +} +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_legacy_import/with_riverpod_import_fix.diff b/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_legacy_import/with_riverpod_import_fix.diff new file mode 100644 index 000000000..aa70d8a78 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_legacy_import/with_riverpod_import_fix.diff @@ -0,0 +1,11 @@ +Message: `Import "package:flutter_riverpod/legacy.dart"` +Priority: 100 +Diff for file `test/lints/missing_legacy_import/with_riverpod_import.dart:1`: +``` +- import 'package:riverpod/legacy.dart'; ++ import 'package:flutter_riverpod/legacy.dart'; ++ import 'package:riverpod/legacy.dart'; + +// ignore_for_file: undefined_function, undefined_identifier +``` +--- diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_legacy_import/with_riverpod_import_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_legacy_import/with_riverpod_import_lint.md new file mode 100644 index 000000000..8d473b22f --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_legacy_import/with_riverpod_import_lint.md @@ -0,0 +1,12 @@ +code: missing_legacy_import +severity: Severity.info +message: StateProvider/StateNotifierProvider/ChangeNotifierProvider/StateNotifier were used without importing `package:flutter_riverpod/legacy.dart`. +test/lints/missing_legacy_import/with_riverpod_import.dart:10:12 + +```dart + +// expect_lint: missing_legacy_import +final p3 = >>>ChangeNotifierProvider<<<((ref) => 0); + +final p4 = StateNotifierProvider, int>((ref) { +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_provider_scope/missing_provider_scope_fix.diff b/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_provider_scope/missing_provider_scope_fix.diff new file mode 100644 index 000000000..9958dc430 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_provider_scope/missing_provider_scope_fix.diff @@ -0,0 +1,24 @@ +Message: `Add ProviderScope` +Priority: 80 +Diff for file `test/lints/missing_provider_scope.dart:7`: +``` + // expect_lint: missing_provider_scope + runApp( +- MyApp(), ++ ProviderScope(child: MyApp()), + ); + runApp(ProviderScope(child: MyApp())); +``` +--- +Message: `Add ProviderScope` +Priority: 80 +Diff for file `test/lints/missing_provider_scope.dart:21`: +``` + // expect_lint: missing_provider_scope + runApp( +- MyApp(), ++ ProviderScope(child: MyApp()), + ); + runApp(ProviderScope(child: MyApp())); +``` +--- diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_provider_scope/missing_provider_scope_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_provider_scope/missing_provider_scope_lint.md new file mode 100644 index 000000000..5490f8e59 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/missing_provider_scope/missing_provider_scope_lint.md @@ -0,0 +1,27 @@ +code: missing_provider_scope +severity: Severity.warning +message: Flutter applications should have a ProviderScope widget at the top of the widget tree. +test/lints/missing_provider_scope.dart:6:3 + +```dart +void main() { + // expect_lint: missing_provider_scope + >>>runApp<<<( + MyApp(), + ); +``` + +======= + +code: missing_provider_scope +severity: Severity.warning +message: Flutter applications should have a ProviderScope widget at the top of the widget tree. +test/lints/missing_provider_scope.dart:20:3 + +```dart +void definitelyNotAMain() { + // expect_lint: missing_provider_scope + >>>runApp<<<( + MyApp(), + ); +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/notifier_build/fix/notifier_build.diff b/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_build/notifier_build_fix.diff similarity index 81% rename from packages/riverpod_lint_flutter_test/test/lints/notifier_build/fix/notifier_build.diff rename to packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_build/notifier_build_fix.diff index 6e6005b7a..6f31f48bc 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/notifier_build/fix/notifier_build.diff +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_build/notifier_build_fix.diff @@ -1,6 +1,6 @@ Message: `Add build method` Priority: 80 -Diff for file `test/lints/notifier_build/fix/notifier_build.dart:8`: +Diff for file `test/lints/notifier_build.dart:12`: ``` @riverpod // expect_lint: notifier_build @@ -12,5 +12,7 @@ Diff for file `test/lints/notifier_build/fix/notifier_build.dart:8`: + throw UnimplementedError(); + } + } + +@riverpod ``` --- diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_build/notifier_build_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_build/notifier_build_lint.md new file mode 100644 index 000000000..5972ba3e3 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_build/notifier_build_lint.md @@ -0,0 +1,12 @@ +code: notifier_build +severity: Severity.error +message: Classes annotated by `@riverpod` must have the `build` method +test/lints/notifier_build.dart:12:7 + +```dart +@riverpod +// expect_lint: notifier_build +class >>>ExampleProvider1<<< extends _$ExampleProvider1 {} + +@riverpod +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/notifier_extends.diff b/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_extends/failing_notifier_extends_fix.diff similarity index 72% rename from packages/riverpod_lint_flutter_test/test/lints/notifier_extends/notifier_extends.diff rename to packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_extends/failing_notifier_extends_fix.diff index 985408240..5edb63ff6 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/notifier_extends.diff +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_extends/failing_notifier_extends_fix.diff @@ -1,6 +1,6 @@ Message: `Extend _$NoExtends` Priority: 90 -Diff for file `test/lints/notifier_extends/notifier_extends.dart:12`: +Diff for file `test/lints/notifier_extends/failing_notifier_extends.dart:6`: ``` @riverpod // expect_lint: notifier_extends @@ -12,7 +12,7 @@ Diff for file `test/lints/notifier_extends/notifier_extends.dart:12`: --- Message: `Extend _$WrongExtends` Priority: 90 -Diff for file `test/lints/notifier_extends/notifier_extends.dart:18`: +Diff for file `test/lints/notifier_extends/failing_notifier_extends.dart:12`: ``` @riverpod // expect_lint: notifier_extends diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_extends/failing_notifier_extends_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_extends/failing_notifier_extends_lint.md new file mode 100644 index 000000000..4c0aeb796 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_extends/failing_notifier_extends_lint.md @@ -0,0 +1,27 @@ +code: notifier_extends +severity: Severity.warning +message: Classes annotated by @riverpod must extend _$ClassName +test/lints/notifier_extends/failing_notifier_extends.dart:6:7 + +```dart +@riverpod +// expect_lint: notifier_extends +class >>>NoExtends<<< { + int build() => 0; +} +``` + +======= + +code: notifier_extends +severity: Severity.warning +message: Classes annotated by @riverpod must extend _$ClassName +test/lints/notifier_extends/failing_notifier_extends.dart:12:28 + +```dart +@riverpod +// expect_lint: notifier_extends +class WrongExtends extends >>>AsyncNotifier<<< { + int build() => 0; +} +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_extends/notifier_extends_fix.diff b/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_extends/notifier_extends_fix.diff new file mode 100644 index 000000000..ba14cfc6d --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_extends/notifier_extends_fix.diff @@ -0,0 +1,36 @@ +Message: `Extend _$NoGenerics` +Priority: 90 +Diff for file `test/lints/notifier_extends/notifier_extends.dart:26`: +``` +@riverpod +// expect_lint: notifier_extends +- class NoGenerics extends _$NoGenerics { ++ class NoGenerics extends _$NoGenerics { + int build() => 0; +} +``` +--- +Message: `Extend _$MissingGenerics` +Priority: 90 +Diff for file `test/lints/notifier_extends/notifier_extends.dart:32`: +``` +@riverpod +// expect_lint: notifier_extends +- class MissingGenerics extends _$MissingGenerics { ++ class MissingGenerics extends _$MissingGenerics { + int build() => 0; +} +``` +--- +Message: `Extend _$WrongOrder` +Priority: 90 +Diff for file `test/lints/notifier_extends/notifier_extends.dart:38`: +``` +@riverpod +// expect_lint: notifier_extends +- class WrongOrder extends _$WrongOrder { ++ class WrongOrder extends _$WrongOrder { + int build() => 0; +} +``` +--- diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_extends/notifier_extends_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_extends/notifier_extends_lint.md new file mode 100644 index 000000000..d573cb8c3 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/notifier_extends/notifier_extends_lint.md @@ -0,0 +1,42 @@ +code: notifier_extends +severity: Severity.warning +message: Classes annotated by @riverpod must extend _$ClassName +test/lints/notifier_extends/notifier_extends.dart:26:44 + +```dart +@riverpod +// expect_lint: notifier_extends +class NoGenerics extends >>>_$NoGenerics<<< { + int build() => 0; +} +``` + +======= + +code: notifier_extends +severity: Severity.warning +message: Classes annotated by @riverpod must extend _$ClassName +test/lints/notifier_extends/notifier_extends.dart:32:37 + +```dart +@riverpod +// expect_lint: notifier_extends +class MissingGenerics extends >>>_$MissingGenerics<<< { + int build() => 0; +} +``` + +======= + +code: notifier_extends +severity: Severity.warning +message: Classes annotated by @riverpod must extend _$ClassName +test/lints/notifier_extends/notifier_extends.dart:38:32 + +```dart +@riverpod +// expect_lint: notifier_extends +class WrongOrder extends >>>_$WrongOrder<<< { + int build() => 0; +} +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/only_use_keep_alive_inside_keep_alive/only_use_keep_alive_inside_keep_alive_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/only_use_keep_alive_inside_keep_alive/only_use_keep_alive_inside_keep_alive_lint.md new file mode 100644 index 000000000..173c5cc9a --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/only_use_keep_alive_inside_keep_alive/only_use_keep_alive_inside_keep_alive_lint.md @@ -0,0 +1,29 @@ +code: only_use_keep_alive_inside_keep_alive +severity: Severity.warning +correctionMessage: Either stop marking this provider as `keepAlive` or remove `keepAlive` from the used provider. +message: If a provider is declared as `keepAlive`, it can only use providers that are also declared as `keepAlive. +test/lints/only_use_keep_alive_inside_keep_alive.dart:27:3 + +```dart + + // expect_lint: only_use_keep_alive_inside_keep_alive + >>>ref.watch(autoDisposeProvider)<<<; + // expect_lint: only_use_keep_alive_inside_keep_alive + ref.watch(autoDisposeClassProvider); +``` + +======= + +code: only_use_keep_alive_inside_keep_alive +severity: Severity.warning +correctionMessage: Either stop marking this provider as `keepAlive` or remove `keepAlive` from the used provider. +message: If a provider is declared as `keepAlive`, it can only use providers that are also declared as `keepAlive. +test/lints/only_use_keep_alive_inside_keep_alive.dart:29:3 + +```dart + ref.watch(autoDisposeProvider); + // expect_lint: only_use_keep_alive_inside_keep_alive + >>>ref.watch(autoDisposeClassProvider)<<<; + + return 0; +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/protected_notifier_properties/protected_notifier_properties_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/protected_notifier_properties/protected_notifier_properties_lint.md new file mode 100644 index 000000000..16ecd760f --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/protected_notifier_properties/protected_notifier_properties_lint.md @@ -0,0 +1,297 @@ +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:66:34 + +```dart + + // expect_lint: protected_notifier_properties + ref.read(aProvider.notifier).>>>state<<< = 42; + + // expect_lint: protected_notifier_properties +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:69:34 + +```dart + + // expect_lint: protected_notifier_properties + ref.read(aProvider.notifier).>>>state<<<++; + // expect_lint: protected_notifier_properties + ref.read(a2Provider.notifier).state++; +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:71:35 + +```dart + ref.read(aProvider.notifier).state++; + // expect_lint: protected_notifier_properties + ref.read(a2Provider.notifier).>>>state<<<++; + // expect_lint: protected_notifier_properties + ref.read(a3Provider(42).notifier).state++; +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:73:39 + +```dart + ref.read(a2Provider.notifier).state++; + // expect_lint: protected_notifier_properties + ref.read(a3Provider(42).notifier).>>>state<<<++; + // expect_lint: protected_notifier_properties + ref.read(a4Provider(42).notifier).state++; +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:75:39 + +```dart + ref.read(a3Provider(42).notifier).state++; + // expect_lint: protected_notifier_properties + ref.read(a4Provider(42).notifier).>>>state<<<++; + // expect_lint: protected_notifier_properties + ref.read(a5Provider(42).notifier).state = AsyncData(42); +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:77:39 + +```dart + ref.read(a4Provider(42).notifier).state++; + // expect_lint: protected_notifier_properties + ref.read(a5Provider(42).notifier).>>>state<<< = AsyncData(42); + // expect_lint: protected_notifier_properties + ref.read(a6Provider(42).notifier).state = AsyncData(42); +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:79:39 + +```dart + ref.read(a5Provider(42).notifier).state = AsyncData(42); + // expect_lint: protected_notifier_properties + ref.read(a6Provider(42).notifier).>>>state<<< = AsyncData(42); + // expect_lint: protected_notifier_properties + ref.read(a7Provider(42).notifier).state = AsyncData(42); +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:81:39 + +```dart + ref.read(a6Provider(42).notifier).state = AsyncData(42); + // expect_lint: protected_notifier_properties + ref.read(a7Provider(42).notifier).>>>state<<< = AsyncData(42); + // expect_lint: protected_notifier_properties + ref.read(a8Provider(42).notifier).state = AsyncData(42); +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:83:39 + +```dart + ref.read(a7Provider(42).notifier).state = AsyncData(42); + // expect_lint: protected_notifier_properties + ref.read(a8Provider(42).notifier).>>>state<<< = AsyncData(42); + // expect_lint: protected_notifier_properties + ref.read(a8Provider(42).notifier).state; +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:85:39 + +```dart + ref.read(a8Provider(42).notifier).state = AsyncData(42); + // expect_lint: protected_notifier_properties + ref.read(a8Provider(42).notifier).>>>state<<<; + + // expect_lint: protected_notifier_properties +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:88:39 + +```dart + + // expect_lint: protected_notifier_properties + ref.read(a8Provider(42).notifier).>>>future<<<; + // expect_lint: protected_notifier_properties + ref.read(a8Provider(42).notifier).ref; +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:90:39 + +```dart + ref.read(a8Provider(42).notifier).future; + // expect_lint: protected_notifier_properties + ref.read(a8Provider(42).notifier).>>>ref<<<; + } +} +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:106:34 + +```dart + + // expect_lint: protected_notifier_properties + ref.read(aProvider.notifier).>>>state<<<++; + // expect_lint: protected_notifier_properties + ref.read(a2Provider.notifier).state++; +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:108:35 + +```dart + ref.read(aProvider.notifier).state++; + // expect_lint: protected_notifier_properties + ref.read(a2Provider.notifier).>>>state<<<++; + // expect_lint: protected_notifier_properties + ref.read(a3Provider(42).notifier).state++; +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:110:39 + +```dart + ref.read(a2Provider.notifier).state++; + // expect_lint: protected_notifier_properties + ref.read(a3Provider(42).notifier).>>>state<<<++; + // expect_lint: protected_notifier_properties + ref.read(a4Provider(42).notifier).state++; +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:112:39 + +```dart + ref.read(a3Provider(42).notifier).state++; + // expect_lint: protected_notifier_properties + ref.read(a4Provider(42).notifier).>>>state<<<++; + // expect_lint: protected_notifier_properties + ref.read(a5Provider(42).notifier).state = AsyncData(42); +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:114:39 + +```dart + ref.read(a4Provider(42).notifier).state++; + // expect_lint: protected_notifier_properties + ref.read(a5Provider(42).notifier).>>>state<<< = AsyncData(42); + // expect_lint: protected_notifier_properties + ref.read(a6Provider(42).notifier).state = AsyncData(42); +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:116:39 + +```dart + ref.read(a5Provider(42).notifier).state = AsyncData(42); + // expect_lint: protected_notifier_properties + ref.read(a6Provider(42).notifier).>>>state<<< = AsyncData(42); + // expect_lint: protected_notifier_properties + ref.read(a7Provider(42).notifier).state = AsyncData(42); +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:118:39 + +```dart + ref.read(a6Provider(42).notifier).state = AsyncData(42); + // expect_lint: protected_notifier_properties + ref.read(a7Provider(42).notifier).>>>state<<< = AsyncData(42); + // expect_lint: protected_notifier_properties + ref.read(a8Provider(42).notifier).state = AsyncData(42); +``` + +======= + +code: protected_notifier_properties +severity: Severity.info +message: Notifier.state should not be used outside of its own class. +test/lints/protected_notifier_properties.dart:120:39 + +```dart + ref.read(a7Provider(42).notifier).state = AsyncData(42); + // expect_lint: protected_notifier_properties + ref.read(a8Provider(42).notifier).>>>state<<< = AsyncData(42); + } +} +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/missing_dependencies2_fix.diff b/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/missing_dependencies2_fix.diff new file mode 100644 index 000000000..6ca7213de --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/missing_dependencies2_fix.diff @@ -0,0 +1,192 @@ +Message: `Specify "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:32`: +``` + +// expect_lint: provider_dependencies +- @riverpod ++ @Riverpod(dependencies: [generatedScoped]) +int watchGeneratedScopedButNoDependencies( + Ref ref, +``` +--- +Message: `Update "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:60`: +``` + +// expect_lint: provider_dependencies +- @Riverpod(dependencies: []) ++ @Riverpod(dependencies: [generatedScoped]) +int watchGeneratedScopedButEmptyDependencies( + Ref ref, +``` +--- +Message: `Update "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:92`: +``` + +// expect_lint: provider_dependencies +- @Riverpod(dependencies: [dep]) ++ @Riverpod(dependencies: [dep, generatedScoped]) +int watchGeneratedScopedButMissingDependencies( + Ref ref, +``` +--- +Message: `Remove "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:123`: +``` +} + +- @Riverpod( +- dependencies: +- // The dependency is redundant because it is not a scoped provider +- // expect_lint: provider_dependencies +- [ +- generatedRoot, +- ], +- ) ++ @riverpod +int watchGeneratedRootAndContainsDependency( + Ref ref, +``` +--- +Message: `Update "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:142`: +``` + // generatedRoot is extra + // expect_lint: provider_dependencies +- [ +- dep, +- generatedRoot, ++ [dep]) +int specifiedDependencyButNeverUsed(Ref ref) { + ref.watch(depProvider); +``` +--- +Message: `Remove "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:224`: +``` +class MemberDependencies { + // expect_lint: provider_dependencies +- @Dependencies([dep]) ++ + int build() => 0; +} +``` +--- +Message: `Update "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:232`: +``` +class CanUpdateMultipleDependenciesAtOnce { + // expect_lint: provider_dependencies +- @Dependencies([]) ++ @Dependencies([dep]) + int build(WidgetRef ref) { + ref.watch(depProvider); +``` +--- +Message: `Update "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:229`: +``` + +// expect_lint: provider_dependencies +- @Dependencies([]) ++ @Dependencies([dep]) +class CanUpdateMultipleDependenciesAtOnce { + // expect_lint: provider_dependencies +``` +--- +Message: `Remove "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:252`: +``` +// Handle identifiers with dependencies +// expect_lint: provider_dependencies +- @Dependencies([dep]) ++ +void fn() {} + +``` +--- +Message: `Specify "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:256`: +``` + +// expect_lint: provider_dependencies +- void fn2() { ++ @Dependencies([dep]) ++ void fn2() { + fn(); +} +``` +--- +Message: `Specify "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:264`: +``` + +// expect_lint: provider_dependencies +- @riverpod ++ @Riverpod(dependencies: [dep]) +int foo(Ref ref) { + fn(); +``` +--- +Message: `Specify "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:281`: +``` + +// expect_lint: provider_dependencies +- class WidgetDependencies2 extends StatelessWidget { ++ @Dependencies([dep]) ++ class WidgetDependencies2 extends StatelessWidget { + @override + Widget build(BuildContext context) { +``` +--- +Message: `Update "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:312`: +``` + +// expect_lint: provider_dependencies +- @Dependencies([]) ++ @Dependencies([dep]) +class Stateful2 extends StatefulWidget { + const Stateful2({super.key}); +``` +--- +Message: `Specify "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:328`: +``` + +// expect_lint: provider_dependencies +- class FindStateFromClassList extends StatefulWidget { ++ @Dependencies([dep]) ++ class FindStateFromClassList extends StatefulWidget { + const FindStateFromClassList({super.key}); + +``` +--- +Message: `Specify "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies2.dart:341`: +``` + +// expect_lint: provider_dependencies +- @riverpod ++ @Riverpod(dependencies: [anotherNonEmptyScoped]) +int crossFileDependency(Ref ref) { + ref.watch(anotherNonEmptyScopedProvider); +``` +--- diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/missing_dependencies2_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/missing_dependencies2_lint.md new file mode 100644 index 000000000..8b9fb8e7d --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/missing_dependencies2_lint.md @@ -0,0 +1,360 @@ +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: generatedScoped + test/lints/provider_dependencies/missing_dependencies2.dart:36:20 + + ```dart + Ref ref, + ) { + return ref.watch(>>>generatedScopedProvider<<<); + } + + ``` +message: Missing dependencies: generatedScoped +test/lints/provider_dependencies/missing_dependencies2.dart:32:1 + +```dart + +// expect_lint: provider_dependencies +>>>@riverpod<<< +int watchGeneratedScopedButNoDependencies( + Ref ref, +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: generatedScoped + test/lints/provider_dependencies/missing_dependencies2.dart:64:20 + + ```dart + Ref ref, + ) { + return ref.watch(>>>generatedScopedProvider<<<); + } + + ``` +message: Missing dependencies: generatedScoped +test/lints/provider_dependencies/missing_dependencies2.dart:60:25 + +```dart + +// expect_lint: provider_dependencies +@Riverpod(dependencies: >>>[]<<<) +int watchGeneratedScopedButEmptyDependencies( + Ref ref, +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: generatedScoped + test/lints/provider_dependencies/missing_dependencies2.dart:97:20 + + ```dart + ) { + ref.watch(depProvider); + return ref.watch(>>>generatedScopedProvider<<<); + } + + ``` +message: Missing dependencies: generatedScoped +test/lints/provider_dependencies/missing_dependencies2.dart:92:25 + +```dart + +// expect_lint: provider_dependencies +@Riverpod(dependencies: >>>[dep]<<<) +int watchGeneratedScopedButMissingDependencies( + Ref ref, +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: generatedRoot +test/lints/provider_dependencies/missing_dependencies2.dart:127:7 + +```dart + // The dependency is redundant because it is not a scoped provider + // expect_lint: provider_dependencies + >>>[ + generatedRoot, + ]<<<, +) +int watchGeneratedRootAndContainsDependency( +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: generatedRoot +test/lints/provider_dependencies/missing_dependencies2.dart:142:5 + +```dart + // generatedRoot is extra + // expect_lint: provider_dependencies + >>>[ + dep, + generatedRoot, +]<<<) +int specifiedDependencyButNeverUsed(Ref ref) { + ref.watch(depProvider); +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: dep +test/lints/provider_dependencies/missing_dependencies2.dart:224:17 + +```dart +class MemberDependencies { + // expect_lint: provider_dependencies + @Dependencies(>>>[dep]<<<) + int build() => 0; +} +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies2.dart:234:15 + + ```dart + @Dependencies([]) + int build(WidgetRef ref) { + ref.watch(>>>depProvider<<<); + return 0; + } + ``` +message: Missing dependencies: dep +test/lints/provider_dependencies/missing_dependencies2.dart:232:17 + +```dart +class CanUpdateMultipleDependenciesAtOnce { + // expect_lint: provider_dependencies + @Dependencies(>>>[]<<<) + int build(WidgetRef ref) { + ref.watch(depProvider); +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies2.dart:234:15 + + ```dart + @Dependencies([]) + int build(WidgetRef ref) { + ref.watch(>>>depProvider<<<); + return 0; + } + ``` +message: Missing dependencies: dep +test/lints/provider_dependencies/missing_dependencies2.dart:229:15 + +```dart + +// expect_lint: provider_dependencies +@Dependencies(>>>[]<<<) +class CanUpdateMultipleDependenciesAtOnce { + // expect_lint: provider_dependencies +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: dep +test/lints/provider_dependencies/missing_dependencies2.dart:252:15 + +```dart +// Handle identifiers with dependencies +// expect_lint: provider_dependencies +@Dependencies(>>>[dep]<<<) +void fn() {} + +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies2.dart:257:3 + + ```dart + // expect_lint: provider_dependencies + void fn2() { + >>>fn<<<(); + } + + ``` +message: Missing dependencies: dep +test/lints/provider_dependencies/missing_dependencies2.dart:256:1 + +```dart + +// expect_lint: provider_dependencies +>>>void fn2() { + fn(); +}<<< + +@Dependencies([dep]) +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies2.dart:266:3 + + ```dart + @riverpod + int foo(Ref ref) { + >>>fn<<<(); + return 0; + } + ``` +message: Missing dependencies: dep +test/lints/provider_dependencies/missing_dependencies2.dart:264:1 + +```dart + +// expect_lint: provider_dependencies +>>>@riverpod<<< +int foo(Ref ref) { + fn(); +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies2.dart:284:12 + + ```dart + @override + Widget build(BuildContext context) { + return >>>WidgetDependencies<<<(); + } + } + ``` +message: Missing dependencies: dep +test/lints/provider_dependencies/missing_dependencies2.dart:281:1 + +```dart + +// expect_lint: provider_dependencies +>>>class WidgetDependencies2 extends StatelessWidget { + @override + Widget build(BuildContext context) { + return WidgetDependencies(); + } +}<<< + +@Dependencies([dep]) +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies2.dart:323:12 + + ```dart + @override + Widget build(BuildContext context) { + return >>>WidgetDependencies<<<(); + } + } + ``` +message: Missing dependencies: dep +test/lints/provider_dependencies/missing_dependencies2.dart:312:15 + +```dart + +// expect_lint: provider_dependencies +@Dependencies(>>>[]<<<) +class Stateful2 extends StatefulWidget { + const Stateful2({super.key}); +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies2.dart:337:41 + + ```dart + class _Stateful3State extends State { + @override + Widget build(BuildContext context) => >>>WidgetDependencies<<<(); + } + + ``` +message: Missing dependencies: dep +test/lints/provider_dependencies/missing_dependencies2.dart:328:1 + +```dart + +// expect_lint: provider_dependencies +>>>class FindStateFromClassList extends StatefulWidget { + const FindStateFromClassList({super.key}); + + @override + State createState() => _Stateful3State(); +}<<< + +class _Stateful3State extends State { +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: anotherNonEmptyScoped + test/lints/provider_dependencies/missing_dependencies2.dart:343:13 + + ```dart + @riverpod + int crossFileDependency(Ref ref) { + ref.watch(>>>anotherNonEmptyScopedProvider<<<); + return 0; + } + ``` +message: Missing dependencies: anotherNonEmptyScoped +test/lints/provider_dependencies/missing_dependencies2.dart:341:1 + +```dart + +// expect_lint: provider_dependencies +>>>@riverpod<<< +int crossFileDependency(Ref ref) { + ref.watch(anotherNonEmptyScopedProvider); +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/missing_dependencies_fix.diff b/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/missing_dependencies_fix.diff new file mode 100644 index 000000000..78d2d523c --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/missing_dependencies_fix.diff @@ -0,0 +1,161 @@ +Message: `Remove "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies.dart:20`: +``` + +// expect_lint: provider_dependencies +- @Dependencies([dep]) ++ +void depFn() {} + +``` +--- +Message: `Remove "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies.dart:24`: +``` + +// expect_lint: provider_dependencies +- @Dependencies([depFamily]) ++ +void depFamilyFn() {} + +``` +--- +Message: `Remove "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies.dart:40`: +``` + +// expect_lint: provider_dependencies +- @Dependencies([dep]) ++ +class UnusedDepWidget extends ConsumerWidget { + const UnusedDepWidget({super.key}); +``` +--- +Message: `Specify "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies.dart:77`: +``` + +// expect_lint: provider_dependencies +- @riverpod ++ @Riverpod(dependencies: [dep]) +int plainAnnotation(Ref ref) { + ref.watch(depProvider); +``` +--- +Message: `Specify "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies.dart:84`: +``` + +// expect_lint: provider_dependencies +- @Riverpod(keepAlive: false) ++ @Riverpod(keepAlive: false, dependencies: [dep]) +int customAnnotation(Ref ref) { + ref.watch(depProvider); +``` +--- +Message: `Specify "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies.dart:92`: +``` +// expect_lint: provider_dependencies +@Riverpod( +- keepAlive: false, ++ keepAlive: false, dependencies: [dep], +) +int customAnnotationWithTrailingComma( +``` +--- +Message: `Update "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies.dart:104`: +``` + keepAlive: false, + // expect_lint: provider_dependencies +- dependencies: [], ++ dependencies: [dep], +) +int existingDep(Ref ref) { +``` +--- +Message: `Update "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies.dart:114`: +``` + keepAlive: false, + // expect_lint: provider_dependencies +- dependencies: [], ++ dependencies: [dep, dep2], +) +int multipleDeps(Ref ref) { +``` +--- +Message: `Specify "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies.dart:133`: +``` + +// expect_lint: provider_dependencies +- class AboveScope extends ConsumerWidget { ++ @Dependencies([dep]) ++ class AboveScope extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { +``` +--- +Message: `Specify "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies.dart:146`: +``` + +// expect_lint: provider_dependencies +- class Scope2 extends ConsumerWidget { ++ @Dependencies([dep]) ++ class Scope2 extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { +``` +--- +Message: `Specify "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies.dart:157`: +``` + +// expect_lint: provider_dependencies +- class ConditionalScope extends ConsumerWidget { ++ @Dependencies([dep]) ++ class ConditionalScope extends ConsumerWidget { + ConditionalScope({super.key, required this.condition}); + final bool condition; +``` +--- +Message: `Specify "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies.dart:222`: +``` + +// expect_lint: provider_dependencies +- class SupportsMultipleScopes2 extends ConsumerWidget { ++ @Dependencies([depFamily, dep]) ++ class SupportsMultipleScopes2 extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { +``` +--- +Message: `Specify "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/missing_dependencies.dart:253`: +``` + +// expect_lint: provider_dependencies +- class IncompleteFamilyOverride extends ConsumerWidget { ++ @Dependencies([depFamily]) ++ class IncompleteFamilyOverride extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { +``` +--- diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/missing_dependencies_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/missing_dependencies_lint.md new file mode 100644 index 000000000..2ca7be720 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/missing_dependencies_lint.md @@ -0,0 +1,378 @@ +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: dep +test/lints/provider_dependencies/missing_dependencies.dart:20:15 + +```dart + +// expect_lint: provider_dependencies +@Dependencies(>>>[dep]<<<) +void depFn() {} + +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: depFamily +test/lints/provider_dependencies/missing_dependencies.dart:24:15 + +```dart + +// expect_lint: provider_dependencies +@Dependencies(>>>[depFamily]<<<) +void depFamilyFn() {} + +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: dep +test/lints/provider_dependencies/missing_dependencies.dart:40:15 + +```dart + +// expect_lint: provider_dependencies +@Dependencies(>>>[dep]<<<) +class UnusedDepWidget extends ConsumerWidget { + const UnusedDepWidget({super.key}); +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies.dart:79:13 + + ```dart + @riverpod + int plainAnnotation(Ref ref) { + ref.watch(>>>depProvider<<<); + return 0; + } + ``` +message: Missing dependencies: dep +test/lints/provider_dependencies/missing_dependencies.dart:77:1 + +```dart + +// expect_lint: provider_dependencies +>>>@riverpod<<< +int plainAnnotation(Ref ref) { + ref.watch(depProvider); +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies.dart:86:13 + + ```dart + @Riverpod(keepAlive: false) + int customAnnotation(Ref ref) { + ref.watch(>>>depProvider<<<); + return 0; + } + ``` +message: Missing dependencies: dep +test/lints/provider_dependencies/missing_dependencies.dart:84:1 + +```dart + +// expect_lint: provider_dependencies +>>>@Riverpod(keepAlive: false)<<< +int customAnnotation(Ref ref) { + ref.watch(depProvider); +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies.dart:97:13 + + ```dart + Ref ref, + ) { + ref.watch(>>>depProvider<<<); + return 0; + } + ``` +message: Missing dependencies: dep +test/lints/provider_dependencies/missing_dependencies.dart:91:1 + +```dart + +// expect_lint: provider_dependencies +>>>@Riverpod( + keepAlive: false, +)<<< +int customAnnotationWithTrailingComma( + Ref ref, +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies.dart:107:13 + + ```dart + ) + int existingDep(Ref ref) { + ref.watch(>>>depProvider<<<); + return 0; + } + ``` +message: Missing dependencies: dep +test/lints/provider_dependencies/missing_dependencies.dart:104:17 + +```dart + keepAlive: false, + // expect_lint: provider_dependencies + dependencies: >>>[]<<<, +) +int existingDep(Ref ref) { +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies.dart:117:13 + + ```dart + ) + int multipleDeps(Ref ref) { + ref.watch(>>>depProvider<<<); + ref.watch(dep2Provider); + return 0; + ``` + + message: dep2 + test/lints/provider_dependencies/missing_dependencies.dart:118:13 + + ```dart + int multipleDeps(Ref ref) { + ref.watch(depProvider); + ref.watch(>>>dep2Provider<<<); + return 0; + } + ``` +message: Missing dependencies: dep, dep2 +test/lints/provider_dependencies/missing_dependencies.dart:114:17 + +```dart + keepAlive: false, + // expect_lint: provider_dependencies + dependencies: >>>[]<<<, +) +int multipleDeps(Ref ref) { +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies.dart:136:12 + + ```dart + @override + Widget build(BuildContext context, WidgetRef ref) { + return >>>DepWidget<<<( + child: ProviderScope( + overrides: [depProvider.overrideWithValue(42)], + ``` +message: Missing dependencies: dep +test/lints/provider_dependencies/missing_dependencies.dart:133:1 + +```dart + +// expect_lint: provider_dependencies +>>>class AboveScope extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + return DepWidget( + child: ProviderScope( + overrides: [depProvider.overrideWithValue(42)], + child: Container(), + ), + ); + } +}<<< + +// expect_lint: provider_dependencies +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies.dart:151:32 + + ```dart + return ProviderScope( + overrides: [depProvider.overrideWithValue(42)], + child: Text('${ref.watch(>>>depProvider<<<)}'), + ); + } + ``` +message: Missing dependencies: dep +test/lints/provider_dependencies/missing_dependencies.dart:146:1 + +```dart + +// expect_lint: provider_dependencies +>>>class Scope2 extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + return ProviderScope( + overrides: [depProvider.overrideWithValue(42)], + child: Text('${ref.watch(depProvider)}'), + ); + } +}<<< + +// expect_lint: provider_dependencies +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: dep + test/lints/provider_dependencies/missing_dependencies.dart:167:14 + + ```dart + if (condition) depProvider.overrideWithValue(42), + ], + child: >>>DepWidget<<<(), + ); + } + ``` +message: Missing dependencies: dep +test/lints/provider_dependencies/missing_dependencies.dart:157:1 + +```dart + +// expect_lint: provider_dependencies +>>>class ConditionalScope extends ConsumerWidget { + ConditionalScope({super.key, required this.condition}); + final bool condition; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return ProviderScope( + overrides: [ + if (condition) depProvider.overrideWithValue(42), + ], + child: DepWidget(), + ); + } +}<<< + +class Scope4 extends ConsumerWidget { +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: depFamily + test/lints/provider_dependencies/missing_dependencies.dart:227:14 + + ```dart + ProviderScope( + overrides: [depProvider.overrideWith((ref) => 0)], + child: >>>DepFamily<<<(), + ); + + ``` + + message: dep + test/lints/provider_dependencies/missing_dependencies.dart:232:14 + + ```dart + return ProviderScope( + overrides: [depFamilyProvider.overrideWith((ref, arg) => 0)], + child: >>>DepWidget<<<(), + ); + } + ``` +message: Missing dependencies: depFamily, dep +test/lints/provider_dependencies/missing_dependencies.dart:222:1 + +```dart + +// expect_lint: provider_dependencies +>>>class SupportsMultipleScopes2 extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + ProviderScope( + overrides: [depProvider.overrideWith((ref) => 0)], + child: DepFamily(), + ); + + return ProviderScope( + overrides: [depFamilyProvider.overrideWith((ref, arg) => 0)], + child: DepWidget(), + ); + } +}<<< + +class SupportsNestedScopes extends ConsumerWidget { +``` + +======= + +code: provider_dependencies +severity: Severity.warning +contextMessages: + message: depFamily + test/lints/provider_dependencies/missing_dependencies.dart:258:14 + + ```dart + return ProviderScope( + overrides: [depFamilyProvider(42).overrideWith((ref) => 0)], + child: >>>DepFamily<<<(), + ); + } + ``` +message: Missing dependencies: depFamily +test/lints/provider_dependencies/missing_dependencies.dart:253:1 + +```dart + +// expect_lint: provider_dependencies +>>>class IncompleteFamilyOverride extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + return ProviderScope( + overrides: [depFamilyProvider(42).overrideWith((ref) => 0)], + child: DepFamily(), + ); + } +}<<< + +@Dependencies([dep]) +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/unused_dependency_fix.diff b/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/unused_dependency_fix.diff new file mode 100644 index 000000000..8537bff58 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/unused_dependency_fix.diff @@ -0,0 +1,141 @@ +Message: `Update "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/unused_dependency.dart:22`: +``` + keepAlive: false, + // expect_lint: provider_dependencies +- dependencies: [ +- dep, +- dep2, +- ], ++ dependencies: [dep2], +) +int extraDep(Ref ref) { +``` +--- +Message: `Remove "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/unused_dependency.dart:34`: +``` +@Riverpod( + keepAlive: false, +- // expect_lint: provider_dependencies +- dependencies: [ +- dep, +- ], +- ) ++ ) +int noDep(Ref ref) { + return 0; +``` +--- +Message: `Remove "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/unused_dependency.dart:45`: +``` +@Riverpod( + // expect_lint: provider_dependencies +- dependencies: [ +- dep, +- ], +- keepAlive: false, ++ keepAlive: false, +) +int dependenciesFirstThenKeepAlive(Ref ref) { +``` +--- +Message: `Remove "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/unused_dependency.dart:54`: +``` +} + +- @Riverpod( +- // expect_lint: provider_dependencies +- dependencies: [ +- dep, +- ], +- ) ++ @riverpod +int noDepNoParam(Ref ref) { + return 0; +``` +--- +Message: `Remove "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/unused_dependency.dart:65`: +``` + +// expect_lint: provider_dependencies +- @Riverpod(keepAlive: false, dependencies: [dep]) ++ @Riverpod(keepAlive: false) +int noDepWithoutComma(Ref ref) { + return 0; +``` +--- +Message: `Remove "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/unused_dependency.dart:72`: +``` +@Riverpod( + keepAlive: false, +- // expect_lint: provider_dependencies +- dependencies: [ +- root, +- ], +- ) ++ ) +int rootDep(Ref ref) => 0; + +``` +--- +Message: `Remove "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/unused_dependency.dart:80`: +``` + +// expect_lint: provider_dependencies +- @Dependencies([dep]) ++ +class StateNotFound extends ConsumerStatefulWidget { + @override +``` +--- +Message: `Remove "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/unused_dependency.dart:118`: +``` + +// expect_lint: provider_dependencies +- @Dependencies([dep]) ++ +void fn() {} + +``` +--- +Message: `Update "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/unused_dependency.dart:131`: +``` + +// expect_lint: provider_dependencies +- @Dependencies([dep2, dep]) ++ @Dependencies([dep2]) +void secondUnused() { + dep2Provider; +``` +--- +Message: `Update "dependencies"` +Priority: 100 +Diff for file `test/lints/provider_dependencies/unused_dependency.dart:137`: +``` + +// expect_lint: provider_dependencies +- @Dependencies([ +- dep2, +- dep, ++ @Dependencies([dep2]) +void secondUnusedWithTrailingComma() { + dep2Provider; +``` +--- diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/unused_dependency_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/unused_dependency_lint.md new file mode 100644 index 000000000..69ef74d66 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_dependencies/unused_dependency_lint.md @@ -0,0 +1,161 @@ +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: dep +test/lints/provider_dependencies/unused_dependency.dart:22:17 + +```dart + keepAlive: false, + // expect_lint: provider_dependencies + dependencies: >>>[ + dep, + dep2, + ]<<<, +) +int extraDep(Ref ref) { +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: dep +test/lints/provider_dependencies/unused_dependency.dart:35:17 + +```dart + keepAlive: false, + // expect_lint: provider_dependencies + dependencies: >>>[ + dep, + ]<<<, +) +int noDep(Ref ref) { +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: dep +test/lints/provider_dependencies/unused_dependency.dart:45:17 + +```dart +@Riverpod( + // expect_lint: provider_dependencies + dependencies: >>>[ + dep, + ]<<<, + keepAlive: false, +) +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: dep +test/lints/provider_dependencies/unused_dependency.dart:56:17 + +```dart +@Riverpod( + // expect_lint: provider_dependencies + dependencies: >>>[ + dep, + ]<<<, +) +int noDepNoParam(Ref ref) { +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: dep +test/lints/provider_dependencies/unused_dependency.dart:65:43 + +```dart + +// expect_lint: provider_dependencies +@Riverpod(keepAlive: false, dependencies: >>>[dep]<<<) +int noDepWithoutComma(Ref ref) { + return 0; +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: root +test/lints/provider_dependencies/unused_dependency.dart:73:17 + +```dart + keepAlive: false, + // expect_lint: provider_dependencies + dependencies: >>>[ + root, + ]<<<, +) +int rootDep(Ref ref) => 0; +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: dep +test/lints/provider_dependencies/unused_dependency.dart:80:15 + +```dart + +// expect_lint: provider_dependencies +@Dependencies(>>>[dep]<<<) +class StateNotFound extends ConsumerStatefulWidget { + @override +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: dep +test/lints/provider_dependencies/unused_dependency.dart:118:15 + +```dart + +// expect_lint: provider_dependencies +@Dependencies(>>>[dep]<<<) +void fn() {} + +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: dep +test/lints/provider_dependencies/unused_dependency.dart:131:15 + +```dart + +// expect_lint: provider_dependencies +@Dependencies(>>>[dep2, dep]<<<) +void secondUnused() { + dep2Provider; +``` + +======= + +code: provider_dependencies +severity: Severity.warning +message: Unused dependencies: dep +test/lints/provider_dependencies/unused_dependency.dart:137:15 + +```dart + +// expect_lint: provider_dependencies +@Dependencies(>>>[ + dep2, + dep, +]<<<) +void secondUnusedWithTrailingComma() { + dep2Provider; +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_parameters/provider_parameters_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_parameters/provider_parameters_lint.md new file mode 100644 index 000000000..297148c35 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/provider_parameters/provider_parameters_lint.md @@ -0,0 +1,432 @@ +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:20:19 + +```dart + ref.read(legacy(list)); + // expect_lint: provider_parameters + ref.read(legacy(>>>[42]<<<)); + ref.listen(legacy(42), (prev, next) {}); + // expect_lint: provider_parameters +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:23:21 + +```dart + ref.listen(legacy(42), (prev, next) {}); + // expect_lint: provider_parameters + ref.listen(legacy(>>>[42]<<<), (prev, next) {}); + + ref.watch(legacy(42)); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:27:20 + +```dart + ref.watch(legacy(42)); + // expect_lint: provider_parameters + ref.watch(legacy(>>>[42]<<<)); + // expect_lint: provider_parameters + ref.watch(legacy({'string': 42})); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:29:20 + +```dart + ref.watch(legacy([42])); + // expect_lint: provider_parameters + ref.watch(legacy(>>>{'string': 42}<<<)); + // expect_lint: provider_parameters + ref.watch(legacy({42})); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:31:20 + +```dart + ref.watch(legacy({'string': 42})); + // expect_lint: provider_parameters + ref.watch(legacy(>>>{42}<<<)); + ref.watch(legacy(const [42])); + ref.watch(legacy(const {'string': 42})); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:37:20 + +```dart + ref.watch(legacy(null)); + // expect_lint: provider_parameters + ref.watch(legacy(>>>Object()<<<)); + ref.watch(legacy(const Object())); + ref.watch(legacy(FreezedExample())); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:44:22 + +```dart + + // expect_lint: provider_parameters + ref.watch(provider(>>>() {}<<<)); + ref.watch(provider(fn)); + +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:50:20 + +```dart + ref.watch(legacy(const ClassThatOverridesEqual())); + // expect_lint: provider_parameters + ref.watch(legacy(>>>Factory.bar()<<<)); + ref.watch(legacy(const Factory.bar())); + ref.watch(legacy(Factory.foo())); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:57:38 + +```dart + ref.watch(generatorProvider(value: 42)); + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: >>>[42]<<<)); + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: {'string': 42})); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:59:38 + +```dart + ref.watch(generatorProvider(value: [42])); + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: >>>{'string': 42}<<<)); + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: {42})); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:61:38 + +```dart + ref.watch(generatorProvider(value: {'string': 42})); + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: >>>{42}<<<)); + ref.watch(generatorProvider(value: const [42])); + ref.watch(generatorProvider(value: const {'string': 42})); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:67:38 + +```dart + ref.watch(generatorProvider(value: null)); + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: >>>Object()<<<)); + ref.watch(generatorProvider(value: const Object())); + +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:74:38 + +```dart + + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: >>>Bar()<<<)); + ref.watch(generatorProvider(value: const Bar())); + +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:78:38 + +```dart + + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: >>>Factory.bar()<<<)); + ref.watch(generatorProvider(value: const Factory.bar())); + ref.watch(generatorProvider(value: Factory.foo())); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:115:21 + +```dart + ref.read(legacy(42)); + // expect_lint: provider_parameters + ref.read(legacy(>>>[42]<<<)); + ref.listen(legacy(42), (prev, next) {}); + // expect_lint: provider_parameters +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:118:23 + +```dart + ref.listen(legacy(42), (prev, next) {}); + // expect_lint: provider_parameters + ref.listen(legacy(>>>[42]<<<), (prev, next) {}); + ref.listenManual(legacy(42), (prev, next) {}); + // expect_lint: provider_parameters +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:121:29 + +```dart + ref.listenManual(legacy(42), (prev, next) {}); + // expect_lint: provider_parameters + ref.listenManual(legacy(>>>[42]<<<), (prev, next) {}); + + ref.watch(legacy(42)); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:126:22 + +```dart + ref.read(legacy(list)); + // expect_lint: provider_parameters + ref.watch(legacy(>>>[42]<<<)); + // expect_lint: provider_parameters + ref.watch(legacy({'string': 42})); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:128:22 + +```dart + ref.watch(legacy([42])); + // expect_lint: provider_parameters + ref.watch(legacy(>>>{'string': 42}<<<)); + // expect_lint: provider_parameters + ref.watch(legacy({42})); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:130:22 + +```dart + ref.watch(legacy({'string': 42})); + // expect_lint: provider_parameters + ref.watch(legacy(>>>{42}<<<)); + ref.watch(legacy(const [42])); + ref.watch(legacy(const {'string': 42})); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:136:22 + +```dart + ref.watch(legacy(null)); + // expect_lint: provider_parameters + ref.watch(legacy(>>>Object()<<<)); + ref.watch(legacy(const Object())); + +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:144:22 + +```dart + ref.watch(legacy(const IndirectEqual())); + // expect_lint: provider_parameters + ref.watch(legacy(>>>Bar()<<<)); + ref.watch(legacy(const Bar())); + // expect_lint: provider_parameters +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:147:22 + +```dart + ref.watch(legacy(const Bar())); + // expect_lint: provider_parameters + ref.watch(legacy(>>>Factory.bar()<<<)); + ref.watch(legacy(const Factory.bar())); + ref.watch(legacy(Factory.foo())); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:154:40 + +```dart + ref.watch(generatorProvider(value: 42)); + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: >>>[42]<<<)); + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: {'string': 42})); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:156:40 + +```dart + ref.watch(generatorProvider(value: [42])); + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: >>>{'string': 42}<<<)); + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: {42})); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:158:40 + +```dart + ref.watch(generatorProvider(value: {'string': 42})); + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: >>>{42}<<<)); + ref.watch(generatorProvider(value: const [42])); + ref.watch(generatorProvider(value: const {'string': 42})); +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:164:40 + +```dart + ref.watch(generatorProvider(value: null)); + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: >>>Object()<<<)); + ref.watch(generatorProvider(value: const Object())); + +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:173:40 + +```dart + + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: >>>Bar()<<<)); + ref.watch(generatorProvider(value: const Bar())); + +``` + +======= + +code: provider_parameters +severity: Severity.warning +message: Providers parameters should have a consistent ==. Meaning either the values should be cached, or the parameters should override == +test/lints/provider_parameters.dart:177:40 + +```dart + + // expect_lint: provider_parameters + ref.watch(generatorProvider(value: >>>Factory.bar()<<<)); + ref.watch(generatorProvider(value: const Factory.bar())); + ref.watch(generatorProvider(value: Factory.foo())); +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/riverpod_syntax_error/riverpod_syntax_error_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/riverpod_syntax_error/riverpod_syntax_error_lint.md new file mode 100644 index 000000000..5e3a6c118 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/riverpod_syntax_error/riverpod_syntax_error_lint.md @@ -0,0 +1,12 @@ +code: riverpod_syntax_error +severity: Severity.error +message: Classes annotated with @riverpod cannot be abstract. +test/lints/riverpod_syntax_error.dart:8:16 + +```dart +@riverpod +// expect_lint: riverpod_syntax_error +abstract class >>>ExampleProvider1<<< extends _$ExampleProvider1 { + int build() => 0; +} +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/scoped_providers_should_specify_dependencies/scoped_providers_should_specify_dependencies_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/scoped_providers_should_specify_dependencies/scoped_providers_should_specify_dependencies_lint.md new file mode 100644 index 000000000..f00c9aca2 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/scoped_providers_should_specify_dependencies/scoped_providers_should_specify_dependencies_lint.md @@ -0,0 +1,102 @@ +code: scoped_providers_should_specify_dependencies +severity: Severity.warning +message: Providers which are overridden in a non-root ProviderContainer/ProviderScope should specify dependencies. +test/lints/scoped_providers_should_specify_dependencies.dart:43:7 + +```dart + .overrideWith(() => throw UnimplementedError()), + // expect_lint: scoped_providers_should_specify_dependencies + >>>rootProvider.overrideWith((ref) => 0)<<<, + ], + ); +``` + +======= + +code: scoped_providers_should_specify_dependencies +severity: Severity.warning +message: Providers which are overridden in a non-root ProviderContainer/ProviderScope should specify dependencies. +test/lints/scoped_providers_should_specify_dependencies.dart:67:9 + +```dart + // This is not a Flutter's runApp, so the ProviderScope is considered scoped + // expect_lint: scoped_providers_should_specify_dependencies + >>>rootProvider.overrideWith((ref) => 0)<<<, + ], + child: Container(), +``` + +======= + +code: scoped_providers_should_specify_dependencies +severity: Severity.warning +message: Providers which are overridden in a non-root ProviderContainer/ProviderScope should specify dependencies. +test/lints/scoped_providers_should_specify_dependencies.dart:92:7 + +```dart + .overrideWith(() => throw UnimplementedError()), + // expect_lint: scoped_providers_should_specify_dependencies + >>>rootProvider.overrideWith((ref) => 0)<<<, + ], + ); +``` + +======= + +code: scoped_providers_should_specify_dependencies +severity: Severity.warning +message: Providers which are overridden in a non-root ProviderContainer/ProviderScope should specify dependencies. +test/lints/scoped_providers_should_specify_dependencies.dart:140:13 + +```dart + .overrideWith(() => throw UnimplementedError()), + // expect_lint: scoped_providers_should_specify_dependencies + >>>rootProvider.overrideWith((ref) => 0)<<<, + ], + child: Container(), +``` + +======= + +code: scoped_providers_should_specify_dependencies +severity: Severity.warning +message: Providers which are overridden in a non-root ProviderContainer/ProviderScope should specify dependencies. +test/lints/scoped_providers_should_specify_dependencies.dart:156:7 + +```dart + .overrideWith(() => throw UnimplementedError()), + // expect_lint: scoped_providers_should_specify_dependencies + >>>rootProvider.overrideWith((ref) => 0)<<<, + ], + child: Container(), +``` + +======= + +code: scoped_providers_should_specify_dependencies +severity: Severity.warning +message: Providers which are overridden in a non-root ProviderContainer/ProviderScope should specify dependencies. +test/lints/scoped_providers_should_specify_dependencies.dart:172:11 + +```dart + .overrideWith(() => throw UnimplementedError()), + // expect_lint: scoped_providers_should_specify_dependencies + >>>rootProvider.overrideWith((ref) => 0)<<<, + ], + child: Container(), +``` + +======= + +code: scoped_providers_should_specify_dependencies +severity: Severity.warning +message: Providers which are overridden in a non-root ProviderContainer/ProviderScope should specify dependencies. +test/lints/scoped_providers_should_specify_dependencies.dart:191:9 + +```dart + .overrideWith(() => throw UnimplementedError()), + // expect_lint: scoped_providers_should_specify_dependencies + >>>rootProvider.overrideWith((ref) => 0)<<<, + ], + child: Container(), +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/unknown_scoped_usage/unknown_scoped_usage_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/unknown_scoped_usage/unknown_scoped_usage_lint.md new file mode 100644 index 000000000..16b6c5a5d --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/unknown_scoped_usage/unknown_scoped_usage_lint.md @@ -0,0 +1,72 @@ +code: unknown_scoped_usage +severity: Severity.warning +message: A provider was used, but could not find the associated `ref`. +test/lints/unknown_scoped_usage.dart:16:3 + +```dart +void fn(WidgetRef widgetRef, Ref ref) { + // expect_lint: unknown_scoped_usage + >>>scopedProvider<<<; + rootProvider; + +``` + +======= + +code: unknown_scoped_usage +severity: Severity.warning +message: A provider was used, but could not find the associated `ref`. +test/lints/unknown_scoped_usage.dart:25:28 + +```dart + // Unknown ref usage inside a ref expression + // expect_lint: unknown_scoped_usage + widgetRef.watch(identity(>>>scopedProvider<<<)); + // expect_lint: unknown_scoped_usage + ref.watch(identity(scopedProvider)); +``` + +======= + +code: unknown_scoped_usage +severity: Severity.warning +message: A provider was used, but could not find the associated `ref`. +test/lints/unknown_scoped_usage.dart:27:22 + +```dart + widgetRef.watch(identity(scopedProvider)); + // expect_lint: unknown_scoped_usage + ref.watch(identity(>>>scopedProvider<<<)); + // expect_lint: unknown_scoped_usage + ref.watch(identityMap[scopedProvider]); +``` + +======= + +code: unknown_scoped_usage +severity: Severity.warning +message: A provider was used, but could not find the associated `ref`. +test/lints/unknown_scoped_usage.dart:29:25 + +```dart + ref.watch(identity(scopedProvider)); + // expect_lint: unknown_scoped_usage + ref.watch(identityMap[>>>scopedProvider<<<]); + + // Overrides are OK +``` + +======= + +code: unknown_scoped_usage +severity: Severity.warning +message: A provider was used, but could not find the associated `ref`. +test/lints/unknown_scoped_usage.dart:36:16 + +```dart + // If passed as widget constructor parameter, it's OK + // expect_lint: unknown_scoped_usage + RandomObject(>>>scopedProvider<<<); + MyWidget(scopedProvider); +} +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/goldens/unsupported_provider_value/unsupported_provider_value_lint.md b/packages/riverpod_lint_flutter_test/test/lints/goldens/unsupported_provider_value/unsupported_provider_value_lint.md new file mode 100644 index 000000000..52e37eb86 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/goldens/unsupported_provider_value/unsupported_provider_value_lint.md @@ -0,0 +1,189 @@ +code: unsupported_provider_value +severity: Severity.info +correctionMessage: If using StateNotifier even though riverpod_generator does not support it, you can wrap the type in "Raw" to silence the warning. For example by returning Raw. +message: The riverpod_generator package does not support StateNotifier values. +test/lints/unsupported_provider_value.dart:13:17 + +```dart +@riverpod +// expect_lint: unsupported_provider_value +MyStateNotifier >>>stateNotifier<<<(Ref ref) => MyStateNotifier(); + +@riverpod +``` + +======= + +code: unsupported_provider_value +severity: Severity.info +correctionMessage: If using StateNotifier even though riverpod_generator does not support it, you can wrap the type in "Raw" to silence the warning. For example by returning Raw. +message: The riverpod_generator package does not support StateNotifier values. +test/lints/unsupported_provider_value.dart:17:25 + +```dart +@riverpod +// expect_lint: unsupported_provider_value +Future >>>asyncStateNotifier<<<(Ref ref) async { + return MyStateNotifier(); +} +``` + +======= + +code: unsupported_provider_value +severity: Severity.info +correctionMessage: If using StateNotifier even though riverpod_generator does not support it, you can wrap the type in "Raw" to silence the warning. For example by returning Raw. +message: The riverpod_generator package does not support StateNotifier values. +test/lints/unsupported_provider_value.dart:23:7 + +```dart +@riverpod +// expect_lint: unsupported_provider_value +class >>>StateNotifierClass<<< extends _$StateNotifierClass { + MyStateNotifier build() => MyStateNotifier(); +} +``` + +======= + +code: unsupported_provider_value +severity: Severity.info +correctionMessage: If using StateNotifier even though riverpod_generator does not support it, you can wrap the type in "Raw" to silence the warning. For example by returning Raw. +message: The riverpod_generator package does not support StateNotifier values. +test/lints/unsupported_provider_value.dart:29:25 + +```dart +@riverpod +// expect_lint: unsupported_provider_value +Future >>>stateNotifierAsync<<<(Ref ref) async => MyStateNotifier(); + +// Regression tests for https://github.com/rrousselGit/riverpod/issues/2302 +``` + +======= + +code: unsupported_provider_value +severity: Severity.info +correctionMessage: If using StateNotifier even though riverpod_generator does not support it, you can wrap the type in "Raw" to silence the warning. For example by returning Raw. +message: The riverpod_generator package does not support StateNotifier values. +test/lints/unsupported_provider_value.dart:51:7 + +```dart +@riverpod +// expect_lint: unsupported_provider_value +class >>>StateNotifierClassAsync<<< extends _$StateNotifierClassAsync { + Future build() async => MyStateNotifier(); +} +``` + +======= + +code: unsupported_provider_value +severity: Severity.info +correctionMessage: If using ChangeNotifier even though riverpod_generator does not support it, you can wrap the type in "Raw" to silence the warning. For example by returning Raw. +message: The riverpod_generator package does not support ChangeNotifier values. +test/lints/unsupported_provider_value.dart:61:18 + +```dart +@riverpod +// expect_lint: unsupported_provider_value +MyChangeNotifier >>>changeNotifier<<<(Ref ref) => MyChangeNotifier(); + +@riverpod +``` + +======= + +code: unsupported_provider_value +severity: Severity.info +correctionMessage: If using ChangeNotifier even though riverpod_generator does not support it, you can wrap the type in "Raw" to silence the warning. For example by returning Raw. +message: The riverpod_generator package does not support ChangeNotifier values. +test/lints/unsupported_provider_value.dart:65:7 + +```dart +@riverpod +// expect_lint: unsupported_provider_value +class >>>ChangeNotifierClass<<< extends _$ChangeNotifierClass { + MyChangeNotifier build() => MyChangeNotifier(); +} +``` + +======= + +code: unsupported_provider_value +severity: Severity.info +correctionMessage: If using Notifier even though riverpod_generator does not support it, you can wrap the type in "Raw" to silence the warning. For example by returning Raw. +message: The riverpod_generator package does not support Notifier values. +test/lints/unsupported_provider_value.dart:73:12 + +```dart +@riverpod +// expect_lint: unsupported_provider_value +MyNotifier >>>notifier<<<(Ref ref) => MyNotifier(); + +@riverpod +``` + +======= + +code: unsupported_provider_value +severity: Severity.info +correctionMessage: If using Notifier even though riverpod_generator does not support it, you can wrap the type in "Raw" to silence the warning. For example by returning Raw. +message: The riverpod_generator package does not support Notifier values. +test/lints/unsupported_provider_value.dart:77:23 + +```dart +@riverpod +// expect_lint: unsupported_provider_value +MyAutoDisposeNotifier >>>autoDisposeNotifier<<<(Ref ref) { + return MyAutoDisposeNotifier(); +} +``` + +======= + +code: unsupported_provider_value +severity: Severity.info +correctionMessage: If using Notifier even though riverpod_generator does not support it, you can wrap the type in "Raw" to silence the warning. For example by returning Raw. +message: The riverpod_generator package does not support Notifier values. +test/lints/unsupported_provider_value.dart:83:7 + +```dart +@riverpod +// expect_lint: unsupported_provider_value +class >>>NotifierClass<<< extends _$NotifierClass { + MyNotifier build() => MyNotifier(); +} +``` + +======= + +code: unsupported_provider_value +severity: Severity.info +correctionMessage: If using AsyncNotifier even though riverpod_generator does not support it, you can wrap the type in "Raw" to silence the warning. For example by returning Raw. +message: The riverpod_generator package does not support AsyncNotifier values. +test/lints/unsupported_provider_value.dart:99:17 + +```dart +@riverpod +// expect_lint: unsupported_provider_value +MyAsyncNotifier >>>asyncNotifier<<<(Ref ref) => MyAsyncNotifier(); + +@riverpod +``` + +======= + +code: unsupported_provider_value +severity: Severity.info +correctionMessage: If using AsyncNotifier even though riverpod_generator does not support it, you can wrap the type in "Raw" to silence the warning. For example by returning Raw. +message: The riverpod_generator package does not support AsyncNotifier values. +test/lints/unsupported_provider_value.dart:103:7 + +```dart +@riverpod +// expect_lint: unsupported_provider_value +class >>>AsyncNotifierClass<<< extends _$AsyncNotifierClass { + MyAsyncNotifier build() => MyAsyncNotifier(); +} +``` diff --git a/packages/riverpod_lint_flutter_test/test/lints/missing_legacy_import/missing_legacy_import.dart b/packages/riverpod_lint_flutter_test/test/lints/missing_legacy_import/missing_legacy_import.dart new file mode 100644 index 000000000..dd209c051 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/missing_legacy_import/missing_legacy_import.dart @@ -0,0 +1,24 @@ +// ignore_for_file: unused_import + +import 'package:riverpod/riverpod.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +// ignore_for_file: undefined_function, undefined_identifier, extends_non_class, extra_positional_arguments + +// expect_lint: missing_legacy_import +final p = StateProvider((ref) => 0); + +// expect_lint: missing_legacy_import +final p2 = StateProvider.autoDispose((ref) => 0); + +// expect_lint: missing_legacy_import +final p3 = ChangeNotifierProvider((ref) => 0); + +// expect_lint: missing_legacy_import +final p4 = StateNotifierProvider((ref) => 0); + +// expect_lint: missing_legacy_import +class MyNotifier extends StateNotifier { + MyNotifier() : super(0); +} diff --git a/packages/riverpod_lint_flutter_test/test/lints/missing_legacy_import/with_flutter_riverpod.dart b/packages/riverpod_lint_flutter_test/test/lints/missing_legacy_import/with_flutter_riverpod.dart new file mode 100644 index 000000000..79cf23ed8 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/missing_legacy_import/with_flutter_riverpod.dart @@ -0,0 +1,18 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_riverpod/legacy.dart'; + +final p = StateProvider((ref) => 0); + +final p2 = StateProvider.autoDispose((ref) => 0); + +final p3 = ChangeNotifierProvider>((ref) { + throw UnimplementedError(); +}); + +final p4 = StateNotifierProvider, int>((ref) { + throw UnimplementedError(); +}); + +class MyNotifier extends StateNotifier { + MyNotifier() : super(0); +} diff --git a/packages/riverpod_lint_flutter_test/test/lints/missing_legacy_import/with_hooks_riverpod.dart b/packages/riverpod_lint_flutter_test/test/lints/missing_legacy_import/with_hooks_riverpod.dart new file mode 100644 index 000000000..98ddff515 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/missing_legacy_import/with_hooks_riverpod.dart @@ -0,0 +1,18 @@ +import 'package:flutter/foundation.dart'; +import 'package:hooks_riverpod/legacy.dart'; + +final p = StateProvider((ref) => 0); + +final p2 = StateProvider.autoDispose((ref) => 0); + +final p3 = ChangeNotifierProvider>((ref) { + throw UnimplementedError(); +}); + +final p4 = StateNotifierProvider, int>((ref) { + throw UnimplementedError(); +}); + +class MyNotifier extends StateNotifier { + MyNotifier() : super(0); +} diff --git a/packages/riverpod_lint_flutter_test/test/lints/missing_legacy_import/with_riverpod_import.dart b/packages/riverpod_lint_flutter_test/test/lints/missing_legacy_import/with_riverpod_import.dart new file mode 100644 index 000000000..dc0b9e879 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/missing_legacy_import/with_riverpod_import.dart @@ -0,0 +1,18 @@ +import 'package:riverpod/legacy.dart'; + +// ignore_for_file: undefined_function, undefined_identifier + +final p = StateProvider((ref) => 0); + +final p2 = StateProvider.autoDispose((ref) => 0); + +// expect_lint: missing_legacy_import +final p3 = ChangeNotifierProvider((ref) => 0); + +final p4 = StateNotifierProvider, int>((ref) { + throw UnimplementedError(); +}); + +class MyNotifier extends StateNotifier { + MyNotifier() : super(0); +} diff --git a/packages/riverpod_lint_flutter_test/test/lints/notifier_build/notifier_build.dart b/packages/riverpod_lint_flutter_test/test/lints/notifier_build.dart similarity index 77% rename from packages/riverpod_lint_flutter_test/test/lints/notifier_build/notifier_build.dart rename to packages/riverpod_lint_flutter_test/test/lints/notifier_build.dart index 52ac1fe58..e8fee3138 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/notifier_build/notifier_build.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/notifier_build.dart @@ -1,10 +1,11 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; /// Fake Provider typedef _$ExampleProvider1 = Object; /// Fake Provider -typedef _$ExampleProvider = AutoDisposeNotifier; +typedef _$ExampleProvider = Notifier; @riverpod // expect_lint: notifier_build diff --git a/packages/riverpod_lint_flutter_test/test/lints/notifier_build/fix/notifier_build_test.dart b/packages/riverpod_lint_flutter_test/test/lints/notifier_build/fix/notifier_build_test.dart deleted file mode 100644 index 72bc6ab1c..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/notifier_build/fix/notifier_build_test.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:riverpod_lint/src/lints/notifier_build.dart'; - -import '../../../golden.dart'; - -void main() { - testGolden( - 'Verify that @riverpod classes has the build method', - 'lints/notifier_build/fix/notifier_build.diff', - sourcePath: 'test/lints/notifier_build/fix/notifier_build.dart', - (result, helper) async { - const lint = NotifierBuild(); - final fix = lint.getFixes().single; - - final errors = await lint.testRun(result); - - final changes = await Future.wait([ - for (final error in errors) fix.testRun(result, error, errors), - ]); - - return changes.flattened; - }, - ); -} diff --git a/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/failing_notifier_extends.dart b/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/failing_notifier_extends.dart new file mode 100644 index 000000000..a07051376 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/failing_notifier_extends.dart @@ -0,0 +1,14 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +@riverpod +// expect_lint: notifier_extends +class NoExtends { + int build() => 0; +} + +@riverpod +// expect_lint: notifier_extends +class WrongExtends extends AsyncNotifier { + int build() => 0; +} diff --git a/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/notifier_extends.dart b/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/notifier_extends.dart index 75d795152..0c134f500 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/notifier_extends.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/notifier_extends.dart @@ -2,26 +2,39 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'notifier_extends.g.dart'; +// ignore_for_file: wrong_number_of_type_arguments + @riverpod class MyNotifier extends _$MyNotifier { int build() => 0; } +// Regression test for https://github.com/rrousselGit/riverpod/issues/2165 +@riverpod +class _PrivateClass extends _$PrivateClass { + @override + String build() => 'Hello World!'; +} + +@riverpod +class Generics extends _$Generics { + int build() => 0; +} + @riverpod // expect_lint: notifier_extends -class NoExtends { +class NoGenerics extends _$NoGenerics { int build() => 0; } @riverpod // expect_lint: notifier_extends -class WrongExtends extends AsyncNotifier { +class MissingGenerics extends _$MissingGenerics { int build() => 0; } -// Regression test for https://github.com/rrousselGit/riverpod/issues/2165 @riverpod -class _PrivateClass extends _$PrivateClass { - @override - String build() => 'Hello World!'; +// expect_lint: notifier_extends +class WrongOrder extends _$WrongOrder { + int build() => 0; } diff --git a/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/notifier_extends.g.dart b/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/notifier_extends.g.dart index 3ae5e0c67..cdc6ae99a 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/notifier_extends.g.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/notifier_extends.g.dart @@ -6,64 +6,774 @@ part of 'notifier_extends.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(MyNotifier) +const myNotifierProvider = MyNotifierProvider._(); + +final class MyNotifierProvider extends $NotifierProvider { + const MyNotifierProvider._( + {super.runNotifierBuildOverride, MyNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$myNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + MyNotifier create() => _createCb?.call() ?? MyNotifier(); + + @$internal + @override + MyNotifierProvider $copyWithCreate( + MyNotifier Function() create, + ) { + return MyNotifierProvider._(create: create); + } + + @$internal + @override + MyNotifierProvider $copyWithBuild( + int Function( + Ref, + MyNotifier, + ) build, + ) { + return MyNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$myNotifierHash() => r'58f5439a3b1036ba7804f63a5a6ebe0114125039'; -/// See also [MyNotifier]. -@ProviderFor(MyNotifier) -final myNotifierProvider = - AutoDisposeNotifierProvider.internal( - MyNotifier.new, - name: r'myNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$myNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$MyNotifier = AutoDisposeNotifier; -String _$noExtendsHash() => r'3f1276999a9a6d3676c628c25ed853cbefb21ce9'; - -/// See also [NoExtends]. -@ProviderFor(NoExtends) -final noExtendsProvider = AutoDisposeNotifierProvider.internal( - NoExtends.new, - name: r'noExtendsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$noExtendsHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$NoExtends = AutoDisposeNotifier; -String _$wrongExtendsHash() => r'6479055793af10a34e225373a67f7eaac4d7c0de'; - -/// See also [WrongExtends]. -@ProviderFor(WrongExtends) -final wrongExtendsProvider = - AutoDisposeNotifierProvider.internal( - WrongExtends.new, - name: r'wrongExtendsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$wrongExtendsHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$WrongExtends = AutoDisposeNotifier; -String _$privateClassHash() => r'ba68a29a609566bb8bc0792391f842762356e124'; +abstract class _$MyNotifier extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} -/// See also [_PrivateClass]. @ProviderFor(_PrivateClass) -final _privateClassProvider = - AutoDisposeNotifierProvider<_PrivateClass, String>.internal( - _PrivateClass.new, - name: r'_privateClassProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$privateClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$PrivateClass = AutoDisposeNotifier; +const _privateClassProvider = _PrivateClassProvider._(); + +final class _PrivateClassProvider + extends $NotifierProvider<_PrivateClass, String> { + const _PrivateClassProvider._( + {super.runNotifierBuildOverride, _PrivateClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'_privateClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final _PrivateClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$privateClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + _PrivateClass create() => _createCb?.call() ?? _PrivateClass(); + + @$internal + @override + _PrivateClassProvider $copyWithCreate( + _PrivateClass Function() create, + ) { + return _PrivateClassProvider._(create: create); + } + + @$internal + @override + _PrivateClassProvider $copyWithBuild( + String Function( + Ref, + _PrivateClass, + ) build, + ) { + return _PrivateClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement<_PrivateClass, String> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$privateClassHash() => r'ba68a29a609566bb8bc0792391f842762356e124'; + +abstract class _$PrivateClass extends $Notifier { + String build(); + @$internal + @override + String runBuild() => build(); +} + +@ProviderFor(Generics) +const genericsProvider = GenericsFamily._(); + +final class GenericsProvider + extends $NotifierProvider, int> { + const GenericsProvider._( + {required GenericsFamily super.from, + super.runNotifierBuildOverride, + Generics Function()? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'genericsProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Generics Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$genericsHash(); + + GenericsProvider _copyWithCreate( + Generics Function() create, + ) { + return GenericsProvider._( + from: from! as GenericsFamily, create: create); + } + + GenericsProvider _copyWithBuild( + int Function( + Ref, + Generics, + ) build, + ) { + return GenericsProvider._( + from: from! as GenericsFamily, runNotifierBuildOverride: build); + } + + @override + String toString() { + return r'genericsProvider' + '<${A}, ${B}>' + '()'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Generics create() => _createCb?.call() ?? Generics(); + + @$internal + @override + GenericsProvider $copyWithCreate( + Generics Function() create, + ) { + return GenericsProvider._( + from: from! as GenericsFamily, create: create); + } + + @$internal + @override + GenericsProvider $copyWithBuild( + int Function( + Ref, + Generics, + ) build, + ) { + return GenericsProvider._( + from: from! as GenericsFamily, runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement, int> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is GenericsProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } + + @override + int get hashCode { + return Object.hash(runtimeType, argument); + } +} + +String _$genericsHash() => r'0a1bf00e0610ccb1fb5615460e1bc4afb2555f69'; + +final class GenericsFamily extends Family { + const GenericsFamily._() + : super( + retry: null, + name: r'genericsProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + GenericsProvider call() => + GenericsProvider._(from: this); + + @override + String debugGetCreateSourceHash() => _$genericsHash(); + + @override + String toString() => r'genericsProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Generics Function() create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericsProvider; + + return provider._copyWithCreate(create).$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function(Ref ref, Generics notifier) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GenericsProvider; + + return provider._copyWithBuild(build).$createElement(pointer); + }, + ); + } +} + +abstract class _$Generics extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + +@ProviderFor(NoGenerics) +const noGenericsProvider = NoGenericsFamily._(); + +final class NoGenericsProvider + extends $NotifierProvider, int> { + const NoGenericsProvider._( + {required NoGenericsFamily super.from, + super.runNotifierBuildOverride, + NoGenerics Function()? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'noGenericsProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final NoGenerics Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$noGenericsHash(); + + NoGenericsProvider _copyWithCreate( + NoGenerics Function() create, + ) { + return NoGenericsProvider._( + from: from! as NoGenericsFamily, create: create); + } + + NoGenericsProvider _copyWithBuild( + int Function( + Ref, + NoGenerics, + ) build, + ) { + return NoGenericsProvider._( + from: from! as NoGenericsFamily, runNotifierBuildOverride: build); + } + + @override + String toString() { + return r'noGenericsProvider' + '<${A}, ${B}>' + '()'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + NoGenerics create() => _createCb?.call() ?? NoGenerics(); + + @$internal + @override + NoGenericsProvider $copyWithCreate( + NoGenerics Function() create, + ) { + return NoGenericsProvider._( + from: from! as NoGenericsFamily, create: create); + } + + @$internal + @override + NoGenericsProvider $copyWithBuild( + int Function( + Ref, + NoGenerics, + ) build, + ) { + return NoGenericsProvider._( + from: from! as NoGenericsFamily, runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement, int> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is NoGenericsProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } + + @override + int get hashCode { + return Object.hash(runtimeType, argument); + } +} + +String _$noGenericsHash() => r'30d5d20092f43cb17ede1f619773757df7cecb30'; + +final class NoGenericsFamily extends Family { + const NoGenericsFamily._() + : super( + retry: null, + name: r'noGenericsProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + NoGenericsProvider call() => + NoGenericsProvider._(from: this); + + @override + String debugGetCreateSourceHash() => _$noGenericsHash(); + + @override + String toString() => r'noGenericsProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + NoGenerics Function() create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as NoGenericsProvider; + + return provider._copyWithCreate(create).$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function(Ref ref, NoGenerics notifier) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as NoGenericsProvider; + + return provider._copyWithBuild(build).$createElement(pointer); + }, + ); + } +} + +abstract class _$NoGenerics extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + +@ProviderFor(MissingGenerics) +const missingGenericsProvider = MissingGenericsFamily._(); + +final class MissingGenericsProvider + extends $NotifierProvider, int> { + const MissingGenericsProvider._( + {required MissingGenericsFamily super.from, + super.runNotifierBuildOverride, + MissingGenerics Function()? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'missingGenericsProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MissingGenerics Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$missingGenericsHash(); + + MissingGenericsProvider _copyWithCreate( + MissingGenerics Function() create, + ) { + return MissingGenericsProvider._( + from: from! as MissingGenericsFamily, create: create); + } + + MissingGenericsProvider _copyWithBuild( + int Function( + Ref, + MissingGenerics, + ) build, + ) { + return MissingGenericsProvider._( + from: from! as MissingGenericsFamily, + runNotifierBuildOverride: build); + } + + @override + String toString() { + return r'missingGenericsProvider' + '<${A}, ${B}>' + '()'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + MissingGenerics create() => + _createCb?.call() ?? MissingGenerics(); + + @$internal + @override + MissingGenericsProvider $copyWithCreate( + MissingGenerics Function() create, + ) { + return MissingGenericsProvider._( + from: from! as MissingGenericsFamily, create: create); + } + + @$internal + @override + MissingGenericsProvider $copyWithBuild( + int Function( + Ref, + MissingGenerics, + ) build, + ) { + return MissingGenericsProvider._( + from: from! as MissingGenericsFamily, runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement, int> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is MissingGenericsProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } + + @override + int get hashCode { + return Object.hash(runtimeType, argument); + } +} + +String _$missingGenericsHash() => r'b611c76d5fb87fdde78b5fc017912e0569762c23'; + +final class MissingGenericsFamily extends Family { + const MissingGenericsFamily._() + : super( + retry: null, + name: r'missingGenericsProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + MissingGenericsProvider call() => + MissingGenericsProvider._(from: this); + + @override + String debugGetCreateSourceHash() => _$missingGenericsHash(); + + @override + String toString() => r'missingGenericsProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + MissingGenerics Function() create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as MissingGenericsProvider; + + return provider._copyWithCreate(create).$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function(Ref ref, MissingGenerics notifier) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as MissingGenericsProvider; + + return provider._copyWithBuild(build).$createElement(pointer); + }, + ); + } +} + +abstract class _$MissingGenerics extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + +@ProviderFor(WrongOrder) +const wrongOrderProvider = WrongOrderFamily._(); + +final class WrongOrderProvider + extends $NotifierProvider, int> { + const WrongOrderProvider._( + {required WrongOrderFamily super.from, + super.runNotifierBuildOverride, + WrongOrder Function()? create}) + : _createCb = create, + super( + argument: null, + retry: null, + name: r'wrongOrderProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final WrongOrder Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$wrongOrderHash(); + + WrongOrderProvider _copyWithCreate( + WrongOrder Function() create, + ) { + return WrongOrderProvider._( + from: from! as WrongOrderFamily, create: create); + } + + WrongOrderProvider _copyWithBuild( + int Function( + Ref, + WrongOrder, + ) build, + ) { + return WrongOrderProvider._( + from: from! as WrongOrderFamily, runNotifierBuildOverride: build); + } + + @override + String toString() { + return r'wrongOrderProvider' + '<${A}, ${B}>' + '()'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + WrongOrder create() => _createCb?.call() ?? WrongOrder(); + + @$internal + @override + WrongOrderProvider $copyWithCreate( + WrongOrder Function() create, + ) { + return WrongOrderProvider._( + from: from! as WrongOrderFamily, create: create); + } + + @$internal + @override + WrongOrderProvider $copyWithBuild( + int Function( + Ref, + WrongOrder, + ) build, + ) { + return WrongOrderProvider._( + from: from! as WrongOrderFamily, runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement, int> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); + + @override + bool operator ==(Object other) { + return other is WrongOrderProvider && + other.runtimeType == runtimeType && + other.argument == argument; + } + + @override + int get hashCode { + return Object.hash(runtimeType, argument); + } +} + +String _$wrongOrderHash() => r'7757670a2f67406ebc96c87edf088deb9cb248a1'; + +final class WrongOrderFamily extends Family { + const WrongOrderFamily._() + : super( + retry: null, + name: r'wrongOrderProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + WrongOrderProvider call() => + WrongOrderProvider._(from: this); + + @override + String debugGetCreateSourceHash() => _$wrongOrderHash(); + + @override + String toString() => r'wrongOrderProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + WrongOrder Function() create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as WrongOrderProvider; + + return provider._copyWithCreate(create).$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function(Ref ref, WrongOrder notifier) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as WrongOrderProvider; + + return provider._copyWithBuild(build).$createElement(pointer); + }, + ); + } +} + +abstract class _$WrongOrder extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/notifier_extends_test.dart b/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/notifier_extends_test.dart deleted file mode 100644 index 75c2507e2..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/notifier_extends/notifier_extends_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:custom_lint_builder/custom_lint_builder.dart'; -import 'package:collection/collection.dart'; -import 'package:riverpod_lint/src/lints/notifier_extends.dart'; -import 'package:test/test.dart'; - -import '../../golden.dart'; - -void main() { - testGolden( - 'Verify that @riverpod classes extend the generated typedef', - 'lints/notifier_extends/notifier_extends.diff', - sourcePath: 'test/lints/notifier_extends/notifier_extends.dart', - (result, helper) async { - final lint = NotifierExtends(); - final fix = lint.getFixes().single as DartFix; - - final errors = await lint.testRun(result); - expect(errors, hasLength(2)); - - final changes = await Future.wait([ - for (final error in errors) fix.testRun(result, error, errors), - ]); - - return changes.flattened; - }, - ); -} diff --git a/packages/riverpod_lint_flutter_test/test/lints/only_use_keep_alive_inside_keep_alive.dart b/packages/riverpod_lint_flutter_test/test/lints/only_use_keep_alive_inside_keep_alive.dart new file mode 100644 index 000000000..c01d1bcac --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/only_use_keep_alive_inside_keep_alive.dart @@ -0,0 +1,32 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'only_use_keep_alive_inside_keep_alive.g.dart'; + +@Riverpod(keepAlive: true) +int keepAlive(Ref ref) => 42; + +@Riverpod(keepAlive: true) +class KeepAliveClass extends _$KeepAliveClass { + int build() => 0; +} + +@riverpod +int autoDispose(Ref ref) => 42; + +@riverpod +class AutoDisposeClass extends _$AutoDisposeClass { + int build() => 0; +} + +@Riverpod(keepAlive: true) +int fn(Ref ref) { + ref.watch(keepAliveProvider); + ref.watch(keepAliveClassProvider); + + // expect_lint: only_use_keep_alive_inside_keep_alive + ref.watch(autoDisposeProvider); + // expect_lint: only_use_keep_alive_inside_keep_alive + ref.watch(autoDisposeClassProvider); + + return 0; +} diff --git a/packages/riverpod_lint_flutter_test/test/lints/only_use_keep_alive_inside_keep_alive.g.dart b/packages/riverpod_lint_flutter_test/test/lints/only_use_keep_alive_inside_keep_alive.g.dart new file mode 100644 index 000000000..dea54858d --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/only_use_keep_alive_inside_keep_alive.g.dart @@ -0,0 +1,324 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'only_use_keep_alive_inside_keep_alive.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +@ProviderFor(keepAlive) +const keepAliveProvider = KeepAliveProvider._(); + +final class KeepAliveProvider extends $FunctionalProvider + with $Provider { + const KeepAliveProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'keepAliveProvider', + isAutoDispose: false, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$keepAliveHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + KeepAliveProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return KeepAliveProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? keepAlive; + return _$cb(ref); + } +} + +String _$keepAliveHash() => r'095b2cb2261b9d79721aa6552b8aaf0d8a7bb7ee'; + +@ProviderFor(KeepAliveClass) +const keepAliveClassProvider = KeepAliveClassProvider._(); + +final class KeepAliveClassProvider + extends $NotifierProvider { + const KeepAliveClassProvider._( + {super.runNotifierBuildOverride, KeepAliveClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'keepAliveClassProvider', + isAutoDispose: false, + dependencies: null, + allTransitiveDependencies: null, + ); + + final KeepAliveClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$keepAliveClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + KeepAliveClass create() => _createCb?.call() ?? KeepAliveClass(); + + @$internal + @override + KeepAliveClassProvider $copyWithCreate( + KeepAliveClass Function() create, + ) { + return KeepAliveClassProvider._(create: create); + } + + @$internal + @override + KeepAliveClassProvider $copyWithBuild( + int Function( + Ref, + KeepAliveClass, + ) build, + ) { + return KeepAliveClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$keepAliveClassHash() => r'e2fffa4d14837dfef71f6a2cc230b826b82541ea'; + +abstract class _$KeepAliveClass extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + +@ProviderFor(autoDispose) +const autoDisposeProvider = AutoDisposeProvider._(); + +final class AutoDisposeProvider extends $FunctionalProvider + with $Provider { + const AutoDisposeProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'autoDisposeProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$autoDisposeHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + AutoDisposeProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return AutoDisposeProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? autoDispose; + return _$cb(ref); + } +} + +String _$autoDisposeHash() => r'1ace7b4b2957ecf77b683b868e91a2614fc77d03'; + +@ProviderFor(AutoDisposeClass) +const autoDisposeClassProvider = AutoDisposeClassProvider._(); + +final class AutoDisposeClassProvider + extends $NotifierProvider { + const AutoDisposeClassProvider._( + {super.runNotifierBuildOverride, AutoDisposeClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'autoDisposeClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final AutoDisposeClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$autoDisposeClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + AutoDisposeClass create() => _createCb?.call() ?? AutoDisposeClass(); + + @$internal + @override + AutoDisposeClassProvider $copyWithCreate( + AutoDisposeClass Function() create, + ) { + return AutoDisposeClassProvider._(create: create); + } + + @$internal + @override + AutoDisposeClassProvider $copyWithBuild( + int Function( + Ref, + AutoDisposeClass, + ) build, + ) { + return AutoDisposeClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$autoDisposeClassHash() => r'5127ab94f7ab4ccf90deb3fca90d7a3c3c4c83f5'; + +abstract class _$AutoDisposeClass extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + +@ProviderFor(fn) +const fnProvider = FnProvider._(); + +final class FnProvider extends $FunctionalProvider + with $Provider { + const FnProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'fnProvider', + isAutoDispose: false, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$fnHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + FnProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return FnProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? fn; + return _$cb(ref); + } +} + +String _$fnHash() => r'e96b0302f7492f5aecedd46f6edeeea456839d01'; + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/protected_notifier_properties.dart b/packages/riverpod_lint_flutter_test/test/lints/protected_notifier_properties.dart index 5c06bafd5..cc76321f9 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/protected_notifier_properties.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/protected_notifier_properties.dart @@ -1,3 +1,4 @@ +// ignore_for_file: only_use_keep_alive_inside_keep_alive import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'protected_notifier_properties.g.dart'; diff --git a/packages/riverpod_lint_flutter_test/test/lints/protected_notifier_properties.g.dart b/packages/riverpod_lint_flutter_test/test/lints/protected_notifier_properties.g.dart index 612dec073..ca5340382 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/protected_notifier_properties.g.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/protected_notifier_properties.g.dart @@ -6,915 +6,1187 @@ part of 'protected_notifier_properties.dart'; // RiverpodGenerator // ************************************************************************** -String _$aHash() => r'9bf449b010f4dd5800e78f9f5b8a431b1a79c8b7'; - -/// See also [A]. @ProviderFor(A) -final aProvider = AutoDisposeNotifierProvider.internal( - A.new, - name: r'aProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$aHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$A = AutoDisposeNotifier; -String _$a2Hash() => r'898d46cbcec03233c7b8b0754810a6903226aa2e'; +const aProvider = AProvider._(); + +final class AProvider extends $NotifierProvider { + const AProvider._({super.runNotifierBuildOverride, A Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'aProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [A2]. -@ProviderFor(A2) -final a2Provider = NotifierProvider.internal( - A2.new, - name: r'a2Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$a2Hash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$A2 = Notifier; -String _$a3Hash() => r'2e21e9af8b67b5412611e0d23b862ead56deb8e1'; + final A Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$aHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); + @$internal + @override + A create() => _createCb?.call() ?? A(); - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); + @$internal + @override + AProvider $copyWithCreate( + A Function() create, + ) { + return AProvider._(create: create); } - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + @$internal + @override + AProvider $copyWithBuild( + int Function( + Ref, + A, + ) build, + ) { + return AProvider._(runNotifierBuildOverride: build); } + + @$internal + @override + $NotifierProviderElement $createElement($ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); } -abstract class _$A3 extends BuildlessAutoDisposeNotifier { - late final int param; +String _$aHash() => r'9bf449b010f4dd5800e78f9f5b8a431b1a79c8b7'; - int build( - int param, - ); +abstract class _$A extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); } -/// See also [A3]. -@ProviderFor(A3) -const a3Provider = A3Family(); +@ProviderFor(A2) +const a2Provider = A2Provider._(); + +final class A2Provider extends $NotifierProvider { + const A2Provider._({super.runNotifierBuildOverride, A2 Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'a2Provider', + isAutoDispose: false, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [A3]. -class A3Family extends Family { - /// See also [A3]. - const A3Family(); + final A2 Function()? _createCb; - /// See also [A3]. - A3Provider call( - int param, - ) { - return A3Provider( - param, + @override + String debugGetCreateSourceHash() => _$a2Hash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } + @$internal @override - A3Provider getProviderOverride( - covariant A3Provider provider, + A2 create() => _createCb?.call() ?? A2(); + + @$internal + @override + A2Provider $copyWithCreate( + A2 Function() create, ) { - return call( - provider.param, - ); + return A2Provider._(create: create); } - static const Iterable? _dependencies = null; - + @$internal @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; + A2Provider $copyWithBuild( + int Function( + Ref, + A2, + ) build, + ) { + return A2Provider._(runNotifierBuildOverride: build); + } + @$internal @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + $NotifierProviderElement $createElement($ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$a2Hash() => r'898d46cbcec03233c7b8b0754810a6903226aa2e'; +abstract class _$A2 extends $Notifier { + int build(); + @$internal @override - String? get name => r'a3Provider'; + int runBuild() => build(); } -/// See also [A3]. -class A3Provider extends AutoDisposeNotifierProviderImpl { - /// See also [A3]. - A3Provider( - int param, - ) : this._internal( - () => A3()..param = param, - from: a3Provider, +@ProviderFor(A3) +const a3Provider = A3Family._(); + +final class A3Provider extends $NotifierProvider { + const A3Provider._( + {required A3Family super.from, + required int super.argument, + super.runNotifierBuildOverride, + A3 Function()? create}) + : _createCb = create, + super( + retry: null, name: r'a3Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$a3Hash, - dependencies: A3Family._dependencies, - allTransitiveDependencies: A3Family._allTransitiveDependencies, - param: param, + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - A3Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.param, - }) : super.internal(); + final A3 Function()? _createCb; - final int param; + @override + String debugGetCreateSourceHash() => _$a3Hash(); @override - int runNotifierBuild( - covariant A3 notifier, - ) { - return notifier.build( - param, - ); + String toString() { + return r'a3Provider' + '' + '($argument)'; } - @override - Override overrideWith(A3 Function() create) { - return ProviderOverride( + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( origin: this, - override: A3Provider._internal( - () => create()..param = param, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - param: param, - ), + providerOverride: $ValueProvider(value), ); } + @$internal @override - AutoDisposeNotifierProviderElement createElement() { - return _A3ProviderElement(this); - } + A3 create() => _createCb?.call() ?? A3(); + @$internal @override - bool operator ==(Object other) { - return other is A3Provider && other.param == param; + A3Provider $copyWithCreate( + A3 Function() create, + ) { + return A3Provider._( + argument: argument as int, from: from! as A3Family, create: create); } + @$internal @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, param.hashCode); - - return _SystemHash.finish(hash); + A3Provider $copyWithBuild( + int Function( + Ref, + A3, + ) build, + ) { + return A3Provider._( + argument: argument as int, + from: from! as A3Family, + runNotifierBuildOverride: build); } -} -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin A3Ref on AutoDisposeNotifierProviderRef { - /// The parameter `param` of this provider. - int get param; -} + @$internal + @override + $NotifierProviderElement $createElement($ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); -class _A3ProviderElement extends AutoDisposeNotifierProviderElement - with A3Ref { - _A3ProviderElement(super.provider); + @override + bool operator ==(Object other) { + return other is A3Provider && other.argument == argument; + } @override - int get param => (origin as A3Provider).param; + int get hashCode { + return argument.hashCode; + } } -String _$a4Hash() => r'cdd9ad09099881cafe06d7b3095a8b06dbe7d876'; +String _$a3Hash() => r'2e21e9af8b67b5412611e0d23b862ead56deb8e1'; -abstract class _$A4 extends BuildlessNotifier { - late final int param; +final class A3Family extends Family { + const A3Family._() + : super( + retry: null, + name: r'a3Provider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); - int build( + A3Provider call( int param, - ); -} + ) => + A3Provider._(argument: param, from: this); -/// See also [A4]. -@ProviderFor(A4) -const a4Provider = A4Family(); + @override + String debugGetCreateSourceHash() => _$a3Hash(); -/// See also [A4]. -class A4Family extends Family { - /// See also [A4]. - const A4Family(); + @override + String toString() => r'a3Provider'; - /// See also [A4]. - A4Provider call( - int param, + /// {@macro riverpod.override_with} + Override overrideWith( + A3 Function( + int args, + ) create, ) { - return A4Provider( - param, - ); - } + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as A3Provider; - @override - A4Provider getProviderOverride( - covariant A4Provider provider, - ) { - return call( - provider.param, + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, ); } - static const Iterable? _dependencies = null; + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function(Ref ref, A3 notifier, int argument) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as A3Provider; - @override - Iterable? get dependencies => _dependencies; + final argument = provider.argument as int; - static const Iterable? _allTransitiveDependencies = null; + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; +abstract class _$A3 extends $Notifier { + late final _$args = ref.$arg as int; + int get param => _$args; + int build( + int param, + ); + @$internal @override - String? get name => r'a4Provider'; + int runBuild() => build( + _$args, + ); } -/// See also [A4]. -class A4Provider extends NotifierProviderImpl { - /// See also [A4]. - A4Provider( - int param, - ) : this._internal( - () => A4()..param = param, - from: a4Provider, +@ProviderFor(A4) +const a4Provider = A4Family._(); + +final class A4Provider extends $NotifierProvider { + const A4Provider._( + {required A4Family super.from, + required int super.argument, + super.runNotifierBuildOverride, + A4 Function()? create}) + : _createCb = create, + super( + retry: null, name: r'a4Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$a4Hash, - dependencies: A4Family._dependencies, - allTransitiveDependencies: A4Family._allTransitiveDependencies, - param: param, + isAutoDispose: false, + dependencies: null, + allTransitiveDependencies: null, ); - A4Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.param, - }) : super.internal(); + final A4 Function()? _createCb; - final int param; + @override + String debugGetCreateSourceHash() => _$a4Hash(); @override - int runNotifierBuild( - covariant A4 notifier, - ) { - return notifier.build( - param, - ); + String toString() { + return r'a4Provider' + '' + '($argument)'; } - @override - Override overrideWith(A4 Function() create) { - return ProviderOverride( + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( origin: this, - override: A4Provider._internal( - () => create()..param = param, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - param: param, - ), + providerOverride: $ValueProvider(value), ); } + @$internal @override - NotifierProviderElement createElement() { - return _A4ProviderElement(this); - } + A4 create() => _createCb?.call() ?? A4(); + @$internal @override - bool operator ==(Object other) { - return other is A4Provider && other.param == param; + A4Provider $copyWithCreate( + A4 Function() create, + ) { + return A4Provider._( + argument: argument as int, from: from! as A4Family, create: create); } + @$internal @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, param.hashCode); - - return _SystemHash.finish(hash); + A4Provider $copyWithBuild( + int Function( + Ref, + A4, + ) build, + ) { + return A4Provider._( + argument: argument as int, + from: from! as A4Family, + runNotifierBuildOverride: build); } -} -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin A4Ref on NotifierProviderRef { - /// The parameter `param` of this provider. - int get param; -} + @$internal + @override + $NotifierProviderElement $createElement($ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); -class _A4ProviderElement extends NotifierProviderElement with A4Ref { - _A4ProviderElement(super.provider); + @override + bool operator ==(Object other) { + return other is A4Provider && other.argument == argument; + } @override - int get param => (origin as A4Provider).param; + int get hashCode { + return argument.hashCode; + } } -String _$a5Hash() => r'c83634c22b6a9149aa8787e45c3b7cd6c88b5958'; +String _$a4Hash() => r'cdd9ad09099881cafe06d7b3095a8b06dbe7d876'; -abstract class _$A5 extends BuildlessAutoDisposeAsyncNotifier { - late final int param; +final class A4Family extends Family { + const A4Family._() + : super( + retry: null, + name: r'a4Provider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: false, + ); - FutureOr build( + A4Provider call( int param, - ); -} + ) => + A4Provider._(argument: param, from: this); -/// See also [A5]. -@ProviderFor(A5) -const a5Provider = A5Family(); + @override + String debugGetCreateSourceHash() => _$a4Hash(); -/// See also [A5]. -class A5Family extends Family> { - /// See also [A5]. - const A5Family(); + @override + String toString() => r'a4Provider'; - /// See also [A5]. - A5Provider call( - int param, + /// {@macro riverpod.override_with} + Override overrideWith( + A4 Function( + int args, + ) create, ) { - return A5Provider( - param, - ); - } + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as A4Provider; - @override - A5Provider getProviderOverride( - covariant A5Provider provider, - ) { - return call( - provider.param, + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, ); } - static const Iterable? _dependencies = null; + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + int Function(Ref ref, A4 notifier, int argument) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as A4Provider; - @override - Iterable? get dependencies => _dependencies; + final argument = provider.argument as int; - static const Iterable? _allTransitiveDependencies = null; + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; +abstract class _$A4 extends $Notifier { + late final _$args = ref.$arg as int; + int get param => _$args; + int build( + int param, + ); + @$internal @override - String? get name => r'a5Provider'; + int runBuild() => build( + _$args, + ); } -/// See also [A5]. -class A5Provider extends AutoDisposeAsyncNotifierProviderImpl { - /// See also [A5]. - A5Provider( - int param, - ) : this._internal( - () => A5()..param = param, - from: a5Provider, +@ProviderFor(A5) +const a5Provider = A5Family._(); + +final class A5Provider extends $AsyncNotifierProvider { + const A5Provider._( + {required A5Family super.from, + required int super.argument, + super.runNotifierBuildOverride, + A5 Function()? create}) + : _createCb = create, + super( + retry: null, name: r'a5Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$a5Hash, - dependencies: A5Family._dependencies, - allTransitiveDependencies: A5Family._allTransitiveDependencies, - param: param, + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - A5Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.param, - }) : super.internal(); + final A5 Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$a5Hash(); + + @override + String toString() { + return r'a5Provider' + '' + '($argument)'; + } - final int param; + @$internal + @override + A5 create() => _createCb?.call() ?? A5(); + @$internal @override - FutureOr runNotifierBuild( - covariant A5 notifier, + A5Provider $copyWithCreate( + A5 Function() create, ) { - return notifier.build( - param, - ); + return A5Provider._( + argument: argument as int, from: from! as A5Family, create: create); } + @$internal @override - Override overrideWith(A5 Function() create) { - return ProviderOverride( - origin: this, - override: A5Provider._internal( - () => create()..param = param, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - param: param, - ), - ); + A5Provider $copyWithBuild( + FutureOr Function( + Ref, + A5, + ) build, + ) { + return A5Provider._( + argument: argument as int, + from: from! as A5Family, + runNotifierBuildOverride: build); } + @$internal @override - AutoDisposeAsyncNotifierProviderElement createElement() { - return _A5ProviderElement(this); - } + $AsyncNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); @override bool operator ==(Object other) { - return other is A5Provider && other.param == param; + return other is A5Provider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, param.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin A5Ref on AutoDisposeAsyncNotifierProviderRef { - /// The parameter `param` of this provider. - int get param; -} +String _$a5Hash() => r'c83634c22b6a9149aa8787e45c3b7cd6c88b5958'; -class _A5ProviderElement - extends AutoDisposeAsyncNotifierProviderElement with A5Ref { - _A5ProviderElement(super.provider); +final class A5Family extends Family { + const A5Family._() + : super( + retry: null, + name: r'a5Provider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + A5Provider call( + int param, + ) => + A5Provider._(argument: param, from: this); @override - int get param => (origin as A5Provider).param; -} + String debugGetCreateSourceHash() => _$a5Hash(); -String _$a6Hash() => r'fe641c72cacf3dd119eb77a34fe8fc71c5c30139'; + @override + String toString() => r'a5Provider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + A5 Function( + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as A5Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + FutureOr Function(Ref ref, A5 notifier, int argument) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as A5Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} -abstract class _$A6 extends BuildlessAsyncNotifier { - late final int param; +abstract class _$A5 extends $AsyncNotifier { + late final _$args = ref.$arg as int; + int get param => _$args; FutureOr build( int param, ); + @$internal + @override + FutureOr runBuild() => build( + _$args, + ); } -/// See also [A6]. @ProviderFor(A6) -const a6Provider = A6Family(); +const a6Provider = A6Family._(); + +final class A6Provider extends $AsyncNotifierProvider { + const A6Provider._( + {required A6Family super.from, + required int super.argument, + super.runNotifierBuildOverride, + A6 Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'a6Provider', + isAutoDispose: false, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [A6]. -class A6Family extends Family> { - /// See also [A6]. - const A6Family(); + final A6 Function()? _createCb; - /// See also [A6]. - A6Provider call( - int param, - ) { - return A6Provider( - param, - ); + @override + String debugGetCreateSourceHash() => _$a6Hash(); + + @override + String toString() { + return r'a6Provider' + '' + '($argument)'; } + @$internal + @override + A6 create() => _createCb?.call() ?? A6(); + + @$internal @override - A6Provider getProviderOverride( - covariant A6Provider provider, + A6Provider $copyWithCreate( + A6 Function() create, ) { - return call( - provider.param, - ); + return A6Provider._( + argument: argument as int, from: from! as A6Family, create: create); } - static const Iterable? _dependencies = null; - + @$internal @override - Iterable? get dependencies => _dependencies; + A6Provider $copyWithBuild( + FutureOr Function( + Ref, + A6, + ) build, + ) { + return A6Provider._( + argument: argument as int, + from: from! as A6Family, + runNotifierBuildOverride: build); + } - static const Iterable? _allTransitiveDependencies = null; + @$internal + @override + $AsyncNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + bool operator ==(Object other) { + return other is A6Provider && other.argument == argument; + } @override - String? get name => r'a6Provider'; + int get hashCode { + return argument.hashCode; + } } -/// See also [A6]. -class A6Provider extends AsyncNotifierProviderImpl { - /// See also [A6]. - A6Provider( - int param, - ) : this._internal( - () => A6()..param = param, - from: a6Provider, +String _$a6Hash() => r'fe641c72cacf3dd119eb77a34fe8fc71c5c30139'; + +final class A6Family extends Family { + const A6Family._() + : super( + retry: null, name: r'a6Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$a6Hash, - dependencies: A6Family._dependencies, - allTransitiveDependencies: A6Family._allTransitiveDependencies, - param: param, + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: false, ); - A6Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.param, - }) : super.internal(); + A6Provider call( + int param, + ) => + A6Provider._(argument: param, from: this); - final int param; + @override + String debugGetCreateSourceHash() => _$a6Hash(); @override - FutureOr runNotifierBuild( - covariant A6 notifier, + String toString() => r'a6Provider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + A6 Function( + int args, + ) create, ) { - return notifier.build( - param, + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as A6Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, ); } - @override - Override overrideWith(A6 Function() create) { - return ProviderOverride( - origin: this, - override: A6Provider._internal( - () => create()..param = param, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - param: param, - ), + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + FutureOr Function(Ref ref, A6 notifier, int argument) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as A6Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, ); } +} +abstract class _$A6 extends $AsyncNotifier { + late final _$args = ref.$arg as int; + int get param => _$args; + + FutureOr build( + int param, + ); + @$internal @override - AsyncNotifierProviderElement createElement() { - return _A6ProviderElement(this); - } + FutureOr runBuild() => build( + _$args, + ); +} + +@ProviderFor(A7) +const a7Provider = A7Family._(); + +final class A7Provider extends $StreamNotifierProvider { + const A7Provider._( + {required A7Family super.from, + required int super.argument, + super.runNotifierBuildOverride, + A7 Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'a7Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final A7 Function()? _createCb; @override - bool operator ==(Object other) { - return other is A6Provider && other.param == param; + String debugGetCreateSourceHash() => _$a7Hash(); + + @override + String toString() { + return r'a7Provider' + '' + '($argument)'; } + @$internal @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, param.hashCode); + A7 create() => _createCb?.call() ?? A7(); + + @$internal + @override + A7Provider $copyWithCreate( + A7 Function() create, + ) { + return A7Provider._( + argument: argument as int, from: from! as A7Family, create: create); + } - return _SystemHash.finish(hash); + @$internal + @override + A7Provider $copyWithBuild( + Stream Function( + Ref, + A7, + ) build, + ) { + return A7Provider._( + argument: argument as int, + from: from! as A7Family, + runNotifierBuildOverride: build); } -} -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin A6Ref on AsyncNotifierProviderRef { - /// The parameter `param` of this provider. - int get param; -} + @$internal + @override + $StreamNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $StreamNotifierProviderElement(this, pointer); -class _A6ProviderElement extends AsyncNotifierProviderElement - with A6Ref { - _A6ProviderElement(super.provider); + @override + bool operator ==(Object other) { + return other is A7Provider && other.argument == argument; + } @override - int get param => (origin as A6Provider).param; + int get hashCode { + return argument.hashCode; + } } String _$a7Hash() => r'd3d9ab5090e21987d65522f14ebb70d0058fc56a'; -abstract class _$A7 extends BuildlessAutoDisposeStreamNotifier { - late final int param; +final class A7Family extends Family { + const A7Family._() + : super( + retry: null, + name: r'a7Provider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); - Stream build( + A7Provider call( int param, - ); -} + ) => + A7Provider._(argument: param, from: this); -/// See also [A7]. -@ProviderFor(A7) -const a7Provider = A7Family(); + @override + String debugGetCreateSourceHash() => _$a7Hash(); -/// See also [A7]. -class A7Family extends Family> { - /// See also [A7]. - const A7Family(); + @override + String toString() => r'a7Provider'; - /// See also [A7]. - A7Provider call( - int param, + /// {@macro riverpod.override_with} + Override overrideWith( + A7 Function( + int args, + ) create, ) { - return A7Provider( - param, + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as A7Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, ); } - @override - A7Provider getProviderOverride( - covariant A7Provider provider, + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + Stream Function(Ref ref, A7 notifier, int argument) build, ) { - return call( - provider.param, + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as A7Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, ); } +} - static const Iterable? _dependencies = null; +abstract class _$A7 extends $StreamNotifier { + late final _$args = ref.$arg as int; + int get param => _$args; + Stream build( + int param, + ); + @$internal @override - Iterable? get dependencies => _dependencies; + Stream runBuild() => build( + _$args, + ); +} - static const Iterable? _allTransitiveDependencies = null; +@ProviderFor(A8) +const a8Provider = A8Family._(); + +final class A8Provider extends $StreamNotifierProvider { + const A8Provider._( + {required A8Family super.from, + required int super.argument, + super.runNotifierBuildOverride, + A8 Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'a8Provider', + isAutoDispose: false, + dependencies: null, + allTransitiveDependencies: null, + ); - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + final A8 Function()? _createCb; @override - String? get name => r'a7Provider'; -} - -/// See also [A7]. -class A7Provider extends AutoDisposeStreamNotifierProviderImpl { - /// See also [A7]. - A7Provider( - int param, - ) : this._internal( - () => A7()..param = param, - from: a7Provider, - name: r'a7Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$a7Hash, - dependencies: A7Family._dependencies, - allTransitiveDependencies: A7Family._allTransitiveDependencies, - param: param, - ); + String debugGetCreateSourceHash() => _$a8Hash(); - A7Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.param, - }) : super.internal(); + @override + String toString() { + return r'a8Provider' + '' + '($argument)'; + } - final int param; + @$internal + @override + A8 create() => _createCb?.call() ?? A8(); + @$internal @override - Stream runNotifierBuild( - covariant A7 notifier, + A8Provider $copyWithCreate( + A8 Function() create, ) { - return notifier.build( - param, - ); + return A8Provider._( + argument: argument as int, from: from! as A8Family, create: create); } + @$internal @override - Override overrideWith(A7 Function() create) { - return ProviderOverride( - origin: this, - override: A7Provider._internal( - () => create()..param = param, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - param: param, - ), - ); + A8Provider $copyWithBuild( + Stream Function( + Ref, + A8, + ) build, + ) { + return A8Provider._( + argument: argument as int, + from: from! as A8Family, + runNotifierBuildOverride: build); } + @$internal @override - AutoDisposeStreamNotifierProviderElement createElement() { - return _A7ProviderElement(this); - } + $StreamNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $StreamNotifierProviderElement(this, pointer); @override bool operator ==(Object other) { - return other is A7Provider && other.param == param; + return other is A8Provider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, param.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin A7Ref on AutoDisposeStreamNotifierProviderRef { - /// The parameter `param` of this provider. - int get param; -} +String _$a8Hash() => r'54f4a841a283161bed3d444dcee53bf367958678'; -class _A7ProviderElement - extends AutoDisposeStreamNotifierProviderElement with A7Ref { - _A7ProviderElement(super.provider); +final class A8Family extends Family { + const A8Family._() + : super( + retry: null, + name: r'a8Provider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: false, + ); + + A8Provider call( + int param, + ) => + A8Provider._(argument: param, from: this); @override - int get param => (origin as A7Provider).param; -} + String debugGetCreateSourceHash() => _$a8Hash(); -String _$a8Hash() => r'54f4a841a283161bed3d444dcee53bf367958678'; + @override + String toString() => r'a8Provider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + A8 Function( + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as A8Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + Stream Function(Ref ref, A8 notifier, int argument) build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as A8Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } +} -abstract class _$A8 extends BuildlessStreamNotifier { - late final int param; +abstract class _$A8 extends $StreamNotifier { + late final _$args = ref.$arg as int; + int get param => _$args; Stream build( int param, ); + @$internal + @override + Stream runBuild() => build( + _$args, + ); } -/// See also [A8]. -@ProviderFor(A8) -const a8Provider = A8Family(); +@ProviderFor(B) +const bProvider = BProvider._(); + +final class BProvider extends $NotifierProvider { + const BProvider._({super.runNotifierBuildOverride, B Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'bProvider', + isAutoDispose: false, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [A8]. -class A8Family extends Family> { - /// See also [A8]. - const A8Family(); + final B Function()? _createCb; - /// See also [A8]. - A8Provider call( - int param, - ) { - return A8Provider( - param, + @override + String debugGetCreateSourceHash() => _$bHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } + @$internal + @override + B create() => _createCb?.call() ?? B(); + + @$internal @override - A8Provider getProviderOverride( - covariant A8Provider provider, + BProvider $copyWithCreate( + B Function() create, ) { - return call( - provider.param, - ); + return BProvider._(create: create); } - static const Iterable? _dependencies = null; - + @$internal @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; + BProvider $copyWithBuild( + int Function( + Ref, + B, + ) build, + ) { + return BProvider._(runNotifierBuildOverride: build); + } + @$internal @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + $NotifierProviderElement $createElement($ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$bHash() => r'44288285e9c28f846d609ba892520f577ecf7867'; +abstract class _$B extends $Notifier { + int build(); + @$internal @override - String? get name => r'a8Provider'; + int runBuild() => build(); } -/// See also [A8]. -class A8Provider extends StreamNotifierProviderImpl { - /// See also [A8]. - A8Provider( - int param, - ) : this._internal( - () => A8()..param = param, - from: a8Provider, - name: r'a8Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$a8Hash, - dependencies: A8Family._dependencies, - allTransitiveDependencies: A8Family._allTransitiveDependencies, - param: param, +@ProviderFor(B2) +const b2Provider = B2Provider._(); + +final class B2Provider extends $NotifierProvider { + const B2Provider._({super.runNotifierBuildOverride, B2 Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'b2Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, ); - A8Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.param, - }) : super.internal(); - - final int param; + final B2 Function()? _createCb; @override - Stream runNotifierBuild( - covariant A8 notifier, - ) { - return notifier.build( - param, - ); - } + String debugGetCreateSourceHash() => _$b2Hash(); - @override - Override overrideWith(A8 Function() create) { - return ProviderOverride( + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( origin: this, - override: A8Provider._internal( - () => create()..param = param, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - param: param, - ), + providerOverride: $ValueProvider(value), ); } + @$internal @override - StreamNotifierProviderElement createElement() { - return _A8ProviderElement(this); - } + B2 create() => _createCb?.call() ?? B2(); + @$internal @override - bool operator ==(Object other) { - return other is A8Provider && other.param == param; + B2Provider $copyWithCreate( + B2 Function() create, + ) { + return B2Provider._(create: create); } + @$internal @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, param.hashCode); - - return _SystemHash.finish(hash); + B2Provider $copyWithBuild( + int Function( + Ref, + B2, + ) build, + ) { + return B2Provider._(runNotifierBuildOverride: build); } -} -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin A8Ref on StreamNotifierProviderRef { - /// The parameter `param` of this provider. - int get param; + @$internal + @override + $NotifierProviderElement $createElement($ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); } -class _A8ProviderElement extends StreamNotifierProviderElement - with A8Ref { - _A8ProviderElement(super.provider); +String _$b2Hash() => r'292925c285c6975ed6585d541c5a9ae18977d73c'; +abstract class _$B2 extends $Notifier { + int build(); + @$internal @override - int get param => (origin as A8Provider).param; + int runBuild() => build(); } -String _$bHash() => r'44288285e9c28f846d609ba892520f577ecf7867'; - -/// See also [B]. -@ProviderFor(B) -final bProvider = NotifierProvider.internal( - B.new, - name: r'bProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$bHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$B = Notifier; -String _$b2Hash() => r'292925c285c6975ed6585d541c5a9ae18977d73c'; - -/// See also [B2]. -@ProviderFor(B2) -final b2Provider = AutoDisposeNotifierProvider.internal( - B2.new, - name: r'b2Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$b2Hash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$B2 = AutoDisposeNotifier; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/another.dart b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/another.dart similarity index 53% rename from packages/riverpod_lint_flutter_test/test/lints/another.dart rename to packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/another.dart index 23241bf93..0396dce89 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/another.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/another.dart @@ -7,3 +7,12 @@ final aProvider = Provider((ref) => 0); @riverpod int b(Ref ref) => 0; + +@Riverpod(dependencies: []) +int anotherScoped(Ref ref) => 0; + +@Riverpod(dependencies: [anotherScoped]) +int anotherNonEmptyScoped(Ref ref) { + ref.watch(anotherScopedProvider); + return 0; +} diff --git a/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/another.g.dart b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/another.g.dart new file mode 100644 index 000000000..acb68d2ab --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/another.g.dart @@ -0,0 +1,189 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'another.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +@ProviderFor(b) +const bProvider = BProvider._(); + +final class BProvider extends $FunctionalProvider + with $Provider { + const BProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'bProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$bHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + BProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return BProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? b; + return _$cb(ref); + } +} + +String _$bHash() => r'31624e9aa10c9cd7941c9626e841c6df3468723b'; + +@ProviderFor(anotherScoped) +const anotherScopedProvider = AnotherScopedProvider._(); + +final class AnotherScopedProvider extends $FunctionalProvider + with $Provider { + const AnotherScopedProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'anotherScopedProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$anotherScopedHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + AnotherScopedProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return AnotherScopedProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? anotherScoped; + return _$cb(ref); + } +} + +String _$anotherScopedHash() => r'edf3ccffb7c3ce1b1e4ffdd4009aeed4fa38c3f8'; + +@ProviderFor(anotherNonEmptyScoped) +const anotherNonEmptyScopedProvider = AnotherNonEmptyScopedProvider._(); + +final class AnotherNonEmptyScopedProvider extends $FunctionalProvider + with $Provider { + const AnotherNonEmptyScopedProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'anotherNonEmptyScopedProvider', + isAutoDispose: true, + dependencies: const [anotherScopedProvider], + allTransitiveDependencies: const [ + AnotherNonEmptyScopedProvider.$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = anotherScopedProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$anotherNonEmptyScopedHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + AnotherNonEmptyScopedProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return AnotherNonEmptyScopedProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? anotherNonEmptyScoped; + return _$cb(ref); + } +} + +String _$anotherNonEmptyScopedHash() => + r'56da5331e55d74e8e10ff710d20618f217394a69'; + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/missing_dependencies.dart b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/missing_dependencies.dart new file mode 100644 index 000000000..a10cd4dd1 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/missing_dependencies.dart @@ -0,0 +1,280 @@ +import 'package:flutter/widgets.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'missing_dependencies.g.dart'; + +@Riverpod(dependencies: []) +int dep(Ref ref) => 0; + +@Riverpod(dependencies: [dep]) +int transitiveDep(Ref ref) => ref.watch(depProvider); + +@Riverpod(dependencies: []) +int dep2(Ref ref) => 0; + +@Riverpod(dependencies: []) +int depFamily(Ref ref, int id) => 0; + +// expect_lint: provider_dependencies +@Dependencies([dep]) +void depFn() {} + +// expect_lint: provider_dependencies +@Dependencies([depFamily]) +void depFamilyFn() {} + +@Dependencies([dep]) +class DepWidget extends StatelessWidget { + const DepWidget({super.key, this.child}); + final Widget? child; + + @override + Widget build(BuildContext context) { + depFn(); + return const Placeholder(); + } +} + +// expect_lint: provider_dependencies +@Dependencies([dep]) +class UnusedDepWidget extends ConsumerWidget { + const UnusedDepWidget({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return const Placeholder(); + } +} + +@Dependencies([transitiveDep]) +class TransitiveDepWidget extends ConsumerWidget { + const TransitiveDepWidget({super.key, this.child}); + final Widget? child; + + @override + Widget build(BuildContext context, WidgetRef ref) { + ref.watch(transitiveDepProvider); + return const Placeholder(); + } +} + +@Dependencies([depFamily]) +class DepFamily extends StatelessWidget { + const DepFamily({super.key, this.child}); + final Widget? child; + + @override + Widget build(BuildContext context) { + depFamilyFn(); + return const Placeholder(); + } +} + +//////////// + +// expect_lint: provider_dependencies +@riverpod +int plainAnnotation(Ref ref) { + ref.watch(depProvider); + return 0; +} + +// expect_lint: provider_dependencies +@Riverpod(keepAlive: false) +int customAnnotation(Ref ref) { + ref.watch(depProvider); + return 0; +} + +// expect_lint: provider_dependencies +@Riverpod( + keepAlive: false, +) +int customAnnotationWithTrailingComma( + Ref ref, +) { + ref.watch(depProvider); + return 0; +} + +@Riverpod( + keepAlive: false, + // expect_lint: provider_dependencies + dependencies: [], +) +int existingDep(Ref ref) { + ref.watch(depProvider); + return 0; +} + +@Riverpod( + keepAlive: false, + // expect_lint: provider_dependencies + dependencies: [], +) +int multipleDeps(Ref ref) { + ref.watch(depProvider); + ref.watch(dep2Provider); + return 0; +} + +class Scope extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + return ProviderScope( + overrides: [depProvider.overrideWithValue(42)], + child: DepWidget(), + ); + } +} + +// expect_lint: provider_dependencies +class AboveScope extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + return DepWidget( + child: ProviderScope( + overrides: [depProvider.overrideWithValue(42)], + child: Container(), + ), + ); + } +} + +// expect_lint: provider_dependencies +class Scope2 extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + return ProviderScope( + overrides: [depProvider.overrideWithValue(42)], + child: Text('${ref.watch(depProvider)}'), + ); + } +} + +// expect_lint: provider_dependencies +class ConditionalScope extends ConsumerWidget { + ConditionalScope({super.key, required this.condition}); + final bool condition; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return ProviderScope( + overrides: [ + if (condition) depProvider.overrideWithValue(42), + ], + child: DepWidget(), + ); + } +} + +class Scope4 extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + return ProviderScope( + overrides: [depFamilyProvider.overrideWith((ref, arg) => 0)], + child: DepFamily(), + ); + } +} + +class OnlyNeedToOverrideProviderWithEmptyDependencies extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + return ProviderScope( + overrides: [ + depProvider.overrideWithValue(42), + ], + child: TransitiveDepWidget(), + ); + } +} + +class CanOverrideTransitiveProviderDirectly extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + return ProviderScope( + overrides: [ + transitiveDepProvider.overrideWithValue(42), + ], + child: TransitiveDepWidget(), + ); + } +} + +class SupportsMultipleScopes extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + ProviderScope( + overrides: [depProvider.overrideWith((ref) => 0)], + child: DepWidget(), + ); + + return ProviderScope( + overrides: [depFamilyProvider.overrideWith((ref, arg) => 0)], + child: DepFamily(), + ); + } +} + +// expect_lint: provider_dependencies +class SupportsMultipleScopes2 extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + ProviderScope( + overrides: [depProvider.overrideWith((ref) => 0)], + child: DepFamily(), + ); + + return ProviderScope( + overrides: [depFamilyProvider.overrideWith((ref, arg) => 0)], + child: DepWidget(), + ); + } +} + +class SupportsNestedScopes extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + return ProviderScope( + overrides: [depFamilyProvider.overrideWith((ref, arg) => 0)], + child: ProviderScope( + overrides: [depProvider.overrideWith((ref) => 0)], + child: DepFamily( + child: DepWidget(), + ), + ), + ); + } +} + +// expect_lint: provider_dependencies +class IncompleteFamilyOverride extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + return ProviderScope( + overrides: [depFamilyProvider(42).overrideWith((ref) => 0)], + child: DepFamily(), + ); + } +} + +@Dependencies([dep]) +class NotFoundWidget extends ConsumerStatefulWidget { + @override + _NotFoundWidgetState createState() => _NotFoundWidgetState(); +} + +class _NotFoundWidgetState extends ConsumerState { + @override + Widget build(BuildContext context) { + ref.watch(depProvider); + return const Placeholder(); + } +} + +/// Random doc to test that identifiers in docs don't trigger the lint. +/// [dep], [DepWidget], [depProvider] +@Riverpod(dependencies: []) +int providerWithDartDoc(Ref ref) => 0; diff --git a/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/missing_dependencies.g.dart b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/missing_dependencies.g.dart new file mode 100644 index 000000000..06a7b3727 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/missing_dependencies.g.dart @@ -0,0 +1,683 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'missing_dependencies.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +@ProviderFor(dep) +const depProvider = DepProvider._(); + +final class DepProvider extends $FunctionalProvider + with $Provider { + const DepProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'depProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$depHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + DepProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return DepProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? dep; + return _$cb(ref); + } +} + +String _$depHash() => r'578a350a40cda46444ecd9fa3ea2fd7bd0994692'; + +@ProviderFor(transitiveDep) +const transitiveDepProvider = TransitiveDepProvider._(); + +final class TransitiveDepProvider extends $FunctionalProvider + with $Provider { + const TransitiveDepProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'transitiveDepProvider', + isAutoDispose: true, + dependencies: const [depProvider], + allTransitiveDependencies: const [ + TransitiveDepProvider.$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = depProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$transitiveDepHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + TransitiveDepProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return TransitiveDepProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? transitiveDep; + return _$cb(ref); + } +} + +String _$transitiveDepHash() => r'cedc000b7d16447684dff970ddea659cca24cdf6'; + +@ProviderFor(dep2) +const dep2Provider = Dep2Provider._(); + +final class Dep2Provider extends $FunctionalProvider + with $Provider { + const Dep2Provider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'dep2Provider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$dep2Hash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + Dep2Provider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return Dep2Provider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? dep2; + return _$cb(ref); + } +} + +String _$dep2Hash() => r'97901e825cdcf5b1ac455b0fe8a2111662ce9f13'; + +@ProviderFor(depFamily) +const depFamilyProvider = DepFamilyFamily._(); + +final class DepFamilyProvider extends $FunctionalProvider + with $Provider { + const DepFamilyProvider._( + {required DepFamilyFamily super.from, + required int super.argument, + int Function( + Ref ref, + int id, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'depFamilyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + int id, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$depFamilyHash(); + + @override + String toString() { + return r'depFamilyProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + DepFamilyProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return DepFamilyProvider._( + argument: argument as int, + from: from! as DepFamilyFamily, + create: ( + ref, + int id, + ) => + create(ref)); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? depFamily; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is DepFamilyProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$depFamilyHash() => r'6cca68b98693e352e9b801b1fc441d438fc72525'; + +final class DepFamilyFamily extends Family { + const DepFamilyFamily._() + : super( + retry: null, + name: r'depFamilyProvider', + dependencies: const [], + allTransitiveDependencies: const [], + isAutoDispose: true, + ); + + DepFamilyProvider call( + int id, + ) => + DepFamilyProvider._(argument: id, from: this); + + @override + String debugGetCreateSourceHash() => _$depFamilyHash(); + + @override + String toString() => r'depFamilyProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + int Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as DepFamilyProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +//////////// +// expect_lint: provider_dependencies +@ProviderFor(plainAnnotation) +const plainAnnotationProvider = PlainAnnotationProvider._(); + +//////////// +// expect_lint: provider_dependencies +final class PlainAnnotationProvider extends $FunctionalProvider + with $Provider { + //////////// +// expect_lint: provider_dependencies + const PlainAnnotationProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'plainAnnotationProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$plainAnnotationHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + PlainAnnotationProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return PlainAnnotationProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? plainAnnotation; + return _$cb(ref); + } +} + +String _$plainAnnotationHash() => r'6a3d1f1f2e53902af56cd7ce6ceba17358690b70'; + +@ProviderFor(customAnnotation) +const customAnnotationProvider = CustomAnnotationProvider._(); + +final class CustomAnnotationProvider extends $FunctionalProvider + with $Provider { + const CustomAnnotationProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'customAnnotationProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$customAnnotationHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CustomAnnotationProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return CustomAnnotationProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? customAnnotation; + return _$cb(ref); + } +} + +String _$customAnnotationHash() => r'8081bbad2cfbe5bff1ace9aa3be450dd28112488'; + +@ProviderFor(customAnnotationWithTrailingComma) +const customAnnotationWithTrailingCommaProvider = + CustomAnnotationWithTrailingCommaProvider._(); + +final class CustomAnnotationWithTrailingCommaProvider + extends $FunctionalProvider with $Provider { + const CustomAnnotationWithTrailingCommaProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'customAnnotationWithTrailingCommaProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => + _$customAnnotationWithTrailingCommaHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CustomAnnotationWithTrailingCommaProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return CustomAnnotationWithTrailingCommaProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? customAnnotationWithTrailingComma; + return _$cb(ref); + } +} + +String _$customAnnotationWithTrailingCommaHash() => + r'709613050eb1db7b4c43cb87855e2c32988141d8'; + +@ProviderFor(existingDep) +const existingDepProvider = ExistingDepProvider._(); + +final class ExistingDepProvider extends $FunctionalProvider + with $Provider { + const ExistingDepProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'existingDepProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$existingDepHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ExistingDepProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return ExistingDepProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? existingDep; + return _$cb(ref); + } +} + +String _$existingDepHash() => r'73e7e1a0d4c2ae07ed03fb248408c3d82fe85554'; + +@ProviderFor(multipleDeps) +const multipleDepsProvider = MultipleDepsProvider._(); + +final class MultipleDepsProvider extends $FunctionalProvider + with $Provider { + const MultipleDepsProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'multipleDepsProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$multipleDepsHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + MultipleDepsProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return MultipleDepsProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? multipleDeps; + return _$cb(ref); + } +} + +String _$multipleDepsHash() => r'66de70567c011a294a2c46703dfab8ba7247fd5e'; + +/// Random doc to test that identifiers in docs don't trigger the lint. +/// [dep], [DepWidget], [depProvider] +@ProviderFor(providerWithDartDoc) +const providerWithDartDocProvider = ProviderWithDartDocProvider._(); + +/// Random doc to test that identifiers in docs don't trigger the lint. +/// [dep], [DepWidget], [depProvider] +final class ProviderWithDartDocProvider extends $FunctionalProvider + with $Provider { + /// Random doc to test that identifiers in docs don't trigger the lint. + /// [dep], [DepWidget], [depProvider] + const ProviderWithDartDocProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'providerWithDartDocProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$providerWithDartDocHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ProviderWithDartDocProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return ProviderWithDartDocProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? providerWithDartDoc; + return _$cb(ref); + } +} + +String _$providerWithDartDocHash() => + r'aeb5735ad2dbe1d0b41a529828f9bb2c915071b6'; + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/dependencies.dart b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/missing_dependencies2.dart similarity index 61% rename from packages/riverpod_lint_flutter_test/test/lints/dependencies.dart rename to packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/missing_dependencies2.dart index 036c308a0..88e0d24e1 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/dependencies.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/missing_dependencies2.dart @@ -1,13 +1,12 @@ // ignore_for_file: unused_field +import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'another.dart' as import_alias; +import 'another.dart'; -part 'dependencies.g.dart'; - -@riverpod -external int unimplementedScoped(); +part 'missing_dependencies2.g.dart'; @Riverpod(dependencies: []) int dep(Ref ref) => 0; @@ -23,19 +22,12 @@ final root = Provider((ref) => 0); int generatedRoot(Ref ref) => 0; // dep no "dependencies" - @riverpod int watchScopedButNoDependencies(Ref ref) { // expect_lint: avoid_manual_providers_as_generated_provider_dependency return ref.watch(scoped); } -// expect_lint: provider_dependencies -@riverpod -int watchExternalButNoDependencies(Ref ref) { - return ref.watch(unimplementedScopedProvider); -} - // expect_lint: provider_dependencies @riverpod int watchGeneratedScopedButNoDependencies( @@ -58,7 +50,6 @@ int watchGeneratedRootButNoDependencies( } // Check "dependencies" specified but missing dependency - @Riverpod(dependencies: []) int watchScopedButEmptyDependencies(Ref ref) { // expect_lint: avoid_manual_providers_as_generated_provider_dependency @@ -122,7 +113,6 @@ int watchGeneratedRootButMissingDependencies( } // Check "dependencies" specified and contains dependency - @Riverpod(dependencies: [generatedScoped]) int watchGeneratedScopedAndContainsDependency( Ref ref, @@ -130,11 +120,14 @@ int watchGeneratedScopedAndContainsDependency( return ref.watch(generatedScopedProvider); } -@Riverpod(dependencies: [ - // The dependency is redundant because it is not a scoped provider - // expect_lint: provider_dependencies - generatedRoot, -]) +@Riverpod( + dependencies: + // The dependency is redundant because it is not a scoped provider + // expect_lint: provider_dependencies + [ + generatedRoot, + ], +) int watchGeneratedRootAndContainsDependency( Ref ref, ) { @@ -143,9 +136,11 @@ int watchGeneratedRootAndContainsDependency( // A dependency is specified but never used -@Riverpod(dependencies: [ +@Riverpod(dependencies: + // generatedRoot is extra + // expect_lint: provider_dependencies + [ dep, - // expect_lint: provider_dependencies generatedRoot, ]) int specifiedDependencyButNeverUsed(Ref ref) { @@ -154,7 +149,6 @@ int specifiedDependencyButNeverUsed(Ref ref) { } // Works with classes too - @Riverpod(dependencies: []) class ClassWatchGeneratedRootButMissingDependencies extends _$ClassWatchGeneratedRootButMissingDependencies { @@ -164,16 +158,6 @@ class ClassWatchGeneratedRootButMissingDependencies } } -// expect_lint: provider_dependencies -@Riverpod(dependencies: []) -class ClassWatchGeneratedScopedButMissingDependencies - extends _$ClassWatchGeneratedScopedButMissingDependencies { - @override - int build() { - return ref.watch(generatedScopedProvider); - } -} - @Riverpod(dependencies: [generatedScoped]) int regression2348(Ref ref) { ref..watch(generatedScopedProvider); @@ -222,3 +206,140 @@ class AliasClass extends _$AliasClass { @override int build() => 0; } + +// === @Dependencies === + +// Can specify dependencies on top-level declarations +@Dependencies([dep]) +class RootDependenciesClass { + void foo() { + fn(); + } +} + +// Specifying @Dependencies on class members requires specifying them on +// the class too: +class MemberDependencies { + // expect_lint: provider_dependencies + @Dependencies([dep]) + int build() => 0; +} + +// expect_lint: provider_dependencies +@Dependencies([]) +class CanUpdateMultipleDependenciesAtOnce { + // expect_lint: provider_dependencies + @Dependencies([]) + int build(WidgetRef ref) { + ref.watch(depProvider); + return 0; + } +} + +// Counts @Riverpod dependencies too +@Riverpod(dependencies: [dep]) +class RiverpodDependencies extends _$RiverpodDependencies { + @Dependencies([dep]) + @override + int build() { + ref.watch(depProvider); + return 0; + } +} + +// Handle identifiers with dependencies +// expect_lint: provider_dependencies +@Dependencies([dep]) +void fn() {} + +// expect_lint: provider_dependencies +void fn2() { + fn(); +} + +@Dependencies([dep]) +void fn3() => fn(); + +// expect_lint: provider_dependencies +@riverpod +int foo(Ref ref) { + fn(); + return 0; +} + +// Handle widget with dependencies +@Dependencies([dep]) +class WidgetDependencies extends StatelessWidget { + @override + Widget build(BuildContext context) { + fn(); + return const SizedBox(); + } +} + +// expect_lint: provider_dependencies +class WidgetDependencies2 extends StatelessWidget { + @override + Widget build(BuildContext context) { + return WidgetDependencies(); + } +} + +@Dependencies([dep]) +class WidgetDependencies3 extends StatelessWidget { + @override + Widget build(BuildContext context) { + return WidgetDependencies(); + } +} + +@Dependencies([dep]) +class Stateful extends StatefulWidget { + const Stateful({super.key}); + + @override + _StatefulState createState() => _StatefulState(); +} + +class _StatefulState extends State { + @override + Widget build(BuildContext context) { + return WidgetDependencies(); + } +} + +// expect_lint: provider_dependencies +@Dependencies([]) +class Stateful2 extends StatefulWidget { + const Stateful2({super.key}); + + @override + _Stateful2State createState() => _Stateful2State(); +} + +class _Stateful2State extends State { + @override + Widget build(BuildContext context) { + return WidgetDependencies(); + } +} + +// expect_lint: provider_dependencies +class FindStateFromClassList extends StatefulWidget { + const FindStateFromClassList({super.key}); + + @override + State createState() => _Stateful3State(); +} + +class _Stateful3State extends State { + @override + Widget build(BuildContext context) => WidgetDependencies(); +} + +// expect_lint: provider_dependencies +@riverpod +int crossFileDependency(Ref ref) { + ref.watch(anotherNonEmptyScopedProvider); + return 0; +} diff --git a/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/missing_dependencies2.g.dart b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/missing_dependencies2.g.dart new file mode 100644 index 000000000..9f0fb55b1 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/missing_dependencies2.g.dart @@ -0,0 +1,1938 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'missing_dependencies2.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +@ProviderFor(dep) +const depProvider = DepProvider._(); + +final class DepProvider extends $FunctionalProvider + with $Provider { + const DepProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'depProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$depHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + DepProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return DepProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? dep; + return _$cb(ref); + } +} + +String _$depHash() => r'578a350a40cda46444ecd9fa3ea2fd7bd0994692'; + +@ProviderFor(generatedScoped) +const generatedScopedProvider = GeneratedScopedProvider._(); + +final class GeneratedScopedProvider extends $FunctionalProvider + with $Provider { + const GeneratedScopedProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'generatedScopedProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$generatedScopedHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + GeneratedScopedProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return GeneratedScopedProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? generatedScoped; + return _$cb(ref); + } +} + +String _$generatedScopedHash() => r'f8e5b6926ce13765c83dbb7f8c8458c9c5fe7d69'; + +@ProviderFor(generatedRoot) +const generatedRootProvider = GeneratedRootProvider._(); + +final class GeneratedRootProvider extends $FunctionalProvider + with $Provider { + const GeneratedRootProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'generatedRootProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$generatedRootHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + GeneratedRootProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return GeneratedRootProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? generatedRoot; + return _$cb(ref); + } +} + +String _$generatedRootHash() => r'179253a56503f28bb616c602d8af9ad3b23d438f'; + +@ProviderFor(watchScopedButNoDependencies) +const watchScopedButNoDependenciesProvider = + WatchScopedButNoDependenciesProvider._(); + +final class WatchScopedButNoDependenciesProvider + extends $FunctionalProvider with $Provider { + const WatchScopedButNoDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'watchScopedButNoDependenciesProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$watchScopedButNoDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + WatchScopedButNoDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return WatchScopedButNoDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? watchScopedButNoDependencies; + return _$cb(ref); + } +} + +String _$watchScopedButNoDependenciesHash() => + r'e326226fdc19ea7a4430900154c071f5a1a98e40'; + +@ProviderFor(watchGeneratedScopedButNoDependencies) +const watchGeneratedScopedButNoDependenciesProvider = + WatchGeneratedScopedButNoDependenciesProvider._(); + +final class WatchGeneratedScopedButNoDependenciesProvider + extends $FunctionalProvider with $Provider { + const WatchGeneratedScopedButNoDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'watchGeneratedScopedButNoDependenciesProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => + _$watchGeneratedScopedButNoDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + WatchGeneratedScopedButNoDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return WatchGeneratedScopedButNoDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? watchGeneratedScopedButNoDependencies; + return _$cb(ref); + } +} + +String _$watchGeneratedScopedButNoDependenciesHash() => + r'2109f8ccbc13632e45f18ccb93bc3059c431eba1'; + +@ProviderFor(watchRootButNoDependencies) +const watchRootButNoDependenciesProvider = + WatchRootButNoDependenciesProvider._(); + +final class WatchRootButNoDependenciesProvider + extends $FunctionalProvider with $Provider { + const WatchRootButNoDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'watchRootButNoDependenciesProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$watchRootButNoDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + WatchRootButNoDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return WatchRootButNoDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? watchRootButNoDependencies; + return _$cb(ref); + } +} + +String _$watchRootButNoDependenciesHash() => + r'cfecc8aeb539e82c46276f9e4dd78c323b4bef12'; + +@ProviderFor(watchGeneratedRootButNoDependencies) +const watchGeneratedRootButNoDependenciesProvider = + WatchGeneratedRootButNoDependenciesProvider._(); + +final class WatchGeneratedRootButNoDependenciesProvider + extends $FunctionalProvider with $Provider { + const WatchGeneratedRootButNoDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'watchGeneratedRootButNoDependenciesProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => + _$watchGeneratedRootButNoDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + WatchGeneratedRootButNoDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return WatchGeneratedRootButNoDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? watchGeneratedRootButNoDependencies; + return _$cb(ref); + } +} + +String _$watchGeneratedRootButNoDependenciesHash() => + r'c839dab901f606c11c78f9c8761931027d3db1d1'; + +@ProviderFor(watchScopedButEmptyDependencies) +const watchScopedButEmptyDependenciesProvider = + WatchScopedButEmptyDependenciesProvider._(); + +final class WatchScopedButEmptyDependenciesProvider + extends $FunctionalProvider with $Provider { + const WatchScopedButEmptyDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'watchScopedButEmptyDependenciesProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$watchScopedButEmptyDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + WatchScopedButEmptyDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return WatchScopedButEmptyDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? watchScopedButEmptyDependencies; + return _$cb(ref); + } +} + +String _$watchScopedButEmptyDependenciesHash() => + r'a194f52730f635e9c92b3467b33b8c302c93b1ab'; + +@ProviderFor(watchGeneratedScopedButEmptyDependencies) +const watchGeneratedScopedButEmptyDependenciesProvider = + WatchGeneratedScopedButEmptyDependenciesProvider._(); + +final class WatchGeneratedScopedButEmptyDependenciesProvider + extends $FunctionalProvider with $Provider { + const WatchGeneratedScopedButEmptyDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'watchGeneratedScopedButEmptyDependenciesProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => + _$watchGeneratedScopedButEmptyDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + WatchGeneratedScopedButEmptyDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return WatchGeneratedScopedButEmptyDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? watchGeneratedScopedButEmptyDependencies; + return _$cb(ref); + } +} + +String _$watchGeneratedScopedButEmptyDependenciesHash() => + r'fa4cb564341e7b3f0dd10f70e17381c67859c643'; + +@ProviderFor(watchRootButEmptyDependencies) +const watchRootButEmptyDependenciesProvider = + WatchRootButEmptyDependenciesProvider._(); + +final class WatchRootButEmptyDependenciesProvider + extends $FunctionalProvider with $Provider { + const WatchRootButEmptyDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'watchRootButEmptyDependenciesProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$watchRootButEmptyDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + WatchRootButEmptyDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return WatchRootButEmptyDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? watchRootButEmptyDependencies; + return _$cb(ref); + } +} + +String _$watchRootButEmptyDependenciesHash() => + r'8669a421efcd8caadc0d070f0c88043668610bbb'; + +@ProviderFor(watchGeneratedRootButEmptyDependencies) +const watchGeneratedRootButEmptyDependenciesProvider = + WatchGeneratedRootButEmptyDependenciesProvider._(); + +final class WatchGeneratedRootButEmptyDependenciesProvider + extends $FunctionalProvider with $Provider { + const WatchGeneratedRootButEmptyDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'watchGeneratedRootButEmptyDependenciesProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => + _$watchGeneratedRootButEmptyDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + WatchGeneratedRootButEmptyDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return WatchGeneratedRootButEmptyDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? watchGeneratedRootButEmptyDependencies; + return _$cb(ref); + } +} + +String _$watchGeneratedRootButEmptyDependenciesHash() => + r'80581ac491e25ae8c6ee7b7f25dff9939f8de37c'; + +@ProviderFor(watchScopedButMissingDependencies) +const watchScopedButMissingDependenciesProvider = + WatchScopedButMissingDependenciesProvider._(); + +final class WatchScopedButMissingDependenciesProvider + extends $FunctionalProvider with $Provider { + const WatchScopedButMissingDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'watchScopedButMissingDependenciesProvider', + isAutoDispose: true, + dependencies: const [depProvider], + allTransitiveDependencies: const [ + WatchScopedButMissingDependenciesProvider + .$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = depProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => + _$watchScopedButMissingDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + WatchScopedButMissingDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return WatchScopedButMissingDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? watchScopedButMissingDependencies; + return _$cb(ref); + } +} + +String _$watchScopedButMissingDependenciesHash() => + r'c890e4845b1fca73ee02442eb7a203734605173c'; + +@ProviderFor(watchGeneratedScopedButMissingDependencies) +const watchGeneratedScopedButMissingDependenciesProvider = + WatchGeneratedScopedButMissingDependenciesProvider._(); + +final class WatchGeneratedScopedButMissingDependenciesProvider + extends $FunctionalProvider with $Provider { + const WatchGeneratedScopedButMissingDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'watchGeneratedScopedButMissingDependenciesProvider', + isAutoDispose: true, + dependencies: const [depProvider], + allTransitiveDependencies: const [ + WatchGeneratedScopedButMissingDependenciesProvider + .$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = depProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => + _$watchGeneratedScopedButMissingDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + WatchGeneratedScopedButMissingDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return WatchGeneratedScopedButMissingDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? watchGeneratedScopedButMissingDependencies; + return _$cb(ref); + } +} + +String _$watchGeneratedScopedButMissingDependenciesHash() => + r'fbbb5f1ea3725a7554dc05073f47a6b9ce5d913d'; + +@ProviderFor(watchRootButMissingDependencies) +const watchRootButMissingDependenciesProvider = + WatchRootButMissingDependenciesProvider._(); + +final class WatchRootButMissingDependenciesProvider + extends $FunctionalProvider with $Provider { + const WatchRootButMissingDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'watchRootButMissingDependenciesProvider', + isAutoDispose: true, + dependencies: const [depProvider], + allTransitiveDependencies: const [ + WatchRootButMissingDependenciesProvider.$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = depProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$watchRootButMissingDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + WatchRootButMissingDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return WatchRootButMissingDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? watchRootButMissingDependencies; + return _$cb(ref); + } +} + +String _$watchRootButMissingDependenciesHash() => + r'cc9c5e6c3a1c34e291a63c429fb031e0cc701499'; + +@ProviderFor(watchGeneratedRootButMissingDependencies) +const watchGeneratedRootButMissingDependenciesProvider = + WatchGeneratedRootButMissingDependenciesProvider._(); + +final class WatchGeneratedRootButMissingDependenciesProvider + extends $FunctionalProvider with $Provider { + const WatchGeneratedRootButMissingDependenciesProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'watchGeneratedRootButMissingDependenciesProvider', + isAutoDispose: true, + dependencies: const [depProvider], + allTransitiveDependencies: const [ + WatchGeneratedRootButMissingDependenciesProvider + .$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = depProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => + _$watchGeneratedRootButMissingDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + WatchGeneratedRootButMissingDependenciesProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return WatchGeneratedRootButMissingDependenciesProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? watchGeneratedRootButMissingDependencies; + return _$cb(ref); + } +} + +String _$watchGeneratedRootButMissingDependenciesHash() => + r'10d01aea2b6b0772e98172f410bdbfce85786243'; + +@ProviderFor(watchGeneratedScopedAndContainsDependency) +const watchGeneratedScopedAndContainsDependencyProvider = + WatchGeneratedScopedAndContainsDependencyProvider._(); + +final class WatchGeneratedScopedAndContainsDependencyProvider + extends $FunctionalProvider with $Provider { + const WatchGeneratedScopedAndContainsDependencyProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'watchGeneratedScopedAndContainsDependencyProvider', + isAutoDispose: true, + dependencies: const [generatedScopedProvider], + allTransitiveDependencies: const [ + WatchGeneratedScopedAndContainsDependencyProvider + .$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = generatedScopedProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => + _$watchGeneratedScopedAndContainsDependencyHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + WatchGeneratedScopedAndContainsDependencyProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return WatchGeneratedScopedAndContainsDependencyProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? watchGeneratedScopedAndContainsDependency; + return _$cb(ref); + } +} + +String _$watchGeneratedScopedAndContainsDependencyHash() => + r'948e75e097500b33ee2fdbd3dc9fdecafa5f3d10'; + +@ProviderFor(watchGeneratedRootAndContainsDependency) +const watchGeneratedRootAndContainsDependencyProvider = + WatchGeneratedRootAndContainsDependencyProvider._(); + +final class WatchGeneratedRootAndContainsDependencyProvider + extends $FunctionalProvider with $Provider { + const WatchGeneratedRootAndContainsDependencyProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'watchGeneratedRootAndContainsDependencyProvider', + isAutoDispose: true, + dependencies: const [generatedRootProvider], + allTransitiveDependencies: const [ + WatchGeneratedRootAndContainsDependencyProvider + .$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = generatedRootProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => + _$watchGeneratedRootAndContainsDependencyHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + WatchGeneratedRootAndContainsDependencyProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return WatchGeneratedRootAndContainsDependencyProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? watchGeneratedRootAndContainsDependency; + return _$cb(ref); + } +} + +String _$watchGeneratedRootAndContainsDependencyHash() => + r'780392b647f1606186ee0f70c81dd5b03f506284'; + +@ProviderFor(specifiedDependencyButNeverUsed) +const specifiedDependencyButNeverUsedProvider = + SpecifiedDependencyButNeverUsedProvider._(); + +final class SpecifiedDependencyButNeverUsedProvider + extends $FunctionalProvider with $Provider { + const SpecifiedDependencyButNeverUsedProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'specifiedDependencyButNeverUsedProvider', + isAutoDispose: true, + dependencies: const [ + depProvider, + generatedRootProvider + ], + allTransitiveDependencies: const [ + SpecifiedDependencyButNeverUsedProvider.$allTransitiveDependencies0, + SpecifiedDependencyButNeverUsedProvider.$allTransitiveDependencies1, + ], + ); + + static const $allTransitiveDependencies0 = depProvider; + static const $allTransitiveDependencies1 = generatedRootProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$specifiedDependencyButNeverUsedHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + SpecifiedDependencyButNeverUsedProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return SpecifiedDependencyButNeverUsedProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? specifiedDependencyButNeverUsed; + return _$cb(ref); + } +} + +String _$specifiedDependencyButNeverUsedHash() => + r'cca97d259bcacbff290f0d459e0de3a9b5b6a510'; + +@ProviderFor(ClassWatchGeneratedRootButMissingDependencies) +const classWatchGeneratedRootButMissingDependenciesProvider = + ClassWatchGeneratedRootButMissingDependenciesProvider._(); + +final class ClassWatchGeneratedRootButMissingDependenciesProvider + extends $NotifierProvider { + const ClassWatchGeneratedRootButMissingDependenciesProvider._( + {super.runNotifierBuildOverride, + ClassWatchGeneratedRootButMissingDependencies Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'classWatchGeneratedRootButMissingDependenciesProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final ClassWatchGeneratedRootButMissingDependencies Function()? _createCb; + + @override + String debugGetCreateSourceHash() => + _$classWatchGeneratedRootButMissingDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + ClassWatchGeneratedRootButMissingDependencies create() => + _createCb?.call() ?? ClassWatchGeneratedRootButMissingDependencies(); + + @$internal + @override + ClassWatchGeneratedRootButMissingDependenciesProvider $copyWithCreate( + ClassWatchGeneratedRootButMissingDependencies Function() create, + ) { + return ClassWatchGeneratedRootButMissingDependenciesProvider._( + create: create); + } + + @$internal + @override + ClassWatchGeneratedRootButMissingDependenciesProvider $copyWithBuild( + int Function( + Ref, + ClassWatchGeneratedRootButMissingDependencies, + ) build, + ) { + return ClassWatchGeneratedRootButMissingDependenciesProvider._( + runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement + $createElement($ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$classWatchGeneratedRootButMissingDependenciesHash() => + r'e36d7126a86ea9ded6dc66a6f33eabb2724455a9'; + +abstract class _$ClassWatchGeneratedRootButMissingDependencies + extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + +@ProviderFor(regression2348) +const regression2348Provider = Regression2348Provider._(); + +final class Regression2348Provider extends $FunctionalProvider + with $Provider { + const Regression2348Provider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'regression2348Provider', + isAutoDispose: true, + dependencies: const [generatedScopedProvider], + allTransitiveDependencies: const [ + Regression2348Provider.$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = generatedScopedProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$regression2348Hash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + Regression2348Provider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return Regression2348Provider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? regression2348; + return _$cb(ref); + } +} + +String _$regression2348Hash() => r'6ad005595ee202c8b0188562ed8c4a33d01260e2'; + +@ProviderFor(Regression2417) +const regression2417Provider = Regression2417Provider._(); + +final class Regression2417Provider + extends $NotifierProvider { + const Regression2417Provider._( + {super.runNotifierBuildOverride, Regression2417 Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'regression2417Provider', + isAutoDispose: true, + dependencies: const [generatedScopedProvider], + allTransitiveDependencies: const [ + Regression2417Provider.$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = generatedScopedProvider; + + final Regression2417 Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$regression2417Hash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Regression2417 create() => _createCb?.call() ?? Regression2417(); + + @$internal + @override + Regression2417Provider $copyWithCreate( + Regression2417 Function() create, + ) { + return Regression2417Provider._(create: create); + } + + @$internal + @override + Regression2417Provider $copyWithBuild( + int Function( + Ref, + Regression2417, + ) build, + ) { + return Regression2417Provider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$regression2417Hash() => r'c9ac0ba44e849ea1460c79c1f676feba1b5400da'; + +abstract class _$Regression2417 extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + +@ProviderFor(familyDep) +const familyDepProvider = FamilyDepFamily._(); + +final class FamilyDepProvider extends $FunctionalProvider + with $Provider { + const FamilyDepProvider._( + {required FamilyDepFamily super.from, + required int super.argument, + int Function( + Ref ref, + int p, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'familyDepProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + static const $allTransitiveDependencies0 = depProvider; + + final int Function( + Ref ref, + int p, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$familyDepHash(); + + @override + String toString() { + return r'familyDepProvider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + FamilyDepProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return FamilyDepProvider._( + argument: argument as int, + from: from! as FamilyDepFamily, + create: ( + ref, + int p, + ) => + create(ref)); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? familyDep; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is FamilyDepProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$familyDepHash() => r'ed674a44492b3871b72b4fbc68180ea0839723e5'; + +final class FamilyDepFamily extends Family { + const FamilyDepFamily._() + : super( + retry: null, + name: r'familyDepProvider', + dependencies: const [depProvider], + allTransitiveDependencies: const [ + FamilyDepProvider.$allTransitiveDependencies0, + ], + isAutoDispose: true, + ); + + FamilyDepProvider call( + int p, + ) => + FamilyDepProvider._(argument: p, from: this); + + @override + String debugGetCreateSourceHash() => _$familyDepHash(); + + @override + String toString() => r'familyDepProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + int Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyDepProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +@ProviderFor(familyDep2) +const familyDep2Provider = FamilyDep2Family._(); + +final class FamilyDep2Provider extends $FunctionalProvider + with $Provider { + const FamilyDep2Provider._( + {required FamilyDep2Family super.from, + required int super.argument, + int Function( + Ref ref, + int p, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'familyDep2Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + static const $allTransitiveDependencies0 = familyDepProvider; + static const $allTransitiveDependencies1 = + FamilyDepProvider.$allTransitiveDependencies0; + + final int Function( + Ref ref, + int p, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$familyDep2Hash(); + + @override + String toString() { + return r'familyDep2Provider' + '' + '($argument)'; + } + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + FamilyDep2Provider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return FamilyDep2Provider._( + argument: argument as int, + from: from! as FamilyDep2Family, + create: ( + ref, + int p, + ) => + create(ref)); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? familyDep2; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); + } + + @override + bool operator ==(Object other) { + return other is FamilyDep2Provider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$familyDep2Hash() => r'ee9c96f7a1d65e1b66c29aa8d8c030146995504c'; + +final class FamilyDep2Family extends Family { + const FamilyDep2Family._() + : super( + retry: null, + name: r'familyDep2Provider', + dependencies: const [familyDepProvider], + allTransitiveDependencies: const [ + FamilyDep2Provider.$allTransitiveDependencies0, + FamilyDep2Provider.$allTransitiveDependencies1, + ], + isAutoDispose: true, + ); + + FamilyDep2Provider call( + int p, + ) => + FamilyDep2Provider._(argument: p, from: this); + + @override + String debugGetCreateSourceHash() => _$familyDep2Hash(); + + @override + String toString() => r'familyDep2Provider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + int Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FamilyDep2Provider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } +} + +@ProviderFor(alias) +const aliasProvider = AliasProvider._(); + +final class AliasProvider extends $FunctionalProvider + with $Provider { + const AliasProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'aliasProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$aliasHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + AliasProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return AliasProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? alias; + return _$cb(ref); + } +} + +String _$aliasHash() => r'b410585ad56c66160898a05647e09e1a606aa9d2'; + +@ProviderFor(AliasClass) +const aliasClassProvider = AliasClassProvider._(); + +final class AliasClassProvider extends $NotifierProvider { + const AliasClassProvider._( + {super.runNotifierBuildOverride, AliasClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'aliasClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final AliasClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$aliasClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + AliasClass create() => _createCb?.call() ?? AliasClass(); + + @$internal + @override + AliasClassProvider $copyWithCreate( + AliasClass Function() create, + ) { + return AliasClassProvider._(create: create); + } + + @$internal + @override + AliasClassProvider $copyWithBuild( + int Function( + Ref, + AliasClass, + ) build, + ) { + return AliasClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$aliasClassHash() => r'f5c1f43e7541638274ca7dc334a713763c9c8071'; + +abstract class _$AliasClass extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + +@ProviderFor(RiverpodDependencies) +const riverpodDependenciesProvider = RiverpodDependenciesProvider._(); + +final class RiverpodDependenciesProvider + extends $NotifierProvider { + const RiverpodDependenciesProvider._( + {super.runNotifierBuildOverride, RiverpodDependencies Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'riverpodDependenciesProvider', + isAutoDispose: true, + dependencies: const [depProvider], + allTransitiveDependencies: const [ + RiverpodDependenciesProvider.$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = depProvider; + + final RiverpodDependencies Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$riverpodDependenciesHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + RiverpodDependencies create() => _createCb?.call() ?? RiverpodDependencies(); + + @$internal + @override + RiverpodDependenciesProvider $copyWithCreate( + RiverpodDependencies Function() create, + ) { + return RiverpodDependenciesProvider._(create: create); + } + + @$internal + @override + RiverpodDependenciesProvider $copyWithBuild( + int Function( + Ref, + RiverpodDependencies, + ) build, + ) { + return RiverpodDependenciesProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$riverpodDependenciesHash() => + r'a0a94e21f6d98df529e4e8a469ed3aec5af37061'; + +abstract class _$RiverpodDependencies extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + +@ProviderFor(foo) +const fooProvider = FooProvider._(); + +final class FooProvider extends $FunctionalProvider + with $Provider { + const FooProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'fooProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$fooHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + FooProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return FooProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? foo; + return _$cb(ref); + } +} + +String _$fooHash() => r'a390b7b969bb0eec183426bfc85bec32750e9475'; + +@ProviderFor(crossFileDependency) +const crossFileDependencyProvider = CrossFileDependencyProvider._(); + +final class CrossFileDependencyProvider extends $FunctionalProvider + with $Provider { + const CrossFileDependencyProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'crossFileDependencyProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$crossFileDependencyHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CrossFileDependencyProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return CrossFileDependencyProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? crossFileDependency; + return _$cb(ref); + } +} + +String _$crossFileDependencyHash() => + r'3ab740fe1903f2c126412df43ee34eed87a6f4fe'; + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/provider_dependencies.dart b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/provider_dependencies.dart deleted file mode 100644 index 5996683e9..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/provider_dependencies.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; - -part 'provider_dependencies.g.dart'; - -@Riverpod(dependencies: []) -int dep(Ref ref) => 0; - -@Riverpod(dependencies: []) -int dep2(Ref ref) => 0; - -@Riverpod(dependencies: []) -int dep3(Ref ref, int parameter) => 0; - -//////////// - -// expect_lint: provider_dependencies -@riverpod -int plainAnnotation(Ref ref) { - ref.watch(depProvider); - return 0; -} - -// expect_lint: provider_dependencies -@Riverpod(keepAlive: false) -int customAnnotation(Ref ref) { - ref.watch(depProvider); - return 0; -} - -// expect_lint: provider_dependencies -@Riverpod( - keepAlive: false, -) -int customAnnotationWithTrailingComma( - Ref ref, -) { - ref.watch(depProvider); - return 0; -} - -@Riverpod( - keepAlive: false, - // expect_lint: provider_dependencies - dependencies: [], -) -int existingDep(Ref ref) { - ref.watch(depProvider); - return 0; -} - -@Riverpod( - keepAlive: false, - // expect_lint: provider_dependencies - dependencies: [], -) -int multipleDeps(Ref ref) { - ref.watch(depProvider); - ref.watch(dep2Provider); - return 0; -} - -@Riverpod( - keepAlive: false, - dependencies: [ - // expect_lint: provider_dependencies - dep, - dep2, - ], -) -int extraDep(Ref ref) { - ref.watch(dep2Provider); - return 0; -} - -@Riverpod( - keepAlive: false, - // expect_lint: provider_dependencies - dependencies: [ - dep3, - ], -) -int onFamilyDep(Ref ref) { - ref.read(dep3Provider(ref.read(dep2Provider))); - return 0; -} - -@Riverpod( - keepAlive: false, - dependencies: [ - // expect_lint: provider_dependencies - dep, - ], -) -int noDep(Ref ref) { - return 0; -} - -@Riverpod( - dependencies: [ - // expect_lint: provider_dependencies - dep, - ], - keepAlive: false, -) -int dependenciesFirstThenKeepAlive(Ref ref) { - return 0; -} - -@Riverpod( - dependencies: [ - // expect_lint: provider_dependencies - dep, - ], -) -int noDepNoParam(Ref ref) { - return 0; -} - -// expect_lint: provider_dependencies -@Riverpod(keepAlive: false, dependencies: [dep]) -int noDepWithoutComma(Ref ref) { - return 0; -} diff --git a/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/provider_dependencies.diff b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/provider_dependencies.diff deleted file mode 100644 index 253a68dff..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/provider_dependencies.diff +++ /dev/null @@ -1,151 +0,0 @@ -Message: `Specify "dependencies"` -Priority: 100 -Diff for file `test/lints/provider_dependencies/provider_dependencies.dart:18`: -``` - -// expect_lint: provider_dependencies -- @riverpod -+ @Riverpod(dependencies: [dep]) -int plainAnnotation(Ref ref) { - ref.watch(depProvider); -``` ---- -Message: `Specify "dependencies"` -Priority: 100 -Diff for file `test/lints/provider_dependencies/provider_dependencies.dart:25`: -``` - -// expect_lint: provider_dependencies -- @Riverpod(keepAlive: false) -+ @Riverpod(keepAlive: false, dependencies: [dep]) -int customAnnotation(Ref ref) { - ref.watch(depProvider); -``` ---- -Message: `Specify "dependencies"` -Priority: 100 -Diff for file `test/lints/provider_dependencies/provider_dependencies.dart:33`: -``` -// expect_lint: provider_dependencies -@Riverpod( -- keepAlive: false, -+ keepAlive: false, dependencies: [dep], -) -int customAnnotationWithTrailingComma( -``` ---- -Message: `Update "dependencies"` -Priority: 100 -Diff for file `test/lints/provider_dependencies/provider_dependencies.dart:45`: -``` - keepAlive: false, - // expect_lint: provider_dependencies -- dependencies: [], -+ dependencies: [dep], -) -int existingDep(Ref ref) { -``` ---- -Message: `Update "dependencies"` -Priority: 100 -Diff for file `test/lints/provider_dependencies/provider_dependencies.dart:55`: -``` - keepAlive: false, - // expect_lint: provider_dependencies -- dependencies: [], -+ dependencies: [dep, dep2], -) -int multipleDeps(Ref ref) { -``` ---- -Message: `Update "dependencies"` -Priority: 100 -Diff for file `test/lints/provider_dependencies/provider_dependencies.dart:65`: -``` -@Riverpod( - keepAlive: false, -- dependencies: [ -- // expect_lint: provider_dependencies -- dep, -- dep2, -- ], -+ dependencies: [dep2], -) -int extraDep(Ref ref) { -``` ---- -Message: `Update "dependencies"` -Priority: 100 -Diff for file `test/lints/provider_dependencies/provider_dependencies.dart:79`: -``` - keepAlive: false, - // expect_lint: provider_dependencies -- dependencies: [ -- dep3, -- ], -+ dependencies: [dep3, dep2], -) -int onFamilyDep(Ref ref) { -``` ---- -Message: `Remove "dependencies"` -Priority: 100 -Diff for file `test/lints/provider_dependencies/provider_dependencies.dart:90`: -``` -@Riverpod( - keepAlive: false, -- dependencies: [ -- // expect_lint: provider_dependencies -- dep, -- ], -- ) -+ ) -int noDep(Ref ref) { - return 0; -``` ---- -Message: `Remove "dependencies"` -Priority: 100 -Diff for file `test/lints/provider_dependencies/provider_dependencies.dart:100`: -``` - -@Riverpod( -- dependencies: [ -- // expect_lint: provider_dependencies -- dep, -- ], -- keepAlive: false, -+ keepAlive: false, -) -int dependenciesFirstThenKeepAlive(Ref ref) { -``` ---- -Message: `Remove "dependencies"` -Priority: 100 -Diff for file `test/lints/provider_dependencies/provider_dependencies.dart:110`: -``` -} - -- @Riverpod( -- dependencies: [ -- // expect_lint: provider_dependencies -- dep, -- ], -- ) -+ @riverpod -int noDepNoParam(Ref ref) { - return 0; -``` ---- -Message: `Remove "dependencies"` -Priority: 100 -Diff for file `test/lints/provider_dependencies/provider_dependencies.dart:121`: -``` - -// expect_lint: provider_dependencies -- @Riverpod(keepAlive: false, dependencies: [dep]) -+ @Riverpod(keepAlive: false,) -int noDepWithoutComma(Ref ref) { - return 0; -``` ---- diff --git a/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/provider_dependencies.g.dart b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/provider_dependencies.g.dart deleted file mode 100644 index 1a75a8aec..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/provider_dependencies.g.dart +++ /dev/null @@ -1,398 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'provider_dependencies.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$depHash() => r'578a350a40cda46444ecd9fa3ea2fd7bd0994692'; - -/// See also [dep]. -@ProviderFor(dep) -final depProvider = AutoDisposeProvider.internal( - dep, - name: r'depProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$depHash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef DepRef = AutoDisposeProviderRef; -String _$dep2Hash() => r'97901e825cdcf5b1ac455b0fe8a2111662ce9f13'; - -/// See also [dep2]. -@ProviderFor(dep2) -final dep2Provider = AutoDisposeProvider.internal( - dep2, - name: r'dep2Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$dep2Hash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef Dep2Ref = AutoDisposeProviderRef; -String _$dep3Hash() => r'bb6a5b0a89055bc25a5edfd22f8674cf8b1ce771'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// See also [dep3]. -@ProviderFor(dep3) -const dep3Provider = Dep3Family(); - -/// See also [dep3]. -class Dep3Family extends Family { - /// See also [dep3]. - const Dep3Family(); - - /// See also [dep3]. - Dep3Provider call( - int parameter, - ) { - return Dep3Provider( - parameter, - ); - } - - @override - Dep3Provider getProviderOverride( - covariant Dep3Provider provider, - ) { - return call( - provider.parameter, - ); - } - - static final Iterable _dependencies = - const []; - - @override - Iterable? get dependencies => _dependencies; - - static final Iterable _allTransitiveDependencies = - const {}; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; - - @override - String? get name => r'dep3Provider'; -} - -/// See also [dep3]. -class Dep3Provider extends AutoDisposeProvider { - /// See also [dep3]. - Dep3Provider( - int parameter, - ) : this._internal( - (ref) => dep3( - ref as Dep3Ref, - parameter, - ), - from: dep3Provider, - name: r'dep3Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$dep3Hash, - dependencies: Dep3Family._dependencies, - allTransitiveDependencies: Dep3Family._allTransitiveDependencies, - parameter: parameter, - ); - - Dep3Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.parameter, - }) : super.internal(); - - final int parameter; - - @override - Override overrideWith( - int Function(Dep3Ref provider) create, - ) { - return ProviderOverride( - origin: this, - override: Dep3Provider._internal( - (ref) => create(ref as Dep3Ref), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - parameter: parameter, - ), - ); - } - - @override - AutoDisposeProviderElement createElement() { - return _Dep3ProviderElement(this); - } - - @override - bool operator ==(Object other) { - return other is Dep3Provider && other.parameter == parameter; - } - - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, parameter.hashCode); - - return _SystemHash.finish(hash); - } -} - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin Dep3Ref on AutoDisposeProviderRef { - /// The parameter `parameter` of this provider. - int get parameter; -} - -class _Dep3ProviderElement extends AutoDisposeProviderElement - with Dep3Ref { - _Dep3ProviderElement(super.provider); - - @override - int get parameter => (origin as Dep3Provider).parameter; -} - -String _$plainAnnotationHash() => - r'6a3d1f1f2e53902af56cd7ce6ceba17358690b70'; //////////// -/// -/// Copied from [plainAnnotation]. -@ProviderFor(plainAnnotation) -final plainAnnotationProvider = AutoDisposeProvider.internal( - plainAnnotation, - name: r'plainAnnotationProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$plainAnnotationHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef PlainAnnotationRef = AutoDisposeProviderRef; -String _$customAnnotationHash() => r'8081bbad2cfbe5bff1ace9aa3be450dd28112488'; - -/// See also [customAnnotation]. -@ProviderFor(customAnnotation) -final customAnnotationProvider = AutoDisposeProvider.internal( - customAnnotation, - name: r'customAnnotationProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$customAnnotationHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CustomAnnotationRef = AutoDisposeProviderRef; -String _$customAnnotationWithTrailingCommaHash() => - r'709613050eb1db7b4c43cb87855e2c32988141d8'; - -/// See also [customAnnotationWithTrailingComma]. -@ProviderFor(customAnnotationWithTrailingComma) -final customAnnotationWithTrailingCommaProvider = - AutoDisposeProvider.internal( - customAnnotationWithTrailingComma, - name: r'customAnnotationWithTrailingCommaProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$customAnnotationWithTrailingCommaHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CustomAnnotationWithTrailingCommaRef = AutoDisposeProviderRef; -String _$existingDepHash() => r'73e7e1a0d4c2ae07ed03fb248408c3d82fe85554'; - -/// See also [existingDep]. -@ProviderFor(existingDep) -final existingDepProvider = AutoDisposeProvider.internal( - existingDep, - name: r'existingDepProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$existingDepHash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExistingDepRef = AutoDisposeProviderRef; -String _$multipleDepsHash() => r'66de70567c011a294a2c46703dfab8ba7247fd5e'; - -/// See also [multipleDeps]. -@ProviderFor(multipleDeps) -final multipleDepsProvider = AutoDisposeProvider.internal( - multipleDeps, - name: r'multipleDepsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$multipleDepsHash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef MultipleDepsRef = AutoDisposeProviderRef; -String _$extraDepHash() => r'586c1a0f0ac120f8608c025a6a47fe5282b80320'; - -/// See also [extraDep]. -@ProviderFor(extraDep) -final extraDepProvider = AutoDisposeProvider.internal( - extraDep, - name: r'extraDepProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$extraDepHash, - dependencies: [depProvider, dep2Provider], - allTransitiveDependencies: { - depProvider, - ...?depProvider.allTransitiveDependencies, - dep2Provider, - ...?dep2Provider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExtraDepRef = AutoDisposeProviderRef; -String _$onFamilyDepHash() => r'ad04da6286b3475658b531d9a7dd8a47f11c56f2'; - -/// See also [onFamilyDep]. -@ProviderFor(onFamilyDep) -final onFamilyDepProvider = AutoDisposeProvider.internal( - onFamilyDep, - name: r'onFamilyDepProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$onFamilyDepHash, - dependencies: [dep3Provider], - allTransitiveDependencies: { - dep3Provider, - ...?dep3Provider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef OnFamilyDepRef = AutoDisposeProviderRef; -String _$noDepHash() => r'99022366e7dd3e19464747d1e2f23184691aa134'; - -/// See also [noDep]. -@ProviderFor(noDep) -final noDepProvider = AutoDisposeProvider.internal( - noDep, - name: r'noDepProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$noDepHash, - dependencies: [depProvider], - allTransitiveDependencies: { - depProvider, - ...?depProvider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef NoDepRef = AutoDisposeProviderRef; -String _$dependenciesFirstThenKeepAliveHash() => - r'b9bd9082ce9a72feea33f9327b26e7b428cadfd3'; - -/// See also [dependenciesFirstThenKeepAlive]. -@ProviderFor(dependenciesFirstThenKeepAlive) -final dependenciesFirstThenKeepAliveProvider = - AutoDisposeProvider.internal( - dependenciesFirstThenKeepAlive, - name: r'dependenciesFirstThenKeepAliveProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$dependenciesFirstThenKeepAliveHash, - dependencies: [depProvider], - allTransitiveDependencies: { - depProvider, - ...?depProvider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef DependenciesFirstThenKeepAliveRef = AutoDisposeProviderRef; -String _$noDepNoParamHash() => r'ea3e66e28bbfb716adf89cea37a1607c78283e06'; - -/// See also [noDepNoParam]. -@ProviderFor(noDepNoParam) -final noDepNoParamProvider = AutoDisposeProvider.internal( - noDepNoParam, - name: r'noDepNoParamProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$noDepNoParamHash, - dependencies: [depProvider], - allTransitiveDependencies: { - depProvider, - ...?depProvider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef NoDepNoParamRef = AutoDisposeProviderRef; -String _$noDepWithoutCommaHash() => r'a3b07e526b4829ee4ed1848de4ff64c3b05c1a30'; - -/// See also [noDepWithoutComma]. -@ProviderFor(noDepWithoutComma) -final noDepWithoutCommaProvider = AutoDisposeProvider.internal( - noDepWithoutComma, - name: r'noDepWithoutCommaProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$noDepWithoutCommaHash, - dependencies: [depProvider], - allTransitiveDependencies: { - depProvider, - ...?depProvider.allTransitiveDependencies - }, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef NoDepWithoutCommaRef = AutoDisposeProviderRef; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/provider_dependencies_test.dart b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/provider_dependencies_test.dart deleted file mode 100644 index 158b90406..000000000 --- a/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/provider_dependencies_test.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:riverpod_lint/src/lints/provider_dependencies.dart'; -import 'package:test/test.dart'; - -import '../../golden.dart'; - -void main() { - testGolden( - 'Verify that @riverpod classes extend the generated typedef', - 'lints/provider_dependencies/provider_dependencies.diff', - sourcePath: 'test/lints/provider_dependencies/provider_dependencies.dart', - (result, helper) async { - const lint = ProviderDependencies(); - final fix = lint.getFixes().single; - - final errors = await lint.testRun(result); - expect(errors, hasLength(11)); - - final changes = await Future.wait([ - for (final error in errors) fix.testRun(result, error, errors), - ]); - - return changes.flattened; - }, - ); -} diff --git a/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/unused_dependency.dart b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/unused_dependency.dart new file mode 100644 index 000000000..1ba634f07 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/unused_dependency.dart @@ -0,0 +1,143 @@ +// ignore_for_file: unknown_scoped_usage +import 'package:flutter/widgets.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'unused_dependency.g.dart'; + +@riverpod +int root(Ref ref) => 0; + +@Riverpod(dependencies: []) +int dep(Ref ref) => 0; + +@Riverpod(dependencies: []) +int dep2(Ref ref) => 0; + +//////////// + +@Riverpod( + keepAlive: false, + // expect_lint: provider_dependencies + dependencies: [ + dep, + dep2, + ], +) +int extraDep(Ref ref) { + ref.watch(dep2Provider); + return 0; +} + +@Riverpod( + keepAlive: false, + // expect_lint: provider_dependencies + dependencies: [ + dep, + ], +) +int noDep(Ref ref) { + return 0; +} + +@Riverpod( + // expect_lint: provider_dependencies + dependencies: [ + dep, + ], + keepAlive: false, +) +int dependenciesFirstThenKeepAlive(Ref ref) { + return 0; +} + +@Riverpod( + // expect_lint: provider_dependencies + dependencies: [ + dep, + ], +) +int noDepNoParam(Ref ref) { + return 0; +} + +// expect_lint: provider_dependencies +@Riverpod(keepAlive: false, dependencies: [dep]) +int noDepWithoutComma(Ref ref) { + return 0; +} + +@Riverpod( + keepAlive: false, + // expect_lint: provider_dependencies + dependencies: [ + root, + ], +) +int rootDep(Ref ref) => 0; + +// expect_lint: provider_dependencies +@Dependencies([dep]) +class StateNotFound extends ConsumerStatefulWidget { + @override + // Can't track down state due to not typing it as StateNotFoundState + ConsumerState createState() { + // Throwing to avoid "dep" counting as used indirectly. + throw UnimplementedError(); + } +} + +// Hijack generic too to prevent finding the state from the State. +class StateNotFoundState extends ConsumerState { + @override + Widget build(BuildContext context) { + ref.watch(depProvider); + return const Placeholder(); + } +} + +// Count the state too for determining if a dependency is unused +@Dependencies([dep]) +class IndirectlyUsed extends ConsumerStatefulWidget { + IndirectlyUsed({super.key, this.child}); + final Widget? child; + + @override + IndirectlyUsedState createState() => IndirectlyUsedState(); +} + +class IndirectlyUsedState extends ConsumerState { + @override + Widget build(BuildContext context) { + ref.watch(depProvider); + return const Placeholder(); + } +} + +// expect_lint: provider_dependencies +@Dependencies([dep]) +void fn() {} + +@Dependencies([dep]) +class Identifiers extends StatelessWidget { + @override + Widget build(BuildContext context) { + fn(); + return const Placeholder(); + } +} + +// expect_lint: provider_dependencies +@Dependencies([dep2, dep]) +void secondUnused() { + dep2Provider; +} + +// expect_lint: provider_dependencies +@Dependencies([ + dep2, + dep, +]) +void secondUnusedWithTrailingComma() { + dep2Provider; +} diff --git a/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/unused_dependency.g.dart b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/unused_dependency.g.dart new file mode 100644 index 000000000..381eebcf0 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/provider_dependencies/unused_dependency.g.dart @@ -0,0 +1,563 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'unused_dependency.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +@ProviderFor(root) +const rootProvider = RootProvider._(); + +final class RootProvider extends $FunctionalProvider + with $Provider { + const RootProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'rootProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$rootHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + RootProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return RootProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? root; + return _$cb(ref); + } +} + +String _$rootHash() => r'dda8bbb46cb4d7c658597669e3be92e2447dcfb0'; + +@ProviderFor(dep) +const depProvider = DepProvider._(); + +final class DepProvider extends $FunctionalProvider + with $Provider { + const DepProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'depProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$depHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + DepProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return DepProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? dep; + return _$cb(ref); + } +} + +String _$depHash() => r'578a350a40cda46444ecd9fa3ea2fd7bd0994692'; + +@ProviderFor(dep2) +const dep2Provider = Dep2Provider._(); + +final class Dep2Provider extends $FunctionalProvider + with $Provider { + const Dep2Provider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'dep2Provider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$dep2Hash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + Dep2Provider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return Dep2Provider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? dep2; + return _$cb(ref); + } +} + +String _$dep2Hash() => r'97901e825cdcf5b1ac455b0fe8a2111662ce9f13'; + +//////////// +@ProviderFor(extraDep) +const extraDepProvider = ExtraDepProvider._(); + +//////////// +final class ExtraDepProvider extends $FunctionalProvider + with $Provider { + //////////// + const ExtraDepProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'extraDepProvider', + isAutoDispose: true, + dependencies: const [depProvider, dep2Provider], + allTransitiveDependencies: const [ + ExtraDepProvider.$allTransitiveDependencies0, + ExtraDepProvider.$allTransitiveDependencies1, + ], + ); + + static const $allTransitiveDependencies0 = depProvider; + static const $allTransitiveDependencies1 = dep2Provider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$extraDepHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ExtraDepProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return ExtraDepProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? extraDep; + return _$cb(ref); + } +} + +String _$extraDepHash() => r'586c1a0f0ac120f8608c025a6a47fe5282b80320'; + +@ProviderFor(noDep) +const noDepProvider = NoDepProvider._(); + +final class NoDepProvider extends $FunctionalProvider + with $Provider { + const NoDepProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'noDepProvider', + isAutoDispose: true, + dependencies: const [depProvider], + allTransitiveDependencies: const [ + NoDepProvider.$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = depProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$noDepHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + NoDepProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return NoDepProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? noDep; + return _$cb(ref); + } +} + +String _$noDepHash() => r'99022366e7dd3e19464747d1e2f23184691aa134'; + +@ProviderFor(dependenciesFirstThenKeepAlive) +const dependenciesFirstThenKeepAliveProvider = + DependenciesFirstThenKeepAliveProvider._(); + +final class DependenciesFirstThenKeepAliveProvider + extends $FunctionalProvider with $Provider { + const DependenciesFirstThenKeepAliveProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'dependenciesFirstThenKeepAliveProvider', + isAutoDispose: true, + dependencies: const [depProvider], + allTransitiveDependencies: const [ + DependenciesFirstThenKeepAliveProvider.$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = depProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$dependenciesFirstThenKeepAliveHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + DependenciesFirstThenKeepAliveProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return DependenciesFirstThenKeepAliveProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? dependenciesFirstThenKeepAlive; + return _$cb(ref); + } +} + +String _$dependenciesFirstThenKeepAliveHash() => + r'b9bd9082ce9a72feea33f9327b26e7b428cadfd3'; + +@ProviderFor(noDepNoParam) +const noDepNoParamProvider = NoDepNoParamProvider._(); + +final class NoDepNoParamProvider extends $FunctionalProvider + with $Provider { + const NoDepNoParamProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'noDepNoParamProvider', + isAutoDispose: true, + dependencies: const [depProvider], + allTransitiveDependencies: const [ + NoDepNoParamProvider.$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = depProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$noDepNoParamHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + NoDepNoParamProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return NoDepNoParamProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? noDepNoParam; + return _$cb(ref); + } +} + +String _$noDepNoParamHash() => r'ea3e66e28bbfb716adf89cea37a1607c78283e06'; + +@ProviderFor(noDepWithoutComma) +const noDepWithoutCommaProvider = NoDepWithoutCommaProvider._(); + +final class NoDepWithoutCommaProvider extends $FunctionalProvider + with $Provider { + const NoDepWithoutCommaProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'noDepWithoutCommaProvider', + isAutoDispose: true, + dependencies: const [depProvider], + allTransitiveDependencies: const [ + NoDepWithoutCommaProvider.$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = depProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$noDepWithoutCommaHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + NoDepWithoutCommaProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return NoDepWithoutCommaProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? noDepWithoutComma; + return _$cb(ref); + } +} + +String _$noDepWithoutCommaHash() => r'a3b07e526b4829ee4ed1848de4ff64c3b05c1a30'; + +@ProviderFor(rootDep) +const rootDepProvider = RootDepProvider._(); + +final class RootDepProvider extends $FunctionalProvider + with $Provider { + const RootDepProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'rootDepProvider', + isAutoDispose: true, + dependencies: const [rootProvider], + allTransitiveDependencies: const [ + RootDepProvider.$allTransitiveDependencies0, + ], + ); + + static const $allTransitiveDependencies0 = rootProvider; + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$rootDepHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + RootDepProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return RootDepProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? rootDep; + return _$cb(ref); + } +} + +String _$rootDepHash() => r'c406dc7e58c18bc46ed722a81208bc13fe62654a'; + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/provider_parameters.dart b/packages/riverpod_lint_flutter_test/test/lints/provider_parameters.dart index 598356ad7..5ad69f049 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/provider_parameters.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/provider_parameters.dart @@ -138,6 +138,8 @@ class MyWidget extends ConsumerWidget { ref.watch(legacy(ClassThatOverridesEqual())); ref.watch(legacy(const ClassThatOverridesEqual())); + ref.watch(legacy(IndirectEqual())); + ref.watch(legacy(const IndirectEqual())); // expect_lint: provider_parameters ref.watch(legacy(Bar())); ref.watch(legacy(const Bar())); @@ -164,6 +166,8 @@ class MyWidget extends ConsumerWidget { ref.watch(generatorProvider(value: ClassThatOverridesEqual())); ref.watch(generatorProvider(value: const ClassThatOverridesEqual())); + ref.watch(generatorProvider(value: IndirectEqual())); + ref.watch(generatorProvider(value: const IndirectEqual())); // expect_lint: provider_parameters ref.watch(generatorProvider(value: Bar())); @@ -178,3 +182,24 @@ class MyWidget extends ConsumerWidget { return const Placeholder(); } } + +// Regression test for https://github.com/rrousselGit/riverpod/issues/3302 +mixin Equatable { + List get props; + @override + bool operator ==(Object other) { + return identical(this, other) || + other is Equatable && + runtimeType == other.runtimeType && + props == other.props; + } + + @override + int get hashCode => runtimeType.hashCode ^ Object.hashAll(props); +} + +class IndirectEqual with Equatable { + const IndirectEqual(); + @override + List get props => const []; +} diff --git a/packages/riverpod_lint_flutter_test/test/lints/provider_parameters.g.dart b/packages/riverpod_lint_flutter_test/test/lints/provider_parameters.g.dart index f6ce2eb2e..96dacfe00 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/provider_parameters.g.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/provider_parameters.g.dart @@ -6,153 +6,135 @@ part of 'provider_parameters.dart'; // RiverpodGenerator // ************************************************************************** -String _$generatorHash() => r'd7d1733f8884b6702f363ddb178ae57797d0034f'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// See also [generator]. @ProviderFor(generator) -const generatorProvider = GeneratorFamily(); - -/// See also [generator]. -class GeneratorFamily extends Family { - /// See also [generator]. - const GeneratorFamily(); +const generatorProvider = GeneratorFamily._(); + +final class GeneratorProvider extends $FunctionalProvider + with $Provider { + const GeneratorProvider._( + {required GeneratorFamily super.from, + required Object? super.argument, + int Function( + Ref ref, { + Object? value, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'generatorProvider', + isAutoDispose: false, + dependencies: null, + allTransitiveDependencies: null, + ); - /// See also [generator]. - GeneratorProvider call({ + final int Function( + Ref ref, { Object? value, - }) { - return GeneratorProvider( - value: value, - ); - } + })? _createCb; @override - GeneratorProvider getProviderOverride( - covariant GeneratorProvider provider, - ) { - return call( - value: provider.value, - ); - } - - static const Iterable? _dependencies = null; + String debugGetCreateSourceHash() => _$generatorHash(); @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; + String toString() { + return r'generatorProvider' + '' + '($argument)'; + } - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + @$internal @override - String? get name => r'generatorProvider'; -} - -/// See also [generator]. -class GeneratorProvider extends Provider { - /// See also [generator]. - GeneratorProvider({ - Object? value, - }) : this._internal( - (ref) => generator( - ref as GeneratorRef, - value: value, - ), - from: generatorProvider, - name: r'generatorProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$generatorHash, - dependencies: GeneratorFamily._dependencies, - allTransitiveDependencies: GeneratorFamily._allTransitiveDependencies, - value: value, - ); - - GeneratorProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.value, - }) : super.internal(); - - final Object? value; + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - Override overrideWith( - int Function(GeneratorRef provider) create, + GeneratorProvider $copyWithCreate( + int Function( + Ref ref, + ) create, ) { - return ProviderOverride( - origin: this, - override: GeneratorProvider._internal( - (ref) => create(ref as GeneratorRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - value: value, - ), - ); + return GeneratorProvider._( + argument: argument, + from: from! as GeneratorFamily, + create: ( + ref, { + Object? value, + }) => + create(ref)); } @override - ProviderElement createElement() { - return _GeneratorProviderElement(this); + int create(Ref ref) { + final _$cb = _createCb ?? generator; + final argument = this.argument; + return _$cb( + ref, + value: argument, + ); } @override bool operator ==(Object other) { - return other is GeneratorProvider && other.value == value; + return other is GeneratorProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, value.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin GeneratorRef on ProviderRef { - /// The parameter `value` of this provider. - Object? get value; -} +String _$generatorHash() => r'd7d1733f8884b6702f363ddb178ae57797d0034f'; + +final class GeneratorFamily extends Family { + const GeneratorFamily._() + : super( + retry: null, + name: r'generatorProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: false, + ); -class _GeneratorProviderElement extends ProviderElement with GeneratorRef { - _GeneratorProviderElement(super.provider); + GeneratorProvider call({ + Object? value, + }) => + GeneratorProvider._(argument: value, from: this); @override - Object? get value => (origin as GeneratorProvider).value; + String debugGetCreateSourceHash() => _$generatorHash(); + + @override + String toString() => r'generatorProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + int Function( + Ref ref, + Object? args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as GeneratorProvider; + + final argument = provider.argument; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/notifier_build/fix/notifier_build.dart b/packages/riverpod_lint_flutter_test/test/lints/riverpod_syntax_error.dart similarity index 51% rename from packages/riverpod_lint_flutter_test/test/lints/notifier_build/fix/notifier_build.dart rename to packages/riverpod_lint_flutter_test/test/lints/riverpod_syntax_error.dart index 27f5e0959..96a2861dc 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/notifier_build/fix/notifier_build.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/riverpod_syntax_error.dart @@ -4,5 +4,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; typedef _$ExampleProvider1 = Object; @riverpod -// expect_lint: notifier_build -class ExampleProvider1 extends _$ExampleProvider1 {} +// expect_lint: riverpod_syntax_error +abstract class ExampleProvider1 extends _$ExampleProvider1 { + int build() => 0; +} diff --git a/packages/riverpod_lint_flutter_test/test/lints/scoped_providers_should_specify_dependencies.dart b/packages/riverpod_lint_flutter_test/test/lints/scoped_providers_should_specify_dependencies.dart index 9003ddd77..a7a06d4eb 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/scoped_providers_should_specify_dependencies.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/scoped_providers_should_specify_dependencies.dart @@ -8,12 +8,15 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'scoped_providers_should_specify_dependencies.g.dart'; +@riverpod +class UnimplementedScoped extends _$UnimplementedScoped { + @override + int build(); +} + @Riverpod(dependencies: []) int scoped(Ref ref) => 0; -@riverpod -external int unimplementedScoped(); - @riverpod int root(Ref ref) => 0; @@ -24,7 +27,8 @@ void main() { final rootContainer = ProviderContainer( overrides: [ scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), + unimplementedScopedProvider + .overrideWith(() => throw UnimplementedError()), rootProvider.overrideWith((ref) => 0), ], ); @@ -33,7 +37,8 @@ void main() { parent: rootContainer, overrides: [ scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), + unimplementedScopedProvider + .overrideWith(() => throw UnimplementedError()), // expect_lint: scoped_providers_should_specify_dependencies rootProvider.overrideWith((ref) => 0), ], @@ -43,7 +48,8 @@ void main() { ProviderScope( overrides: [ scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), + unimplementedScopedProvider + .overrideWith(() => throw UnimplementedError()), rootProvider.overrideWith((ref) => 0), ], child: Container(), @@ -54,7 +60,8 @@ void main() { ProviderScope( overrides: [ scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), + unimplementedScopedProvider + .overrideWith(() => throw UnimplementedError()), // This is not a Flutter's runApp, so the ProviderScope is considered scoped // expect_lint: scoped_providers_should_specify_dependencies rootProvider.overrideWith((ref) => 0), @@ -62,20 +69,6 @@ void main() { child: Container(), ), ); - - flutter.runApp( - ProviderScope( - // ignore: deprecated_member_use - parent: rootContainer, - overrides: [ - scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), - // expect_lint: scoped_providers_should_specify_dependencies - rootProvider.overrideWith((ref) => 0), - ], - child: Container(), - ), - ); } // Regression tests for https://github.com/rrousselGit/riverpod/issues/2340 @@ -83,7 +76,8 @@ void definitelyNotAMain() { final rootContainer = ProviderContainer( overrides: [ scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), + unimplementedScopedProvider + .overrideWith(() => throw UnimplementedError()), rootProvider.overrideWith((ref) => 0), ], ); @@ -92,7 +86,8 @@ void definitelyNotAMain() { parent: rootContainer, overrides: [ scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), + unimplementedScopedProvider + .overrideWith(() => throw UnimplementedError()), // expect_lint: scoped_providers_should_specify_dependencies rootProvider.overrideWith((ref) => 0), ], @@ -102,21 +97,8 @@ void definitelyNotAMain() { ProviderScope( overrides: [ scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), - rootProvider.overrideWith((ref) => 0), - ], - child: Container(), - ), - ); - - flutter.runApp( - ProviderScope( - // ignore: deprecated_member_use - parent: rootContainer, - overrides: [ - scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), - // expect_lint: scoped_providers_should_specify_dependencies + unimplementedScopedProvider + .overrideWith(() => throw UnimplementedError()), rootProvider.overrideWith((ref) => 0), ], child: Container(), @@ -128,7 +110,8 @@ void someTestFunction() { final rootContainer = ProviderContainer( overrides: [ scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), + unimplementedScopedProvider + .overrideWith(() => throw UnimplementedError()), rootProvider.overrideWith((ref) => 0), ], ); @@ -138,21 +121,8 @@ void someTestFunction() { ProviderScope( overrides: [ scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), - rootProvider.overrideWith((ref) => 0), - ], - child: Container(), - ), - ); - - await tester.pumpWidget( - ProviderScope( - // ignore: deprecated_member_use - parent: rootContainer, - overrides: [ - scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), - // expect_lint: scoped_providers_should_specify_dependencies + unimplementedScopedProvider + .overrideWith(() => throw UnimplementedError()), rootProvider.overrideWith((ref) => 0), ], child: Container(), @@ -164,7 +134,8 @@ void someTestFunction() { child: ProviderScope( overrides: [ scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), + unimplementedScopedProvider + .overrideWith(() => throw UnimplementedError()), // expect_lint: scoped_providers_should_specify_dependencies rootProvider.overrideWith((ref) => 0), ], @@ -179,7 +150,8 @@ Widget fn() { return ProviderScope( overrides: [ scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), + unimplementedScopedProvider + .overrideWith(() => throw UnimplementedError()), // expect_lint: scoped_providers_should_specify_dependencies rootProvider.overrideWith((ref) => 0), ], @@ -194,7 +166,8 @@ void showModal(BuildContext context) { return ProviderScope( overrides: [ scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), + unimplementedScopedProvider + .overrideWith(() => throw UnimplementedError()), // expect_lint: scoped_providers_should_specify_dependencies rootProvider.overrideWith((ref) => 0), ], @@ -212,7 +185,8 @@ class MyWidget extends StatelessWidget { return ProviderScope( overrides: [ scopedProvider.overrideWith((ref) => 0), - unimplementedScopedProvider.overrideWith((ref) => 0), + unimplementedScopedProvider + .overrideWith(() => throw UnimplementedError()), // expect_lint: scoped_providers_should_specify_dependencies rootProvider.overrideWith((ref) => 0), ], diff --git a/packages/riverpod_lint_flutter_test/test/lints/scoped_providers_should_specify_dependencies.g.dart b/packages/riverpod_lint_flutter_test/test/lints/scoped_providers_should_specify_dependencies.g.dart index 75b01d5eb..1051c1d99 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/scoped_providers_should_specify_dependencies.g.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/scoped_providers_should_specify_dependencies.g.dart @@ -6,58 +6,192 @@ part of 'scoped_providers_should_specify_dependencies.dart'; // RiverpodGenerator // ************************************************************************** -String _$scopedHash() => r'5a271e9b23e18517694454448b922a6c9d03781e'; +@ProviderFor(UnimplementedScoped) +const unimplementedScopedProvider = UnimplementedScopedProvider._(); + +final class UnimplementedScopedProvider + extends $NotifierProvider { + const UnimplementedScopedProvider._( + {super.runNotifierBuildOverride, UnimplementedScoped Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'unimplementedScopedProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final UnimplementedScoped Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$unimplementedScopedHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + UnimplementedScoped create() => _createCb?.call() ?? UnimplementedScoped(); + + @$internal + @override + UnimplementedScopedProvider $copyWithCreate( + UnimplementedScoped Function() create, + ) { + return UnimplementedScopedProvider._(create: create); + } + + @$internal + @override + UnimplementedScopedProvider $copyWithBuild( + int Function( + Ref, + UnimplementedScoped, + ) build, + ) { + return UnimplementedScopedProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} -/// See also [scoped]. -@ProviderFor(scoped) -final scopedProvider = AutoDisposeProvider.internal( - scoped, - name: r'scopedProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$scopedHash, - dependencies: const [], - allTransitiveDependencies: const {}, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ScopedRef = AutoDisposeProviderRef; String _$unimplementedScopedHash() => - r'5f32fc56f4157238612d62ef54038fe92b7cdfe8'; - -/// See also [unimplementedScoped]. -@ProviderFor(unimplementedScoped) -final unimplementedScopedProvider = AutoDisposeProvider.internal( - (_) => throw UnsupportedError( - 'The provider "unimplementedScopedProvider" is expected to get overridden/scoped, ' - 'but was accessed without an override.', - ), - name: r'unimplementedScopedProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$unimplementedScopedHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef UnimplementedScopedRef = AutoDisposeProviderRef; -String _$rootHash() => r'dda8bbb46cb4d7c658597669e3be92e2447dcfb0'; + r'0511a23bd69f21f42fa4f20a9078f6a200a073cb'; + +abstract class _$UnimplementedScoped extends $Notifier { + int build() => throw MissingScopeException(ref); + @$internal + @override + int runBuild() => build(); +} + +@ProviderFor(scoped) +const scopedProvider = ScopedProvider._(); + +final class ScopedProvider extends $FunctionalProvider + with $Provider { + const ScopedProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'scopedProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$scopedHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ScopedProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return ScopedProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? scoped; + return _$cb(ref); + } +} + +String _$scopedHash() => r'5a271e9b23e18517694454448b922a6c9d03781e'; -/// See also [root]. @ProviderFor(root) -final rootProvider = AutoDisposeProvider.internal( - root, - name: r'rootProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$rootHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef RootRef = AutoDisposeProviderRef; +const rootProvider = RootProvider._(); + +final class RootProvider extends $FunctionalProvider + with $Provider { + const RootProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'rootProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$rootHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + RootProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return RootProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? root; + return _$cb(ref); + } +} + +String _$rootHash() => r'dda8bbb46cb4d7c658597669e3be92e2447dcfb0'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/unknown_scoped_usage.dart b/packages/riverpod_lint_flutter_test/test/lints/unknown_scoped_usage.dart new file mode 100644 index 000000000..ac0546121 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/unknown_scoped_usage.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'unknown_scoped_usage.g.dart'; + +@Riverpod(dependencies: []) +int scoped(Ref ref) => 0; + +@riverpod +int root(Ref ref) => 0; + +@Dependencies([scoped]) +void fn(WidgetRef widgetRef, Ref ref) { + // expect_lint: unknown_scoped_usage + scopedProvider; + rootProvider; + + // Known ref usage + widgetRef.watch(scopedProvider); + ref.watch(scopedProvider); + + // Unknown ref usage inside a ref expression + // expect_lint: unknown_scoped_usage + widgetRef.watch(identity(scopedProvider)); + // expect_lint: unknown_scoped_usage + ref.watch(identity(scopedProvider)); + // expect_lint: unknown_scoped_usage + ref.watch(identityMap[scopedProvider]); + + // Overrides are OK + scopedProvider.overrideWith((ref) => 0); + + // If passed as widget constructor parameter, it's OK + // expect_lint: unknown_scoped_usage + RandomObject(scopedProvider); + MyWidget(scopedProvider); +} + +class RandomObject { + RandomObject(this.provider); + final ProviderListenable provider; +} + +final identityMap = IdentityMap>(); + +class IdentityMap { + T operator [](T key) => key; +} + +T identity(T value) => value; + +class MyWidget extends ConsumerWidget { + const MyWidget(this.provider); + + final ProviderListenable provider; + + @override + Widget build(BuildContext context, WidgetRef ref) { + ref.watch(provider); + return Container(); + } +} diff --git a/packages/riverpod_lint_flutter_test/test/lints/unknown_scoped_usage.g.dart b/packages/riverpod_lint_flutter_test/test/lints/unknown_scoped_usage.g.dart new file mode 100644 index 000000000..b9df28400 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/lints/unknown_scoped_usage.g.dart @@ -0,0 +1,126 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'unknown_scoped_usage.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +@ProviderFor(scoped) +const scopedProvider = ScopedProvider._(); + +final class ScopedProvider extends $FunctionalProvider + with $Provider { + const ScopedProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'scopedProvider', + isAutoDispose: true, + dependencies: const [], + allTransitiveDependencies: const [], + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$scopedHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ScopedProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return ScopedProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? scoped; + return _$cb(ref); + } +} + +String _$scopedHash() => r'5a271e9b23e18517694454448b922a6c9d03781e'; + +@ProviderFor(root) +const rootProvider = RootProvider._(); + +final class RootProvider extends $FunctionalProvider + with $Provider { + const RootProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'rootProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$rootHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + RootProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return RootProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? root; + return _$cb(ref); + } +} + +String _$rootHash() => r'dda8bbb46cb4d7c658597669e3be92e2447dcfb0'; + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/lints/unsupported_provider_value.dart b/packages/riverpod_lint_flutter_test/test/lints/unsupported_provider_value.dart index fa7a44743..4df784c6d 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/unsupported_provider_value.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/unsupported_provider_value.dart @@ -1,5 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:hooks_riverpod/legacy.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'unsupported_provider_value.g.dart'; @@ -88,7 +89,7 @@ class MyNotifier extends Notifier { int build() => 0; } -class MyAutoDisposeNotifier extends AutoDisposeNotifier { +class MyAutoDisposeNotifier extends Notifier { @override int build() => 0; } diff --git a/packages/riverpod_lint_flutter_test/test/lints/unsupported_provider_value.g.dart b/packages/riverpod_lint_flutter_test/test/lints/unsupported_provider_value.g.dart index adef6eb78..dd62d2b38 100644 --- a/packages/riverpod_lint_flutter_test/test/lints/unsupported_provider_value.g.dart +++ b/packages/riverpod_lint_flutter_test/test/lints/unsupported_provider_value.g.dart @@ -6,369 +6,1298 @@ part of 'unsupported_provider_value.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(integer) +const integerProvider = IntegerProvider._(); + +final class IntegerProvider extends $FunctionalProvider + with $Provider { + const IntegerProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'integerProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$integerHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + IntegerProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return IntegerProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? integer; + return _$cb(ref); + } +} + String _$integerHash() => r'8ad63bb35c89ffcf2ef281d7c39539760afff303'; -/// See also [integer]. -@ProviderFor(integer) -final integerProvider = AutoDisposeProvider.internal( - integer, - name: r'integerProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$integerHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef IntegerRef = AutoDisposeProviderRef; +@ProviderFor(stateNotifier) +const stateNotifierProvider = StateNotifierProvider._(); + +final class StateNotifierProvider + extends $FunctionalProvider + with $Provider { + const StateNotifierProvider._( + {MyStateNotifier Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'stateNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyStateNotifier Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$stateNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(MyStateNotifier value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + StateNotifierProvider $copyWithCreate( + MyStateNotifier Function( + Ref ref, + ) create, + ) { + return StateNotifierProvider._(create: create); + } + + @override + MyStateNotifier create(Ref ref) { + final _$cb = _createCb ?? stateNotifier; + return _$cb(ref); + } +} + String _$stateNotifierHash() => r'2505b564fd3a623976548c715b1623dea507f6d3'; -/// See also [stateNotifier]. -@ProviderFor(stateNotifier) -final stateNotifierProvider = AutoDisposeProvider.internal( - stateNotifier, - name: r'stateNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$stateNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef StateNotifierRef = AutoDisposeProviderRef; +@ProviderFor(asyncStateNotifier) +const asyncStateNotifierProvider = AsyncStateNotifierProvider._(); + +final class AsyncStateNotifierProvider extends $FunctionalProvider< + AsyncValue, FutureOr> + with $FutureModifier, $FutureProvider { + const AsyncStateNotifierProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'asyncStateNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$asyncStateNotifierHash(); + + @$internal + @override + $FutureProviderElement $createElement( + $ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + AsyncStateNotifierProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return AsyncStateNotifierProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? asyncStateNotifier; + return _$cb(ref); + } +} + String _$asyncStateNotifierHash() => r'5c5954eb030f5688abdf881e047c8893c864b1a2'; -/// See also [asyncStateNotifier]. -@ProviderFor(asyncStateNotifier) -final asyncStateNotifierProvider = - AutoDisposeFutureProvider.internal( - asyncStateNotifier, - name: r'asyncStateNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$asyncStateNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef AsyncStateNotifierRef = AutoDisposeFutureProviderRef; -String _$stateNotifierAsyncHash() => - r'ce67cf8c6f4bda46835042c17ea01186b5b399a5'; - -/// See also [stateNotifierAsync]. -@ProviderFor(stateNotifierAsync) -final stateNotifierAsyncProvider = - AutoDisposeFutureProvider.internal( - stateNotifierAsync, - name: r'stateNotifierAsyncProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$stateNotifierAsyncHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef StateNotifierAsyncRef = AutoDisposeFutureProviderRef; -String _$changeNotifierHash() => r'1686043b72e25b3143c5131906924f1393569400'; +@ProviderFor(StateNotifierClass) +const stateNotifierClassProvider = StateNotifierClassProvider._(); -/// See also [changeNotifier]. -@ProviderFor(changeNotifier) -final changeNotifierProvider = AutoDisposeProvider.internal( - changeNotifier, - name: r'changeNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$changeNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ChangeNotifierRef = AutoDisposeProviderRef; -String _$notifierHash() => r'5ad63d9ccd05ab78e7a6ba5c763cacf0b1decb7b'; +final class StateNotifierClassProvider + extends $NotifierProvider { + const StateNotifierClassProvider._( + {super.runNotifierBuildOverride, StateNotifierClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'stateNotifierClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [notifier]. -@ProviderFor(notifier) -final notifierProvider = AutoDisposeProvider.internal( - notifier, - name: r'notifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$notifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef NotifierRef = AutoDisposeProviderRef; -String _$autoDisposeNotifierHash() => - r'6aecd9dee1e2734c3acf8eab05145418d10656e1'; + final StateNotifierClass Function()? _createCb; -/// See also [autoDisposeNotifier]. -@ProviderFor(autoDisposeNotifier) -final autoDisposeNotifierProvider = - AutoDisposeProvider.internal( - autoDisposeNotifier, - name: r'autoDisposeNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$autoDisposeNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef AutoDisposeNotifierRef = AutoDisposeProviderRef; -String _$asyncNotifierHash() => r'8800a97f6bf80a56caf5d968d4b4ab91f7f0a64e'; + @override + String debugGetCreateSourceHash() => _$stateNotifierClassHash(); -/// See also [asyncNotifier]. -@ProviderFor(asyncNotifier) -final asyncNotifierProvider = AutoDisposeProvider.internal( - asyncNotifier, - name: r'asyncNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$asyncNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef AsyncNotifierRef = AutoDisposeProviderRef; -String _$rawNotifierHash() => r'c667d10419c9ce1fdd227e2afd1f3aaf63c3380b'; + /// {@macro riverpod.override_with_value} + Override overrideWithValue(MyStateNotifier value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } -/// See also [rawNotifier]. -@ProviderFor(rawNotifier) -final rawNotifierProvider = AutoDisposeProvider>.internal( - rawNotifier, - name: r'rawNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$rawNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef RawNotifierRef = AutoDisposeProviderRef>; -String _$rawFutureNotifierHash() => r'ff2744c369ebd96615f19451eae416d7afeef03f'; + @$internal + @override + StateNotifierClass create() => _createCb?.call() ?? StateNotifierClass(); -/// See also [rawFutureNotifier]. -@ProviderFor(rawFutureNotifier) -final rawFutureNotifierProvider = - AutoDisposeProvider>>.internal( - rawFutureNotifier, - name: r'rawFutureNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$rawFutureNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef RawFutureNotifierRef - = AutoDisposeProviderRef>>; -String _$rawStreamNotifierHash() => r'9a13efb8fbcef6c4388d5a2535b1b0aec6e46a9a'; + @$internal + @override + StateNotifierClassProvider $copyWithCreate( + StateNotifierClass Function() create, + ) { + return StateNotifierClassProvider._(create: create); + } -/// See also [rawStreamNotifier]. -@ProviderFor(rawStreamNotifier) -final rawStreamNotifierProvider = - AutoDisposeProvider>>.internal( - rawStreamNotifier, - name: r'rawStreamNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$rawStreamNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef RawStreamNotifierRef - = AutoDisposeProviderRef>>; -String _$futureRawNotifierHash() => r'87103845bce1f4cae4ad62ae3b7da6ca3539581f'; + @$internal + @override + StateNotifierClassProvider $copyWithBuild( + MyStateNotifier Function( + Ref, + StateNotifierClass, + ) build, + ) { + return StateNotifierClassProvider._(runNotifierBuildOverride: build); + } -/// See also [futureRawNotifier]. -@ProviderFor(futureRawNotifier) -final futureRawNotifierProvider = - AutoDisposeFutureProvider>.internal( - futureRawNotifier, - name: r'futureRawNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$futureRawNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef FutureRawNotifierRef - = AutoDisposeFutureProviderRef>; -String _$streamRawNotifierHash() => r'1d4abe389b7dfe1381879d8ffb174f6d1d9325e0'; + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} -/// See also [streamRawNotifier]. -@ProviderFor(streamRawNotifier) -final streamRawNotifierProvider = - AutoDisposeStreamProvider>.internal( - streamRawNotifier, - name: r'streamRawNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$streamRawNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef StreamRawNotifierRef - = AutoDisposeStreamProviderRef>; String _$stateNotifierClassHash() => r'576978be5b8a02c212afe7afbe37c733a49ecbce'; -/// See also [StateNotifierClass]. -@ProviderFor(StateNotifierClass) -final stateNotifierClassProvider = - AutoDisposeNotifierProvider.internal( - StateNotifierClass.new, - name: r'stateNotifierClassProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$stateNotifierClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$StateNotifierClass = AutoDisposeNotifier; -String _$selfNotifierHash() => r'5a857f5c92a9b7a35daa4e527bd333cf3d8d19ac'; +abstract class _$StateNotifierClass extends $Notifier { + MyStateNotifier build(); + @$internal + @override + MyStateNotifier runBuild() => build(); +} + +@ProviderFor(stateNotifierAsync) +const stateNotifierAsyncProvider = StateNotifierAsyncProvider._(); + +final class StateNotifierAsyncProvider extends $FunctionalProvider< + AsyncValue, FutureOr> + with $FutureModifier, $FutureProvider { + const StateNotifierAsyncProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'stateNotifierAsyncProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$stateNotifierAsyncHash(); + + @$internal + @override + $FutureProviderElement $createElement( + $ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + StateNotifierAsyncProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return StateNotifierAsyncProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? stateNotifierAsync; + return _$cb(ref); + } +} + +String _$stateNotifierAsyncHash() => + r'ce67cf8c6f4bda46835042c17ea01186b5b399a5'; -/// See also [SelfNotifier]. @ProviderFor(SelfNotifier) -final selfNotifierProvider = - AutoDisposeAsyncNotifierProvider.internal( - SelfNotifier.new, - name: r'selfNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$selfNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$SelfNotifier = AutoDisposeAsyncNotifier; -String _$syncSelfNotifierHash() => r'4f3a2463cb5693a5c8d7e772b4d7c9774b9ba637'; +const selfNotifierProvider = SelfNotifierProvider._(); + +final class SelfNotifierProvider + extends $AsyncNotifierProvider { + const SelfNotifierProvider._( + {super.runNotifierBuildOverride, SelfNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'selfNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final SelfNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$selfNotifierHash(); + + @$internal + @override + SelfNotifier create() => _createCb?.call() ?? SelfNotifier(); + + @$internal + @override + SelfNotifierProvider $copyWithCreate( + SelfNotifier Function() create, + ) { + return SelfNotifierProvider._(create: create); + } + + @$internal + @override + SelfNotifierProvider $copyWithBuild( + FutureOr Function( + Ref, + SelfNotifier, + ) build, + ) { + return SelfNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $AsyncNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); +} + +String _$selfNotifierHash() => r'5a857f5c92a9b7a35daa4e527bd333cf3d8d19ac'; + +abstract class _$SelfNotifier extends $AsyncNotifier { + FutureOr build(); + @$internal + @override + FutureOr runBuild() => build(); +} -/// See also [SyncSelfNotifier]. @ProviderFor(SyncSelfNotifier) -final syncSelfNotifierProvider = - AutoDisposeNotifierProvider.internal( - SyncSelfNotifier.new, - name: r'syncSelfNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$syncSelfNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$SyncSelfNotifier = AutoDisposeNotifier; +const syncSelfNotifierProvider = SyncSelfNotifierProvider._(); + +final class SyncSelfNotifierProvider + extends $NotifierProvider { + const SyncSelfNotifierProvider._( + {super.runNotifierBuildOverride, SyncSelfNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'syncSelfNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final SyncSelfNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$syncSelfNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(SyncSelfNotifier value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + SyncSelfNotifier create() => _createCb?.call() ?? SyncSelfNotifier(); + + @$internal + @override + SyncSelfNotifierProvider $copyWithCreate( + SyncSelfNotifier Function() create, + ) { + return SyncSelfNotifierProvider._(create: create); + } + + @$internal + @override + SyncSelfNotifierProvider $copyWithBuild( + SyncSelfNotifier Function( + Ref, + SyncSelfNotifier, + ) build, + ) { + return SyncSelfNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$syncSelfNotifierHash() => r'4f3a2463cb5693a5c8d7e772b4d7c9774b9ba637'; + +abstract class _$SyncSelfNotifier extends $Notifier { + SyncSelfNotifier build(); + @$internal + @override + SyncSelfNotifier runBuild() => build(); +} + +@ProviderFor(StreamSelfNotifier) +const streamSelfNotifierProvider = StreamSelfNotifierProvider._(); + +final class StreamSelfNotifierProvider + extends $StreamNotifierProvider { + const StreamSelfNotifierProvider._( + {super.runNotifierBuildOverride, StreamSelfNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'streamSelfNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final StreamSelfNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$streamSelfNotifierHash(); + + @$internal + @override + StreamSelfNotifier create() => _createCb?.call() ?? StreamSelfNotifier(); + + @$internal + @override + StreamSelfNotifierProvider $copyWithCreate( + StreamSelfNotifier Function() create, + ) { + return StreamSelfNotifierProvider._(create: create); + } + + @$internal + @override + StreamSelfNotifierProvider $copyWithBuild( + Stream Function( + Ref, + StreamSelfNotifier, + ) build, + ) { + return StreamSelfNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $StreamNotifierProviderElement + $createElement($ProviderPointer pointer) => + $StreamNotifierProviderElement(this, pointer); +} + String _$streamSelfNotifierHash() => r'18705475d157d8e592205406c0b884b7213d329e'; -/// See also [StreamSelfNotifier]. -@ProviderFor(StreamSelfNotifier) -final streamSelfNotifierProvider = AutoDisposeStreamNotifierProvider< - StreamSelfNotifier, StreamSelfNotifier>.internal( - StreamSelfNotifier.new, - name: r'streamSelfNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$streamSelfNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$StreamSelfNotifier = AutoDisposeStreamNotifier; +abstract class _$StreamSelfNotifier + extends $StreamNotifier { + Stream build(); + @$internal + @override + Stream runBuild() => build(); +} + +@ProviderFor(StateNotifierClassAsync) +const stateNotifierClassAsyncProvider = StateNotifierClassAsyncProvider._(); + +final class StateNotifierClassAsyncProvider + extends $AsyncNotifierProvider { + const StateNotifierClassAsyncProvider._( + {super.runNotifierBuildOverride, + StateNotifierClassAsync Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'stateNotifierClassAsyncProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final StateNotifierClassAsync Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$stateNotifierClassAsyncHash(); + + @$internal + @override + StateNotifierClassAsync create() => + _createCb?.call() ?? StateNotifierClassAsync(); + + @$internal + @override + StateNotifierClassAsyncProvider $copyWithCreate( + StateNotifierClassAsync Function() create, + ) { + return StateNotifierClassAsyncProvider._(create: create); + } + + @$internal + @override + StateNotifierClassAsyncProvider $copyWithBuild( + FutureOr Function( + Ref, + StateNotifierClassAsync, + ) build, + ) { + return StateNotifierClassAsyncProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $AsyncNotifierProviderElement + $createElement($ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); +} + String _$stateNotifierClassAsyncHash() => r'06c519ed7dbdcd9440365dd2dc3ec12e603b6b7e'; -/// See also [StateNotifierClassAsync]. -@ProviderFor(StateNotifierClassAsync) -final stateNotifierClassAsyncProvider = AutoDisposeAsyncNotifierProvider< - StateNotifierClassAsync, MyStateNotifier>.internal( - StateNotifierClassAsync.new, - name: r'stateNotifierClassAsyncProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$stateNotifierClassAsyncHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$StateNotifierClassAsync = AutoDisposeAsyncNotifier; +abstract class _$StateNotifierClassAsync + extends $AsyncNotifier { + FutureOr build(); + @$internal + @override + FutureOr runBuild() => build(); +} + +@ProviderFor(changeNotifier) +const changeNotifierProvider = ChangeNotifierProvider._(); + +final class ChangeNotifierProvider + extends $FunctionalProvider + with $Provider { + const ChangeNotifierProvider._( + {MyChangeNotifier Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'changeNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyChangeNotifier Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$changeNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(MyChangeNotifier value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ChangeNotifierProvider $copyWithCreate( + MyChangeNotifier Function( + Ref ref, + ) create, + ) { + return ChangeNotifierProvider._(create: create); + } + + @override + MyChangeNotifier create(Ref ref) { + final _$cb = _createCb ?? changeNotifier; + return _$cb(ref); + } +} + +String _$changeNotifierHash() => r'1686043b72e25b3143c5131906924f1393569400'; + +@ProviderFor(ChangeNotifierClass) +const changeNotifierClassProvider = ChangeNotifierClassProvider._(); + +final class ChangeNotifierClassProvider + extends $NotifierProvider { + const ChangeNotifierClassProvider._( + {super.runNotifierBuildOverride, ChangeNotifierClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'changeNotifierClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final ChangeNotifierClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$changeNotifierClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(MyChangeNotifier value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + ChangeNotifierClass create() => _createCb?.call() ?? ChangeNotifierClass(); + + @$internal + @override + ChangeNotifierClassProvider $copyWithCreate( + ChangeNotifierClass Function() create, + ) { + return ChangeNotifierClassProvider._(create: create); + } + + @$internal + @override + ChangeNotifierClassProvider $copyWithBuild( + MyChangeNotifier Function( + Ref, + ChangeNotifierClass, + ) build, + ) { + return ChangeNotifierClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement + $createElement($ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$changeNotifierClassHash() => r'c9716469ce2f8e7a1a6063587ae8733999e51a6e'; -/// See also [ChangeNotifierClass]. -@ProviderFor(ChangeNotifierClass) -final changeNotifierClassProvider = - AutoDisposeNotifierProvider.internal( - ChangeNotifierClass.new, - name: r'changeNotifierClassProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$changeNotifierClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$ChangeNotifierClass = AutoDisposeNotifier; -String _$notifierClassHash() => r'e7eefebec2fca4f982582449e7ec14322932b748'; +abstract class _$ChangeNotifierClass extends $Notifier { + MyChangeNotifier build(); + @$internal + @override + MyChangeNotifier runBuild() => build(); +} + +@ProviderFor(notifier) +const notifierProvider = NotifierProvider._(); + +final class NotifierProvider extends $FunctionalProvider + with $Provider { + const NotifierProvider._( + {MyNotifier Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'notifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyNotifier Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$notifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(MyNotifier value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + NotifierProvider $copyWithCreate( + MyNotifier Function( + Ref ref, + ) create, + ) { + return NotifierProvider._(create: create); + } + + @override + MyNotifier create(Ref ref) { + final _$cb = _createCb ?? notifier; + return _$cb(ref); + } +} + +String _$notifierHash() => r'5ad63d9ccd05ab78e7a6ba5c763cacf0b1decb7b'; + +@ProviderFor(autoDisposeNotifier) +const autoDisposeNotifierProvider = AutoDisposeNotifierProvider._(); + +final class AutoDisposeNotifierProvider + extends $FunctionalProvider + with $Provider { + const AutoDisposeNotifierProvider._( + {MyAutoDisposeNotifier Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'autoDisposeNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyAutoDisposeNotifier Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$autoDisposeNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(MyAutoDisposeNotifier value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement( + $ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + AutoDisposeNotifierProvider $copyWithCreate( + MyAutoDisposeNotifier Function( + Ref ref, + ) create, + ) { + return AutoDisposeNotifierProvider._(create: create); + } + + @override + MyAutoDisposeNotifier create(Ref ref) { + final _$cb = _createCb ?? autoDisposeNotifier; + return _$cb(ref); + } +} + +String _$autoDisposeNotifierHash() => + r'6aecd9dee1e2734c3acf8eab05145418d10656e1'; -/// See also [NotifierClass]. @ProviderFor(NotifierClass) -final notifierClassProvider = - AutoDisposeNotifierProvider.internal( - NotifierClass.new, - name: r'notifierClassProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$notifierClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$NotifierClass = AutoDisposeNotifier; +const notifierClassProvider = NotifierClassProvider._(); + +final class NotifierClassProvider + extends $NotifierProvider { + const NotifierClassProvider._( + {super.runNotifierBuildOverride, NotifierClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'notifierClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final NotifierClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$notifierClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(MyNotifier value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + NotifierClass create() => _createCb?.call() ?? NotifierClass(); + + @$internal + @override + NotifierClassProvider $copyWithCreate( + NotifierClass Function() create, + ) { + return NotifierClassProvider._(create: create); + } + + @$internal + @override + NotifierClassProvider $copyWithBuild( + MyNotifier Function( + Ref, + NotifierClass, + ) build, + ) { + return NotifierClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$notifierClassHash() => r'e7eefebec2fca4f982582449e7ec14322932b748'; + +abstract class _$NotifierClass extends $Notifier { + MyNotifier build(); + @$internal + @override + MyNotifier runBuild() => build(); +} + +@ProviderFor(asyncNotifier) +const asyncNotifierProvider = AsyncNotifierProvider._(); + +final class AsyncNotifierProvider + extends $FunctionalProvider + with $Provider { + const AsyncNotifierProvider._( + {MyAsyncNotifier Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'asyncNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyAsyncNotifier Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$asyncNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(MyAsyncNotifier value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + AsyncNotifierProvider $copyWithCreate( + MyAsyncNotifier Function( + Ref ref, + ) create, + ) { + return AsyncNotifierProvider._(create: create); + } + + @override + MyAsyncNotifier create(Ref ref) { + final _$cb = _createCb ?? asyncNotifier; + return _$cb(ref); + } +} + +String _$asyncNotifierHash() => r'8800a97f6bf80a56caf5d968d4b4ab91f7f0a64e'; + +@ProviderFor(AsyncNotifierClass) +const asyncNotifierClassProvider = AsyncNotifierClassProvider._(); + +final class AsyncNotifierClassProvider + extends $NotifierProvider { + const AsyncNotifierClassProvider._( + {super.runNotifierBuildOverride, AsyncNotifierClass Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'asyncNotifierClassProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final AsyncNotifierClass Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$asyncNotifierClassHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(MyAsyncNotifier value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + AsyncNotifierClass create() => _createCb?.call() ?? AsyncNotifierClass(); + + @$internal + @override + AsyncNotifierClassProvider $copyWithCreate( + AsyncNotifierClass Function() create, + ) { + return AsyncNotifierClassProvider._(create: create); + } + + @$internal + @override + AsyncNotifierClassProvider $copyWithBuild( + MyAsyncNotifier Function( + Ref, + AsyncNotifierClass, + ) build, + ) { + return AsyncNotifierClassProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$asyncNotifierClassHash() => r'815a238752d324b136166c409a39fd3f0db67267'; -/// See also [AsyncNotifierClass]. -@ProviderFor(AsyncNotifierClass) -final asyncNotifierClassProvider = - AutoDisposeNotifierProvider.internal( - AsyncNotifierClass.new, - name: r'asyncNotifierClassProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$asyncNotifierClassHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$AsyncNotifierClass = AutoDisposeNotifier; +abstract class _$AsyncNotifierClass extends $Notifier { + MyAsyncNotifier build(); + @$internal + @override + MyAsyncNotifier runBuild() => build(); +} + +@ProviderFor(rawNotifier) +const rawNotifierProvider = RawNotifierProvider._(); + +final class RawNotifierProvider + extends $FunctionalProvider, Raw> + with $Provider> { + const RawNotifierProvider._( + {Raw Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'rawNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Raw Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$rawNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + $ProviderElement> $createElement( + $ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + RawNotifierProvider $copyWithCreate( + Raw Function( + Ref ref, + ) create, + ) { + return RawNotifierProvider._(create: create); + } + + @override + Raw create(Ref ref) { + final _$cb = _createCb ?? rawNotifier; + return _$cb(ref); + } +} + +String _$rawNotifierHash() => r'c667d10419c9ce1fdd227e2afd1f3aaf63c3380b'; + +@ProviderFor(rawFutureNotifier) +const rawFutureNotifierProvider = RawFutureNotifierProvider._(); + +final class RawFutureNotifierProvider extends $FunctionalProvider< + Raw>, Raw>> + with $Provider>> { + const RawFutureNotifierProvider._( + {Raw> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'rawFutureNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Raw> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$rawFutureNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>>(value), + ); + } + + @$internal + @override + $ProviderElement>> $createElement( + $ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + RawFutureNotifierProvider $copyWithCreate( + Raw> Function( + Ref ref, + ) create, + ) { + return RawFutureNotifierProvider._(create: create); + } + + @override + Raw> create(Ref ref) { + final _$cb = _createCb ?? rawFutureNotifier; + return _$cb(ref); + } +} + +String _$rawFutureNotifierHash() => r'ff2744c369ebd96615f19451eae416d7afeef03f'; + +@ProviderFor(rawStreamNotifier) +const rawStreamNotifierProvider = RawStreamNotifierProvider._(); + +final class RawStreamNotifierProvider extends $FunctionalProvider< + Raw>, Raw>> + with $Provider>> { + const RawStreamNotifierProvider._( + {Raw> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'rawStreamNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Raw> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$rawStreamNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>>(value), + ); + } + + @$internal + @override + $ProviderElement>> $createElement( + $ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + RawStreamNotifierProvider $copyWithCreate( + Raw> Function( + Ref ref, + ) create, + ) { + return RawStreamNotifierProvider._(create: create); + } + + @override + Raw> create(Ref ref) { + final _$cb = _createCb ?? rawStreamNotifier; + return _$cb(ref); + } +} + +String _$rawStreamNotifierHash() => r'9a13efb8fbcef6c4388d5a2535b1b0aec6e46a9a'; + +@ProviderFor(futureRawNotifier) +const futureRawNotifierProvider = FutureRawNotifierProvider._(); + +final class FutureRawNotifierProvider extends $FunctionalProvider< + AsyncValue>, FutureOr>> + with + $FutureModifier>, + $FutureProvider> { + const FutureRawNotifierProvider._( + {FutureOr> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'futureRawNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$futureRawNotifierHash(); + + @$internal + @override + $FutureProviderElement> $createElement( + $ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + FutureRawNotifierProvider $copyWithCreate( + FutureOr> Function( + Ref ref, + ) create, + ) { + return FutureRawNotifierProvider._(create: create); + } + + @override + FutureOr> create(Ref ref) { + final _$cb = _createCb ?? futureRawNotifier; + return _$cb(ref); + } +} + +String _$futureRawNotifierHash() => r'87103845bce1f4cae4ad62ae3b7da6ca3539581f'; + +@ProviderFor(streamRawNotifier) +const streamRawNotifierProvider = StreamRawNotifierProvider._(); + +final class StreamRawNotifierProvider extends $FunctionalProvider< + AsyncValue>, Stream>> + with + $FutureModifier>, + $StreamProvider> { + const StreamRawNotifierProvider._( + {Stream> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'streamRawNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Stream> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$streamRawNotifierHash(); + + @$internal + @override + $StreamProviderElement> $createElement( + $ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + + @override + StreamRawNotifierProvider $copyWithCreate( + Stream> Function( + Ref ref, + ) create, + ) { + return StreamRawNotifierProvider._(create: create); + } + + @override + Stream> create(Ref ref) { + final _$cb = _createCb ?? streamRawNotifier; + return _$cb(ref); + } +} + +String _$streamRawNotifierHash() => r'1d4abe389b7dfe1381879d8ffb174f6d1d9325e0'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/packages/riverpod_lint_flutter_test/test/riverpod_test.dart b/packages/riverpod_lint_flutter_test/test/riverpod_test.dart new file mode 100644 index 000000000..415ad35d8 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/riverpod_test.dart @@ -0,0 +1,45 @@ +import 'dart:io'; + +import 'package:custom_lint_core/custom_lint_core.dart'; +import 'package:glob/glob.dart'; +import 'package:glob/list_local_fs.dart'; +import 'package:riverpod_lint/riverpod_lint.dart'; +import 'package:riverpod_lint/src/riverpod_custom_lint.dart'; +import 'package:test/test.dart'; + +import 'test_lint.dart'; + +void main() { + final plugin = createPlugin(); + + // ignore: invalid_use_of_internal_member + for (final lint in plugin.getLintRules(CustomLintConfigs.empty)) { + final code = lint.code; + lint as RiverpodLintRule; + + group(code.name, () { + final filesToTest = [ + File('test/lints/${code.name}.dart'), + ...Glob('test/lints/${code.name}/*.dart').listSync(), + ] + .whereType() + .where((e) => + !e.path.endsWith('_test.dart') && !e.path.endsWith('.g.dart')) + .where((e) => e.existsSync()) + .toList(); + + if (filesToTest.isEmpty) { + stderr.writeln('Missing test source for ${code.name}'); + } + + for (final file in filesToTest) { + testLint( + 'for ${file.path}', + file.path, + lint, + goldensDirectory: 'test/lints/goldens/${code.name}', + ); + } + }); + } +} diff --git a/packages/riverpod_lint_flutter_test/test/test_lint.dart b/packages/riverpod_lint_flutter_test/test/test_lint.dart new file mode 100644 index 000000000..8f89c0da9 --- /dev/null +++ b/packages/riverpod_lint_flutter_test/test/test_lint.dart @@ -0,0 +1,210 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:analyzer/source/source_range.dart'; +import 'package:analyzer_plugin/protocol/protocol_generated.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; +import 'package:test/test.dart'; +import 'package:path/path.dart'; +import 'package:riverpod_lint/src/riverpod_custom_lint.dart'; +import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/dart/analysis/utilities.dart'; +import 'package:collection/collection.dart'; + +import 'encoders.dart'; + +@isTest +void testLint( + String description, + String file, + RiverpodLintRule lint, { + required String goldensDirectory, +}) { + assert(file.endsWith('.dart')); + final fixesGoldenPath = join( + goldensDirectory, + '${basenameWithoutExtension(file)}_fix.diff', + ); + final lintGoldenPath = join( + goldensDirectory, + '${basenameWithoutExtension(file)}_lint.md', + ); + + test(description, () async { + final sourcePath = File(normalize(file)).absolute; + final result = await resolveFile2(path: sourcePath.path); + result as ResolvedUnitResult; + + final errors = await lint.testRun(result); + expect( + errors, + matchesAnalysisErrorGoldens(lintGoldenPath), + ); + + final fixes = await lint.getFixes(); + final changes = await Future.wait([ + for (final fix in fixes) + for (final error in errors) fix.testRun(result, error, errors), + ]); + + expect( + changes.flattened, + matchesPrioritizedSourceChangesGolden( + File(fixesGoldenPath), + source: result.content, + sourcePath: sourcePath.path, + ), + ); + }); +} + +@isTest +void testGolden( + String description, + String goldensFile, + Future> Function( + ResolvedUnitResult, + OffsetHelper helper, + ) body, { + required String sourcePath, +}) { + assert(sourcePath.endsWith('.dart')); + test(description, () async { + final absoluteSource = File(normalize(sourcePath)).absolute; + + final sourceFile = File(sourcePath).absolute; + + final result = await resolveFile2(path: absoluteSource.path); + result as ResolvedUnitResult; + + final source = sourceFile.readAsStringSync(); + final changes = await body(result, OffsetHelper._(source)).then( + (value) => value.toList(), + ); + + expect( + await changes, + matchesPrioritizedSourceChangesGolden( + File(goldensFile), + source: result.content, + sourcePath: sourcePath, + ), + ); + }); +} + +const _cursor = '<>'; + +class OffsetHelper { + OffsetHelper._(this._content); + + final String _content; + + /// Strings must be code of the format: + /// + /// ``dart + /// Some<>Code + /// ``` + /// + /// where `<>` is the location of the cursor. + /// + /// At least one `<>` must be present, or the function will throw. + Iterable rangesForString(String string) sync* { + final cursors = '<>'.allMatches(string).toList(); + if (cursors.isEmpty) { + throw ArgumentError('String does not contain any cursors: $string'); + } + + final stringWithoutCursors = string.replaceAll(_cursor, ''); + + final start = _content.indexOf(stringWithoutCursors); + if (start == -1) { + throw ArgumentError('String not found in content: $stringWithoutCursors'); + } + + if (_content.indexOf(stringWithoutCursors, start + 1) != -1) { + throw ArgumentError( + 'Found the string twice in the content: $stringWithoutCursors', + ); + } + + for (final (index, cursor) in cursors.indexed) { + // In the case of multiple cursors, we need to adjust the offset + // to account for the previous cursors. + final actualCursorStart = cursor.start - 2 * index; + yield SourceRange(start + actualCursorStart, 0); + } + } + + Future> runAssist( + RiverpodAssist assist, + ResolvedUnitResult result, + Iterable cursorRanges, { + Pubspec? pubspec, + }) async { + return Future.wait( + cursorRanges.map( + (range) => assist.testRun(result, range, pubspec: pubspec), + ), + ).then((value) => value.expand((e) => e)); + } + + void debugOffset(List offsets) { + offsets.sort(); + + var mappedContent = _content; + for (final offset in offsets.reversed) { + mappedContent = mappedContent.substring(0, offset) + + '<>' + + mappedContent.substring(offset); + } + + final lines = LineSplitter.split(mappedContent).toList(); + + final codes = []; + + StringBuffer? buffer; + + void openBuffer() { + buffer ??= StringBuffer("helper.rangesForString('''\n"); + } + + void closeBuffer() { + if (buffer == null) return; + + buffer!.write("''')"); + codes.add(buffer.toString()); + buffer = null; + } + + // Print all lines with <> in them and one line before and after. + for (final (index, line) in lines.indexed) { + if (buffer == null && line.trim().isEmpty) continue; + + final hasCursor = line.contains(_cursor); + late final hadCursor = index >= 1 && lines[index - 1].contains(_cursor); + late final willHaveCursor = + index + 1 < lines.length && lines[index + 1].contains(_cursor); + + if (hasCursor || hadCursor || willHaveCursor) { + openBuffer(); + buffer!.writeln(line); + } else { + closeBuffer(); + } + } + + if (buffer != null) closeBuffer(); + + if (codes.length == 1) { + print(' final cursors = ${codes.single};'); + } else { + print(' final cursors = ['); + for (final code in codes) { + print(' ...$code,'); + } + print(' ];'); + } + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 79f43313c..3a82b4933 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,6 +2,7 @@ name: melos_root publish_to: none environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0<4.0.0" + dev_dependencies: melos: ^3.0.0 diff --git a/tools/generate_providers/bin/generate_providers.dart b/tools/generate_providers/bin/generate_providers.dart index 11d0fa8c4..e374260d0 100644 --- a/tools/generate_providers/bin/generate_providers.dart +++ b/tools/generate_providers/bin/generate_providers.dart @@ -1,888 +1,415 @@ -// ignore_for_file: avoid_print import 'dart:io'; import 'package:dart_style/dart_style.dart'; -import 'package:trotter/trotter.dart'; -import 'package:tuple/tuple.dart'; -enum DisposeType { - none, - autoDispose, -} +import '../src/docs.dart'; + +const _header = ''' +// GENERATED CODE - DO NOT MODIFY BY HAND +// +// If you need to modify this file, instead update /tools/generate_providers/bin/generate_providers.dart +// +// You can install this utility by executing: +// dart pub global activate -s path /tools/generate_providers +// +// You can then use it in your terminal by executing: +// generate_providers + +'''; + +enum _DisposeType { keepAlive, autoDispose } + +enum _ProviderKind { orphan, family } -const disposeLabel = { - DisposeType.none: '', - DisposeType.autoDispose: 'AutoDispose', -}; - -class StateDetails { - StateDetails({ - required this.className, - required this.ref, - required this.constraints, - required this.generics, - required this.createType, +sealed class _Builder { + const _Builder( + this.providerName, { + required this.genericsDefinition, + required this.genericsUsage, }); - final String className; - final String ref; - final String constraints; - final String generics; - final String createType; + final String genericsDefinition; + final String genericsUsage; + final String providerName; } -enum ProviderType { - single, - family, -} +class _FunctionalBuilder extends _Builder { + _FunctionalBuilder( + super.providerName, { + required super.genericsDefinition, + required super.genericsUsage, + required this.createdT, + }); -const providerLabel = { - ProviderType.single: '', - ProviderType.family: 'Family', -}; - -const _autoDisposeDoc = ''' -/// {@template riverpod.autoDispose} -/// Marks the provider as automatically disposed when no longer listened to. -/// -/// Some typical use-cases: -/// -/// - Combined with [StreamProvider], this can be used as a mean to keep -/// the connection with Firebase alive only when truly needed (to reduce costs). -/// - Automatically reset a form state when leaving the screen. -/// - Automatically retry HTTP requests that failed when the user exit and -/// re-enter the screen. -/// - Cancel HTTP requests if the user leaves a screen before the request completed. -/// -/// Marking a provider with `autoDispose` also adds an extra method on `ref`: `keepAlive`. -/// -/// The `keepAlive` function is used to tell Riverpod that the state of the provider -/// should be preserved even if no longer listened to. -/// -/// A use-case would be to set this flag to `true` after an HTTP request have -/// completed: -/// -/// ```dart -/// final myProvider = FutureProvider.autoDispose((ref) async { -/// final response = await httpClient.get(...); -/// ref.keepAlive(); -/// return response; -/// }); -/// ``` -/// -/// This way, if the request failed and the UI leaves the screen then re-enters -/// it, then the request will be performed again. -/// But if the request completed successfully, the state will be preserved -/// and re-entering the screen will not trigger a new request. -/// -/// It can be combined with `ref.onDispose` for more advanced behaviors, such -/// as cancelling pending HTTP requests when the user leaves a screen. -/// For example, modifying our previous snippet and using `dio`, we would have: -/// -/// ```diff -/// final myProvider = FutureProvider.autoDispose((ref) async { -/// + final cancelToken = CancelToken(); -/// + ref.onDispose(() => cancelToken.cancel()); -/// -/// + final response = await dio.get('path', cancelToken: cancelToken); -/// - final response = await dio.get('path'); -/// ref.keepAlive(); -/// return response; -/// }); -/// ``` -/// {@endtemplate}'''; - -bool _didAddAutoDisposeTemplate = false; - -String autoDisposeDoc() { - if (_didAddAutoDisposeTemplate) { - return '/// {@macro riverpod.autoDispose}'; - } - _didAddAutoDisposeTemplate = true; - return _autoDisposeDoc; + final String createdT; } -const _familyDoc = r''' -/// {@template riverpod.family} -/// A group of providers that builds their value from an external parameter. -/// -/// Families can be useful to connect a provider with values that it doesn't -/// have access to. For example: -/// -/// - Allowing a "title provider" access the `Locale` -/// -/// ```dart -/// final titleFamily = Provider.family((ref, locale) { -/// if (locale == const Locale('en')) { -/// return 'English title'; -/// } else if (locale == const Locale('fr')) { -/// return 'Titre Français'; -/// } -/// }); -/// -/// // ... -/// -/// @override -/// Widget build(BuildContext context, WidgetRef ref) { -/// final locale = Localizations.localeOf(context); -/// -/// // Obtains the title based on the current Locale. -/// // Will automatically update the title when the Locale changes. -/// final title = ref.watch(titleFamily(locale)); -/// -/// return Text(title); -/// } -/// ``` -/// -/// - Have a "user provider" that receives the user ID as a parameter -/// -/// ```dart -/// final userFamily = FutureProvider.family((ref, userId) async { -/// final userRepository = ref.read(userRepositoryProvider); -/// return await userRepository.fetch(userId); -/// }); -/// -/// // ... -/// -/// @override -/// Widget build(BuildContext context, WidgetRef ref) { -/// int userId; // Read the user ID from somewhere -/// -/// // Read and potentially fetch the user with id `userId`. -/// // When `userId` changes, this will automatically update the UI -/// // Similarly, if two widgets tries to read `userFamily` with the same `userId` -/// // then the user will be fetched only once. -/// final user = ref.watch(userFamily(userId)); -/// -/// return user.when( -/// data: (user) => Text(user.name), -/// loading: () => const CircularProgressIndicator(), -/// error: (err, stack) => const Text('error'), -/// ); -/// } -/// ``` -/// -/// - Connect a provider with another provider without having a direct reference on it. -/// -/// ```dart -/// final repositoryProvider = Provider.family>((ref, configurationsProvider) { -/// // Read a provider without knowing what that provider is. -/// final configurations = await ref.read(configurationsProvider.future); -/// return Repository(host: configurations.host); -/// }); -/// ``` -/// -/// ## Usage -/// -/// The way families works is by adding an extra parameter to the provider. -/// This parameter can then be freely used in our provider to create some state. -/// -/// For example, we could combine `family` with [FutureProvider] to fetch -/// a `Message` from its ID: -/// -/// ```dart -/// final messagesFamily = FutureProvider.family((ref, id) async { -/// return dio.get('http://my_api.dev/messages/$id'); -/// }); -/// ``` -/// -/// Then, when using our `messagesFamily` provider, the syntax is slightly modified. -/// The usual: -/// -/// ```dart -/// Widget build(BuildContext context, WidgetRef ref) { -/// // Error – messagesFamily is not a provider -/// final response = ref.watch(messagesFamily); -/// } -/// ``` -/// -/// will not work anymore. -/// Instead, we need to pass a parameter to `messagesFamily`: -/// -/// ```dart -/// Widget build(BuildContext context, WidgetRef ref) { -/// final response = ref.watch(messagesFamily('id')); -/// } -/// ``` -/// -/// **NOTE**: It is totally possible to use a family with different parameters -/// simultaneously. For example, we could use a `titleFamily` to read both -/// the french and english translations at the same time: -/// -/// ```dart -/// @override -/// Widget build(BuildContext context, WidgetRef ref) { -/// final frenchTitle = ref.watch(titleFamily(const Locale('fr'))); -/// final englishTitle = ref.watch(titleFamily(const Locale('en'))); -/// -/// return Text('fr: $frenchTitle en: $englishTitle'); -/// } -/// ``` -/// -/// # Parameter restrictions -/// -/// For families to work correctly, it is critical for the parameter passed to -/// a provider to have a consistent `hashCode` and `==`. -/// -/// Ideally the parameter should either be a primitive (bool/int/double/String), -/// a constant (providers), or an immutable object that override `==` and `hashCode`. -/// -/// -/// - **PREFER** using `family` in combination with `autoDispose` if the -/// parameter passed to providers is a complex object: -/// -/// ```dart -/// final example = Provider.autoDispose.family((ref, param) { -/// }); -/// ``` -/// -/// This ensures that there is no memory leak if the parameter changed and is -/// never used again. -/// -/// # Passing multiple parameters to a family -/// -/// Families have no built-in support for passing multiple values to a provider. -/// -/// On the other hand, that value could be _anything_ (as long as it matches with -/// the restrictions mentioned previously). -/// -/// This includes: -/// - A tuple (using `package:tuple`) -/// - Objects generated with Freezed/built_value -/// - Objects based on `package:equatable` -/// -/// This includes: -/// - A tuple (using `package:tuple`) -/// - Objects generated with Freezed/built_value, such as: -/// ```dart -/// @freezed -/// abstract class MyParameter with _$MyParameter { -/// factory MyParameter({ -/// required int userId, -/// required Locale locale, -/// }) = _MyParameter; -/// } -/// -/// final exampleProvider = Provider.family((ref, myParameter) { -/// print(myParameter.userId); -/// print(myParameter.locale); -/// // Do something with userId/locale -/// }); -/// -/// @override -/// Widget build(BuildContext context, WidgetRef ref) { -/// int userId; // Read the user ID from somewhere -/// final locale = Localizations.localeOf(context); -/// -/// final something = ref.watch( -/// exampleProvider(MyParameter(userId: userId, locale: locale)), -/// ); -/// } -/// ``` -/// -/// - Objects based on `package:equatable`, such as: -/// ```dart -/// class MyParameter extends Equatable { -/// factory MyParameter({ -/// required this.userId, -/// requires this.locale, -/// }); -/// -/// final int userId; -/// final Local locale; -/// -/// @override -/// List get props => [userId, locale]; -/// } -/// -/// final exampleProvider = Provider.family((ref, myParameter) { -/// print(myParameter.userId); -/// print(myParameter.locale); -/// // Do something with userId/locale -/// }); -/// -/// @override -/// Widget build(BuildContext context, WidgetRef ref) { -/// int userId; // Read the user ID from somewhere -/// final locale = Localizations.localeOf(context); -/// -/// final something = ref.watch( -/// exampleProvider(MyParameter(userId: userId, locale: locale)), -/// ); -/// } -/// ``` -/// {@endtemplate}'''; - -bool _didAddFamilyTemplate = false; - -String familyDoc() { - if (_didAddFamilyTemplate) { - return '/// {@macro riverpod.family}'; - } - _didAddFamilyTemplate = true; - return _familyDoc; +class _NotifierBuilder extends _Builder { + _NotifierBuilder( + super.providerName, { + required this.isFamily, + required super.genericsDefinition, + required super.genericsUsage, + }); + + // final String? familyBuilderName; + final bool isFamily; } +typedef Matrix = ({ + List<_DisposeType> disposeTypes, + List<_Builder> providers, + List<_ProviderKind> kinds, +}); + Future main(List args) async { if (args.length != 2) { - print('usage: generate_providers file'); + stdout.writeln( + 'usage: generate_providers file', + ); return; } if (args.first != 'riverpod' && args.first != 'flutter_riverpod') { - print('Unknown argument ${args.first}'); + stderr.writeln('Unknown argument ${args.first}'); return; } final file = File.fromUri(Uri.parse(args[1])); if (file.existsSync() && file.statSync().type != FileSystemEntityType.file) { - print('${args[1]} is not a file'); + stderr.writeln('${args[1]} is not a file'); return; } - Tuple3, List, List> matrix; - - final builder = StringBuffer( - ''' -// GENERATED CODE - DO NOT MODIFY BY HAND -// -// If you need to modify this file, instead update /tools/generate_providers/bin/generate_providers.dart -// -// You can install this utility by executing: -// dart pub global activate -s path /tools/generate_providers -// -// You can then use it in your terminal by executing: -// generate_providers - -''', - ); + Matrix matrix; + final buffer = StringBuffer(_header); switch (args.first) { case 'riverpod': - matrix = Tuple3( - DisposeType.values, - [ - StateDetails( - className: 'StateProvider', - ref: 'StateProviderRef', - constraints: 'State', - generics: 'State', - createType: 'State', + matrix = ( + disposeTypes: _DisposeType.values, + providers: [ + _FunctionalBuilder( + 'StateProvider', + genericsUsage: 'StateT', + genericsDefinition: 'StateT', + createdT: 'StateT', + ), + _FunctionalBuilder( + 'StateNotifierProvider', + genericsUsage: 'NotifierT, StateT', + genericsDefinition: + 'NotifierT extends StateNotifier, StateT', + createdT: 'NotifierT', ), - StateDetails( - className: 'StateNotifierProvider', - ref: 'StateNotifierProviderRef', - constraints: 'Notifier extends StateNotifier, State', - generics: 'Notifier, State', - createType: 'Notifier', + _FunctionalBuilder( + 'Provider', + genericsUsage: 'StateT', + genericsDefinition: 'StateT', + createdT: 'StateT', ), - StateDetails( - className: 'Provider', - ref: 'ProviderRef', - constraints: 'State', - generics: 'State', - createType: 'State', + _FunctionalBuilder( + 'FutureProvider', + genericsUsage: 'StateT', + genericsDefinition: 'StateT', + createdT: 'FutureOr', ), - StateDetails( - className: 'FutureProvider', - ref: 'FutureProviderRef', - constraints: 'State', - generics: 'State', - createType: 'FutureOr', + _FunctionalBuilder( + 'StreamProvider', + genericsUsage: 'StateT', + genericsDefinition: 'StateT', + createdT: 'Stream', ), - StateDetails( - className: 'StreamProvider', - ref: 'StreamProviderRef', - constraints: 'State', - generics: 'State', - createType: 'Stream', + _NotifierBuilder( + 'NotifierProvider', + isFamily: false, + genericsUsage: '', + genericsDefinition: ', StateT>', + ), + _NotifierBuilder( + 'NotifierProvider', + isFamily: true, + genericsUsage: '', + genericsDefinition: + ', StateT, ArgT>', + ), + _NotifierBuilder( + 'StreamNotifierProvider', + isFamily: false, + genericsUsage: '', + genericsDefinition: + ', StateT>', + ), + _NotifierBuilder( + 'StreamNotifierProvider', + isFamily: true, + genericsUsage: '', + genericsDefinition: + ', StateT, ArgT>', + ), + _NotifierBuilder( + 'AsyncNotifierProvider', + isFamily: false, + genericsUsage: '', + genericsDefinition: + ', StateT>', + ), + _NotifierBuilder( + 'AsyncNotifierProvider', + isFamily: true, + genericsUsage: '', + genericsDefinition: + ', StateT, ArgT>', ), ], - ProviderType.values, + kinds: _ProviderKind.values, ); - builder.writeln( + buffer.writeln( """ import 'dart:async'; + +import 'package:meta/meta.dart'; import 'package:state_notifier/state_notifier.dart'; import 'internals.dart'; +""", + ); -/// Builds a [AsyncNotifierProvider]. -class AsyncNotifierProviderBuilder { - /// Builds a [AsyncNotifierProvider]. - const AsyncNotifierProviderBuilder(); + case 'flutter_riverpod': + matrix = ( + disposeTypes: _DisposeType.values, + providers: [ + _FunctionalBuilder( + 'ChangeNotifierProvider', + genericsUsage: 'NotifierT', + genericsDefinition: 'NotifierT extends ChangeNotifier?', + createdT: 'NotifierT', + ), + ], + kinds: _ProviderKind.values, + ); + buffer.writeln( + """ +import 'package:flutter/foundation.dart'; +import 'package:meta/meta.dart'; - /// {@macro riverpod.autoDispose} - AsyncNotifierProvider - call, T>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return AsyncNotifierProvider( - create, - name: name, - dependencies: dependencies, - ); +import 'internals.dart'; +""", + ); + default: + throw UnsupportedError('Unknown package ${args.first}'); } - /// {@macro riverpod.autoDispose} - AutoDisposeAsyncNotifierProviderBuilder get autoDispose { - return const AutoDisposeAsyncNotifierProviderBuilder(); - } + _generateAll(buffer, matrix); - /// {@macro riverpod.family} - AsyncNotifierProviderFamilyBuilder get family { - return const AsyncNotifierProviderFamilyBuilder(); - } + await file.writeAsString( + DartFormatter().format(buffer.toString()), + ); } -/// Builds a [AsyncNotifierProviderFamily]. -class AsyncNotifierProviderFamilyBuilder { - /// Builds a [AsyncNotifierProviderFamily]. - const AsyncNotifierProviderFamilyBuilder(); - - /// {@macro riverpod.family} - AsyncNotifierProviderFamily - call, T, Arg>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return AsyncNotifierProviderFamily( - create, - name: name, - dependencies: dependencies, - ); - } - - /// {@macro riverpod.autoDispose} - AutoDisposeAsyncNotifierProviderFamilyBuilder get autoDispose { - return const AutoDisposeAsyncNotifierProviderFamilyBuilder(); +void _generateAll(StringBuffer buffer, Matrix matrix) { + for (final provider in matrix.providers) { + for (final disposeType in matrix.disposeTypes) { + for (final kind in matrix.kinds) { + if (kind == _ProviderKind.orphan && + disposeType == _DisposeType.keepAlive) { + // That's handled by the provider itself + continue; + } + + switch (provider) { + case _NotifierBuilder(): + if (provider.isFamily == (kind == _ProviderKind.family)) { + _generateNotifier(buffer, disposeType, provider, kind); + } + case _FunctionalBuilder(): + switch (kind) { + case _ProviderKind.orphan: + _generateFunctionalOrphan(buffer, disposeType, provider); + case _ProviderKind.family: + _generateFunctionalFamily(buffer, disposeType, provider); + } + } + } + } } } -/// Builds a [AutoDisposeAsyncNotifierProvider]. -class AutoDisposeAsyncNotifierProviderBuilder { - /// Builds a [AutoDisposeAsyncNotifierProvider]. - const AutoDisposeAsyncNotifierProviderBuilder(); +void _generateFunctionalFamily( + StringBuffer buffer, + _DisposeType disposeType, + _FunctionalBuilder provider, +) { + final builderName = _builderName( + provider.providerName, + disposeType, + kind: _ProviderKind.family, + ); - /// {@macro riverpod.autoDispose} - AutoDisposeAsyncNotifierProvider - call, T>( - NotifierT Function() create, { + buffer.writeln(''' +@internal +class $builderName { + const $builderName(); + + $familyDoc + ${provider.providerName}Family<${provider.genericsUsage}, ArgT> call<${provider.genericsDefinition}, ArgT>( + ${provider.createdT} Function(Ref ref, ArgT param) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return AutoDisposeAsyncNotifierProvider( + return ${provider.providerName}Family<${provider.genericsUsage}, ArgT>( create, name: name, + ${_isAutoDisposeParam(disposeType)} dependencies: dependencies, + retry: retry, ); } - /// {@macro riverpod.family} - AutoDisposeAsyncNotifierProviderFamilyBuilder get family { - return const AutoDisposeAsyncNotifierProviderFamilyBuilder(); - } + ${_autoDisposeModifier(provider.providerName, disposeType, _ProviderKind.family)} } - -/// Builds a [AutoDisposeAsyncNotifierProviderFamily]. -class AutoDisposeAsyncNotifierProviderFamilyBuilder { - /// Builds a [AutoDisposeAsyncNotifierProviderFamily]. - const AutoDisposeAsyncNotifierProviderFamilyBuilder(); - - /// {@macro riverpod.family} - AutoDisposeAsyncNotifierProviderFamily - call, T, Arg>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return AutoDisposeAsyncNotifierProviderFamily( - create, - name: name, - dependencies: dependencies, - ); - } +'''); } -/// Builds a [NotifierProvider]. -class NotifierProviderBuilder { - /// Builds a [NotifierProvider]. - const NotifierProviderBuilder(); - - /// {@macro riverpod.autoDispose} - NotifierProvider - call, State>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return NotifierProvider( - create, - name: name, - dependencies: dependencies, - ); - } - - /// {@macro riverpod.autoDispose} - AutoDisposeNotifierProviderBuilder get autoDispose { - return const AutoDisposeNotifierProviderBuilder(); - } - - /// {@macro riverpod.family} - NotifierProviderFamilyBuilder get family { - return const NotifierProviderFamilyBuilder(); - } -} +void _generateFunctionalOrphan( + StringBuffer buffer, + _DisposeType disposeType, + _FunctionalBuilder provider, +) { + final builderName = _builderName( + provider.providerName, + disposeType, + ); -/// Builds a [NotifierProviderFamily]. -class NotifierProviderFamilyBuilder { - /// Builds a [NotifierProviderFamily]. - const NotifierProviderFamilyBuilder(); + buffer.writeln(''' +@internal +class $builderName { + const $builderName(); - /// {@macro riverpod.family} - NotifierProviderFamily - call, State, Arg>( - NotifierT Function() create, { + $familyDoc + ${provider.providerName}<${provider.genericsUsage}> call<${provider.genericsDefinition}>( + ${provider.createdT} Function(Ref ref) create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return NotifierProviderFamily( + return ${provider.providerName}<${provider.genericsUsage}>( create, name: name, + ${_isAutoDisposeParam(disposeType)} dependencies: dependencies, + retry: retry, ); } - /// {@macro riverpod.autoDispose} - AutoDisposeNotifierProviderFamilyBuilder get autoDispose { - return const AutoDisposeNotifierProviderFamilyBuilder(); - } + ${_familyModifier(provider.providerName, disposeType, _ProviderKind.orphan)} + ${_autoDisposeModifier(provider.providerName, disposeType, _ProviderKind.orphan)} +} +'''); } -/// Builds a [AutoDisposeNotifierProvider]. -class AutoDisposeNotifierProviderBuilder { - /// Builds a [AutoDisposeNotifierProvider]. - const AutoDisposeNotifierProviderBuilder(); - - /// {@macro riverpod.autoDispose} - AutoDisposeNotifierProvider - call, State>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return AutoDisposeNotifierProvider( - create, - name: name, - dependencies: dependencies, - ); +String _builderName( + String name, + _DisposeType disposeType, { + _ProviderKind kind = _ProviderKind.orphan, +}) { + var builderName = 'Builder'; + if (kind == _ProviderKind.family) { + builderName = 'Family$builderName'; } - - /// {@macro riverpod.family} - AutoDisposeNotifierProviderFamilyBuilder get family { - return const AutoDisposeNotifierProviderFamilyBuilder(); + builderName = '$name$builderName'; + if (disposeType == _DisposeType.autoDispose) { + builderName = 'AutoDispose$builderName'; } + return builderName; } -/// Builds a [AutoDisposeNotifierProviderFamily]. -class AutoDisposeNotifierProviderFamilyBuilder { - /// Builds a [AutoDisposeNotifierProviderFamily]. - const AutoDisposeNotifierProviderFamilyBuilder(); +String _autoDisposeModifier( + String name, + _DisposeType disposeType, + _ProviderKind kind, +) { + if (disposeType == _DisposeType.autoDispose) return ''; + + final targetBuilder = _builderName( + name, + _DisposeType.autoDispose, + kind: kind, + ); - /// {@macro riverpod.family} - AutoDisposeNotifierProviderFamily - call, State, Arg>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return AutoDisposeNotifierProviderFamily( - create, - name: name, - dependencies: dependencies, - ); - } + return ''' + $autoDisposeDoc + $targetBuilder get autoDispose => const $targetBuilder(); +'''; } -/// Builds a [StreamNotifierProvider]. -class StreamNotifierProviderBuilder { - /// Builds a [StreamNotifierProvider]. - const StreamNotifierProviderBuilder(); - - /// {@macro riverpod.autoDispose} - StreamNotifierProvider - call, T>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return StreamNotifierProvider( - create, - name: name, - dependencies: dependencies, - ); - } - - /// {@macro riverpod.autoDispose} - AutoDisposeStreamNotifierProviderBuilder get autoDispose { - return const AutoDisposeStreamNotifierProviderBuilder(); - } +String _familyModifier( + String name, + _DisposeType disposeType, + _ProviderKind kind, +) { + if (kind == _ProviderKind.family) return ''; + + final targetBuilder = _builderName( + name, + disposeType, + kind: _ProviderKind.family, + ); - /// {@macro riverpod.family} - StreamNotifierProviderFamilyBuilder get family { - return const StreamNotifierProviderFamilyBuilder(); - } + return ''' + $familyDoc + $targetBuilder get family => const $targetBuilder(); +'''; } -/// Builds a [StreamNotifierProviderFamily]. -class StreamNotifierProviderFamilyBuilder { - /// Builds a [StreamNotifierProviderFamily]. - const StreamNotifierProviderFamilyBuilder(); - - /// {@macro riverpod.family} - StreamNotifierProviderFamily - call, T, Arg>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return StreamNotifierProviderFamily( - create, - name: name, - dependencies: dependencies, - ); - } - - /// {@macro riverpod.autoDispose} - AutoDisposeStreamNotifierProviderFamilyBuilder get autoDispose { - return const AutoDisposeStreamNotifierProviderFamilyBuilder(); +String _isAutoDisposeParam(_DisposeType disposeType) { + if (disposeType == _DisposeType.autoDispose) { + return 'isAutoDispose: true,'; } + return ''; } -/// Builds a [AutoDisposeStreamNotifierProvider]. -class AutoDisposeStreamNotifierProviderBuilder { - /// Builds a [AutoDisposeStreamNotifierProvider]. - const AutoDisposeStreamNotifierProviderBuilder(); +void _generateNotifier( + StringBuffer buffer, + _DisposeType disposeType, + _NotifierBuilder provider, + _ProviderKind kind, +) { + final builderName = _builderName( + provider.providerName, + disposeType, + kind: kind, + ); - /// {@macro riverpod.autoDispose} - AutoDisposeStreamNotifierProvider - call, T>( - NotifierT Function() create, { - String? name, - Iterable? dependencies, - }) { - return AutoDisposeStreamNotifierProvider( - create, - name: name, - dependencies: dependencies, - ); - } + final providerName = provider.isFamily + ? '${provider.providerName}Family' + : provider.providerName; - /// {@macro riverpod.family} - AutoDisposeStreamNotifierProviderFamilyBuilder get family { - return const AutoDisposeStreamNotifierProviderFamilyBuilder(); - } -} + final ctor = provider.isFamily ? '.internal' : ''; -/// Builds a [AutoDisposeStreamNotifierProviderFamily]. -class AutoDisposeStreamNotifierProviderFamilyBuilder { - /// Builds a [AutoDisposeStreamNotifierProviderFamily]. - const AutoDisposeStreamNotifierProviderFamilyBuilder(); + buffer.writeln(''' +@internal +class $builderName { + const $builderName(); - /// {@macro riverpod.family} - AutoDisposeStreamNotifierProviderFamily - call, T, Arg>( + $autoDisposeDoc + $providerName${provider.genericsUsage} call${provider.genericsDefinition}( NotifierT Function() create, { String? name, Iterable? dependencies, + Retry? retry, }) { - return AutoDisposeStreamNotifierProviderFamily( + return $providerName${provider.genericsUsage}$ctor( create, name: name, + ${_isAutoDisposeParam(disposeType)} dependencies: dependencies, + retry: retry, ); } -} -""", - ); - break; - case 'flutter_riverpod': - matrix = Tuple3( - DisposeType.values, - [ - StateDetails( - className: 'ChangeNotifierProvider', - ref: 'ChangeNotifierProviderRef', - constraints: 'Notifier extends ChangeNotifier?', - generics: 'Notifier', - createType: 'Notifier', - ), - ], - ProviderType.values, - ); - builder.writeln( - """ -import 'package:flutter/foundation.dart'; -import 'internals.dart'; -""", - ); - break; - default: - throw UnsupportedError('Unknown package ${args.first}'); - } - - builder.writeAll(generateAll(matrix), '\n'); - - await file.writeAsString( - DartFormatter().format(builder.toString()), - ); -} - -Iterable generateAll( - Tuple3, List, List> matrix, -) sync* { - final combos = Combinations(3, [ - ...matrix.item1, - ...matrix.item2, - ...matrix.item3, - ]); - for (final permutation in combos()) { - final first = permutation.first; - final second = permutation[1]; - final third = permutation[2]; - - if (first is DisposeType && - second is StateDetails && - third is ProviderType) { - yield* generate(Tuple3(first, second, third), matrix); - } - } -} - -extension on Tuple3 { - String get providerName { - return '${disposeLabel[item1]}${item2.className}${providerLabel[item3]}'; - } - - String get ref { - switch (item1) { - case DisposeType.autoDispose: - return 'AutoDispose${item2.ref}'; - case DisposeType.none: - default: - return item2.ref; - } - } - - String get constraint => item2.constraints; - - String get createType => item2.createType; - - String links( - Tuple3, List, List> matrix, - ) { - Iterable other() sync* { - if (item1 == DisposeType.none) { - yield ''' - -${autoDisposeDoc().replaceAll('///', ' ///')} - AutoDispose${providerName}Builder get autoDispose { - return const AutoDispose${providerName}Builder(); - }'''; - } - - if (item3 == ProviderType.single) { - yield ''' - -${familyDoc().replaceAll('///', ' ///')} - ${providerName}FamilyBuilder get family { - return const ${providerName}FamilyBuilder(); - }'''; - } - } - - return other().join('\n'); - } -} - -Iterable generate( - Tuple3 configs, - Tuple3, List, List> matrix, -) sync* { - if (configs.item3 == ProviderType.family) { - yield FamilyBuilder(configs, matrix); - } else { - yield ProviderBuilder(configs, matrix); - } -} - -class FamilyBuilder { - FamilyBuilder(this.configs, this.matrix); - final Tuple3 configs; - final Tuple3, List, List> - matrix; - - @override - String toString() { - final createNamedParams = [ - 'String? name', - 'Iterable? dependencies', - ].map((e) => '$e,').join(); - final providerParams = [ - 'create', - 'name: name', - 'dependencies: dependencies', - ].map((e) => '$e,').join(); - return ''' -/// Builds a [${configs.providerName}]. -class ${configs.providerName}Builder { - /// Builds a [${configs.providerName}]. - const ${configs.providerName}Builder(); - -${familyDoc().replaceAll('///', ' ///')} - ${configs.providerName}<${configs.item2.generics}, Arg> call<${configs.constraint}, Arg>( - FamilyCreate<${configs.createType}, ${configs.ref}, Arg> create, { $createNamedParams }) { - return ${configs.providerName}<${configs.item2.generics}, Arg>($providerParams); - } -${configs.links(matrix)} -} -'''; - } -} -class ProviderBuilder { - ProviderBuilder(this.configs, this.matrix); - final Tuple3 configs; - final Tuple3, List, List> - matrix; - - @override - String toString() { - final callNamedParams = [ - 'String? name', - 'Iterable? dependencies', - ].map((e) => '$e,').join(); - final providerParams = [ - 'create', - 'name: name', - 'dependencies: dependencies', - ].map((e) => '$e,').join(); - - return ''' -/// Builds a [${configs.providerName}]. -class ${configs.providerName}Builder { - /// Builds a [${configs.providerName}]. - const ${configs.providerName}Builder(); - -${autoDisposeDoc().replaceAll('///', ' ///')} - ${configs.providerName}<${configs.item2.generics}> call<${configs.constraint}>( - Create<${configs.createType}, ${configs.ref}> create, { $callNamedParams }) { - return ${configs.providerName}<${configs.item2.generics}>($providerParams); - } -${configs.links(matrix)} + ${_familyModifier(provider.providerName, disposeType, kind)} + ${_autoDisposeModifier(provider.providerName, disposeType, kind)} } -'''; - } +'''); } diff --git a/tools/generate_providers/pubspec.yaml b/tools/generate_providers/pubspec.yaml index ab6a1bc59..e086bf931 100644 --- a/tools/generate_providers/pubspec.yaml +++ b/tools/generate_providers/pubspec.yaml @@ -2,12 +2,11 @@ name: generate_providers publish_to: "none" environment: - sdk: ">=2.19.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: dart_style: ^2.0.0 trotter: ^2.0.0 - tuple: ^2.0.0 executables: generate_providers: \ No newline at end of file diff --git a/tools/generate_providers/src/docs.dart b/tools/generate_providers/src/docs.dart new file mode 100644 index 000000000..43f28a444 --- /dev/null +++ b/tools/generate_providers/src/docs.dart @@ -0,0 +1,280 @@ +final _writtenDocs = {}; + +String _docs(String doc, {required String key}) { + if (!_writtenDocs.add(key)) { + return '/// {@macro $key}'; + } + + return ''' +/// {@template $key} +$doc +/// {@endtemplate}'''; +} + +String get autoDisposeDoc => _docs( + _autoDisposeDoc, + key: 'riverpod.autoDispose', + ); + +const _autoDisposeDoc = ''' +/// Marks the provider as automatically disposed when no longer listened to. +/// +/// Some typical use-cases: +/// +/// - Combined with [StreamProvider], this can be used as a mean to keep +/// the connection with Firebase alive only when truly needed (to reduce costs). +/// - Automatically reset a form state when leaving the screen. +/// - Automatically retry HTTP requests that failed when the user exit and +/// re-enter the screen. +/// - Cancel HTTP requests if the user leaves a screen before the request completed. +/// +/// Marking a provider with `autoDispose` also adds an extra method on `ref`: `keepAlive`. +/// +/// The `keepAlive` function is used to tell Riverpod that the state of the provider +/// should be preserved even if no longer listened to. +/// +/// A use-case would be to set this flag to `true` after an HTTP request have +/// completed: +/// +/// ```dart +/// final myProvider = FutureProvider.autoDispose((ref) async { +/// final response = await httpClient.get(...); +/// ref.keepAlive(); +/// return response; +/// }); +/// ``` +/// +/// This way, if the request failed and the UI leaves the screen then re-enters +/// it, then the request will be performed again. +/// But if the request completed successfully, the state will be preserved +/// and re-entering the screen will not trigger a new request. +/// +/// It can be combined with `ref.onDispose` for more advanced behaviors, such +/// as cancelling pending HTTP requests when the user leaves a screen. +/// For example, modifying our previous snippet and using `dio`, we would have: +/// +/// ```diff +/// final myProvider = FutureProvider.autoDispose((ref) async { +/// + final cancelToken = CancelToken(); +/// + ref.onDispose(() => cancelToken.cancel()); +/// +/// + final response = await dio.get('path', cancelToken: cancelToken); +/// - final response = await dio.get('path'); +/// ref.keepAlive(); +/// return response; +/// }); +/// ```'''; + +String get familyDoc => _docs( + _familyDoc, + key: 'riverpod.family', + ); + +const _familyDoc = r''' +/// A group of providers that builds their value from an external parameter. +/// +/// Families can be useful to connect a provider with values that it doesn't +/// have access to. For example: +/// +/// - Allowing a "title provider" access the `Locale` +/// +/// ```dart +/// final titleFamily = Provider.family((ref, locale) { +/// if (locale == const Locale('en')) { +/// return 'English title'; +/// } else if (locale == const Locale('fr')) { +/// return 'Titre Français'; +/// } +/// }); +/// +/// // ... +/// +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// final locale = Localizations.localeOf(context); +/// +/// // Obtains the title based on the current Locale. +/// // Will automatically update the title when the Locale changes. +/// final title = ref.watch(titleFamily(locale)); +/// +/// return Text(title); +/// } +/// ``` +/// +/// - Have a "user provider" that receives the user ID as a parameter +/// +/// ```dart +/// final userFamily = FutureProvider.family((ref, userId) async { +/// final userRepository = ref.read(userRepositoryProvider); +/// return await userRepository.fetch(userId); +/// }); +/// +/// // ... +/// +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// int userId; // Read the user ID from somewhere +/// +/// // Read and potentially fetch the user with id `userId`. +/// // When `userId` changes, this will automatically update the UI +/// // Similarly, if two widgets tries to read `userFamily` with the same `userId` +/// // then the user will be fetched only once. +/// final user = ref.watch(userFamily(userId)); +/// +/// return user.when( +/// data: (user) => Text(user.name), +/// loading: () => const CircularProgressIndicator(), +/// error: (err, stack) => const Text('error'), +/// ); +/// } +/// ``` +/// +/// - Connect a provider with another provider without having a direct reference on it. +/// +/// ```dart +/// final repositoryProvider = Provider.family>((ref, configurationsProvider) { +/// // Read a provider without knowing what that provider is. +/// final configurations = await ref.read(configurationsProvider.future); +/// return Repository(host: configurations.host); +/// }); +/// ``` +/// +/// ## Usage +/// +/// The way families works is by adding an extra parameter to the provider. +/// This parameter can then be freely used in our provider to create some state. +/// +/// For example, we could combine `family` with [FutureProvider] to fetch +/// a `Message` from its ID: +/// +/// ```dart +/// final messagesFamily = FutureProvider.family((ref, id) async { +/// return dio.get('http://my_api.dev/messages/$id'); +/// }); +/// ``` +/// +/// Then, when using our `messagesFamily` provider, the syntax is slightly modified. +/// The usual: +/// +/// ```dart +/// Widget build(BuildContext context, WidgetRef ref) { +/// // Error – messagesFamily is not a provider +/// final response = ref.watch(messagesFamily); +/// } +/// ``` +/// +/// will not work anymore. +/// Instead, we need to pass a parameter to `messagesFamily`: +/// +/// ```dart +/// Widget build(BuildContext context, WidgetRef ref) { +/// final response = ref.watch(messagesFamily('id')); +/// } +/// ``` +/// +/// **NOTE**: It is totally possible to use a family with different parameters +/// simultaneously. For example, we could use a `titleFamily` to read both +/// the french and english translations at the same time: +/// +/// ```dart +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// final frenchTitle = ref.watch(titleFamily(const Locale('fr'))); +/// final englishTitle = ref.watch(titleFamily(const Locale('en'))); +/// +/// return Text('fr: $frenchTitle en: $englishTitle'); +/// } +/// ``` +/// +/// # Parameter restrictions +/// +/// For families to work correctly, it is critical for the parameter passed to +/// a provider to have a consistent `hashCode` and `==`. +/// +/// Ideally the parameter should either be a primitive (bool/int/double/String), +/// a constant (providers), or an immutable object that override `==` and `hashCode`. +/// +/// +/// - **PREFER** using `family` in combination with `autoDispose` if the +/// parameter passed to providers is a complex object: +/// +/// ```dart +/// final example = Provider.autoDispose.family((ref, param) { +/// }); +/// ``` +/// +/// This ensures that there is no memory leak if the parameter changed and is +/// never used again. +/// +/// # Passing multiple parameters to a family +/// +/// Families have no built-in support for passing multiple values to a provider. +/// +/// On the other hand, that value could be _anything_ (as long as it matches with +/// the restrictions mentioned previously). +/// +/// This includes: +/// - A tuple (using `package:tuple`) +/// - Objects generated with Freezed/built_value +/// - Objects based on `package:equatable` +/// +/// This includes: +/// - A tuple (using `package:tuple`) +/// - Objects generated with Freezed/built_value, such as: +/// ```dart +/// @freezed +/// abstract class MyParameter with _$MyParameter { +/// factory MyParameter({ +/// required int userId, +/// required Locale locale, +/// }) = _MyParameter; +/// } +/// +/// final exampleProvider = Provider.family((ref, myParameter) { +/// print(myParameter.userId); +/// print(myParameter.locale); +/// // Do something with userId/locale +/// }); +/// +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// int userId; // Read the user ID from somewhere +/// final locale = Localizations.localeOf(context); +/// +/// final something = ref.watch( +/// exampleProvider(MyParameter(userId: userId, locale: locale)), +/// ); +/// } +/// ``` +/// +/// - Objects based on `package:equatable`, such as: +/// ```dart +/// class MyParameter extends Equatable { +/// factory MyParameter({ +/// required this.userId, +/// requires this.locale, +/// }); +/// +/// final int userId; +/// final Local locale; +/// +/// @override +/// List get props => [userId, locale]; +/// } +/// +/// final exampleProvider = Provider.family((ref, myParameter) { +/// print(myParameter.userId); +/// print(myParameter.locale); +/// // Do something with userId/locale +/// }); +/// +/// @override +/// Widget build(BuildContext context, WidgetRef ref) { +/// int userId; // Read the user ID from somewhere +/// final locale = Localizations.localeOf(context); +/// +/// final something = ref.watch( +/// exampleProvider(MyParameter(userId: userId, locale: locale)), +/// ); +/// } +/// ```'''; diff --git a/website/analysis_options.yaml b/website/analysis_options.yaml index bcfceea03..80538664b 100644 --- a/website/analysis_options.yaml +++ b/website/analysis_options.yaml @@ -5,7 +5,5 @@ analyzer: - i18n/** errors: todo: ignore - -linter: - rules: - require_trailing_commas: false + plugins: + - custom_lint diff --git a/website/docs/advanced/select/select/codegen.g.dart b/website/docs/advanced/select/select/codegen.g.dart index 8b9547de9..0aaaa5a94 100644 --- a/website/docs/advanced/select/select/codegen.g.dart +++ b/website/docs/advanced/select/select/codegen.g.dart @@ -8,21 +8,63 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider extends $FunctionalProvider + with $Provider { + const ExampleProvider._( + {User Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final User Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(User value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + User Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + User create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + String _$exampleHash() => r'c9865d4390aad5b8480addcbb73dbd225c8417b8'; -/// See also [example]. -@ProviderFor(example) -final exampleProvider = AutoDisposeProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/advanced/select/select_async/codegen.dart b/website/docs/advanced/select/select_async/codegen.dart index 083d0ce4b..9ddf7256b 100644 --- a/website/docs/advanced/select/select_async/codegen.dart +++ b/website/docs/advanced/select/select_async/codegen.dart @@ -1,6 +1,5 @@ // ignore_for_file: unused_local_variable, avoid_multiple_declarations_per_line, omit_local_variable_types, prefer_final_locals, use_key_in_widget_constructors, body_might_complete_normally_nullable -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; @@ -9,11 +8,13 @@ class User { late String firstName, lastName; } -final userProvider = FutureProvider( - (ref) => User() +@riverpod +FutureOr user(Ref ref) { + return User() ..firstName = 'John' - ..lastName = 'Doe', -); + ..lastName = 'Doe'; +} + /* SNIPPET START */ @riverpod Object? example(Ref ref) async { diff --git a/website/docs/advanced/select/select_async/codegen.g.dart b/website/docs/advanced/select/select_async/codegen.g.dart index 2edad0ab6..c53201dae 100644 --- a/website/docs/advanced/select/select_async/codegen.g.dart +++ b/website/docs/advanced/select/select_async/codegen.g.dart @@ -8,21 +8,114 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** -String _$exampleHash() => r'05abb7bf29fe43807cb1a31a17eb23c821529a69'; +@ProviderFor(user) +const userProvider = UserProvider._(); + +final class UserProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const UserProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'userProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$userHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + UserProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return UserProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? user; + return _$cb(ref); + } +} + +String _$userHash() => r'b83ca110a6fae2341d1bfca73fb3d89c4d12723d'; -/// See also [example]. @ProviderFor(example) -final exampleProvider = AutoDisposeProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeProviderRef; +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider extends $FunctionalProvider + with $Provider { + const ExampleProvider._( + {Object? Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Object? Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Object? value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + Object? Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + Object? create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + +String _$exampleHash() => r'05abb7bf29fe43807cb1a31a17eb23c821529a69'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/case_studies/cancel/detail_screen/codegen.dart b/website/docs/case_studies/cancel/detail_screen/codegen.dart index 997da0be0..7a47d834a 100644 --- a/website/docs/case_studies/cancel/detail_screen/codegen.dart +++ b/website/docs/case_studies/cancel/detail_screen/codegen.dart @@ -49,7 +49,7 @@ class DetailPageView extends ConsumerWidget { child: ListView( children: [ switch (activity) { - AsyncValue(:final valueOrNull?) => Text(valueOrNull.activity), + AsyncValue(:final value?) => Text(value.activity), AsyncValue(:final error?) => Text('Error: $error'), _ => const Center(child: CircularProgressIndicator()), }, diff --git a/website/docs/case_studies/cancel/detail_screen/codegen.g.dart b/website/docs/case_studies/cancel/detail_screen/codegen.g.dart index adcd39185..30cf3b400 100644 --- a/website/docs/case_studies/cancel/detail_screen/codegen.g.dart +++ b/website/docs/case_studies/cancel/detail_screen/codegen.g.dart @@ -28,21 +28,56 @@ Map _$$ActivityImplToJson(_$ActivityImpl instance) => // RiverpodGenerator // ************************************************************************** +@ProviderFor(activity) +const activityProvider = ActivityProvider._(); + +final class ActivityProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const ActivityProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'activityProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$activityHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ActivityProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return ActivityProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? activity; + return _$cb(ref); + } +} + String _$activityHash() => r'609ac1c1d8008d8109ea5869c7aa88013032917c'; -/// See also [activity]. -@ProviderFor(activity) -final activityProvider = AutoDisposeFutureProvider.internal( - activity, - name: r'activityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$activityHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ActivityRef = AutoDisposeFutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/case_studies/cancel/detail_screen/raw.dart b/website/docs/case_studies/cancel/detail_screen/raw.dart index b3ad1faaf..46c76ff89 100644 --- a/website/docs/case_studies/cancel/detail_screen/raw.dart +++ b/website/docs/case_studies/cancel/detail_screen/raw.dart @@ -53,7 +53,7 @@ class DetailPageView extends ConsumerWidget { child: ListView( children: [ switch (activity) { - AsyncValue(:final valueOrNull?) => Text(valueOrNull.activity), + AsyncValue(:final value?) => Text(value.activity), AsyncValue(:final error?) => Text('Error: $error'), _ => const Center(child: CircularProgressIndicator()), }, diff --git a/website/docs/case_studies/cancel/detail_screen_cancel/codegen.dart b/website/docs/case_studies/cancel/detail_screen_cancel/codegen.dart index 0fe63d984..8094b9996 100644 --- a/website/docs/case_studies/cancel/detail_screen_cancel/codegen.dart +++ b/website/docs/case_studies/cancel/detail_screen_cancel/codegen.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../detail_screen/codegen.dart'; diff --git a/website/docs/case_studies/cancel/detail_screen_cancel/codegen.g.dart b/website/docs/case_studies/cancel/detail_screen_cancel/codegen.g.dart index 7847e8582..d5d5e9bdc 100644 --- a/website/docs/case_studies/cancel/detail_screen_cancel/codegen.g.dart +++ b/website/docs/case_studies/cancel/detail_screen_cancel/codegen.g.dart @@ -8,21 +8,56 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(activity) +const activityProvider = ActivityProvider._(); + +final class ActivityProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const ActivityProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'activityProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$activityHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ActivityProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return ActivityProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? activity; + return _$cb(ref); + } +} + String _$activityHash() => r'ac82ab2c8ff5c059f8f9abebed0a5c1fb6dc66c8'; -/// See also [activity]. -@ProviderFor(activity) -final activityProvider = AutoDisposeFutureProvider.internal( - activity, - name: r'activityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$activityHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ActivityRef = AutoDisposeFutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/case_studies/cancel/detail_screen_debounce/codegen.dart b/website/docs/case_studies/cancel/detail_screen_debounce/codegen.dart index 54ec304da..732f8e134 100644 --- a/website/docs/case_studies/cancel/detail_screen_debounce/codegen.dart +++ b/website/docs/case_studies/cancel/detail_screen_debounce/codegen.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; import 'package:riverpod_annotation/riverpod_annotation.dart'; diff --git a/website/docs/case_studies/cancel/detail_screen_debounce/codegen.g.dart b/website/docs/case_studies/cancel/detail_screen_debounce/codegen.g.dart index 70976646c..5599a976f 100644 --- a/website/docs/case_studies/cancel/detail_screen_debounce/codegen.g.dart +++ b/website/docs/case_studies/cancel/detail_screen_debounce/codegen.g.dart @@ -8,21 +8,56 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(activity) +const activityProvider = ActivityProvider._(); + +final class ActivityProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const ActivityProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'activityProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$activityHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ActivityProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return ActivityProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? activity; + return _$cb(ref); + } +} + String _$activityHash() => r'dd7d10807315da17e775b99e8e03f5d9634fc822'; -/// See also [activity]. -@ProviderFor(activity) -final activityProvider = AutoDisposeFutureProvider.internal( - activity, - name: r'activityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$activityHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ActivityRef = AutoDisposeFutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/case_studies/cancel/provider_with_extension/codegen.dart b/website/docs/case_studies/cancel/provider_with_extension/codegen.dart index 85db5313b..e3663c7a0 100644 --- a/website/docs/case_studies/cancel/provider_with_extension/codegen.dart +++ b/website/docs/case_studies/cancel/provider_with_extension/codegen.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../detail_screen/codegen.dart'; diff --git a/website/docs/case_studies/cancel/provider_with_extension/codegen.g.dart b/website/docs/case_studies/cancel/provider_with_extension/codegen.g.dart index fbedd48ec..bfc06dfd6 100644 --- a/website/docs/case_studies/cancel/provider_with_extension/codegen.g.dart +++ b/website/docs/case_studies/cancel/provider_with_extension/codegen.g.dart @@ -8,21 +8,56 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(activity) +const activityProvider = ActivityProvider._(); + +final class ActivityProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const ActivityProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'activityProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$activityHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ActivityProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return ActivityProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? activity; + return _$cb(ref); + } +} + String _$activityHash() => r'c1d2d6dca725a8b75b31c73630a5641dba0bec2b'; -/// See also [activity]. -@ProviderFor(activity) -final activityProvider = AutoDisposeFutureProvider.internal( - activity, - name: r'activityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$activityHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ActivityRef = AutoDisposeFutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/case_studies/pull_to_refresh/display_activity.dart b/website/docs/case_studies/pull_to_refresh/display_activity.dart index a14b22136..a025d2677 100644 --- a/website/docs/case_studies/pull_to_refresh/display_activity.dart +++ b/website/docs/case_studies/pull_to_refresh/display_activity.dart @@ -17,7 +17,7 @@ class ActivityView extends ConsumerWidget { // {@template render} // If we have an activity, display it, otherwise wait // {@endtemplate} - child: Text(activity.valueOrNull?.activity ?? ''), + child: Text(activity.value?.activity ?? ''), ), ); } diff --git a/website/docs/case_studies/pull_to_refresh/display_activity2.dart b/website/docs/case_studies/pull_to_refresh/display_activity2.dart index 7394ea664..9a1673bdd 100644 --- a/website/docs/case_studies/pull_to_refresh/display_activity2.dart +++ b/website/docs/case_studies/pull_to_refresh/display_activity2.dart @@ -19,7 +19,7 @@ class ActivityView extends ConsumerWidget { child: ListView( children: [ /* highlight-end */ - Text(activity.valueOrNull?.activity ?? ''), + Text(activity.value?.activity ?? ''), ], ), ), diff --git a/website/docs/case_studies/pull_to_refresh/display_activity3.dart b/website/docs/case_studies/pull_to_refresh/display_activity3.dart index 53b6bd9d8..ed7960b18 100644 --- a/website/docs/case_studies/pull_to_refresh/display_activity3.dart +++ b/website/docs/case_studies/pull_to_refresh/display_activity3.dart @@ -24,7 +24,7 @@ class ActivityView extends ConsumerWidget { /* highlight-end */ child: ListView( children: [ - Text(activity.valueOrNull?.activity ?? ''), + Text(activity.value?.activity ?? ''), ], ), ), diff --git a/website/docs/case_studies/pull_to_refresh/display_activity4.dart b/website/docs/case_studies/pull_to_refresh/display_activity4.dart index 7cda1335d..b4ce0722e 100644 --- a/website/docs/case_studies/pull_to_refresh/display_activity4.dart +++ b/website/docs/case_studies/pull_to_refresh/display_activity4.dart @@ -23,7 +23,7 @@ class ActivityView extends ConsumerWidget { // If some data is available, we display it. // Note that data will still be available during a refresh. // {@endtemplate} - AsyncValue(:final valueOrNull?) => Text(valueOrNull.activity), + AsyncValue(:final value?) => Text(value.activity), // {@template error} // An error is available, so we render it. // {@endtemplate} diff --git a/website/docs/case_studies/pull_to_refresh/fetch_activity/codegen.dart b/website/docs/case_studies/pull_to_refresh/fetch_activity/codegen.dart index 54cbd50da..16ac99207 100644 --- a/website/docs/case_studies/pull_to_refresh/fetch_activity/codegen.dart +++ b/website/docs/case_studies/pull_to_refresh/fetch_activity/codegen.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; import 'package:riverpod_annotation/riverpod_annotation.dart'; diff --git a/website/docs/case_studies/pull_to_refresh/fetch_activity/codegen.g.dart b/website/docs/case_studies/pull_to_refresh/fetch_activity/codegen.g.dart index ad56ff467..ce5bea251 100644 --- a/website/docs/case_studies/pull_to_refresh/fetch_activity/codegen.g.dart +++ b/website/docs/case_studies/pull_to_refresh/fetch_activity/codegen.g.dart @@ -8,21 +8,56 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(activity) +const activityProvider = ActivityProvider._(); + +final class ActivityProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const ActivityProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'activityProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$activityHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ActivityProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return ActivityProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? activity; + return _$cb(ref); + } +} + String _$activityHash() => r'609ac1c1d8008d8109ea5869c7aa88013032917c'; -/// See also [activity]. -@ProviderFor(activity) -final activityProvider = AutoDisposeFutureProvider.internal( - activity, - name: r'activityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$activityHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ActivityRef = AutoDisposeFutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/case_studies/pull_to_refresh/fetch_activity/raw.dart b/website/docs/case_studies/pull_to_refresh/fetch_activity/raw.dart index 1cec3b128..04de1232b 100644 --- a/website/docs/case_studies/pull_to_refresh/fetch_activity/raw.dart +++ b/website/docs/case_studies/pull_to_refresh/fetch_activity/raw.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; import '../activity/raw.dart'; diff --git a/website/docs/case_studies/pull_to_refresh/full_app/codegen.dart b/website/docs/case_studies/pull_to_refresh/full_app/codegen.dart index 84f4d8b62..865e30cc7 100644 --- a/website/docs/case_studies/pull_to_refresh/full_app/codegen.dart +++ b/website/docs/case_studies/pull_to_refresh/full_app/codegen.dart @@ -33,8 +33,7 @@ class ActivityView extends ConsumerWidget { child: ListView( children: [ switch (activity) { - AsyncValue(:final valueOrNull?) => - Text(valueOrNull.activity), + AsyncValue(:final value?) => Text(value.activity), AsyncValue(:final error?) => Text('Error: $error'), _ => const CircularProgressIndicator(), }, diff --git a/website/docs/case_studies/pull_to_refresh/full_app/codegen.g.dart b/website/docs/case_studies/pull_to_refresh/full_app/codegen.g.dart index adcd39185..30cf3b400 100644 --- a/website/docs/case_studies/pull_to_refresh/full_app/codegen.g.dart +++ b/website/docs/case_studies/pull_to_refresh/full_app/codegen.g.dart @@ -28,21 +28,56 @@ Map _$$ActivityImplToJson(_$ActivityImpl instance) => // RiverpodGenerator // ************************************************************************** +@ProviderFor(activity) +const activityProvider = ActivityProvider._(); + +final class ActivityProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const ActivityProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'activityProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$activityHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ActivityProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return ActivityProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? activity; + return _$cb(ref); + } +} + String _$activityHash() => r'609ac1c1d8008d8109ea5869c7aa88013032917c'; -/// See also [activity]. -@ProviderFor(activity) -final activityProvider = AutoDisposeFutureProvider.internal( - activity, - name: r'activityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$activityHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ActivityRef = AutoDisposeFutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/case_studies/pull_to_refresh/full_app/raw.dart b/website/docs/case_studies/pull_to_refresh/full_app/raw.dart index c4186fb31..491e8295e 100644 --- a/website/docs/case_studies/pull_to_refresh/full_app/raw.dart +++ b/website/docs/case_studies/pull_to_refresh/full_app/raw.dart @@ -28,8 +28,7 @@ class ActivityView extends ConsumerWidget { child: ListView( children: [ switch (activity) { - AsyncValue(:final valueOrNull?) => - Text(valueOrNull.activity), + AsyncValue(:final value?) => Text(value.activity), AsyncValue(:final error?) => Text('Error: $error'), _ => const CircularProgressIndicator(), }, diff --git a/website/docs/concepts/about_codegen/main.dart b/website/docs/concepts/about_codegen/main.dart index b0a1a4347..6dbbf14aa 100644 --- a/website/docs/concepts/about_codegen/main.dart +++ b/website/docs/concepts/about_codegen/main.dart @@ -1,6 +1,5 @@ // ignore_for_file: use_key_in_widget_constructors, omit_local_variable_types, avoid_unused_constructor_parameters -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'main.g.dart'; diff --git a/website/docs/concepts/about_codegen/main.g.dart b/website/docs/concepts/about_codegen/main.g.dart index 8a4d76e76..889d75b37 100644 --- a/website/docs/concepts/about_codegen/main.g.dart +++ b/website/docs/concepts/about_codegen/main.g.dart @@ -8,154 +8,128 @@ part of 'main.dart'; // RiverpodGenerator // ************************************************************************** -String _$fetchUserHash() => r'0ea61464a124f8af2cf15b830a1a012d4272eb47'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// See also [fetchUser]. @ProviderFor(fetchUser) -const fetchUserProvider = FetchUserFamily(); - -/// See also [fetchUser]. -class FetchUserFamily extends Family> { - /// See also [fetchUser]. - const FetchUserFamily(); +const fetchUserProvider = FetchUserFamily._(); + +final class FetchUserProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const FetchUserProvider._( + {required FetchUserFamily super.from, + required int super.argument, + FutureOr Function( + Ref ref, { + required int userId, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'fetchUserProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// See also [fetchUser]. - FetchUserProvider call({ + final FutureOr Function( + Ref ref, { required int userId, - }) { - return FetchUserProvider( - userId: userId, - ); - } + })? _createCb; @override - FetchUserProvider getProviderOverride( - covariant FetchUserProvider provider, - ) { - return call( - userId: provider.userId, - ); - } - - static const Iterable? _dependencies = null; + String debugGetCreateSourceHash() => _$fetchUserHash(); @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; + String toString() { + return r'fetchUserProvider' + '' + '($argument)'; + } + @$internal @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); @override - String? get name => r'fetchUserProvider'; -} - -/// See also [fetchUser]. -class FetchUserProvider extends AutoDisposeFutureProvider { - /// See also [fetchUser]. - FetchUserProvider({ - required int userId, - }) : this._internal( - (ref) => fetchUser( - ref as FetchUserRef, - userId: userId, - ), - from: fetchUserProvider, - name: r'fetchUserProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$fetchUserHash, - dependencies: FetchUserFamily._dependencies, - allTransitiveDependencies: FetchUserFamily._allTransitiveDependencies, - userId: userId, - ); - - FetchUserProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.userId, - }) : super.internal(); - - final int userId; - - @override - Override overrideWith( - FutureOr Function(FetchUserRef provider) create, + FetchUserProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, ) { - return ProviderOverride( - origin: this, - override: FetchUserProvider._internal( - (ref) => create(ref as FetchUserRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - userId: userId, - ), - ); + return FetchUserProvider._( + argument: argument as int, + from: from! as FetchUserFamily, + create: ( + ref, { + required int userId, + }) => + create(ref)); } @override - AutoDisposeFutureProviderElement createElement() { - return _FetchUserProviderElement(this); + FutureOr create(Ref ref) { + final _$cb = _createCb ?? fetchUser; + final argument = this.argument as int; + return _$cb( + ref, + userId: argument, + ); } @override bool operator ==(Object other) { - return other is FetchUserProvider && other.userId == userId; + return other is FetchUserProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, userId.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FetchUserRef on AutoDisposeFutureProviderRef { - /// The parameter `userId` of this provider. - int get userId; -} +String _$fetchUserHash() => r'0ea61464a124f8af2cf15b830a1a012d4272eb47'; + +final class FetchUserFamily extends Family { + const FetchUserFamily._() + : super( + retry: null, + name: r'fetchUserProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + FetchUserProvider call({ + required int userId, + }) => + FetchUserProvider._(argument: userId, from: this); -class _FetchUserProviderElement extends AutoDisposeFutureProviderElement - with FetchUserRef { - _FetchUserProviderElement(super.provider); + @override + String debugGetCreateSourceHash() => _$fetchUserHash(); @override - int get userId => (origin as FetchUserProvider).userId; + String toString() => r'fetchUserProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + FutureOr Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FetchUserProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/about_codegen/provider_type/async_class_future.g.dart b/website/docs/concepts/about_codegen/provider_type/async_class_future.g.dart index d08d18894..52014d865 100644 --- a/website/docs/concepts/about_codegen/provider_type/async_class_future.g.dart +++ b/website/docs/concepts/about_codegen/provider_type/async_class_future.g.dart @@ -8,20 +8,66 @@ part of 'async_class_future.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(Example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider extends $AsyncNotifierProvider { + const ExampleProvider._( + {super.runNotifierBuildOverride, Example Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Example Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + @$internal + @override + Example create() => _createCb?.call() ?? Example(); + + @$internal + @override + ExampleProvider $copyWithCreate( + Example Function() create, + ) { + return ExampleProvider._(create: create); + } + + @$internal + @override + ExampleProvider $copyWithBuild( + FutureOr Function( + Ref, + Example, + ) build, + ) { + return ExampleProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $AsyncNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); +} + String _$exampleHash() => r'8a906741b8ea4b9b0d3f0b924779704b3e1773a1'; -/// See also [Example]. -@ProviderFor(Example) -final exampleProvider = - AutoDisposeAsyncNotifierProvider.internal( - Example.new, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Example = AutoDisposeAsyncNotifier; +abstract class _$Example extends $AsyncNotifier { + FutureOr build(); + @$internal + @override + FutureOr runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/about_codegen/provider_type/async_class_stream.g.dart b/website/docs/concepts/about_codegen/provider_type/async_class_stream.g.dart index a3b9bcd95..382965278 100644 --- a/website/docs/concepts/about_codegen/provider_type/async_class_stream.g.dart +++ b/website/docs/concepts/about_codegen/provider_type/async_class_stream.g.dart @@ -8,20 +8,66 @@ part of 'async_class_stream.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(Example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider extends $StreamNotifierProvider { + const ExampleProvider._( + {super.runNotifierBuildOverride, Example Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Example Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + @$internal + @override + Example create() => _createCb?.call() ?? Example(); + + @$internal + @override + ExampleProvider $copyWithCreate( + Example Function() create, + ) { + return ExampleProvider._(create: create); + } + + @$internal + @override + ExampleProvider $copyWithBuild( + Stream Function( + Ref, + Example, + ) build, + ) { + return ExampleProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $StreamNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $StreamNotifierProviderElement(this, pointer); +} + String _$exampleHash() => r'4bca936132b77a9a804549f086f33571724b4804'; -/// See also [Example]. -@ProviderFor(Example) -final exampleProvider = - AutoDisposeStreamNotifierProvider.internal( - Example.new, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Example = AutoDisposeStreamNotifier; +abstract class _$Example extends $StreamNotifier { + Stream build(); + @$internal + @override + Stream runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/about_codegen/provider_type/async_fn_future.dart b/website/docs/concepts/about_codegen/provider_type/async_fn_future.dart index 27f448f98..7ab60af7c 100644 --- a/website/docs/concepts/about_codegen/provider_type/async_fn_future.dart +++ b/website/docs/concepts/about_codegen/provider_type/async_fn_future.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'async_fn_future.g.dart'; diff --git a/website/docs/concepts/about_codegen/provider_type/async_fn_future.g.dart b/website/docs/concepts/about_codegen/provider_type/async_fn_future.g.dart index df6393497..779c98093 100644 --- a/website/docs/concepts/about_codegen/provider_type/async_fn_future.g.dart +++ b/website/docs/concepts/about_codegen/provider_type/async_fn_future.g.dart @@ -8,21 +8,56 @@ part of 'async_fn_future.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const ExampleProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + String _$exampleHash() => r'24ba6aa120f9e40b3796d5429f7723b82f8f0970'; -/// See also [example]. -@ProviderFor(example) -final exampleProvider = AutoDisposeFutureProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeFutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/about_codegen/provider_type/async_fn_stream.dart b/website/docs/concepts/about_codegen/provider_type/async_fn_stream.dart index a7080cdc9..43b3a877f 100644 --- a/website/docs/concepts/about_codegen/provider_type/async_fn_stream.dart +++ b/website/docs/concepts/about_codegen/provider_type/async_fn_stream.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'async_fn_stream.g.dart'; diff --git a/website/docs/concepts/about_codegen/provider_type/async_fn_stream.g.dart b/website/docs/concepts/about_codegen/provider_type/async_fn_stream.g.dart index c85eae6cb..f743c75ef 100644 --- a/website/docs/concepts/about_codegen/provider_type/async_fn_stream.g.dart +++ b/website/docs/concepts/about_codegen/provider_type/async_fn_stream.g.dart @@ -8,21 +8,56 @@ part of 'async_fn_stream.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider + extends $FunctionalProvider, Stream> + with $FutureModifier, $StreamProvider { + const ExampleProvider._( + {Stream Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Stream Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + @$internal + @override + $StreamProviderElement $createElement($ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + Stream Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + Stream create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + String _$exampleHash() => r'f7f90ac5fbf939c0259a549b8e01a559b0d95ff1'; -/// See also [example]. -@ProviderFor(example) -final exampleProvider = AutoDisposeStreamProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeStreamProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/about_codegen/provider_type/auto_dispose.dart b/website/docs/concepts/about_codegen/provider_type/auto_dispose.dart index ecc7afa9e..3351a2da9 100644 --- a/website/docs/concepts/about_codegen/provider_type/auto_dispose.dart +++ b/website/docs/concepts/about_codegen/provider_type/auto_dispose.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'auto_dispose.g.dart'; diff --git a/website/docs/concepts/about_codegen/provider_type/auto_dispose.g.dart b/website/docs/concepts/about_codegen/provider_type/auto_dispose.g.dart index b9dfd3706..9abfc469c 100644 --- a/website/docs/concepts/about_codegen/provider_type/auto_dispose.g.dart +++ b/website/docs/concepts/about_codegen/provider_type/auto_dispose.g.dart @@ -8,37 +8,121 @@ part of 'auto_dispose.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(example1) +const example1Provider = Example1Provider._(); + +final class Example1Provider extends $FunctionalProvider + with $Provider { + const Example1Provider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'example1Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$example1Hash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + Example1Provider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return Example1Provider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? example1; + return _$cb(ref); + } +} + String _$example1Hash() => r'6a361ee6f9dd1d0cdbb42f967f6356aa058f7041'; -/// See also [example1]. -@ProviderFor(example1) -final example1Provider = AutoDisposeProvider.internal( - example1, - name: r'example1Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$example1Hash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef Example1Ref = AutoDisposeProviderRef; +@ProviderFor(example2) +const example2Provider = Example2Provider._(); + +final class Example2Provider extends $FunctionalProvider + with $Provider { + const Example2Provider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'example2Provider', + isAutoDispose: false, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$example2Hash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + Example2Provider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return Example2Provider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? example2; + return _$cb(ref); + } +} + String _$example2Hash() => r'181b89435c06a7284a8978c5ab9f13bb4a3693b0'; -/// See also [example2]. -@ProviderFor(example2) -final example2Provider = Provider.internal( - example2, - name: r'example2Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$example2Hash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef Example2Ref = ProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/about_codegen/provider_type/family.dart b/website/docs/concepts/about_codegen/provider_type/family.dart index 22db5c5f8..d4a88c306 100644 --- a/website/docs/concepts/about_codegen/provider_type/family.dart +++ b/website/docs/concepts/about_codegen/provider_type/family.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'family.g.dart'; diff --git a/website/docs/concepts/about_codegen/provider_type/family.g.dart b/website/docs/concepts/about_codegen/provider_type/family.g.dart index 75da15ae4..703faf3b7 100644 --- a/website/docs/concepts/about_codegen/provider_type/family.g.dart +++ b/website/docs/concepts/about_codegen/provider_type/family.g.dart @@ -8,154 +8,135 @@ part of 'family.dart'; // RiverpodGenerator // ************************************************************************** -String _$exampleHash() => r'7cd87bca029ed938b0e314a14fdfaa2875bd3079'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// See also [example]. @ProviderFor(example) -const exampleProvider = ExampleFamily(); - -/// See also [example]. -class ExampleFamily extends Family { - /// See also [example]. - const ExampleFamily(); +const exampleProvider = ExampleFamily._(); + +final class ExampleProvider extends $FunctionalProvider + with $Provider { + const ExampleProvider._( + {required ExampleFamily super.from, + required int super.argument, + String Function( + Ref ref, + int param, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// See also [example]. - ExampleProvider call( + final String Function( + Ref ref, int param, - ) { - return ExampleProvider( - param, - ); - } + )? _createCb; @override - ExampleProvider getProviderOverride( - covariant ExampleProvider provider, - ) { - return call( - provider.param, - ); - } - - static const Iterable? _dependencies = null; + String debugGetCreateSourceHash() => _$exampleHash(); @override - Iterable? get dependencies => _dependencies; + String toString() { + return r'exampleProvider' + '' + '($argument)'; + } - static const Iterable? _allTransitiveDependencies = null; + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + @$internal @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - String? get name => r'exampleProvider'; -} - -/// See also [example]. -class ExampleProvider extends AutoDisposeProvider { - /// See also [example]. - ExampleProvider( - int param, - ) : this._internal( - (ref) => example( - ref as ExampleRef, - param, - ), - from: exampleProvider, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$exampleHash, - dependencies: ExampleFamily._dependencies, - allTransitiveDependencies: ExampleFamily._allTransitiveDependencies, - param: param, - ); - - ExampleProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.param, - }) : super.internal(); - - final int param; - - @override - Override overrideWith( - String Function(ExampleRef provider) create, + ExampleProvider $copyWithCreate( + String Function( + Ref ref, + ) create, ) { - return ProviderOverride( - origin: this, - override: ExampleProvider._internal( - (ref) => create(ref as ExampleRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - param: param, - ), - ); + return ExampleProvider._( + argument: argument as int, + from: from! as ExampleFamily, + create: ( + ref, + int param, + ) => + create(ref)); } @override - AutoDisposeProviderElement createElement() { - return _ExampleProviderElement(this); + String create(Ref ref) { + final _$cb = _createCb ?? example; + final argument = this.argument as int; + return _$cb( + ref, + argument, + ); } @override bool operator ==(Object other) { - return other is ExampleProvider && other.param == param; + return other is ExampleProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, param.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin ExampleRef on AutoDisposeProviderRef { - /// The parameter `param` of this provider. - int get param; -} +String _$exampleHash() => r'7cd87bca029ed938b0e314a14fdfaa2875bd3079'; + +final class ExampleFamily extends Family { + const ExampleFamily._() + : super( + retry: null, + name: r'exampleProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); -class _ExampleProviderElement extends AutoDisposeProviderElement - with ExampleRef { - _ExampleProviderElement(super.provider); + ExampleProvider call( + int param, + ) => + ExampleProvider._(argument: param, from: this); @override - int get param => (origin as ExampleProvider).param; + String debugGetCreateSourceHash() => _$exampleHash(); + + @override + String toString() => r'exampleProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + String Function( + Ref ref, + int args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ExampleProvider; + + final argument = provider.argument as int; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/about_codegen/provider_type/family_class.dart b/website/docs/concepts/about_codegen/provider_type/family_class.dart index 868585ad6..0a0a34415 100644 --- a/website/docs/concepts/about_codegen/provider_type/family_class.dart +++ b/website/docs/concepts/about_codegen/provider_type/family_class.dart @@ -10,7 +10,7 @@ class Example extends _$Example { int param1, { String param2 = 'foo', }) { - return 'Hello $param1 & param2'; + return 'Hello $param1 & $param2'; } // Add methods to mutate the state diff --git a/website/docs/concepts/about_codegen/provider_type/family_class.g.dart b/website/docs/concepts/about_codegen/provider_type/family_class.g.dart index 1352c7ec8..c26d84a59 100644 --- a/website/docs/concepts/about_codegen/provider_type/family_class.g.dart +++ b/website/docs/concepts/about_codegen/provider_type/family_class.g.dart @@ -8,190 +8,201 @@ part of 'family_class.dart'; // RiverpodGenerator // ************************************************************************** -String _$exampleHash() => r'c81e9d94e763b25403ab6b7fa03f092003570142'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} +@ProviderFor(Example) +const exampleProvider = ExampleFamily._(); + +final class ExampleProvider extends $NotifierProvider { + const ExampleProvider._( + {required ExampleFamily super.from, + required ( + int, { + String param2, + }) + super.argument, + super.runNotifierBuildOverride, + Example Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -abstract class _$Example extends BuildlessAutoDisposeNotifier { - late final int param1; - late final String param2; + final Example Function()? _createCb; - String build( - int param1, { - String param2 = 'foo', - }); -} - -/// See also [Example]. -@ProviderFor(Example) -const exampleProvider = ExampleFamily(); + @override + String debugGetCreateSourceHash() => _$exampleHash(); -/// See also [Example]. -class ExampleFamily extends Family { - /// See also [Example]. - const ExampleFamily(); + @override + String toString() { + return r'exampleProvider' + '' + '$argument'; + } - /// See also [Example]. - ExampleProvider call( - int param1, { - String param2 = 'foo', - }) { - return ExampleProvider( - param1, - param2: param2, + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), ); } + @$internal @override - ExampleProvider getProviderOverride( - covariant ExampleProvider provider, + Example create() => _createCb?.call() ?? Example(); + + @$internal + @override + ExampleProvider $copyWithCreate( + Example Function() create, ) { - return call( - provider.param1, - param2: provider.param2, - ); + return ExampleProvider._( + argument: argument as ( + int, { + String param2, + }), + from: from! as ExampleFamily, + create: create); } - static const Iterable? _dependencies = null; - + @$internal @override - Iterable? get dependencies => _dependencies; + ExampleProvider $copyWithBuild( + String Function( + Ref, + Example, + ) build, + ) { + return ExampleProvider._( + argument: argument as ( + int, { + String param2, + }), + from: from! as ExampleFamily, + runNotifierBuildOverride: build); + } - static const Iterable? _allTransitiveDependencies = null; + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + bool operator ==(Object other) { + return other is ExampleProvider && other.argument == argument; + } @override - String? get name => r'exampleProvider'; + int get hashCode { + return argument.hashCode; + } } -/// See also [Example]. -class ExampleProvider extends AutoDisposeNotifierProviderImpl { - /// See also [Example]. - ExampleProvider( - int param1, { - String param2 = 'foo', - }) : this._internal( - () => Example() - ..param1 = param1 - ..param2 = param2, - from: exampleProvider, +String _$exampleHash() => r'8025d93d6f5e9286043b1ce7ae55bead44f30acc'; + +final class ExampleFamily extends Family { + const ExampleFamily._() + : super( + retry: null, name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$exampleHash, - dependencies: ExampleFamily._dependencies, - allTransitiveDependencies: ExampleFamily._allTransitiveDependencies, - param1: param1, - param2: param2, + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, ); - ExampleProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.param1, - required this.param2, - }) : super.internal(); + ExampleProvider call( + int param1, { + String param2 = 'foo', + }) => + ExampleProvider._(argument: ( + param1, + param2: param2, + ), from: this); - final int param1; - final String param2; + @override + String debugGetCreateSourceHash() => _$exampleHash(); @override - String runNotifierBuild( - covariant Example notifier, + String toString() => r'exampleProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + Example Function( + ( + int, { + String param2, + }) args, + ) create, ) { - return notifier.build( - param1, - param2: param2, + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ExampleProvider; + + final argument = provider.argument as ( + int, { + String param2, + }); + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, ); } - @override - Override overrideWith(Example Function() create) { - return ProviderOverride( - origin: this, - override: ExampleProvider._internal( - () => create() - ..param1 = param1 - ..param2 = param2, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - param1: param1, - param2: param2, - ), + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + String Function( + Ref ref, + Example notifier, + ( + int, { + String param2, + }) argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ExampleProvider; + + final argument = provider.argument as ( + int, { + String param2, + }); + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, ); } - - @override - AutoDisposeNotifierProviderElement createElement() { - return _ExampleProviderElement(this); - } - - @override - bool operator ==(Object other) { - return other is ExampleProvider && - other.param1 == param1 && - other.param2 == param2; - } - - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, param1.hashCode); - hash = _SystemHash.combine(hash, param2.hashCode); - - return _SystemHash.finish(hash); - } -} - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin ExampleRef on AutoDisposeNotifierProviderRef { - /// The parameter `param1` of this provider. - int get param1; - - /// The parameter `param2` of this provider. - String get param2; } -class _ExampleProviderElement - extends AutoDisposeNotifierProviderElement - with ExampleRef { - _ExampleProviderElement(super.provider); +abstract class _$Example extends $Notifier { + late final _$args = ref.$arg as ( + int, { + String param2, + }); + int get param1 => _$args.$1; + String get param2 => _$args.param2; + String build( + int param1, { + String param2 = 'foo', + }); + @$internal @override - int get param1 => (origin as ExampleProvider).param1; - @override - String get param2 => (origin as ExampleProvider).param2; + String runBuild() => build( + _$args.$1, + param2: _$args.param2, + ); } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/about_codegen/provider_type/family_fn.dart b/website/docs/concepts/about_codegen/provider_type/family_fn.dart index 50c583192..6ac513340 100644 --- a/website/docs/concepts/about_codegen/provider_type/family_fn.dart +++ b/website/docs/concepts/about_codegen/provider_type/family_fn.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'family_fn.g.dart'; @@ -10,5 +9,5 @@ String example( int param1, { String param2 = 'foo', }) { - return 'Hello $param1 & param2'; + return 'Hello $param1 & $param2'; } diff --git a/website/docs/concepts/about_codegen/provider_type/family_fn.g.dart b/website/docs/concepts/about_codegen/provider_type/family_fn.g.dart index 37a94074a..d918bce6f 100644 --- a/website/docs/concepts/about_codegen/provider_type/family_fn.g.dart +++ b/website/docs/concepts/about_codegen/provider_type/family_fn.g.dart @@ -8,171 +8,159 @@ part of 'family_fn.dart'; // RiverpodGenerator // ************************************************************************** -String _$exampleHash() => r'5c55c7f736032d1570f258f5056eb7966c37e8e1'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// See also [example]. @ProviderFor(example) -const exampleProvider = ExampleFamily(); - -/// See also [example]. -class ExampleFamily extends Family { - /// See also [example]. - const ExampleFamily(); +const exampleProvider = ExampleFamily._(); + +final class ExampleProvider extends $FunctionalProvider + with $Provider { + const ExampleProvider._( + {required ExampleFamily super.from, + required ( + int, { + String param2, + }) + super.argument, + String Function( + Ref ref, + int param1, { + String param2, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// See also [example]. - ExampleProvider call( + final String Function( + Ref ref, int param1, { - String param2 = 'foo', - }) { - return ExampleProvider( - param1, - param2: param2, - ); - } + String param2, + })? _createCb; @override - ExampleProvider getProviderOverride( - covariant ExampleProvider provider, - ) { - return call( - provider.param1, - param2: provider.param2, - ); - } - - static const Iterable? _dependencies = null; + String debugGetCreateSourceHash() => _$exampleHash(); @override - Iterable? get dependencies => _dependencies; + String toString() { + return r'exampleProvider' + '' + '$argument'; + } - static const Iterable? _allTransitiveDependencies = null; + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + @$internal @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - String? get name => r'exampleProvider'; -} - -/// See also [example]. -class ExampleProvider extends AutoDisposeProvider { - /// See also [example]. - ExampleProvider( - int param1, { - String param2 = 'foo', - }) : this._internal( - (ref) => example( - ref as ExampleRef, - param1, - param2: param2, - ), - from: exampleProvider, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$exampleHash, - dependencies: ExampleFamily._dependencies, - allTransitiveDependencies: ExampleFamily._allTransitiveDependencies, - param1: param1, - param2: param2, - ); - - ExampleProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.param1, - required this.param2, - }) : super.internal(); - - final int param1; - final String param2; - - @override - Override overrideWith( - String Function(ExampleRef provider) create, + ExampleProvider $copyWithCreate( + String Function( + Ref ref, + ) create, ) { - return ProviderOverride( - origin: this, - override: ExampleProvider._internal( - (ref) => create(ref as ExampleRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - param1: param1, - param2: param2, - ), - ); + return ExampleProvider._( + argument: argument as ( + int, { + String param2, + }), + from: from! as ExampleFamily, + create: ( + ref, + int param1, { + String param2 = 'foo', + }) => + create(ref)); } @override - AutoDisposeProviderElement createElement() { - return _ExampleProviderElement(this); + String create(Ref ref) { + final _$cb = _createCb ?? example; + final argument = this.argument as ( + int, { + String param2, + }); + return _$cb( + ref, + argument.$1, + param2: argument.param2, + ); } @override bool operator ==(Object other) { - return other is ExampleProvider && - other.param1 == param1 && - other.param2 == param2; + return other is ExampleProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, param1.hashCode); - hash = _SystemHash.combine(hash, param2.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin ExampleRef on AutoDisposeProviderRef { - /// The parameter `param1` of this provider. - int get param1; +String _$exampleHash() => r'5795b1f6c6f075de18d0e9789a3a52040c144f0c'; - /// The parameter `param2` of this provider. - String get param2; -} +final class ExampleFamily extends Family { + const ExampleFamily._() + : super( + retry: null, + name: r'exampleProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); -class _ExampleProviderElement extends AutoDisposeProviderElement - with ExampleRef { - _ExampleProviderElement(super.provider); + ExampleProvider call( + int param1, { + String param2 = 'foo', + }) => + ExampleProvider._(argument: ( + param1, + param2: param2, + ), from: this); @override - int get param1 => (origin as ExampleProvider).param1; + String debugGetCreateSourceHash() => _$exampleHash(); + @override - String get param2 => (origin as ExampleProvider).param2; + String toString() => r'exampleProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + String Function( + Ref ref, + ( + int, { + String param2, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ExampleProvider; + + final argument = provider.argument as ( + int, { + String param2, + }); + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/about_codegen/provider_type/non_code_gen/async_notifier_provider.dart b/website/docs/concepts/about_codegen/provider_type/non_code_gen/async_notifier_provider.dart index 7a880eed7..650fd958f 100644 --- a/website/docs/concepts/about_codegen/provider_type/non_code_gen/async_notifier_provider.dart +++ b/website/docs/concepts/about_codegen/provider_type/non_code_gen/async_notifier_provider.dart @@ -6,7 +6,7 @@ final exampleProvider = ExampleNotifier.new, ); -class ExampleNotifier extends AutoDisposeAsyncNotifier { +class ExampleNotifier extends AsyncNotifier { @override Future build() async { return Future.value('foo'); diff --git a/website/docs/concepts/about_codegen/provider_type/non_code_gen/notifier_provider.dart b/website/docs/concepts/about_codegen/provider_type/non_code_gen/notifier_provider.dart index 66b1ca9fc..caef36fc8 100644 --- a/website/docs/concepts/about_codegen/provider_type/non_code_gen/notifier_provider.dart +++ b/website/docs/concepts/about_codegen/provider_type/non_code_gen/notifier_provider.dart @@ -5,7 +5,7 @@ final exampleProvider = NotifierProvider.autoDispose( ExampleNotifier.new, ); -class ExampleNotifier extends AutoDisposeNotifier { +class ExampleNotifier extends Notifier { @override String build() { return 'foo'; diff --git a/website/docs/concepts/about_codegen/provider_type/non_code_gen/stream_notifier_provider.dart b/website/docs/concepts/about_codegen/provider_type/non_code_gen/stream_notifier_provider.dart index 6b8af6b85..35fb9f012 100644 --- a/website/docs/concepts/about_codegen/provider_type/non_code_gen/stream_notifier_provider.dart +++ b/website/docs/concepts/about_codegen/provider_type/non_code_gen/stream_notifier_provider.dart @@ -6,7 +6,7 @@ final exampleProvider = return ExampleNotifier(); }); -class ExampleNotifier extends AutoDisposeStreamNotifier { +class ExampleNotifier extends StreamNotifier { @override Stream build() async* { yield 'foo'; diff --git a/website/docs/concepts/about_codegen/provider_type/sync_class.g.dart b/website/docs/concepts/about_codegen/provider_type/sync_class.g.dart index f5b44c329..3984ef54d 100644 --- a/website/docs/concepts/about_codegen/provider_type/sync_class.g.dart +++ b/website/docs/concepts/about_codegen/provider_type/sync_class.g.dart @@ -8,19 +8,74 @@ part of 'sync_class.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(Example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider extends $NotifierProvider { + const ExampleProvider._( + {super.runNotifierBuildOverride, Example Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Example Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Example create() => _createCb?.call() ?? Example(); + + @$internal + @override + ExampleProvider $copyWithCreate( + Example Function() create, + ) { + return ExampleProvider._(create: create); + } + + @$internal + @override + ExampleProvider $copyWithBuild( + String Function( + Ref, + Example, + ) build, + ) { + return ExampleProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$exampleHash() => r'c237193ab6d57674973aaa02eb73db6f6822eb26'; -/// See also [Example]. -@ProviderFor(Example) -final exampleProvider = AutoDisposeNotifierProvider.internal( - Example.new, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Example = AutoDisposeNotifier; +abstract class _$Example extends $Notifier { + String build(); + @$internal + @override + String runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/about_codegen/provider_type/sync_fn.dart b/website/docs/concepts/about_codegen/provider_type/sync_fn.dart index 0099d43b4..73e82b7e2 100644 --- a/website/docs/concepts/about_codegen/provider_type/sync_fn.dart +++ b/website/docs/concepts/about_codegen/provider_type/sync_fn.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'sync_fn.g.dart'; diff --git a/website/docs/concepts/about_codegen/provider_type/sync_fn.g.dart b/website/docs/concepts/about_codegen/provider_type/sync_fn.g.dart index db280dca6..0da673a93 100644 --- a/website/docs/concepts/about_codegen/provider_type/sync_fn.g.dart +++ b/website/docs/concepts/about_codegen/provider_type/sync_fn.g.dart @@ -8,21 +8,63 @@ part of 'sync_fn.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider extends $FunctionalProvider + with $Provider { + const ExampleProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + String _$exampleHash() => r'e75fe2037fef7a3f80e04fa007fe64d719dba2fd'; -/// See also [example]. -@ProviderFor(example) -final exampleProvider = AutoDisposeProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/about_hooks/hook_and_consumer.dart b/website/docs/concepts/about_hooks/hook_and_consumer.dart index 2c230084a..f9ad71919 100644 --- a/website/docs/concepts/about_hooks/hook_and_consumer.dart +++ b/website/docs/concepts/about_hooks/hook_and_consumer.dart @@ -21,7 +21,7 @@ class Example extends StatelessWidget { final value = ref.watch(myProvider); return Text('Hello $counter $value'); - }); + },); }, ); } diff --git a/website/docs/concepts/async_initialization.dart b/website/docs/concepts/async_initialization.dart index 24541c10c..8163a5896 100644 --- a/website/docs/concepts/async_initialization.dart +++ b/website/docs/concepts/async_initialization.dart @@ -38,7 +38,7 @@ final sharedPreferencesProvider = Future main() async { // Show a loading indicator before running the full app (optional) // The platform's loading screen will be used while awaiting if you omit this. - runApp(const LoadingScreen()); + runApp(const ProviderScope(child: LoadingScreen())); // Get the instance of shared preferences final prefs = await SharedPreferences.getInstance(); diff --git a/website/docs/concepts/combining_provider_states/characters_provider/codegen.dart b/website/docs/concepts/combining_provider_states/characters_provider/codegen.dart index f244ab7d6..dbab1b4e1 100644 --- a/website/docs/concepts/combining_provider_states/characters_provider/codegen.dart +++ b/website/docs/concepts/combining_provider_states/characters_provider/codegen.dart @@ -1,5 +1,4 @@ import 'package:dio/dio.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'models.dart'; @@ -11,7 +10,8 @@ final dio = Dio(); /* SNIPPET START */ // The current search filter -final searchProvider = StateProvider((ref) => ''); +@riverpod +String search(Ref ref) => ''; @riverpod Stream configs(Ref ref) { @@ -23,7 +23,8 @@ Future> characters(Ref ref) async { final search = ref.watch(searchProvider); final configs = await ref.watch(configsProvider.future); final response = await dio.get>>( - '${configs.host}/characters?search=$search'); + '${configs.host}/characters?search=$search', + ); return response.data!.map(Character.fromJson).toList(); } diff --git a/website/docs/concepts/combining_provider_states/characters_provider/codegen.g.dart b/website/docs/concepts/combining_provider_states/characters_provider/codegen.g.dart index 7ee20249a..94def9aad 100644 --- a/website/docs/concepts/combining_provider_states/characters_provider/codegen.g.dart +++ b/website/docs/concepts/combining_provider_states/characters_provider/codegen.g.dart @@ -8,37 +8,167 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** -String _$configsHash() => r'6416514dacd408abb24de2bd1404860e6518c564'; +@ProviderFor(search) +const searchProvider = SearchProvider._(); + +final class SearchProvider extends $FunctionalProvider + with $Provider { + const SearchProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'searchProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$searchHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + SearchProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return SearchProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? search; + return _$cb(ref); + } +} + +String _$searchHash() => r'bc08d7ad4026615f3c0e4824c6b943f315cf18be'; -/// See also [configs]. @ProviderFor(configs) -final configsProvider = AutoDisposeStreamProvider.internal( - configs, - name: r'configsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$configsHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ConfigsRef = AutoDisposeStreamProviderRef; -String _$charactersHash() => r'd2bac558571ceae538d012696be58e2a06e8013f'; +const configsProvider = ConfigsProvider._(); + +final class ConfigsProvider extends $FunctionalProvider< + AsyncValue, Stream> + with $FutureModifier, $StreamProvider { + const ConfigsProvider._( + {Stream Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'configsProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Stream Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$configsHash(); + + @$internal + @override + $StreamProviderElement $createElement( + $ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + + @override + ConfigsProvider $copyWithCreate( + Stream Function( + Ref ref, + ) create, + ) { + return ConfigsProvider._(create: create); + } + + @override + Stream create(Ref ref) { + final _$cb = _createCb ?? configs; + return _$cb(ref); + } +} + +String _$configsHash() => r'6416514dacd408abb24de2bd1404860e6518c564'; -/// See also [characters]. @ProviderFor(characters) -final charactersProvider = AutoDisposeFutureProvider>.internal( - characters, - name: r'charactersProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$charactersHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CharactersRef = AutoDisposeFutureProviderRef>; +const charactersProvider = CharactersProvider._(); + +final class CharactersProvider extends $FunctionalProvider< + AsyncValue>, FutureOr>> + with $FutureModifier>, $FutureProvider> { + const CharactersProvider._( + {FutureOr> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'charactersProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$charactersHash(); + + @$internal + @override + $FutureProviderElement> $createElement( + $ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + CharactersProvider $copyWithCreate( + FutureOr> Function( + Ref ref, + ) create, + ) { + return CharactersProvider._(create: create); + } + + @override + FutureOr> create(Ref ref) { + final _$cb = _createCb ?? characters; + return _$cb(ref); + } +} + +String _$charactersHash() => r'd2bac558571ceae538d012696be58e2a06e8013f'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/combining_provider_states/characters_provider/raw.dart b/website/docs/concepts/combining_provider_states/characters_provider/raw.dart index 948148bd0..57c054dcd 100644 --- a/website/docs/concepts/combining_provider_states/characters_provider/raw.dart +++ b/website/docs/concepts/combining_provider_states/characters_provider/raw.dart @@ -1,5 +1,6 @@ import 'package:dio/dio.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'models.dart'; @@ -19,7 +20,7 @@ final charactersProvider = FutureProvider>((ref) async { final search = ref.watch(searchProvider); final configs = await ref.watch(configsProvider.future); final response = await dio.get>>( - '${configs.host}/characters?search=$search'); + '${configs.host}/characters?search=$search',); return response.data!.map(Character.fromJson).toList(); }); diff --git a/website/docs/concepts/combining_provider_states/city_provider/codegen.dart b/website/docs/concepts/combining_provider_states/city_provider/codegen.dart index 39edaa161..092367038 100644 --- a/website/docs/concepts/combining_provider_states/city_provider/codegen.dart +++ b/website/docs/concepts/combining_provider_states/city_provider/codegen.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/concepts/combining_provider_states/city_provider/codegen.g.dart b/website/docs/concepts/combining_provider_states/city_provider/codegen.g.dart index ebba620a3..b60e09654 100644 --- a/website/docs/concepts/combining_provider_states/city_provider/codegen.g.dart +++ b/website/docs/concepts/combining_provider_states/city_provider/codegen.g.dart @@ -8,21 +8,63 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(city) +const cityProvider = CityProvider._(); + +final class CityProvider extends $FunctionalProvider + with $Provider { + const CityProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'cityProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$cityHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CityProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return CityProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? city; + return _$cb(ref); + } +} + String _$cityHash() => r'6a5023a3aba119f1ecaee6c7db44b3f519e72759'; -/// See also [city]. -@ProviderFor(city) -final cityProvider = AutoDisposeProvider.internal( - city, - name: r'cityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$cityHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CityRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/combining_provider_states/filtered_todo_list_provider/codegen.dart b/website/docs/concepts/combining_provider_states/filtered_todo_list_provider/codegen.dart index 06ae68a81..4e16203ed 100644 --- a/website/docs/concepts/combining_provider_states/filtered_todo_list_provider/codegen.dart +++ b/website/docs/concepts/combining_provider_states/filtered_todo_list_provider/codegen.dart @@ -1,7 +1,6 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; -import '../todo_list_provider/raw.dart'; +import '../todo_list_provider/codegen.dart'; part 'codegen.g.dart'; @@ -11,7 +10,8 @@ enum Filter { uncompleted, } -final filterProvider = StateProvider((ref) => Filter.none); +@riverpod +Filter filter(Ref ref) => Filter.none; /* SNIPPET START */ diff --git a/website/docs/concepts/combining_provider_states/filtered_todo_list_provider/codegen.g.dart b/website/docs/concepts/combining_provider_states/filtered_todo_list_provider/codegen.g.dart index 7c89b2c0c..8d5daac4e 100644 --- a/website/docs/concepts/combining_provider_states/filtered_todo_list_provider/codegen.g.dart +++ b/website/docs/concepts/combining_provider_states/filtered_todo_list_provider/codegen.g.dart @@ -8,22 +8,122 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** -String _$filteredTodoListHash() => r'e0faf3934cd30a62b5771f2e4d64eaa727065c2f'; +@ProviderFor(filter) +const filterProvider = FilterProvider._(); + +final class FilterProvider extends $FunctionalProvider + with $Provider { + const FilterProvider._( + {Filter Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'filterProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Filter Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$filterHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Filter value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + FilterProvider $copyWithCreate( + Filter Function( + Ref ref, + ) create, + ) { + return FilterProvider._(create: create); + } + + @override + Filter create(Ref ref) { + final _$cb = _createCb ?? filter; + return _$cb(ref); + } +} + +String _$filterHash() => r'6583f8bace972f4385964cd26f217751164b537b'; -/// See also [filteredTodoList]. @ProviderFor(filteredTodoList) -final filteredTodoListProvider = AutoDisposeProvider>.internal( - filteredTodoList, - name: r'filteredTodoListProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$filteredTodoListHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef FilteredTodoListRef = AutoDisposeProviderRef>; +const filteredTodoListProvider = FilteredTodoListProvider._(); + +final class FilteredTodoListProvider + extends $FunctionalProvider, List> + with $Provider> { + const FilteredTodoListProvider._( + {List Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'filteredTodoListProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final List Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$filteredTodoListHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + $ProviderElement> $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + FilteredTodoListProvider $copyWithCreate( + List Function( + Ref ref, + ) create, + ) { + return FilteredTodoListProvider._(create: create); + } + + @override + List create(Ref ref) { + final _$cb = _createCb ?? filteredTodoList; + return _$cb(ref); + } +} + +String _$filteredTodoListHash() => r'e0faf3934cd30a62b5771f2e4d64eaa727065c2f'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/combining_provider_states/filtered_todo_list_provider/raw.dart b/website/docs/concepts/combining_provider_states/filtered_todo_list_provider/raw.dart index 85885e800..2c3d79b71 100644 --- a/website/docs/concepts/combining_provider_states/filtered_todo_list_provider/raw.dart +++ b/website/docs/concepts/combining_provider_states/filtered_todo_list_provider/raw.dart @@ -1,4 +1,5 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import '../todo_list_provider/raw.dart'; enum Filter { @@ -23,4 +24,4 @@ final filteredTodoListProvider = Provider>((ref) { case Filter.uncompleted: return todos.where((todo) => !todo.completed).toList(); } -}); \ No newline at end of file +}); diff --git a/website/docs/concepts/combining_provider_states/read_in_provider/codegen.dart b/website/docs/concepts/combining_provider_states/read_in_provider/codegen.dart index 556d228c0..c76e47a97 100644 --- a/website/docs/concepts/combining_provider_states/read_in_provider/codegen.dart +++ b/website/docs/concepts/combining_provider_states/read_in_provider/codegen.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/concepts/combining_provider_states/read_in_provider/codegen.g.dart b/website/docs/concepts/combining_provider_states/read_in_provider/codegen.g.dart index 9cc0e2cfb..309085274 100644 --- a/website/docs/concepts/combining_provider_states/read_in_provider/codegen.g.dart +++ b/website/docs/concepts/combining_provider_states/read_in_provider/codegen.g.dart @@ -8,37 +8,121 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(another) +const anotherProvider = AnotherProvider._(); + +final class AnotherProvider extends $FunctionalProvider + with $Provider { + const AnotherProvider._( + {MyValue Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'anotherProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyValue Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$anotherHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(MyValue value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + AnotherProvider $copyWithCreate( + MyValue Function( + Ref ref, + ) create, + ) { + return AnotherProvider._(create: create); + } + + @override + MyValue create(Ref ref) { + final _$cb = _createCb ?? another; + return _$cb(ref); + } +} + String _$anotherHash() => r'07629e5ae4a53bcd316b91c07d7558edbdea9317'; -/// See also [another]. -@ProviderFor(another) -final anotherProvider = AutoDisposeProvider.internal( - another, - name: r'anotherProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$anotherHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef AnotherRef = AutoDisposeProviderRef; +@ProviderFor(my) +const myProvider = MyProvider._(); + +final class MyProvider extends $FunctionalProvider + with $Provider { + const MyProvider._( + {MyValue Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyValue Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$myHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(MyValue value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + MyProvider $copyWithCreate( + MyValue Function( + Ref ref, + ) create, + ) { + return MyProvider._(create: create); + } + + @override + MyValue create(Ref ref) { + final _$cb = _createCb ?? my; + return _$cb(ref); + } +} + String _$myHash() => r'816efc8816269dabd0944c434946903db197fe0b'; -/// See also [my]. -@ProviderFor(my) -final myProvider = AutoDisposeProvider.internal( - my, - name: r'myProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$myHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef MyRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/combining_provider_states/select_async_provider/codegen.dart b/website/docs/concepts/combining_provider_states/select_async_provider/codegen.dart index 1d76926cc..0bdb6db69 100644 --- a/website/docs/concepts/combining_provider_states/select_async_provider/codegen.dart +++ b/website/docs/concepts/combining_provider_states/select_async_provider/codegen.dart @@ -1,5 +1,4 @@ import 'package:dio/dio.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'models.dart'; diff --git a/website/docs/concepts/combining_provider_states/select_async_provider/codegen.g.dart b/website/docs/concepts/combining_provider_states/select_async_provider/codegen.g.dart index 364dd6831..428bc97f0 100644 --- a/website/docs/concepts/combining_provider_states/select_async_provider/codegen.g.dart +++ b/website/docs/concepts/combining_provider_states/select_async_provider/codegen.g.dart @@ -8,37 +8,109 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(config) +const configProvider = ConfigProvider._(); + +final class ConfigProvider extends $FunctionalProvider< + AsyncValue, Stream> + with $FutureModifier, $StreamProvider { + const ConfigProvider._( + {Stream Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'configProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Stream Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$configHash(); + + @$internal + @override + $StreamProviderElement $createElement( + $ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + + @override + ConfigProvider $copyWithCreate( + Stream Function( + Ref ref, + ) create, + ) { + return ConfigProvider._(create: create); + } + + @override + Stream create(Ref ref) { + final _$cb = _createCb ?? config; + return _$cb(ref); + } +} + String _$configHash() => r'66f48a02bf939463649f0e7ad34137265e5c8b66'; -/// See also [config]. -@ProviderFor(config) -final configProvider = AutoDisposeStreamProvider.internal( - config, - name: r'configProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$configHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ConfigRef = AutoDisposeStreamProviderRef; +@ProviderFor(products) +const productsProvider = ProductsProvider._(); + +final class ProductsProvider extends $FunctionalProvider< + AsyncValue>, FutureOr>> + with $FutureModifier>, $FutureProvider> { + const ProductsProvider._( + {FutureOr> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'productsProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$productsHash(); + + @$internal + @override + $FutureProviderElement> $createElement( + $ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ProductsProvider $copyWithCreate( + FutureOr> Function( + Ref ref, + ) create, + ) { + return ProductsProvider._(create: create); + } + + @override + FutureOr> create(Ref ref) { + final _$cb = _createCb ?? products; + return _$cb(ref); + } +} + String _$productsHash() => r'1915c65cef29cadc8b0adadd6ecddf753586974b'; -/// See also [products]. -@ProviderFor(products) -final productsProvider = AutoDisposeFutureProvider>.internal( - products, - name: r'productsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$productsHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ProductsRef = AutoDisposeFutureProviderRef>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/combining_provider_states/select_async_provider/raw.dart b/website/docs/concepts/combining_provider_states/select_async_provider/raw.dart index ae35300b0..a5208e4e7 100644 --- a/website/docs/concepts/combining_provider_states/select_async_provider/raw.dart +++ b/website/docs/concepts/combining_provider_states/select_async_provider/raw.dart @@ -13,8 +13,9 @@ final configProvider = final productsProvider = FutureProvider>((ref) async { // Listens only to the host. If something else in the configurations // changes, this will not pointlessly re-evaluate our provider. - final host = await ref.watch(configProvider.selectAsync((config) => config.host)); + final host = + await ref.watch(configProvider.selectAsync((config) => config.host)); final result = await dio.get>>('$host/products'); return result.data!.map(Product.fromJson).toList(); -}); \ No newline at end of file +}); diff --git a/website/docs/concepts/combining_provider_states/todo_list_provider/codegen.g.dart b/website/docs/concepts/combining_provider_states/todo_list_provider/codegen.g.dart index 513d59ba3..fd5cc7759 100644 --- a/website/docs/concepts/combining_provider_states/todo_list_provider/codegen.g.dart +++ b/website/docs/concepts/combining_provider_states/todo_list_provider/codegen.g.dart @@ -8,20 +8,74 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(TodoList) +const todoListProvider = TodoListProvider._(); + +final class TodoListProvider extends $NotifierProvider> { + const TodoListProvider._( + {super.runNotifierBuildOverride, TodoList Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'todoListProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final TodoList Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$todoListHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + TodoList create() => _createCb?.call() ?? TodoList(); + + @$internal + @override + TodoListProvider $copyWithCreate( + TodoList Function() create, + ) { + return TodoListProvider._(create: create); + } + + @$internal + @override + TodoListProvider $copyWithBuild( + List Function( + Ref, + TodoList, + ) build, + ) { + return TodoListProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$todoListHash() => r'6c965beb867ffeee119133f0ae2e6ebeb68e6f7e'; -/// See also [TodoList]. -@ProviderFor(TodoList) -final todoListProvider = - AutoDisposeNotifierProvider>.internal( - TodoList.new, - name: r'todoListProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$todoListHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$TodoList = AutoDisposeNotifier>; +abstract class _$TodoList extends $Notifier> { + List build(); + @$internal + @override + List runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/combining_provider_states/weather_provider/codegen.dart b/website/docs/concepts/combining_provider_states/weather_provider/codegen.dart index 0d04d544e..a66b91521 100644 --- a/website/docs/concepts/combining_provider_states/weather_provider/codegen.dart +++ b/website/docs/concepts/combining_provider_states/weather_provider/codegen.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/concepts/combining_provider_states/weather_provider/codegen.g.dart b/website/docs/concepts/combining_provider_states/weather_provider/codegen.g.dart index a10243c19..ac5fc993c 100644 --- a/website/docs/concepts/combining_provider_states/weather_provider/codegen.g.dart +++ b/website/docs/concepts/combining_provider_states/weather_provider/codegen.g.dart @@ -8,37 +8,114 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(city) +const cityProvider = CityProvider._(); + +final class CityProvider extends $FunctionalProvider + with $Provider { + const CityProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'cityProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$cityHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CityProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return CityProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? city; + return _$cb(ref); + } +} + String _$cityHash() => r'6a5023a3aba119f1ecaee6c7db44b3f519e72759'; -/// See also [city]. -@ProviderFor(city) -final cityProvider = AutoDisposeProvider.internal( - city, - name: r'cityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$cityHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CityRef = AutoDisposeProviderRef; +@ProviderFor(weather) +const weatherProvider = WeatherProvider._(); + +final class WeatherProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const WeatherProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'weatherProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$weatherHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + WeatherProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return WeatherProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? weather; + return _$cb(ref); + } +} + String _$weatherHash() => r'277f005f0a4ea0bc28eaa4bc6628ba2a5d1034c8'; -/// See also [weather]. -@ProviderFor(weather) -final weatherProvider = AutoDisposeFutureProvider.internal( - weather, - name: r'weatherProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$weatherHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef WeatherRef = AutoDisposeFutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/combining_provider_states/whole_object_provider/codegen.dart b/website/docs/concepts/combining_provider_states/whole_object_provider/codegen.dart index e52b9f54b..cb3a1ec73 100644 --- a/website/docs/concepts/combining_provider_states/whole_object_provider/codegen.dart +++ b/website/docs/concepts/combining_provider_states/whole_object_provider/codegen.dart @@ -1,5 +1,4 @@ import 'package:dio/dio.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'models.dart'; diff --git a/website/docs/concepts/combining_provider_states/whole_object_provider/codegen.g.dart b/website/docs/concepts/combining_provider_states/whole_object_provider/codegen.g.dart index 9846bfce8..310381d65 100644 --- a/website/docs/concepts/combining_provider_states/whole_object_provider/codegen.g.dart +++ b/website/docs/concepts/combining_provider_states/whole_object_provider/codegen.g.dart @@ -8,37 +8,109 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(config) +const configProvider = ConfigProvider._(); + +final class ConfigProvider extends $FunctionalProvider< + AsyncValue, Stream> + with $FutureModifier, $StreamProvider { + const ConfigProvider._( + {Stream Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'configProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Stream Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$configHash(); + + @$internal + @override + $StreamProviderElement $createElement( + $ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + + @override + ConfigProvider $copyWithCreate( + Stream Function( + Ref ref, + ) create, + ) { + return ConfigProvider._(create: create); + } + + @override + Stream create(Ref ref) { + final _$cb = _createCb ?? config; + return _$cb(ref); + } +} + String _$configHash() => r'66f48a02bf939463649f0e7ad34137265e5c8b66'; -/// See also [config]. -@ProviderFor(config) -final configProvider = AutoDisposeStreamProvider.internal( - config, - name: r'configProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$configHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ConfigRef = AutoDisposeStreamProviderRef; +@ProviderFor(products) +const productsProvider = ProductsProvider._(); + +final class ProductsProvider extends $FunctionalProvider< + AsyncValue>, FutureOr>> + with $FutureModifier>, $FutureProvider> { + const ProductsProvider._( + {FutureOr> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'productsProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$productsHash(); + + @$internal + @override + $FutureProviderElement> $createElement( + $ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ProductsProvider $copyWithCreate( + FutureOr> Function( + Ref ref, + ) create, + ) { + return ProductsProvider._(create: create); + } + + @override + FutureOr> create(Ref ref) { + final _$cb = _createCb ?? products; + return _$cb(ref); + } +} + String _$productsHash() => r'd0ddbfac09629b48b568f0cc07e063bb7d649162'; -/// See also [products]. -@ProviderFor(products) -final productsProvider = AutoDisposeFutureProvider>.internal( - products, - name: r'productsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$productsHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ProductsRef = AutoDisposeFutureProviderRef>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/lifecycle_on_dispose/codegen.dart b/website/docs/concepts/lifecycle_on_dispose/codegen.dart index ab64f257e..35f832a69 100644 --- a/website/docs/concepts/lifecycle_on_dispose/codegen.dart +++ b/website/docs/concepts/lifecycle_on_dispose/codegen.dart @@ -2,7 +2,6 @@ import 'dart:async'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/concepts/lifecycle_on_dispose/codegen.g.dart b/website/docs/concepts/lifecycle_on_dispose/codegen.g.dart index 5a38dedf3..a54e676cf 100644 --- a/website/docs/concepts/lifecycle_on_dispose/codegen.g.dart +++ b/website/docs/concepts/lifecycle_on_dispose/codegen.g.dart @@ -8,21 +8,56 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider + extends $FunctionalProvider, Stream> + with $FutureModifier, $StreamProvider { + const ExampleProvider._( + {Stream Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Stream Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + @$internal + @override + $StreamProviderElement $createElement($ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + Stream Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + Stream create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + String _$exampleHash() => r'ec358d3864f4c5daaab92153afd5cf79e915aebc'; -/// See also [example]. -@ProviderFor(example) -final exampleProvider = AutoDisposeStreamProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeStreamProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/provider_observer_logger.dart b/website/docs/concepts/provider_observer_logger.dart index 40f8120e6..8aa41b77c 100644 --- a/website/docs/concepts/provider_observer_logger.dart +++ b/website/docs/concepts/provider_observer_logger.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -10,14 +11,13 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; class Logger extends ProviderObserver { @override void didUpdateProvider( - ProviderBase provider, + ProviderObserverContext context, Object? previousValue, Object? newValue, - ProviderContainer container, ) { print(''' { - "provider": "${provider.name ?? provider.runtimeType}", + "provider": "${context.provider.name ?? context.provider.runtimeType}", "newValue": "$newValue" }'''); } diff --git a/website/docs/concepts/providers/creating_a_provider/codegen.dart b/website/docs/concepts/providers/creating_a_provider/codegen.dart index 163f5b4b5..15efd2d7a 100644 --- a/website/docs/concepts/providers/creating_a_provider/codegen.dart +++ b/website/docs/concepts/providers/creating_a_provider/codegen.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/concepts/providers/creating_a_provider/codegen.g.dart b/website/docs/concepts/providers/creating_a_provider/codegen.g.dart index 5b4451aea..5e7e8aafb 100644 --- a/website/docs/concepts/providers/creating_a_provider/codegen.g.dart +++ b/website/docs/concepts/providers/creating_a_provider/codegen.g.dart @@ -8,21 +8,63 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(my) +const myProvider = MyProvider._(); + +final class MyProvider extends $FunctionalProvider + with $Provider { + const MyProvider._( + {MyValue Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyValue Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$myHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(MyValue value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + MyProvider $copyWithCreate( + MyValue Function( + Ref ref, + ) create, + ) { + return MyProvider._(create: create); + } + + @override + MyValue create(Ref ref) { + final _$cb = _createCb ?? my; + return _$cb(ref); + } +} + String _$myHash() => r'abf4b86b981ed95db3f391483b0a1497c33e98b8'; -/// See also [my]. -@ProviderFor(my) -final myProvider = AutoDisposeProvider.internal( - my, - name: r'myProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$myHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef MyRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/providers/declaring_many_providers/codegen.dart b/website/docs/concepts/providers/declaring_many_providers/codegen.dart index 17b1ccab8..276cab4dd 100644 --- a/website/docs/concepts/providers/declaring_many_providers/codegen.dart +++ b/website/docs/concepts/providers/declaring_many_providers/codegen.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/concepts/providers/declaring_many_providers/codegen.g.dart b/website/docs/concepts/providers/declaring_many_providers/codegen.g.dart index b56bee810..be7fa50dc 100644 --- a/website/docs/concepts/providers/declaring_many_providers/codegen.g.dart +++ b/website/docs/concepts/providers/declaring_many_providers/codegen.g.dart @@ -8,37 +8,121 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(city) +const cityProvider = CityProvider._(); + +final class CityProvider extends $FunctionalProvider + with $Provider { + const CityProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'cityProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$cityHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CityProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return CityProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? city; + return _$cb(ref); + } +} + String _$cityHash() => r'6a5023a3aba119f1ecaee6c7db44b3f519e72759'; -/// See also [city]. -@ProviderFor(city) -final cityProvider = AutoDisposeProvider.internal( - city, - name: r'cityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$cityHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CityRef = AutoDisposeProviderRef; +@ProviderFor(country) +const countryProvider = CountryProvider._(); + +final class CountryProvider extends $FunctionalProvider + with $Provider { + const CountryProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'countryProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$countryHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CountryProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return CountryProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? country; + return _$cb(ref); + } +} + String _$countryHash() => r'9fabd1cffe35f15a0a03339193da2d646c260137'; -/// See also [country]. -@ProviderFor(country) -final countryProvider = AutoDisposeProvider.internal( - country, - name: r'countryProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$countryHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CountryRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/providers/declaring_many_providers/raw.dart b/website/docs/concepts/providers/declaring_many_providers/raw.dart index 60a3d4786..09d8a659f 100644 --- a/website/docs/concepts/providers/declaring_many_providers/raw.dart +++ b/website/docs/concepts/providers/declaring_many_providers/raw.dart @@ -3,4 +3,4 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; /* SNIPPET START */ final cityProvider = Provider((ref) => 'London'); -final countryProvider = Provider((ref) => 'England'); \ No newline at end of file +final countryProvider = Provider((ref) => 'England'); diff --git a/website/docs/concepts/reading/counter/codegen.dart b/website/docs/concepts/reading/counter/codegen.dart index ca1edf42e..2b464eb1d 100644 --- a/website/docs/concepts/reading/counter/codegen.dart +++ b/website/docs/concepts/reading/counter/codegen.dart @@ -2,7 +2,8 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; -final repositoryProvider = Provider((ref) => Repository()); +@riverpod +Repository repository(Ref ref) => Repository(); class Repository { Future post(String url) async {} diff --git a/website/docs/concepts/reading/counter/codegen.g.dart b/website/docs/concepts/reading/counter/codegen.g.dart index a72f143e7..49c218644 100644 --- a/website/docs/concepts/reading/counter/codegen.g.dart +++ b/website/docs/concepts/reading/counter/codegen.g.dart @@ -8,19 +8,133 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** -String _$counterHash() => r'6bd7806869af024b3288645da03c077af9478083'; +@ProviderFor(repository) +const repositoryProvider = RepositoryProvider._(); + +final class RepositoryProvider + extends $FunctionalProvider + with $Provider { + const RepositoryProvider._( + {Repository Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'repositoryProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Repository Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$repositoryHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Repository value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + RepositoryProvider $copyWithCreate( + Repository Function( + Ref ref, + ) create, + ) { + return RepositoryProvider._(create: create); + } + + @override + Repository create(Ref ref) { + final _$cb = _createCb ?? repository; + return _$cb(ref); + } +} + +String _$repositoryHash() => r'6f859a9d70c3112139aaf826ee2bd541a4c001cb'; -/// See also [Counter]. @ProviderFor(Counter) -final counterProvider = AutoDisposeNotifierProvider.internal( - Counter.new, - name: r'counterProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$counterHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Counter = AutoDisposeNotifier; +const counterProvider = CounterProvider._(); + +final class CounterProvider extends $NotifierProvider { + const CounterProvider._( + {super.runNotifierBuildOverride, Counter Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counterProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Counter Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$counterHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Counter create() => _createCb?.call() ?? Counter(); + + @$internal + @override + CounterProvider $copyWithCreate( + Counter Function() create, + ) { + return CounterProvider._(create: create); + } + + @$internal + @override + CounterProvider $copyWithBuild( + int Function( + Ref, + Counter, + ) build, + ) { + return CounterProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$counterHash() => r'6bd7806869af024b3288645da03c077af9478083'; + +abstract class _$Counter extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/reading/counter/raw.dart b/website/docs/concepts/reading/counter/raw.dart index b97c93b00..579d682a9 100644 --- a/website/docs/concepts/reading/counter/raw.dart +++ b/website/docs/concepts/reading/counter/raw.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; final repositoryProvider = Provider((ref) => Repository()); diff --git a/website/docs/concepts/reading/listen/codegen.dart b/website/docs/concepts/reading/listen/codegen.dart index 3d05fccc1..b4531515a 100644 --- a/website/docs/concepts/reading/listen/codegen.dart +++ b/website/docs/concepts/reading/listen/codegen.dart @@ -1,8 +1,7 @@ // ignore_for_file: omit_local_variable_types, avoid_types_on_closure_parameters, avoid_print -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; -import '../counter/raw.dart'; +import '../counter/codegen.dart'; part 'codegen.g.dart'; diff --git a/website/docs/concepts/reading/listen/codegen.g.dart b/website/docs/concepts/reading/listen/codegen.g.dart index fdba1782e..b3034258c 100644 --- a/website/docs/concepts/reading/listen/codegen.g.dart +++ b/website/docs/concepts/reading/listen/codegen.g.dart @@ -8,21 +8,63 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(another) +const anotherProvider = AnotherProvider._(); + +final class AnotherProvider extends $FunctionalProvider + with $Provider { + const AnotherProvider._( + {void Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'anotherProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final void Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$anotherHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(void value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + AnotherProvider $copyWithCreate( + void Function( + Ref ref, + ) create, + ) { + return AnotherProvider._(create: create); + } + + @override + void create(Ref ref) { + final _$cb = _createCb ?? another; + return _$cb(ref); + } +} + String _$anotherHash() => r'1901cd6ee57ea427f82e6d5bbee79e91ddf71065'; -/// See also [another]. -@ProviderFor(another) -final anotherProvider = AutoDisposeProvider.internal( - another, - name: r'anotherProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$anotherHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef AnotherRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/reading/listen_build/codegen.g.dart b/website/docs/concepts/reading/listen_build/codegen.g.dart index eedfc7060..a8fbbd8c7 100644 --- a/website/docs/concepts/reading/listen_build/codegen.g.dart +++ b/website/docs/concepts/reading/listen_build/codegen.g.dart @@ -8,19 +8,74 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(Counter) +const counterProvider = CounterProvider._(); + +final class CounterProvider extends $NotifierProvider { + const CounterProvider._( + {super.runNotifierBuildOverride, Counter Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counterProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Counter Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$counterHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Counter create() => _createCb?.call() ?? Counter(); + + @$internal + @override + CounterProvider $copyWithCreate( + Counter Function() create, + ) { + return CounterProvider._(create: create); + } + + @$internal + @override + CounterProvider $copyWithBuild( + int Function( + Ref, + Counter, + ) build, + ) { + return CounterProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$counterHash() => r'4320f811608c7a6e7342b83e3031965a34f7cb8e'; -/// See also [Counter]. -@ProviderFor(Counter) -final counterProvider = AutoDisposeNotifierProvider.internal( - Counter.new, - name: r'counterProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$counterHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Counter = AutoDisposeNotifier; +abstract class _$Counter extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/reading/listen_build/codegen_hooks.g.dart b/website/docs/concepts/reading/listen_build/codegen_hooks.g.dart index ffc661bce..ae33ca0fd 100644 --- a/website/docs/concepts/reading/listen_build/codegen_hooks.g.dart +++ b/website/docs/concepts/reading/listen_build/codegen_hooks.g.dart @@ -8,19 +8,74 @@ part of 'codegen_hooks.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(Counter) +const counterProvider = CounterProvider._(); + +final class CounterProvider extends $NotifierProvider { + const CounterProvider._( + {super.runNotifierBuildOverride, Counter Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counterProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Counter Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$counterHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Counter create() => _createCb?.call() ?? Counter(); + + @$internal + @override + CounterProvider $copyWithCreate( + Counter Function() create, + ) { + return CounterProvider._(create: create); + } + + @$internal + @override + CounterProvider $copyWithBuild( + int Function( + Ref, + Counter, + ) build, + ) { + return CounterProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$counterHash() => r'4320f811608c7a6e7342b83e3031965a34f7cb8e'; -/// See also [Counter]. -@ProviderFor(Counter) -final counterProvider = AutoDisposeNotifierProvider.internal( - Counter.new, - name: r'counterProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$counterHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Counter = AutoDisposeNotifier; +abstract class _$Counter extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/reading/listen_build/raw.dart b/website/docs/concepts/reading/listen_build/raw.dart index e59ddaf57..87ba0a7d8 100644 --- a/website/docs/concepts/reading/listen_build/raw.dart +++ b/website/docs/concepts/reading/listen_build/raw.dart @@ -2,12 +2,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import '../counter/raw.dart'; /* SNIPPET START */ -final counterProvider = - StateNotifierProvider(Counter.new); +final counterProvider = StateNotifierProvider(Counter.new); class HomeView extends ConsumerWidget { const HomeView({super.key}); diff --git a/website/docs/concepts/reading/listen_build/raw_hooks.dart b/website/docs/concepts/reading/listen_build/raw_hooks.dart index 9f86fc19f..ac38732a5 100644 --- a/website/docs/concepts/reading/listen_build/raw_hooks.dart +++ b/website/docs/concepts/reading/listen_build/raw_hooks.dart @@ -2,13 +2,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../counter/raw.dart'; /* SNIPPET START */ -final counterProvider = - StateNotifierProvider(Counter.new); +final counterProvider = StateNotifierProvider(Counter.new); class HomeView extends HookConsumerWidget { const HomeView({super.key}); diff --git a/website/docs/concepts/reading/provider/codegen.dart b/website/docs/concepts/reading/provider/codegen.dart index 46e832fe8..954941a9b 100644 --- a/website/docs/concepts/reading/provider/codegen.dart +++ b/website/docs/concepts/reading/provider/codegen.dart @@ -1,5 +1,4 @@ // ignore_for_file: avoid_positional_boolean_parameters -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/concepts/reading/provider/codegen.g.dart b/website/docs/concepts/reading/provider/codegen.g.dart index 8b3ccc481..061312636 100644 --- a/website/docs/concepts/reading/provider/codegen.g.dart +++ b/website/docs/concepts/reading/provider/codegen.g.dart @@ -8,37 +8,122 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(repository) +const repositoryProvider = RepositoryProvider._(); + +final class RepositoryProvider + extends $FunctionalProvider + with $Provider { + const RepositoryProvider._( + {Repository Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'repositoryProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Repository Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$repositoryHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Repository value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + RepositoryProvider $copyWithCreate( + Repository Function( + Ref ref, + ) create, + ) { + return RepositoryProvider._(create: create); + } + + @override + Repository create(Ref ref) { + final _$cb = _createCb ?? repository; + return _$cb(ref); + } +} + String _$repositoryHash() => r'6f859a9d70c3112139aaf826ee2bd541a4c001cb'; -/// See also [repository]. -@ProviderFor(repository) -final repositoryProvider = AutoDisposeProvider.internal( - repository, - name: r'repositoryProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$repositoryHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef RepositoryRef = AutoDisposeProviderRef; +@ProviderFor(value) +const valueProvider = ValueProvider._(); + +final class ValueProvider extends $FunctionalProvider + with $Provider { + const ValueProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'valueProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$valueHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ValueProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return ValueProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? value; + return _$cb(ref); + } +} + String _$valueHash() => r'fcb38a2d2c3755f3691e73cd163e8c895d1af4b5'; -/// See also [value]. -@ProviderFor(value) -final valueProvider = AutoDisposeProvider.internal( - value, - name: r'valueProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$valueHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ValueRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/reading/read/codegen.g.dart b/website/docs/concepts/reading/read/codegen.g.dart index d7eefaa50..43d8edd39 100644 --- a/website/docs/concepts/reading/read/codegen.g.dart +++ b/website/docs/concepts/reading/read/codegen.g.dart @@ -8,19 +8,74 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(Counter) +const counterProvider = CounterProvider._(); + +final class CounterProvider extends $NotifierProvider { + const CounterProvider._( + {super.runNotifierBuildOverride, Counter Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counterProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Counter Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$counterHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Counter create() => _createCb?.call() ?? Counter(); + + @$internal + @override + CounterProvider $copyWithCreate( + Counter Function() create, + ) { + return CounterProvider._(create: create); + } + + @$internal + @override + CounterProvider $copyWithBuild( + int Function( + Ref, + Counter, + ) build, + ) { + return CounterProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$counterHash() => r'600c6beb47b3732049b6955a9e49d7eef30c741a'; -/// See also [Counter]. -@ProviderFor(Counter) -final counterProvider = AutoDisposeNotifierProvider.internal( - Counter.new, - name: r'counterProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$counterHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Counter = AutoDisposeNotifier; +abstract class _$Counter extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/reading/read/codegen_hooks.g.dart b/website/docs/concepts/reading/read/codegen_hooks.g.dart index bc983395f..ba15f2c85 100644 --- a/website/docs/concepts/reading/read/codegen_hooks.g.dart +++ b/website/docs/concepts/reading/read/codegen_hooks.g.dart @@ -8,19 +8,74 @@ part of 'codegen_hooks.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(Counter) +const counterProvider = CounterProvider._(); + +final class CounterProvider extends $NotifierProvider { + const CounterProvider._( + {super.runNotifierBuildOverride, Counter Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counterProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Counter Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$counterHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Counter create() => _createCb?.call() ?? Counter(); + + @$internal + @override + CounterProvider $copyWithCreate( + Counter Function() create, + ) { + return CounterProvider._(create: create); + } + + @$internal + @override + CounterProvider $copyWithBuild( + int Function( + Ref, + Counter, + ) build, + ) { + return CounterProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$counterHash() => r'600c6beb47b3732049b6955a9e49d7eef30c741a'; -/// See also [Counter]. -@ProviderFor(Counter) -final counterProvider = AutoDisposeNotifierProvider.internal( - Counter.new, - name: r'counterProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$counterHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Counter = AutoDisposeNotifier; +abstract class _$Counter extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/reading/read/raw.dart b/website/docs/concepts/reading/read/raw.dart index 7d8685155..c30eeb5f2 100644 --- a/website/docs/concepts/reading/read/raw.dart +++ b/website/docs/concepts/reading/read/raw.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import '../counter/raw.dart'; diff --git a/website/docs/concepts/reading/read/raw_hooks.dart b/website/docs/concepts/reading/read/raw_hooks.dart index acc6ba980..bde77d3a8 100644 --- a/website/docs/concepts/reading/read/raw_hooks.dart +++ b/website/docs/concepts/reading/read/raw_hooks.dart @@ -2,14 +2,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../counter/raw.dart'; /* SNIPPET START */ -final counterProvider = - StateNotifierProvider(Counter.new); +final counterProvider = StateNotifierProvider(Counter.new); class HomeView extends HookConsumerWidget { const HomeView({super.key}); @@ -17,7 +17,7 @@ class HomeView extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final greeting = useState('Hello'); - + return Scaffold( body: Center(child: Text(greeting.value)), floatingActionButton: FloatingActionButton( diff --git a/website/docs/concepts/reading/read_build/codegen.g.dart b/website/docs/concepts/reading/read_build/codegen.g.dart index d7eefaa50..43d8edd39 100644 --- a/website/docs/concepts/reading/read_build/codegen.g.dart +++ b/website/docs/concepts/reading/read_build/codegen.g.dart @@ -8,19 +8,74 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(Counter) +const counterProvider = CounterProvider._(); + +final class CounterProvider extends $NotifierProvider { + const CounterProvider._( + {super.runNotifierBuildOverride, Counter Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counterProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Counter Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$counterHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Counter create() => _createCb?.call() ?? Counter(); + + @$internal + @override + CounterProvider $copyWithCreate( + Counter Function() create, + ) { + return CounterProvider._(create: create); + } + + @$internal + @override + CounterProvider $copyWithBuild( + int Function( + Ref, + Counter, + ) build, + ) { + return CounterProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$counterHash() => r'600c6beb47b3732049b6955a9e49d7eef30c741a'; -/// See also [Counter]. -@ProviderFor(Counter) -final counterProvider = AutoDisposeNotifierProvider.internal( - Counter.new, - name: r'counterProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$counterHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Counter = AutoDisposeNotifier; +abstract class _$Counter extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/reading/read_build/raw.dart b/website/docs/concepts/reading/read_build/raw.dart index 266e00224..f90cd968f 100644 --- a/website/docs/concepts/reading/read_build/raw.dart +++ b/website/docs/concepts/reading/read_build/raw.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/docs/concepts/reading/read_notifier_build/codegen.g.dart b/website/docs/concepts/reading/read_notifier_build/codegen.g.dart index d7eefaa50..43d8edd39 100644 --- a/website/docs/concepts/reading/read_notifier_build/codegen.g.dart +++ b/website/docs/concepts/reading/read_notifier_build/codegen.g.dart @@ -8,19 +8,74 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(Counter) +const counterProvider = CounterProvider._(); + +final class CounterProvider extends $NotifierProvider { + const CounterProvider._( + {super.runNotifierBuildOverride, Counter Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counterProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Counter Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$counterHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Counter create() => _createCb?.call() ?? Counter(); + + @$internal + @override + CounterProvider $copyWithCreate( + Counter Function() create, + ) { + return CounterProvider._(create: create); + } + + @$internal + @override + CounterProvider $copyWithBuild( + int Function( + Ref, + Counter, + ) build, + ) { + return CounterProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$counterHash() => r'600c6beb47b3732049b6955a9e49d7eef30c741a'; -/// See also [Counter]. -@ProviderFor(Counter) -final counterProvider = AutoDisposeNotifierProvider.internal( - Counter.new, - name: r'counterProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$counterHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Counter = AutoDisposeNotifier; +abstract class _$Counter extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/reading/read_notifier_build/raw.dart b/website/docs/concepts/reading/read_notifier_build/raw.dart index 0ec5b5958..95bdab6c5 100644 --- a/website/docs/concepts/reading/read_notifier_build/raw.dart +++ b/website/docs/concepts/reading/read_notifier_build/raw.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/docs/concepts/reading/watch/codegen.dart b/website/docs/concepts/reading/watch/codegen.dart index ebbaa8d7c..947af2a51 100644 --- a/website/docs/concepts/reading/watch/codegen.dart +++ b/website/docs/concepts/reading/watch/codegen.dart @@ -1,6 +1,5 @@ // ignore_for_file: omit_local_variable_types, avoid_types_on_closure_parameters, avoid_print -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/concepts/reading/watch/codegen.g.dart b/website/docs/concepts/reading/watch/codegen.g.dart index 36d52d9fc..185d3178b 100644 --- a/website/docs/concepts/reading/watch/codegen.g.dart +++ b/website/docs/concepts/reading/watch/codegen.g.dart @@ -8,52 +8,192 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(filterType) +const filterTypeProvider = FilterTypeProvider._(); + +final class FilterTypeProvider + extends $FunctionalProvider + with $Provider { + const FilterTypeProvider._( + {FilterType Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'filterTypeProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FilterType Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$filterTypeHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(FilterType value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + FilterTypeProvider $copyWithCreate( + FilterType Function( + Ref ref, + ) create, + ) { + return FilterTypeProvider._(create: create); + } + + @override + FilterType create(Ref ref) { + final _$cb = _createCb ?? filterType; + return _$cb(ref); + } +} + String _$filterTypeHash() => r'68d61a593d49306927c26fbcc66ea9fffa7c52f5'; -/// See also [filterType]. -@ProviderFor(filterType) -final filterTypeProvider = AutoDisposeProvider.internal( - filterType, - name: r'filterTypeProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$filterTypeHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef FilterTypeRef = AutoDisposeProviderRef; -String _$filteredTodoListHash() => r'0508935737f2cb9718bd8150111135cb433bfaeb'; +@ProviderFor(Todos) +const todosProvider = TodosProvider._(); + +final class TodosProvider extends $NotifierProvider> { + const TodosProvider._( + {super.runNotifierBuildOverride, Todos Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'todosProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Todos Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$todosHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + Todos create() => _createCb?.call() ?? Todos(); + + @$internal + @override + TodosProvider $copyWithCreate( + Todos Function() create, + ) { + return TodosProvider._(create: create); + } + + @$internal + @override + TodosProvider $copyWithBuild( + List Function( + Ref, + Todos, + ) build, + ) { + return TodosProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} -/// See also [filteredTodoList]. -@ProviderFor(filteredTodoList) -final filteredTodoListProvider = AutoDisposeProvider>.internal( - filteredTodoList, - name: r'filteredTodoListProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$filteredTodoListHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef FilteredTodoListRef = AutoDisposeProviderRef>; String _$todosHash() => r'b66ac2b1e5cf7ac7957d25864cfdffad1af233a6'; -/// See also [Todos]. -@ProviderFor(Todos) -final todosProvider = AutoDisposeNotifierProvider>.internal( - Todos.new, - name: r'todosProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$todosHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Todos = AutoDisposeNotifier>; +abstract class _$Todos extends $Notifier> { + List build(); + @$internal + @override + List runBuild() => build(); +} + +@ProviderFor(filteredTodoList) +const filteredTodoListProvider = FilteredTodoListProvider._(); + +final class FilteredTodoListProvider + extends $FunctionalProvider, List> + with $Provider> { + const FilteredTodoListProvider._( + {List Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'filteredTodoListProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final List Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$filteredTodoListHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + $ProviderElement> $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + FilteredTodoListProvider $copyWithCreate( + List Function( + Ref ref, + ) create, + ) { + return FilteredTodoListProvider._(create: create); + } + + @override + List create(Ref ref) { + final _$cb = _createCb ?? filteredTodoList; + return _$cb(ref); + } +} + +String _$filteredTodoListHash() => r'0508935737f2cb9718bd8150111135cb433bfaeb'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/reading/watch/raw.dart b/website/docs/concepts/reading/watch/raw.dart index 625c0fe9a..bde11d2e4 100644 --- a/website/docs/concepts/reading/watch/raw.dart +++ b/website/docs/concepts/reading/watch/raw.dart @@ -1,6 +1,7 @@ // ignore_for_file: omit_local_variable_types import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; enum FilterType { none, diff --git a/website/docs/concepts/reading/watch_build/codegen.g.dart b/website/docs/concepts/reading/watch_build/codegen.g.dart index db4580314..03595dcf0 100644 --- a/website/docs/concepts/reading/watch_build/codegen.g.dart +++ b/website/docs/concepts/reading/watch_build/codegen.g.dart @@ -8,36 +8,132 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** -String _$counterHash() => r'784ece48cb20fcfdec1553774ecfbd381d1e081f'; +@ProviderFor(TodoList) +const todoListProvider = TodoListProvider._(); + +final class TodoListProvider extends $NotifierProvider> { + const TodoListProvider._( + {super.runNotifierBuildOverride, TodoList Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'todoListProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final TodoList Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$todoListHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + TodoList create() => _createCb?.call() ?? TodoList(); + + @$internal + @override + TodoListProvider $copyWithCreate( + TodoList Function() create, + ) { + return TodoListProvider._(create: create); + } + + @$internal + @override + TodoListProvider $copyWithBuild( + List Function( + Ref, + TodoList, + ) build, + ) { + return TodoListProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} -/// See also [counter]. -@ProviderFor(counter) -final counterProvider = AutoDisposeProvider.internal( - counter, - name: r'counterProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$counterHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CounterRef = AutoDisposeProviderRef; String _$todoListHash() => r'77f007cd4f5105330a4c2ab8555ea0d1716945c1'; -/// See also [TodoList]. -@ProviderFor(TodoList) -final todoListProvider = - AutoDisposeNotifierProvider>.internal( - TodoList.new, - name: r'todoListProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$todoListHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$TodoList = AutoDisposeNotifier>; +abstract class _$TodoList extends $Notifier> { + List build(); + @$internal + @override + List runBuild() => build(); +} + +@ProviderFor(counter) +const counterProvider = CounterProvider._(); + +final class CounterProvider extends $FunctionalProvider + with $Provider { + const CounterProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counterProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$counterHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CounterProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return CounterProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? counter; + return _$cb(ref); + } +} + +String _$counterHash() => r'784ece48cb20fcfdec1553774ecfbd381d1e081f'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/reading/watch_build/codegen_hooks.g.dart b/website/docs/concepts/reading/watch_build/codegen_hooks.g.dart index 81ea2a821..7ba745f62 100644 --- a/website/docs/concepts/reading/watch_build/codegen_hooks.g.dart +++ b/website/docs/concepts/reading/watch_build/codegen_hooks.g.dart @@ -8,36 +8,132 @@ part of 'codegen_hooks.dart'; // RiverpodGenerator // ************************************************************************** -String _$counterHash() => r'784ece48cb20fcfdec1553774ecfbd381d1e081f'; +@ProviderFor(TodoList) +const todoListProvider = TodoListProvider._(); + +final class TodoListProvider extends $NotifierProvider> { + const TodoListProvider._( + {super.runNotifierBuildOverride, TodoList Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'todoListProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final TodoList Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$todoListHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + TodoList create() => _createCb?.call() ?? TodoList(); + + @$internal + @override + TodoListProvider $copyWithCreate( + TodoList Function() create, + ) { + return TodoListProvider._(create: create); + } + + @$internal + @override + TodoListProvider $copyWithBuild( + List Function( + Ref, + TodoList, + ) build, + ) { + return TodoListProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} -/// See also [counter]. -@ProviderFor(counter) -final counterProvider = AutoDisposeProvider.internal( - counter, - name: r'counterProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$counterHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CounterRef = AutoDisposeProviderRef; String _$todoListHash() => r'77f007cd4f5105330a4c2ab8555ea0d1716945c1'; -/// See also [TodoList]. -@ProviderFor(TodoList) -final todoListProvider = - AutoDisposeNotifierProvider>.internal( - TodoList.new, - name: r'todoListProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$todoListHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$TodoList = AutoDisposeNotifier>; +abstract class _$TodoList extends $Notifier> { + List build(); + @$internal + @override + List runBuild() => build(); +} + +@ProviderFor(counter) +const counterProvider = CounterProvider._(); + +final class CounterProvider extends $FunctionalProvider + with $Provider { + const CounterProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counterProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$counterHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CounterProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return CounterProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? counter; + return _$cb(ref); + } +} + +String _$counterHash() => r'784ece48cb20fcfdec1553774ecfbd381d1e081f'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/reading/watch_build/raw.dart b/website/docs/concepts/reading/watch_build/raw.dart index 8d5d430eb..90639ac8c 100644 --- a/website/docs/concepts/reading/watch_build/raw.dart +++ b/website/docs/concepts/reading/watch_build/raw.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; enum FilterType { none, diff --git a/website/docs/concepts/reading/watch_build/raw_hooks.dart b/website/docs/concepts/reading/watch_build/raw_hooks.dart index 5f297850a..f125f6553 100644 --- a/website/docs/concepts/reading/watch_build/raw_hooks.dart +++ b/website/docs/concepts/reading/watch_build/raw_hooks.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; enum FilterType { @@ -28,7 +29,7 @@ class HomeView extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { // You can use hooks inside a HookConsumerWidget final greeting = useState('Hello'); - + // use ref to listen to a provider final counter = ref.watch(counterProvider); diff --git a/website/docs/concepts/reading/watch_notifier_build/codegen.g.dart b/website/docs/concepts/reading/watch_notifier_build/codegen.g.dart index d7eefaa50..43d8edd39 100644 --- a/website/docs/concepts/reading/watch_notifier_build/codegen.g.dart +++ b/website/docs/concepts/reading/watch_notifier_build/codegen.g.dart @@ -8,19 +8,74 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(Counter) +const counterProvider = CounterProvider._(); + +final class CounterProvider extends $NotifierProvider { + const CounterProvider._( + {super.runNotifierBuildOverride, Counter Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counterProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Counter Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$counterHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Counter create() => _createCb?.call() ?? Counter(); + + @$internal + @override + CounterProvider $copyWithCreate( + Counter Function() create, + ) { + return CounterProvider._(create: create); + } + + @$internal + @override + CounterProvider $copyWithBuild( + int Function( + Ref, + Counter, + ) build, + ) { + return CounterProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$counterHash() => r'600c6beb47b3732049b6955a9e49d7eef30c741a'; -/// See also [Counter]. -@ProviderFor(Counter) -final counterProvider = AutoDisposeNotifierProvider.internal( - Counter.new, - name: r'counterProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$counterHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Counter = AutoDisposeNotifier; +abstract class _$Counter extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/concepts/reading/watch_notifier_build/raw.dart b/website/docs/concepts/reading/watch_notifier_build/raw.dart index 66c630717..94d77b5e3 100644 --- a/website/docs/concepts/reading/watch_notifier_build/raw.dart +++ b/website/docs/concepts/reading/watch_notifier_build/raw.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/docs/concepts/subtree_scope.dart b/website/docs/concepts/subtree_scope.dart index 9aa1903e5..31d6bc5bd 100644 --- a/website/docs/concepts/subtree_scope.dart +++ b/website/docs/concepts/subtree_scope.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -52,7 +53,7 @@ class Home extends ConsumerWidget { // This particular display will use the provider state from the root ProviderScope const CounterDisplay(), ], - )); + ),); } } diff --git a/website/docs/concepts/theme_scope.dart b/website/docs/concepts/theme_scope.dart index d17f15f02..ccd34647d 100644 --- a/website/docs/concepts/theme_scope.dart +++ b/website/docs/concepts/theme_scope.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -43,7 +44,7 @@ class Home extends ConsumerWidget { child: const Text('Increment Count'), ), ], - )); + ),); } } diff --git a/website/docs/concepts/why_immutability/codegen.g.dart b/website/docs/concepts/why_immutability/codegen.g.dart index 3b9ae9c13..f088cdf2c 100644 --- a/website/docs/concepts/why_immutability/codegen.g.dart +++ b/website/docs/concepts/why_immutability/codegen.g.dart @@ -8,21 +8,75 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(ThemeNotifier) +const themeNotifierProvider = ThemeNotifierProvider._(); + +final class ThemeNotifierProvider + extends $NotifierProvider { + const ThemeNotifierProvider._( + {super.runNotifierBuildOverride, ThemeNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'themeNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final ThemeNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$themeNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(ThemeSettings value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + ThemeNotifier create() => _createCb?.call() ?? ThemeNotifier(); + + @$internal + @override + ThemeNotifierProvider $copyWithCreate( + ThemeNotifier Function() create, + ) { + return ThemeNotifierProvider._(create: create); + } + + @$internal + @override + ThemeNotifierProvider $copyWithBuild( + ThemeSettings Function( + Ref, + ThemeNotifier, + ) build, + ) { + return ThemeNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$themeNotifierHash() => r'e119d56d9bf8b8d7c19624997f99d116098b45e9'; -/// See also [ThemeNotifier]. -@ProviderFor(ThemeNotifier) -final themeNotifierProvider = - AutoDisposeNotifierProvider.internal( - ThemeNotifier.new, - name: r'themeNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$themeNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$ThemeNotifier = AutoDisposeNotifier; +abstract class _$ThemeNotifier extends $Notifier { + ThemeSettings build(); + @$internal + @override + ThemeSettings runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/cookbooks/testing_dart.dart b/website/docs/cookbooks/testing_dart.dart index 2d25bb8e8..73c643a16 100644 --- a/website/docs/cookbooks/testing_dart.dart +++ b/website/docs/cookbooks/testing_dart.dart @@ -22,7 +22,7 @@ test('override repositoryProvider', () async { // Override the behavior of repositoryProvider to return // FakeRepository instead of Repository. /* highlight-start */ - repositoryProvider.overrideWithValue(FakeRepository()) + repositoryProvider.overrideWithValue(FakeRepository()), /* highlight-end */ // We do not have to override `todoListProvider`, it will automatically // use the overridden repositoryProvider diff --git a/website/docs/cookbooks/testing_flutter.dart b/website/docs/cookbooks/testing_flutter.dart index 6685a1bdb..650642e95 100644 --- a/website/docs/cookbooks/testing_flutter.dart +++ b/website/docs/cookbooks/testing_flutter.dart @@ -26,7 +26,7 @@ testWidgets('override repositoryProvider', (tester) async { // Override the behavior of repositoryProvider to return // FakeRepository instead of Repository. /* highlight-start */ - repositoryProvider.overrideWithValue(FakeRepository()) + repositoryProvider.overrideWithValue(FakeRepository()), /* highlight-end */ // We do not have to override `todoListProvider`, it will automatically // use the overridden repositoryProvider diff --git a/website/docs/cookbooks/testing_full.dart b/website/docs/cookbooks/testing_full.dart index 0f79b4ee8..e75b5747f 100644 --- a/website/docs/cookbooks/testing_full.dart +++ b/website/docs/cookbooks/testing_full.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -56,24 +55,27 @@ void main() { await tester.pumpWidget( ProviderScope( overrides: [ - repositoryProvider.overrideWithValue(FakeRepository()) + repositoryProvider.overrideWithValue(FakeRepository()), ], // Our application, which will read from todoListProvider to display the todo-list. // You may extract this into a MyApp widget child: MaterialApp( home: Scaffold( - body: Consumer(builder: (context, ref, _) { - final todos = ref.watch(todoListProvider); - // The list of todos is loading or in error - if (todos.asData == null) { - return const CircularProgressIndicator(); - } - return ListView( - children: [ - for (final todo in todos.asData!.value) TodoItem(todo: todo) - ], - ); - }), + body: Consumer( + builder: (context, ref, _) { + final todos = ref.watch(todoListProvider); + // The list of todos is loading or in error + if (todos.asData == null) { + return const CircularProgressIndicator(); + } + return ListView( + children: [ + for (final todo in todos.asData!.value) + TodoItem(todo: todo), + ], + ); + }, + ), ), ), ), @@ -96,4 +98,4 @@ void main() { .having((s) => s.todo.completed, 'todo.completed', false), ]); }); -} \ No newline at end of file +} diff --git a/website/docs/cookbooks/testing_original_test_dart.dart b/website/docs/cookbooks/testing_original_test_dart.dart index 6995de9b8..d78668653 100644 --- a/website/docs/cookbooks/testing_original_test_dart.dart +++ b/website/docs/cookbooks/testing_original_test_dart.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:riverpod/riverpod.dart'; diff --git a/website/docs/cookbooks/testing_original_test_flutter.dart b/website/docs/cookbooks/testing_original_test_flutter.dart index f76abe181..a3044da37 100644 --- a/website/docs/cookbooks/testing_original_test_flutter.dart +++ b/website/docs/cookbooks/testing_original_test_flutter.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; /* SNIPPET START */ @@ -24,7 +25,7 @@ class MyApp extends StatelessWidget { onPressed: () => ref.read(counterProvider.notifier).state++, child: Text('$counter'), ); - }), + },), ); } } diff --git a/website/docs/essentials/auto_dispose/cache_for_usage/codegen.dart b/website/docs/essentials/auto_dispose/cache_for_usage/codegen.dart index 69dad3871..a19f6a897 100644 --- a/website/docs/essentials/auto_dispose/cache_for_usage/codegen.dart +++ b/website/docs/essentials/auto_dispose/cache_for_usage/codegen.dart @@ -1,6 +1,5 @@ // ignore_for_file: unused_local_variable -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; import 'package:riverpod_annotation/riverpod_annotation.dart'; diff --git a/website/docs/essentials/auto_dispose/cache_for_usage/codegen.g.dart b/website/docs/essentials/auto_dispose/cache_for_usage/codegen.g.dart index 745df8e49..4544d0d25 100644 --- a/website/docs/essentials/auto_dispose/cache_for_usage/codegen.g.dart +++ b/website/docs/essentials/auto_dispose/cache_for_usage/codegen.g.dart @@ -8,21 +8,56 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const ExampleProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + String _$exampleHash() => r'7721b15eade2919325624bb7c4fd0bfb0cfc3e68'; -/// See also [example]. -@ProviderFor(example) -final exampleProvider = AutoDisposeFutureProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeFutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/auto_dispose/cache_for_usage/raw.dart b/website/docs/essentials/auto_dispose/cache_for_usage/raw.dart index 8202e00c8..08e46f977 100644 --- a/website/docs/essentials/auto_dispose/cache_for_usage/raw.dart +++ b/website/docs/essentials/auto_dispose/cache_for_usage/raw.dart @@ -1,7 +1,7 @@ // ignore_for_file: unused_local_variable import 'package:http/http.dart' as http; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; import '../cache_for_extension.dart'; diff --git a/website/docs/essentials/auto_dispose/codegen_keep_alive.dart b/website/docs/essentials/auto_dispose/codegen_keep_alive.dart index 48eba50c6..74af13186 100644 --- a/website/docs/essentials/auto_dispose/codegen_keep_alive.dart +++ b/website/docs/essentials/auto_dispose/codegen_keep_alive.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen_keep_alive.g.dart'; diff --git a/website/docs/essentials/auto_dispose/codegen_keep_alive.g.dart b/website/docs/essentials/auto_dispose/codegen_keep_alive.g.dart index 69237ff6f..575faac55 100644 --- a/website/docs/essentials/auto_dispose/codegen_keep_alive.g.dart +++ b/website/docs/essentials/auto_dispose/codegen_keep_alive.g.dart @@ -8,21 +8,63 @@ part of 'codegen_keep_alive.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider extends $FunctionalProvider + with $Provider { + const ExampleProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: false, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + String _$exampleHash() => r'd11e74682b7fd8010a0c2815b780a44a15b7a3da'; -/// See also [example]. -@ProviderFor(example) -final exampleProvider = Provider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = ProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/auto_dispose/invalidate_family_example/codegen.g.dart b/website/docs/essentials/auto_dispose/invalidate_family_example/codegen.g.dart index ebbe6a215..f63db73da 100644 --- a/website/docs/essentials/auto_dispose/invalidate_family_example/codegen.g.dart +++ b/website/docs/essentials/auto_dispose/invalidate_family_example/codegen.g.dart @@ -8,154 +8,135 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** -String _$labelHash() => r'c53d17dd111313633bd7ca6d6cf6b48dded58ca5'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// See also [label]. @ProviderFor(label) -const labelProvider = LabelFamily(); - -/// See also [label]. -class LabelFamily extends Family { - /// See also [label]. - const LabelFamily(); +const labelProvider = LabelFamily._(); + +final class LabelProvider extends $FunctionalProvider + with $Provider { + const LabelProvider._( + {required LabelFamily super.from, + required String super.argument, + String Function( + Ref ref, + String userName, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'labelProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// See also [label]. - LabelProvider call( + final String Function( + Ref ref, String userName, - ) { - return LabelProvider( - userName, - ); - } + )? _createCb; @override - LabelProvider getProviderOverride( - covariant LabelProvider provider, - ) { - return call( - provider.userName, - ); - } - - static const Iterable? _dependencies = null; + String debugGetCreateSourceHash() => _$labelHash(); @override - Iterable? get dependencies => _dependencies; + String toString() { + return r'labelProvider' + '' + '($argument)'; + } - static const Iterable? _allTransitiveDependencies = null; + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + @$internal @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - String? get name => r'labelProvider'; -} - -/// See also [label]. -class LabelProvider extends AutoDisposeProvider { - /// See also [label]. - LabelProvider( - String userName, - ) : this._internal( - (ref) => label( - ref as LabelRef, - userName, - ), - from: labelProvider, - name: r'labelProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$labelHash, - dependencies: LabelFamily._dependencies, - allTransitiveDependencies: LabelFamily._allTransitiveDependencies, - userName: userName, - ); - - LabelProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.userName, - }) : super.internal(); - - final String userName; - - @override - Override overrideWith( - String Function(LabelRef provider) create, + LabelProvider $copyWithCreate( + String Function( + Ref ref, + ) create, ) { - return ProviderOverride( - origin: this, - override: LabelProvider._internal( - (ref) => create(ref as LabelRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - userName: userName, - ), - ); + return LabelProvider._( + argument: argument as String, + from: from! as LabelFamily, + create: ( + ref, + String userName, + ) => + create(ref)); } @override - AutoDisposeProviderElement createElement() { - return _LabelProviderElement(this); + String create(Ref ref) { + final _$cb = _createCb ?? label; + final argument = this.argument as String; + return _$cb( + ref, + argument, + ); } @override bool operator ==(Object other) { - return other is LabelProvider && other.userName == userName; + return other is LabelProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, userName.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin LabelRef on AutoDisposeProviderRef { - /// The parameter `userName` of this provider. - String get userName; -} +String _$labelHash() => r'c53d17dd111313633bd7ca6d6cf6b48dded58ca5'; + +final class LabelFamily extends Family { + const LabelFamily._() + : super( + retry: null, + name: r'labelProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); -class _LabelProviderElement extends AutoDisposeProviderElement - with LabelRef { - _LabelProviderElement(super.provider); + LabelProvider call( + String userName, + ) => + LabelProvider._(argument: userName, from: this); @override - String get userName => (origin as LabelProvider).userName; + String debugGetCreateSourceHash() => _$labelHash(); + + @override + String toString() => r'labelProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + String Function( + Ref ref, + String args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as LabelProvider; + + final argument = provider.argument as String; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/auto_dispose/keep_alive/codegen.dart b/website/docs/essentials/auto_dispose/keep_alive/codegen.dart index 8d2afa235..a3f17cc7c 100644 --- a/website/docs/essentials/auto_dispose/keep_alive/codegen.dart +++ b/website/docs/essentials/auto_dispose/keep_alive/codegen.dart @@ -1,6 +1,5 @@ // ignore_for_file: unused_local_variable -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; import 'package:riverpod_annotation/riverpod_annotation.dart'; diff --git a/website/docs/essentials/auto_dispose/keep_alive/codegen.g.dart b/website/docs/essentials/auto_dispose/keep_alive/codegen.g.dart index d08c9db1f..79e5e3df4 100644 --- a/website/docs/essentials/auto_dispose/keep_alive/codegen.g.dart +++ b/website/docs/essentials/auto_dispose/keep_alive/codegen.g.dart @@ -8,21 +8,56 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const ExampleProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + String _$exampleHash() => r'47fbbb04e2e5aef4d37c361939a7df40ef05e851'; -/// See also [example]. -@ProviderFor(example) -final exampleProvider = AutoDisposeFutureProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeFutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/auto_dispose/keep_alive/raw.dart b/website/docs/essentials/auto_dispose/keep_alive/raw.dart index ab87e9fc4..7d7e7c433 100644 --- a/website/docs/essentials/auto_dispose/keep_alive/raw.dart +++ b/website/docs/essentials/auto_dispose/keep_alive/raw.dart @@ -1,7 +1,7 @@ // ignore_for_file: unused_local_variable import 'package:http/http.dart' as http; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; /* SNIPPET START */ final provider = FutureProvider.autoDispose((ref) async { diff --git a/website/docs/essentials/auto_dispose/on_dispose_example/codegen.dart b/website/docs/essentials/auto_dispose/on_dispose_example/codegen.dart index d783c47fd..a7eafb658 100644 --- a/website/docs/essentials/auto_dispose/on_dispose_example/codegen.dart +++ b/website/docs/essentials/auto_dispose/on_dispose_example/codegen.dart @@ -2,7 +2,6 @@ import 'dart:async'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/essentials/auto_dispose/on_dispose_example/codegen.g.dart b/website/docs/essentials/auto_dispose/on_dispose_example/codegen.g.dart index 12ddffdcf..9e4f5ef90 100644 --- a/website/docs/essentials/auto_dispose/on_dispose_example/codegen.g.dart +++ b/website/docs/essentials/auto_dispose/on_dispose_example/codegen.g.dart @@ -8,37 +8,114 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(other) +const otherProvider = OtherProvider._(); + +final class OtherProvider extends $FunctionalProvider + with $Provider { + const OtherProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'otherProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$otherHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + OtherProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return OtherProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? other; + return _$cb(ref); + } +} + String _$otherHash() => r'5d27b2b1b1c6bd17ba0844f74ade2088611be371'; -/// See also [other]. -@ProviderFor(other) -final otherProvider = AutoDisposeProvider.internal( - other, - name: r'otherProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$otherHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef OtherRef = AutoDisposeProviderRef; +@ProviderFor(example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider + extends $FunctionalProvider, Stream> + with $FutureModifier, $StreamProvider { + const ExampleProvider._( + {Stream Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Stream Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + @$internal + @override + $StreamProviderElement $createElement($ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + Stream Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + Stream create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + String _$exampleHash() => r'012c40f45aaeb96633a857d1407f04d25255f32f'; -/// See also [example]. -@ProviderFor(example) -final exampleProvider = AutoDisposeStreamProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeStreamProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/auto_dispose/on_dispose_example/raw.dart b/website/docs/essentials/auto_dispose/on_dispose_example/raw.dart index 8edf8ffe8..261f2f85b 100644 --- a/website/docs/essentials/auto_dispose/on_dispose_example/raw.dart +++ b/website/docs/essentials/auto_dispose/on_dispose_example/raw.dart @@ -2,7 +2,7 @@ import 'dart:async'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; /* SNIPPET START */ final provider = StreamProvider((ref) { diff --git a/website/docs/essentials/auto_dispose/raw_auto_dispose.dart b/website/docs/essentials/auto_dispose/raw_auto_dispose.dart index 96caf81d4..3f16af5c8 100644 --- a/website/docs/essentials/auto_dispose/raw_auto_dispose.dart +++ b/website/docs/essentials/auto_dispose/raw_auto_dispose.dart @@ -1,4 +1,4 @@ -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; /* SNIPPET START */ // {@template autoDispose} diff --git a/website/docs/essentials/combining_requests/functional_ref/codegen.dart b/website/docs/essentials/combining_requests/functional_ref/codegen.dart index 8f0ea7b7c..e776d8c31 100644 --- a/website/docs/essentials/combining_requests/functional_ref/codegen.dart +++ b/website/docs/essentials/combining_requests/functional_ref/codegen.dart @@ -1,6 +1,5 @@ // ignore_for_file: unused_local_variable -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/essentials/combining_requests/functional_ref/codegen.g.dart b/website/docs/essentials/combining_requests/functional_ref/codegen.g.dart index eebfb85b4..c4f8ef7f1 100644 --- a/website/docs/essentials/combining_requests/functional_ref/codegen.g.dart +++ b/website/docs/essentials/combining_requests/functional_ref/codegen.g.dart @@ -8,37 +8,121 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(other) +const otherProvider = OtherProvider._(); + +final class OtherProvider extends $FunctionalProvider + with $Provider { + const OtherProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'otherProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$otherHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + OtherProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return OtherProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? other; + return _$cb(ref); + } +} + String _$otherHash() => r'5d27b2b1b1c6bd17ba0844f74ade2088611be371'; -/// See also [other]. -@ProviderFor(other) -final otherProvider = AutoDisposeProvider.internal( - other, - name: r'otherProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$otherHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef OtherRef = AutoDisposeProviderRef; +@ProviderFor(example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider extends $FunctionalProvider + with $Provider { + const ExampleProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + String _$exampleHash() => r'6115819b119e2f16f22c69aae827632e3b585775'; -/// See also [example]. -@ProviderFor(example) -final exampleProvider = AutoDisposeProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/combining_requests/functional_ref/raw.dart b/website/docs/essentials/combining_requests/functional_ref/raw.dart index f6c14fa64..480f3878a 100644 --- a/website/docs/essentials/combining_requests/functional_ref/raw.dart +++ b/website/docs/essentials/combining_requests/functional_ref/raw.dart @@ -1,6 +1,6 @@ // ignore_for_file: unused_local_variable -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; final otherProvider = Provider((ref) => 0); diff --git a/website/docs/essentials/combining_requests/listen_example/codegen.dart b/website/docs/essentials/combining_requests/listen_example/codegen.dart index edfc5a973..8de5958a6 100644 --- a/website/docs/essentials/combining_requests/listen_example/codegen.dart +++ b/website/docs/essentials/combining_requests/listen_example/codegen.dart @@ -1,11 +1,11 @@ // ignore_for_file: unused_local_variable, avoid_print -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; -final otherProvider = Provider((ref) => 0); +@riverpod +int other(Ref ref) => 0; /* SNIPPET START */ @riverpod diff --git a/website/docs/essentials/combining_requests/listen_example/codegen.g.dart b/website/docs/essentials/combining_requests/listen_example/codegen.g.dart index 179e800ee..1e04eee5a 100644 --- a/website/docs/essentials/combining_requests/listen_example/codegen.g.dart +++ b/website/docs/essentials/combining_requests/listen_example/codegen.g.dart @@ -8,21 +8,121 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** -String _$exampleHash() => r'42a795719de4bd2ec79f315aaaf87d1c89ebf063'; +@ProviderFor(other) +const otherProvider = OtherProvider._(); + +final class OtherProvider extends $FunctionalProvider + with $Provider { + const OtherProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'otherProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$otherHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + OtherProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return OtherProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? other; + return _$cb(ref); + } +} + +String _$otherHash() => r'5d27b2b1b1c6bd17ba0844f74ade2088611be371'; -/// See also [example]. @ProviderFor(example) -final exampleProvider = AutoDisposeProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeProviderRef; +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider extends $FunctionalProvider + with $Provider { + const ExampleProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + +String _$exampleHash() => r'42a795719de4bd2ec79f315aaaf87d1c89ebf063'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/combining_requests/listen_example/raw.dart b/website/docs/essentials/combining_requests/listen_example/raw.dart index 12b578dd0..334f73b5a 100644 --- a/website/docs/essentials/combining_requests/listen_example/raw.dart +++ b/website/docs/essentials/combining_requests/listen_example/raw.dart @@ -1,5 +1,5 @@ // ignore_for_file: unused_local_variable, avoid_print -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; final otherProvider = Provider((ref) => 0); diff --git a/website/docs/essentials/combining_requests/notifier_ref/codegen.dart b/website/docs/essentials/combining_requests/notifier_ref/codegen.dart index 35609efa1..c184574c3 100644 --- a/website/docs/essentials/combining_requests/notifier_ref/codegen.dart +++ b/website/docs/essentials/combining_requests/notifier_ref/codegen.dart @@ -1,6 +1,5 @@ // ignore_for_file: unused_local_variable -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/essentials/combining_requests/notifier_ref/codegen.g.dart b/website/docs/essentials/combining_requests/notifier_ref/codegen.g.dart index c480a5f8a..6ac7315d9 100644 --- a/website/docs/essentials/combining_requests/notifier_ref/codegen.g.dart +++ b/website/docs/essentials/combining_requests/notifier_ref/codegen.g.dart @@ -8,35 +8,132 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(other) +const otherProvider = OtherProvider._(); + +final class OtherProvider extends $FunctionalProvider + with $Provider { + const OtherProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'otherProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$otherHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + OtherProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return OtherProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? other; + return _$cb(ref); + } +} + String _$otherHash() => r'5d27b2b1b1c6bd17ba0844f74ade2088611be371'; -/// See also [other]. -@ProviderFor(other) -final otherProvider = AutoDisposeProvider.internal( - other, - name: r'otherProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$otherHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef OtherRef = AutoDisposeProviderRef; +@ProviderFor(Example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider extends $NotifierProvider { + const ExampleProvider._( + {super.runNotifierBuildOverride, Example Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Example Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + Example create() => _createCb?.call() ?? Example(); + + @$internal + @override + ExampleProvider $copyWithCreate( + Example Function() create, + ) { + return ExampleProvider._(create: create); + } + + @$internal + @override + ExampleProvider $copyWithBuild( + int Function( + Ref, + Example, + ) build, + ) { + return ExampleProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$exampleHash() => r'893db991b377b8e314e60c429043e5e81f1fd526'; -/// See also [Example]. -@ProviderFor(Example) -final exampleProvider = AutoDisposeNotifierProvider.internal( - Example.new, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Example = AutoDisposeNotifier; +abstract class _$Example extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/combining_requests/notifier_ref/raw.dart b/website/docs/essentials/combining_requests/notifier_ref/raw.dart index 5f00b4d24..6b3fe334c 100644 --- a/website/docs/essentials/combining_requests/notifier_ref/raw.dart +++ b/website/docs/essentials/combining_requests/notifier_ref/raw.dart @@ -1,6 +1,6 @@ // ignore_for_file: unused_local_variable -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; final otherProvider = Provider((ref) => 0); diff --git a/website/docs/essentials/combining_requests/read_example/codegen.dart b/website/docs/essentials/combining_requests/read_example/codegen.dart index d01c73d20..0e3e09226 100644 --- a/website/docs/essentials/combining_requests/read_example/codegen.dart +++ b/website/docs/essentials/combining_requests/read_example/codegen.dart @@ -4,7 +4,8 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; -final otherProvider = Provider((ref) => 0); +@riverpod +int other(Ref ref) => 0; /* SNIPPET START */ @riverpod diff --git a/website/docs/essentials/combining_requests/read_example/codegen.g.dart b/website/docs/essentials/combining_requests/read_example/codegen.g.dart index 5b29529c4..e34ca3d31 100644 --- a/website/docs/essentials/combining_requests/read_example/codegen.g.dart +++ b/website/docs/essentials/combining_requests/read_example/codegen.g.dart @@ -8,20 +8,132 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** -String _$myNotifierHash() => r'353efe22dd5a91b2d036286211ac9e60c9de5f6d'; +@ProviderFor(other) +const otherProvider = OtherProvider._(); + +final class OtherProvider extends $FunctionalProvider + with $Provider { + const OtherProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'otherProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$otherHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + OtherProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return OtherProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? other; + return _$cb(ref); + } +} + +String _$otherHash() => r'5d27b2b1b1c6bd17ba0844f74ade2088611be371'; -/// See also [MyNotifier]. @ProviderFor(MyNotifier) -final myNotifierProvider = - AutoDisposeNotifierProvider.internal( - MyNotifier.new, - name: r'myNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$myNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$MyNotifier = AutoDisposeNotifier; +const myNotifierProvider = MyNotifierProvider._(); + +final class MyNotifierProvider extends $NotifierProvider { + const MyNotifierProvider._( + {super.runNotifierBuildOverride, MyNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$myNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + MyNotifier create() => _createCb?.call() ?? MyNotifier(); + + @$internal + @override + MyNotifierProvider $copyWithCreate( + MyNotifier Function() create, + ) { + return MyNotifierProvider._(create: create); + } + + @$internal + @override + MyNotifierProvider $copyWithBuild( + int Function( + Ref, + MyNotifier, + ) build, + ) { + return MyNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$myNotifierHash() => r'353efe22dd5a91b2d036286211ac9e60c9de5f6d'; + +abstract class _$MyNotifier extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/combining_requests/read_example/raw.dart b/website/docs/essentials/combining_requests/read_example/raw.dart index 2680667d0..a2e0a2859 100644 --- a/website/docs/essentials/combining_requests/read_example/raw.dart +++ b/website/docs/essentials/combining_requests/read_example/raw.dart @@ -1,6 +1,6 @@ // ignore_for_file: unused_local_variable -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; final otherProvider = Provider((ref) => 0); diff --git a/website/docs/essentials/combining_requests/watch_example/codegen.dart b/website/docs/essentials/combining_requests/watch_example/codegen.dart index e394b097b..55a72d895 100644 --- a/website/docs/essentials/combining_requests/watch_example/codegen.dart +++ b/website/docs/essentials/combining_requests/watch_example/codegen.dart @@ -4,6 +4,7 @@ import 'dart:convert'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; +import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/essentials/combining_requests/watch_example/codegen.g.dart b/website/docs/essentials/combining_requests/watch_example/codegen.g.dart index 16bf66677..9fb17601e 100644 --- a/website/docs/essentials/combining_requests/watch_example/codegen.g.dart +++ b/website/docs/essentials/combining_requests/watch_example/codegen.g.dart @@ -8,41 +8,112 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(location) +const locationProvider = LocationProvider._(); + +final class LocationProvider extends $FunctionalProvider< + AsyncValue<({double longitude, double latitude})>, + Stream<({double longitude, double latitude})>> + with + $FutureModifier<({double longitude, double latitude})>, + $StreamProvider<({double longitude, double latitude})> { + const LocationProvider._( + {Stream<({double longitude, double latitude})> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'locationProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Stream<({double longitude, double latitude})> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$locationHash(); + + @$internal + @override + $StreamProviderElement<({double longitude, double latitude})> $createElement( + $ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + + @override + LocationProvider $copyWithCreate( + Stream<({double longitude, double latitude})> Function( + Ref ref, + ) create, + ) { + return LocationProvider._(create: create); + } + + @override + Stream<({double longitude, double latitude})> create(Ref ref) { + final _$cb = _createCb ?? location; + return _$cb(ref); + } +} + String _$locationHash() => r'39328e5d0ec2b97acec14f1aba6c8db3f24f46a8'; -/// See also [location]. -@ProviderFor(location) -final locationProvider = - AutoDisposeStreamProvider<({double longitude, double latitude})>.internal( - location, - name: r'locationProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$locationHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef LocationRef - = AutoDisposeStreamProviderRef<({double longitude, double latitude})>; +@ProviderFor(restaurantsNearMe) +const restaurantsNearMeProvider = RestaurantsNearMeProvider._(); + +final class RestaurantsNearMeProvider extends $FunctionalProvider< + AsyncValue>, FutureOr>> + with $FutureModifier>, $FutureProvider> { + const RestaurantsNearMeProvider._( + {FutureOr> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'restaurantsNearMeProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$restaurantsNearMeHash(); + + @$internal + @override + $FutureProviderElement> $createElement( + $ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + RestaurantsNearMeProvider $copyWithCreate( + FutureOr> Function( + Ref ref, + ) create, + ) { + return RestaurantsNearMeProvider._(create: create); + } + + @override + FutureOr> create(Ref ref) { + final _$cb = _createCb ?? restaurantsNearMe; + return _$cb(ref); + } +} + String _$restaurantsNearMeHash() => r'f577a4362db45208cd34f499d73f39f284807d13'; -/// See also [restaurantsNearMe]. -@ProviderFor(restaurantsNearMe) -final restaurantsNearMeProvider = - AutoDisposeFutureProvider>.internal( - restaurantsNearMe, - name: r'restaurantsNearMeProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$restaurantsNearMeHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef RestaurantsNearMeRef = AutoDisposeFutureProviderRef>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/combining_requests/watch_example/raw.dart b/website/docs/essentials/combining_requests/watch_example/raw.dart index 9d494c108..e8baca922 100644 --- a/website/docs/essentials/combining_requests/watch_example/raw.dart +++ b/website/docs/essentials/combining_requests/watch_example/raw.dart @@ -3,7 +3,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; final otherProvider = Provider((ref) => 0); diff --git a/website/docs/essentials/combining_requests/watch_placement/codegen.dart b/website/docs/essentials/combining_requests/watch_placement/codegen.dart index 8acc4b017..d5a9cd653 100644 --- a/website/docs/essentials/combining_requests/watch_placement/codegen.dart +++ b/website/docs/essentials/combining_requests/watch_placement/codegen.dart @@ -1,12 +1,12 @@ // ignore_for_file: unused_local_variable import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; -final otherProvider = Provider((ref) => 0); +@riverpod +int other(Ref ref) => 0; /* SNIPPET START */ @riverpod diff --git a/website/docs/essentials/combining_requests/watch_placement/codegen.g.dart b/website/docs/essentials/combining_requests/watch_placement/codegen.g.dart index f0547c420..2647a21b1 100644 --- a/website/docs/essentials/combining_requests/watch_placement/codegen.g.dart +++ b/website/docs/essentials/combining_requests/watch_placement/codegen.g.dart @@ -8,36 +8,190 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** -String _$exampleHash() => r'fed2d2be4787bc4a715efa198a89a297967b54a1'; +@ProviderFor(other) +const otherProvider = OtherProvider._(); + +final class OtherProvider extends $FunctionalProvider + with $Provider { + const OtherProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'otherProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$otherHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + OtherProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return OtherProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? other; + return _$cb(ref); + } +} + +String _$otherHash() => r'5d27b2b1b1c6bd17ba0844f74ade2088611be371'; -/// See also [example]. @ProviderFor(example) -final exampleProvider = AutoDisposeProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeProviderRef; -String _$myNotifierHash() => r'ad79fdb5b0e72a800fa03efc1e7157f0d1524844'; +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider extends $FunctionalProvider + with $Provider { + const ExampleProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + +String _$exampleHash() => r'fed2d2be4787bc4a715efa198a89a297967b54a1'; -/// See also [MyNotifier]. @ProviderFor(MyNotifier) -final myNotifierProvider = - AutoDisposeNotifierProvider.internal( - MyNotifier.new, - name: r'myNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$myNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$MyNotifier = AutoDisposeNotifier; +const myNotifierProvider = MyNotifierProvider._(); + +final class MyNotifierProvider extends $NotifierProvider { + const MyNotifierProvider._( + {super.runNotifierBuildOverride, MyNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$myNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + MyNotifier create() => _createCb?.call() ?? MyNotifier(); + + @$internal + @override + MyNotifierProvider $copyWithCreate( + MyNotifier Function() create, + ) { + return MyNotifierProvider._(create: create); + } + + @$internal + @override + MyNotifierProvider $copyWithBuild( + int Function( + Ref, + MyNotifier, + ) build, + ) { + return MyNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$myNotifierHash() => r'ad79fdb5b0e72a800fa03efc1e7157f0d1524844'; + +abstract class _$MyNotifier extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/combining_requests/watch_placement/raw.dart b/website/docs/essentials/combining_requests/watch_placement/raw.dart index 34fa8f6c9..6bec34337 100644 --- a/website/docs/essentials/combining_requests/watch_placement/raw.dart +++ b/website/docs/essentials/combining_requests/watch_placement/raw.dart @@ -1,7 +1,7 @@ // ignore_for_file: unused_local_variable import 'package:flutter/foundation.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; final otherProvider = Provider((ref) => 0); diff --git a/website/docs/essentials/do_dont.mdx b/website/docs/essentials/do_dont.mdx index b02a34891..3e3ff7552 100644 --- a/website/docs/essentials/do_dont.mdx +++ b/website/docs/essentials/do_dont.mdx @@ -1,6 +1,6 @@ --- title: DO/DON'T -version: 1 +version: 2 --- import { Link } from "/src/components/Link"; @@ -60,10 +60,12 @@ ElevatedButton( Providers are designed to be for shared business state. They are not meant to be used for [Ephemeral state](https://docs.flutter.dev/data-and-backend/state-mgmt/ephemeral-vs-app#ephemeral-state), such as for: -- storing form state -- currently selected item -- animations -- generally everything that Flutter deals with a "controller" (e.g. `TextEditingController`) +- The currently selected item. +- Form state/ + Because leaving and re-entering the form should typically reset the form state. + This includes pressing the back button during a multi-page forms. +- Animations. +- Generally everything that Flutter deals with a "controller" (e.g. `TextEditingController`) If you are looking for a way to handle local widget state, consider using [flutter_hooks](https://pub.dev/packages/flutter_hooks) instead. @@ -72,6 +74,22 @@ One reason why this is discouraged is that such state is often scoped to a route Failing to do so could break your app's back button, due to a new page overriding the state of a previous page. +For instance say we were to store the currently selected `book` in a provider: + +```dart +final selectedBookProvider = StateProvider((ref) => null); +``` +One challenge we could face is, the navigation history could look like: +``` +/books +/books/42 +/books/21 +``` + +In this scenario, when pressing the back button, we should expect to go back to `/books/42`. +But if we were to use `selectedBookProvider` to store the selected book, +the selected ID would not reset to its previous value, and we would keep seeing `/books/21`. + ## DON'T perform side effects during the initialization of a provider Providers should generally be used to represent a "read" operation. diff --git a/website/docs/essentials/eager_initialization/async_consumer_example.dart b/website/docs/essentials/eager_initialization/async_consumer_example.dart index af44e2121..edffa6e7c 100644 --- a/website/docs/essentials/eager_initialization/async_consumer_example.dart +++ b/website/docs/essentials/eager_initialization/async_consumer_example.dart @@ -27,4 +27,3 @@ class _EagerInitialization extends ConsumerWidget { } } /* SNIPPET END */ - diff --git a/website/docs/essentials/eager_initialization/consumer_example.dart b/website/docs/essentials/eager_initialization/consumer_example.dart index 814e784c7..7115ed1f5 100644 --- a/website/docs/essentials/eager_initialization/consumer_example.dart +++ b/website/docs/essentials/eager_initialization/consumer_example.dart @@ -37,4 +37,3 @@ class _EagerInitialization extends ConsumerWidget { } } /* SNIPPET END */ - diff --git a/website/docs/essentials/eager_initialization/require_value/codegen.g.dart b/website/docs/essentials/eager_initialization/require_value/codegen.g.dart index 597d3064c..d317584cf 100644 --- a/website/docs/essentials/eager_initialization/require_value/codegen.g.dart +++ b/website/docs/essentials/eager_initialization/require_value/codegen.g.dart @@ -8,21 +8,56 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const ExampleProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + String _$exampleHash() => r'8768b72378809f5cb6ae94b65493cbb49bd23b77'; -/// See also [example]. -@ProviderFor(example) -final exampleProvider = AutoDisposeFutureProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeFutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/first_request/codegen/provider.dart b/website/docs/essentials/first_request/codegen/provider.dart index afaff3513..9a68b34be 100644 --- a/website/docs/essentials/first_request/codegen/provider.dart +++ b/website/docs/essentials/first_request/codegen/provider.dart @@ -1,7 +1,6 @@ /* SNIPPET START */ import 'dart:convert'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'activity.dart'; diff --git a/website/docs/essentials/first_request/codegen/provider.g.dart b/website/docs/essentials/first_request/codegen/provider.g.dart index 80f3c7ded..f507b27f8 100644 --- a/website/docs/essentials/first_request/codegen/provider.g.dart +++ b/website/docs/essentials/first_request/codegen/provider.g.dart @@ -8,24 +8,65 @@ part of 'provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$activityHash() => r'c90b5d6502e5e4c31a2fa8c974683171cad8f38f'; - /// This will create a provider named `activityProvider` /// which will cache the result of this function. -/// -/// Copied from [activity]. +// {@endtemplate} @ProviderFor(activity) -final activityProvider = AutoDisposeFutureProvider.internal( - activity, - name: r'activityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$activityHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ActivityRef = AutoDisposeFutureProviderRef; +const activityProvider = ActivityProvider._(); + +/// This will create a provider named `activityProvider` +/// which will cache the result of this function. +// {@endtemplate} +final class ActivityProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + /// This will create a provider named `activityProvider` + /// which will cache the result of this function. +// {@endtemplate} + const ActivityProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'activityProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$activityHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ActivityProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return ActivityProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? activity; + return _$cb(ref); + } +} + +String _$activityHash() => r'c90b5d6502e5e4c31a2fa8c974683171cad8f38f'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/first_request/raw/consumer.dart b/website/docs/essentials/first_request/raw/consumer.dart index 2215efd83..8743ffcf9 100644 --- a/website/docs/essentials/first_request/raw/consumer.dart +++ b/website/docs/essentials/first_request/raw/consumer.dart @@ -20,7 +20,7 @@ class Home extends StatelessWidget { // {@template activity} // Read the activityProvider. This will start the network request // if it wasn't already started. - // By using ref.watch, this widget will rebuild whenever the + // By using ref.watch, this widget will rebuild whenever // the activityProvider updates. This can happen when: // - The response goes from "loading" to "data/error" // - The request was refreshed diff --git a/website/docs/essentials/passing_args/family/codegen.dart b/website/docs/essentials/passing_args/family/codegen.dart index 5c8a0117e..ab4743a20 100644 --- a/website/docs/essentials/passing_args/family/codegen.dart +++ b/website/docs/essentials/passing_args/family/codegen.dart @@ -1,7 +1,6 @@ // ignore_for_file: avoid_print import 'dart:convert'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../first_request/codegen/activity.dart'; diff --git a/website/docs/essentials/passing_args/family/codegen.g.dart b/website/docs/essentials/passing_args/family/codegen.g.dart index b122b60e2..51bf760fb 100644 --- a/website/docs/essentials/passing_args/family/codegen.g.dart +++ b/website/docs/essentials/passing_args/family/codegen.g.dart @@ -8,301 +8,284 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** -String _$activityHash() => r'6c815736c0d2b40a92695adcd78516534d7ac2fc'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// See also [activity]. @ProviderFor(activity) -const activityProvider = ActivityFamily(); - -/// See also [activity]. -class ActivityFamily extends Family> { - /// See also [activity]. - const ActivityFamily(); +const activityProvider = ActivityFamily._(); + +final class ActivityProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const ActivityProvider._( + {required ActivityFamily super.from, + required String super.argument, + FutureOr Function( + Ref ref, + String activityType, + )? create}) + : _createCb = create, + super( + retry: null, + name: r'activityProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// See also [activity]. - ActivityProvider call( + final FutureOr Function( + Ref ref, String activityType, - ) { - return ActivityProvider( - activityType, - ); - } - - @override - ActivityProvider getProviderOverride( - covariant ActivityProvider provider, - ) { - return call( - provider.activityType, - ); - } - - static const Iterable? _dependencies = null; + )? _createCb; @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; + String debugGetCreateSourceHash() => _$activityHash(); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + String toString() { + return r'activityProvider' + '' + '($argument)'; + } + @$internal @override - String? get name => r'activityProvider'; -} - -/// See also [activity]. -class ActivityProvider extends AutoDisposeFutureProvider { - /// See also [activity]. - ActivityProvider( - String activityType, - ) : this._internal( - (ref) => activity( - ref as ActivityRef, - activityType, - ), - from: activityProvider, - name: r'activityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$activityHash, - dependencies: ActivityFamily._dependencies, - allTransitiveDependencies: ActivityFamily._allTransitiveDependencies, - activityType: activityType, - ); - - ActivityProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.activityType, - }) : super.internal(); - - final String activityType; + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); @override - Override overrideWith( - FutureOr Function(ActivityRef provider) create, + ActivityProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, ) { - return ProviderOverride( - origin: this, - override: ActivityProvider._internal( - (ref) => create(ref as ActivityRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - activityType: activityType, - ), - ); + return ActivityProvider._( + argument: argument as String, + from: from! as ActivityFamily, + create: ( + ref, + String activityType, + ) => + create(ref)); } @override - AutoDisposeFutureProviderElement createElement() { - return _ActivityProviderElement(this); + FutureOr create(Ref ref) { + final _$cb = _createCb ?? activity; + final argument = this.argument as String; + return _$cb( + ref, + argument, + ); } @override bool operator ==(Object other) { - return other is ActivityProvider && other.activityType == activityType; + return other is ActivityProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, activityType.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin ActivityRef on AutoDisposeFutureProviderRef { - /// The parameter `activityType` of this provider. - String get activityType; -} +String _$activityHash() => r'6c815736c0d2b40a92695adcd78516534d7ac2fc'; -class _ActivityProviderElement - extends AutoDisposeFutureProviderElement with ActivityRef { - _ActivityProviderElement(super.provider); +final class ActivityFamily extends Family { + const ActivityFamily._() + : super( + retry: null, + name: r'activityProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + ActivityProvider call( + String activityType, + ) => + ActivityProvider._(argument: activityType, from: this); @override - String get activityType => (origin as ActivityProvider).activityType; -} + String debugGetCreateSourceHash() => _$activityHash(); -String _$activityNotifier2Hash() => r'9e67c655d53a9f98c3b012a0534421385dde0339'; + @override + String toString() => r'activityProvider'; -abstract class _$ActivityNotifier2 - extends BuildlessAutoDisposeAsyncNotifier { - late final String activityType; + /// {@macro riverpod.override_with} + Override overrideWith( + FutureOr Function( + Ref ref, + String args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ActivityProvider; - FutureOr build( - String activityType, - ); + final argument = provider.argument as String; + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } -/// See also [ActivityNotifier2]. @ProviderFor(ActivityNotifier2) -const activityNotifier2Provider = ActivityNotifier2Family(); +const activityNotifier2Provider = ActivityNotifier2Family._(); + +final class ActivityNotifier2Provider + extends $AsyncNotifierProvider { + const ActivityNotifier2Provider._( + {required ActivityNotifier2Family super.from, + required String super.argument, + super.runNotifierBuildOverride, + ActivityNotifier2 Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'activityNotifier2Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// See also [ActivityNotifier2]. -class ActivityNotifier2Family extends Family> { - /// See also [ActivityNotifier2]. - const ActivityNotifier2Family(); + final ActivityNotifier2 Function()? _createCb; - /// See also [ActivityNotifier2]. - ActivityNotifier2Provider call( - String activityType, - ) { - return ActivityNotifier2Provider( - activityType, - ); + @override + String debugGetCreateSourceHash() => _$activityNotifier2Hash(); + + @override + String toString() { + return r'activityNotifier2Provider' + '' + '($argument)'; } + @$internal + @override + ActivityNotifier2 create() => _createCb?.call() ?? ActivityNotifier2(); + + @$internal @override - ActivityNotifier2Provider getProviderOverride( - covariant ActivityNotifier2Provider provider, + ActivityNotifier2Provider $copyWithCreate( + ActivityNotifier2 Function() create, ) { - return call( - provider.activityType, - ); + return ActivityNotifier2Provider._( + argument: argument as String, + from: from! as ActivityNotifier2Family, + create: create); } - static const Iterable? _dependencies = null; - + @$internal @override - Iterable? get dependencies => _dependencies; + ActivityNotifier2Provider $copyWithBuild( + FutureOr Function( + Ref, + ActivityNotifier2, + ) build, + ) { + return ActivityNotifier2Provider._( + argument: argument as String, + from: from! as ActivityNotifier2Family, + runNotifierBuildOverride: build); + } - static const Iterable? _allTransitiveDependencies = null; + @$internal + @override + $AsyncNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + bool operator ==(Object other) { + return other is ActivityNotifier2Provider && other.argument == argument; + } @override - String? get name => r'activityNotifier2Provider'; + int get hashCode { + return argument.hashCode; + } } -/// See also [ActivityNotifier2]. -class ActivityNotifier2Provider - extends AutoDisposeAsyncNotifierProviderImpl { - /// See also [ActivityNotifier2]. - ActivityNotifier2Provider( - String activityType, - ) : this._internal( - () => ActivityNotifier2()..activityType = activityType, - from: activityNotifier2Provider, +String _$activityNotifier2Hash() => r'9e67c655d53a9f98c3b012a0534421385dde0339'; + +final class ActivityNotifier2Family extends Family { + const ActivityNotifier2Family._() + : super( + retry: null, name: r'activityNotifier2Provider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$activityNotifier2Hash, - dependencies: ActivityNotifier2Family._dependencies, - allTransitiveDependencies: - ActivityNotifier2Family._allTransitiveDependencies, - activityType: activityType, + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, ); - ActivityNotifier2Provider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.activityType, - }) : super.internal(); + ActivityNotifier2Provider call( + String activityType, + ) => + ActivityNotifier2Provider._(argument: activityType, from: this); - final String activityType; + @override + String debugGetCreateSourceHash() => _$activityNotifier2Hash(); @override - FutureOr runNotifierBuild( - covariant ActivityNotifier2 notifier, + String toString() => r'activityNotifier2Provider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + ActivityNotifier2 Function( + String args, + ) create, ) { - return notifier.build( - activityType, - ); - } + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ActivityNotifier2Provider; - @override - Override overrideWith(ActivityNotifier2 Function() create) { - return ProviderOverride( - origin: this, - override: ActivityNotifier2Provider._internal( - () => create()..activityType = activityType, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - activityType: activityType, - ), - ); - } + final argument = provider.argument as String; - @override - AutoDisposeAsyncNotifierProviderElement - createElement() { - return _ActivityNotifier2ProviderElement(this); + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); } - @override - bool operator ==(Object other) { - return other is ActivityNotifier2Provider && - other.activityType == activityType; - } + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + FutureOr Function( + Ref ref, ActivityNotifier2 notifier, String argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as ActivityNotifier2Provider; - @override - int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, activityType.hashCode); + final argument = provider.argument as String; - return _SystemHash.finish(hash); + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin ActivityNotifier2Ref on AutoDisposeAsyncNotifierProviderRef { - /// The parameter `activityType` of this provider. - String get activityType; -} - -class _ActivityNotifier2ProviderElement - extends AutoDisposeAsyncNotifierProviderElement - with ActivityNotifier2Ref { - _ActivityNotifier2ProviderElement(super.provider); +abstract class _$ActivityNotifier2 extends $AsyncNotifier { + late final _$args = ref.$arg as String; + String get activityType => _$args; + FutureOr build( + String activityType, + ); + @$internal @override - String get activityType => (origin as ActivityNotifier2Provider).activityType; + FutureOr runBuild() => build( + _$args, + ); } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/passing_args/family/raw.dart b/website/docs/essentials/passing_args/family/raw.dart index c9a7f97af..f5c731846 100644 --- a/website/docs/essentials/passing_args/family/raw.dart +++ b/website/docs/essentials/passing_args/family/raw.dart @@ -5,7 +5,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../first_request/raw/activity.dart'; -FutureOr fetchActivity(String activityType) => throw UnimplementedError(); +FutureOr fetchActivity(String activityType) => + throw UnimplementedError(); /* SNIPPET START */ final activityProvider = FutureProvider.autoDispose @@ -24,16 +25,15 @@ final activityProvider = FutureProvider.autoDispose // {@template raw_activityProvider2} // Again, for notifier we use the ".family" modifier, and specify the argument as type "String". // {@endtemplate} -final activityProvider2 = AsyncNotifierProvider.autoDispose.family( +final activityProvider2 = AsyncNotifierProvider.autoDispose + .family( ActivityNotifier.new, ); // {@template raw_ActivityNotifier} // When using ".family" with notifiers, we need to change the notifier subclass: // AsyncNotifier -> FamilyAsyncNotifier -// AutoDisposeAsyncNotifier -> AutoDisposeFamilyAsyncNotifier -// {@endtemplate} -class ActivityNotifier extends AutoDisposeFamilyAsyncNotifier { +class ActivityNotifier extends FamilyAsyncNotifier { // {@template raw_build} /// Family arguments are passed to the build method and accessible with this.arg // {@endtemplate} diff --git a/website/docs/essentials/passing_args/no_arg_provider/codegen.dart b/website/docs/essentials/passing_args/no_arg_provider/codegen.dart index eee4a724c..da1c34adb 100644 --- a/website/docs/essentials/passing_args/no_arg_provider/codegen.dart +++ b/website/docs/essentials/passing_args/no_arg_provider/codegen.dart @@ -1,6 +1,5 @@ // ignore_for_file: avoid_print -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../first_request/codegen/activity.dart'; diff --git a/website/docs/essentials/passing_args/no_arg_provider/codegen.g.dart b/website/docs/essentials/passing_args/no_arg_provider/codegen.g.dart index ef2043924..3dae20276 100644 --- a/website/docs/essentials/passing_args/no_arg_provider/codegen.g.dart +++ b/website/docs/essentials/passing_args/no_arg_provider/codegen.g.dart @@ -8,37 +8,118 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(activity) +const activityProvider = ActivityProvider._(); + +final class ActivityProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const ActivityProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'activityProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$activityHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ActivityProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return ActivityProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? activity; + return _$cb(ref); + } +} + String _$activityHash() => r'7b532e70a92d6bc198900ca61f0e714c5484c34d'; -/// See also [activity]. -@ProviderFor(activity) -final activityProvider = AutoDisposeFutureProvider.internal( - activity, - name: r'activityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$activityHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ActivityRef = AutoDisposeFutureProviderRef; +@ProviderFor(ActivityNotifier2) +const activityNotifier2Provider = ActivityNotifier2Provider._(); + +final class ActivityNotifier2Provider + extends $AsyncNotifierProvider { + const ActivityNotifier2Provider._( + {super.runNotifierBuildOverride, ActivityNotifier2 Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'activityNotifier2Provider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final ActivityNotifier2 Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$activityNotifier2Hash(); + + @$internal + @override + ActivityNotifier2 create() => _createCb?.call() ?? ActivityNotifier2(); + + @$internal + @override + ActivityNotifier2Provider $copyWithCreate( + ActivityNotifier2 Function() create, + ) { + return ActivityNotifier2Provider._(create: create); + } + + @$internal + @override + ActivityNotifier2Provider $copyWithBuild( + FutureOr Function( + Ref, + ActivityNotifier2, + ) build, + ) { + return ActivityNotifier2Provider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $AsyncNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); +} + String _$activityNotifier2Hash() => r'280f4d82a186cfb62827f4d7c74f5349bb0a9e4a'; -/// See also [ActivityNotifier2]. -@ProviderFor(ActivityNotifier2) -final activityNotifier2Provider = - AutoDisposeAsyncNotifierProvider.internal( - ActivityNotifier2.new, - name: r'activityNotifier2Provider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$activityNotifier2Hash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$ActivityNotifier2 = AutoDisposeAsyncNotifier; +abstract class _$ActivityNotifier2 extends $AsyncNotifier { + FutureOr build(); + @$internal + @override + FutureOr runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/passing_args/raw/consumer_list_family.dart b/website/docs/essentials/passing_args/raw/consumer_list_family.dart index fc681b9f2..1565d5eec 100644 --- a/website/docs/essentials/passing_args/raw/consumer_list_family.dart +++ b/website/docs/essentials/passing_args/raw/consumer_list_family.dart @@ -1,4 +1,4 @@ -// ignore_for_file: omit_local_variable_types, unused_local_variable, prefer_final_locals +// ignore_for_file: omit_local_variable_types, unused_local_variable, prefer_final_locals, provider_parameters import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; diff --git a/website/docs/essentials/passing_args/raw/multiple_consumer_family.dart b/website/docs/essentials/passing_args/raw/multiple_consumer_family.dart index f1b7caa10..daff3a01f 100644 --- a/website/docs/essentials/passing_args/raw/multiple_consumer_family.dart +++ b/website/docs/essentials/passing_args/raw/multiple_consumer_family.dart @@ -28,8 +28,8 @@ class Example extends ConsumerWidget { // {@endtemplate} return Column( children: [ - Text(recreational.valueOrNull?.activity ?? ''), - Text(cooking.valueOrNull?.activity ?? ''), + Text(recreational.value?.activity ?? ''), + Text(cooking.value?.activity ?? ''), ], ); }, diff --git a/website/docs/essentials/provider_observer/provider_observer.dart b/website/docs/essentials/provider_observer/provider_observer.dart index dd75afb01..27c668b2c 100644 --- a/website/docs/essentials/provider_observer/provider_observer.dart +++ b/website/docs/essentials/provider_observer/provider_observer.dart @@ -6,38 +6,34 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; class MyObserver extends ProviderObserver { @override void didAddProvider( - ProviderBase provider, + ProviderObserverContext context, Object? value, - ProviderContainer container, ) { - print('Provider $provider was initialized with $value'); + print('Provider ${context.provider} was initialized with $value'); } @override - void didDisposeProvider( - ProviderBase provider, - ProviderContainer container, - ) { - print('Provider $provider was disposed'); + void didDisposeProvider(ProviderObserverContext context) { + print('Provider ${context.provider} was disposed'); } @override void didUpdateProvider( - ProviderBase provider, + ProviderObserverContext context, Object? previousValue, Object? newValue, - ProviderContainer container, ) { - print('Provider $provider updated from $previousValue to $newValue'); + print( + 'Provider ${context.provider} updated from $previousValue to $newValue', + ); } @override void providerDidFail( - ProviderBase provider, + ProviderObserverContext context, Object error, StackTrace stackTrace, - ProviderContainer container, ) { - print('Provider $provider threw $error at $stackTrace'); + print('Provider ${context.provider} threw $error at $stackTrace'); } } diff --git a/website/docs/essentials/side_effects/codegen/todo_list_notifier.g.dart b/website/docs/essentials/side_effects/codegen/todo_list_notifier.g.dart index 756962faf..c2f3d90b1 100644 --- a/website/docs/essentials/side_effects/codegen/todo_list_notifier.g.dart +++ b/website/docs/essentials/side_effects/codegen/todo_list_notifier.g.dart @@ -23,20 +23,67 @@ Map _$$TodoImplToJson(_$TodoImpl instance) => // RiverpodGenerator // ************************************************************************** +@ProviderFor(TodoList) +const todoListProvider = TodoListProvider._(); + +final class TodoListProvider + extends $AsyncNotifierProvider> { + const TodoListProvider._( + {super.runNotifierBuildOverride, TodoList Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'todoListProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final TodoList Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$todoListHash(); + + @$internal + @override + TodoList create() => _createCb?.call() ?? TodoList(); + + @$internal + @override + TodoListProvider $copyWithCreate( + TodoList Function() create, + ) { + return TodoListProvider._(create: create); + } + + @$internal + @override + TodoListProvider $copyWithBuild( + FutureOr> Function( + Ref, + TodoList, + ) build, + ) { + return TodoListProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $AsyncNotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); +} + String _$todoListHash() => r'c939d438b07da6065dbbcfab86c27ef363bdb76c'; -/// See also [TodoList]. -@ProviderFor(TodoList) -final todoListProvider = - AutoDisposeAsyncNotifierProvider>.internal( - TodoList.new, - name: r'todoListProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$todoListHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$TodoList = AutoDisposeAsyncNotifier>; +abstract class _$TodoList extends $AsyncNotifier> { + FutureOr> build(); + @$internal + @override + FutureOr> runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/side_effects/codegen/todo_list_notifier_add_todo.g.dart b/website/docs/essentials/side_effects/codegen/todo_list_notifier_add_todo.g.dart index 606a53a35..6d57aa996 100644 --- a/website/docs/essentials/side_effects/codegen/todo_list_notifier_add_todo.g.dart +++ b/website/docs/essentials/side_effects/codegen/todo_list_notifier_add_todo.g.dart @@ -8,20 +8,67 @@ part of 'todo_list_notifier_add_todo.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(TodoList) +const todoListProvider = TodoListProvider._(); + +final class TodoListProvider + extends $AsyncNotifierProvider> { + const TodoListProvider._( + {super.runNotifierBuildOverride, TodoList Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'todoListProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final TodoList Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$todoListHash(); + + @$internal + @override + TodoList create() => _createCb?.call() ?? TodoList(); + + @$internal + @override + TodoListProvider $copyWithCreate( + TodoList Function() create, + ) { + return TodoListProvider._(create: create); + } + + @$internal + @override + TodoListProvider $copyWithBuild( + FutureOr> Function( + Ref, + TodoList, + ) build, + ) { + return TodoListProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $AsyncNotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); +} + String _$todoListHash() => r'4008395aaca8f55312f668c0b2a32e7599f82349'; -/// See also [TodoList]. -@ProviderFor(TodoList) -final todoListProvider = - AutoDisposeAsyncNotifierProvider>.internal( - TodoList.new, - name: r'todoListProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$todoListHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$TodoList = AutoDisposeAsyncNotifier>; +abstract class _$TodoList extends $AsyncNotifier> { + FutureOr> build(); + @$internal + @override + FutureOr> runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/side_effects/codegen/todo_list_provider.dart b/website/docs/essentials/side_effects/codegen/todo_list_provider.dart index e80b9d4a0..f0a84c83a 100644 --- a/website/docs/essentials/side_effects/codegen/todo_list_provider.dart +++ b/website/docs/essentials/side_effects/codegen/todo_list_provider.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; diff --git a/website/docs/essentials/side_effects/codegen/todo_list_provider.g.dart b/website/docs/essentials/side_effects/codegen/todo_list_provider.g.dart index 710a3b9ce..2ac49cc27 100644 --- a/website/docs/essentials/side_effects/codegen/todo_list_provider.g.dart +++ b/website/docs/essentials/side_effects/codegen/todo_list_provider.g.dart @@ -8,21 +8,56 @@ part of 'todo_list_provider.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(todoList) +const todoListProvider = TodoListProvider._(); + +final class TodoListProvider + extends $FunctionalProvider>, FutureOr>> + with $FutureModifier>, $FutureProvider> { + const TodoListProvider._( + {FutureOr> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'todoListProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$todoListHash(); + + @$internal + @override + $FutureProviderElement> $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + TodoListProvider $copyWithCreate( + FutureOr> Function( + Ref ref, + ) create, + ) { + return TodoListProvider._(create: create); + } + + @override + FutureOr> create(Ref ref) { + final _$cb = _createCb ?? todoList; + return _$cb(ref); + } +} + String _$todoListHash() => r'79ea254a2b6239c9fbcc2b5e6f075a3e5b72598e'; -/// See also [todoList]. -@ProviderFor(todoList) -final todoListProvider = AutoDisposeFutureProvider>.internal( - todoList, - name: r'todoListProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$todoListHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef TodoListRef = AutoDisposeFutureProviderRef>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/side_effects/raw/invalidate_self_add_todo.dart b/website/docs/essentials/side_effects/raw/invalidate_self_add_todo.dart index 425c11e9d..3b81ee08b 100644 --- a/website/docs/essentials/side_effects/raw/invalidate_self_add_todo.dart +++ b/website/docs/essentials/side_effects/raw/invalidate_self_add_todo.dart @@ -11,7 +11,7 @@ final todoListProvider = AsyncNotifierProvider.autoDispose> TodoList.new, ); -class TodoList extends AutoDisposeAsyncNotifier> { +class TodoList extends AsyncNotifier> { @override Future> build() async => [/* ... */]; diff --git a/website/docs/essentials/side_effects/raw/manual_add_todo.dart b/website/docs/essentials/side_effects/raw/manual_add_todo.dart index 38c0d76db..19db974b7 100644 --- a/website/docs/essentials/side_effects/raw/manual_add_todo.dart +++ b/website/docs/essentials/side_effects/raw/manual_add_todo.dart @@ -11,7 +11,7 @@ final todoListProvider = AsyncNotifierProvider.autoDispose> TodoList.new, ); -class TodoList extends AutoDisposeAsyncNotifier> { +class TodoList extends AsyncNotifier> { @override Future> build() async => [/* ... */]; diff --git a/website/docs/essentials/side_effects/raw/mutable_manual_add_todo.dart b/website/docs/essentials/side_effects/raw/mutable_manual_add_todo.dart index ac068f2a9..68630d117 100644 --- a/website/docs/essentials/side_effects/raw/mutable_manual_add_todo.dart +++ b/website/docs/essentials/side_effects/raw/mutable_manual_add_todo.dart @@ -11,7 +11,7 @@ final todoListProvider = AsyncNotifierProvider.autoDispose> TodoList.new, ); -class TodoList extends AutoDisposeAsyncNotifier> { +class TodoList extends AsyncNotifier> { @override Future> build() async => [/* ... */]; diff --git a/website/docs/essentials/side_effects/raw/render_add_todo.dart b/website/docs/essentials/side_effects/raw/render_add_todo.dart index 3fd083b9d..fbd1f26a2 100644 --- a/website/docs/essentials/side_effects/raw/render_add_todo.dart +++ b/website/docs/essentials/side_effects/raw/render_add_todo.dart @@ -81,11 +81,11 @@ class _ExampleState extends ConsumerState { if (snapshot.connectionState == ConnectionState.waiting) ...[ const SizedBox(width: 8), const CircularProgressIndicator(), - ] + ], ], ); }, ); } } -/* SNIPPET END */ \ No newline at end of file +/* SNIPPET END */ diff --git a/website/docs/essentials/side_effects/raw/render_add_todo_hooks.dart b/website/docs/essentials/side_effects/raw/render_add_todo_hooks.dart index 86afda550..470d66b41 100644 --- a/website/docs/essentials/side_effects/raw/render_add_todo_hooks.dart +++ b/website/docs/essentials/side_effects/raw/render_add_todo_hooks.dart @@ -73,9 +73,9 @@ class Example extends HookConsumerWidget { if (snapshot.connectionState == ConnectionState.waiting) ...[ const SizedBox(width: 8), const CircularProgressIndicator(), - ] + ], ], ); } } -/* SNIPPET END */ \ No newline at end of file +/* SNIPPET END */ diff --git a/website/docs/essentials/side_effects/raw/rest_add_todo.dart b/website/docs/essentials/side_effects/raw/rest_add_todo.dart index 2e3deda76..227970486 100644 --- a/website/docs/essentials/side_effects/raw/rest_add_todo.dart +++ b/website/docs/essentials/side_effects/raw/rest_add_todo.dart @@ -12,7 +12,7 @@ final todoListProvider = TodoList.new, ); -class TodoList extends AutoDisposeAsyncNotifier> { +class TodoList extends AsyncNotifier> { @override Future> build() async => [/* ... */]; diff --git a/website/docs/essentials/side_effects/raw/todo_list_notifier.dart b/website/docs/essentials/side_effects/raw/todo_list_notifier.dart index 85aeb3d19..d597e8755 100644 --- a/website/docs/essentials/side_effects/raw/todo_list_notifier.dart +++ b/website/docs/essentials/side_effects/raw/todo_list_notifier.dart @@ -33,10 +33,10 @@ final todoListProvider = // {@template autoDispose} // We use an AsyncNotifier because our logic is asynchronous. -// More specifically, we'll need AutoDisposeAsyncNotifier because +// More specifically, we'll need AsyncNotifier because // of the "autoDispose" modifier. // {@endtemplate} -class TodoList extends AutoDisposeAsyncNotifier> { +class TodoList extends AsyncNotifier> { @override Future> build() async { // {@template build_method} diff --git a/website/docs/essentials/side_effects/raw/todo_list_notifier_add_todo.dart b/website/docs/essentials/side_effects/raw/todo_list_notifier_add_todo.dart index 2023bdc99..e4f87baee 100644 --- a/website/docs/essentials/side_effects/raw/todo_list_notifier_add_todo.dart +++ b/website/docs/essentials/side_effects/raw/todo_list_notifier_add_todo.dart @@ -13,7 +13,7 @@ final todoListProvider = ); /* SNIPPET START */ -class TodoList extends AutoDisposeAsyncNotifier> { +class TodoList extends AsyncNotifier> { @override Future> build() async => [/* ... */]; diff --git a/website/docs/essentials/testing.mdx b/website/docs/essentials/testing.mdx index 54cbfb579..1733bbf1b 100644 --- a/website/docs/essentials/testing.mdx +++ b/website/docs/essentials/testing.mdx @@ -4,7 +4,6 @@ version: 2 --- import { AutoSnippet, When } from "/src/components/CodeSnippet"; -import createContainer from "!!raw-loader!./testing/create_container.dart"; import unitTest from "!!raw-loader!./testing/unit_test.dart"; import widgetTest from "!!raw-loader!./testing/widget_test.dart"; import fullWidgetTest from "!!raw-loader!./testing/full_widget_test.dart"; @@ -47,12 +46,7 @@ The main difference with any other test is that we will want to create a `ProviderContainer` object. This object will enable our test to interact with providers. -It is encouraged to make a testing utility for both creating and disposing -of a `ProviderContainer` object: - - - -Then, we can define a `test` using this utility: +A typical test using `ProviderContainer` will look like: diff --git a/website/docs/essentials/testing/auto_dispose_listen.dart b/website/docs/essentials/testing/auto_dispose_listen.dart index cdf1fef66..3c4f3246c 100644 --- a/website/docs/essentials/testing/auto_dispose_listen.dart +++ b/website/docs/essentials/testing/auto_dispose_listen.dart @@ -3,13 +3,11 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:riverpod/riverpod.dart'; -import 'create_container.dart'; - final provider = Provider((_) => 'Hello world'); void main() { test('Some description', () { - final container = createContainer(); + final container = ProviderContainer.test(); /* SNIPPET START */ final subscription = container.listen(provider, (_, __) {}); diff --git a/website/docs/essentials/testing/await_future.dart b/website/docs/essentials/testing/await_future.dart index a0cc3fa61..1b73e072a 100644 --- a/website/docs/essentials/testing/await_future.dart +++ b/website/docs/essentials/testing/await_future.dart @@ -3,15 +3,13 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:riverpod/riverpod.dart'; -import 'create_container.dart'; - final provider = FutureProvider((_) async => 42); void main() { test('Some description', () async { // Create a ProviderContainer for this test. // DO NOT share ProviderContainers between tests. - final container = createContainer(); + final container = ProviderContainer.test(); /* SNIPPET START */ // {@template note} diff --git a/website/docs/essentials/testing/create_container.dart b/website/docs/essentials/testing/create_container.dart deleted file mode 100644 index 73d30f8ef..000000000 --- a/website/docs/essentials/testing/create_container.dart +++ /dev/null @@ -1,29 +0,0 @@ -/* SNIPPET START */ -import 'package:riverpod/riverpod.dart'; -import 'package:test/test.dart'; - -// {@template createContainer} -/// A testing utility which creates a [ProviderContainer] and automatically -/// disposes it at the end of the test. -// {@endtemplate} -ProviderContainer createContainer({ - ProviderContainer? parent, - List overrides = const [], - List? observers, -}) { - // {@template container} - // Create a ProviderContainer, and optionally allow specifying parameters. - // {@endtemplate} - final container = ProviderContainer( - parent: parent, - overrides: overrides, - observers: observers, - ); - - // {@template addTearDown} - // When the test ends, dispose the container. - // {@endtemplate} - addTearDown(container.dispose); - - return container; -} diff --git a/website/docs/essentials/testing/listen_provider.dart b/website/docs/essentials/testing/listen_provider.dart index d2e840d9b..2e789c6fb 100644 --- a/website/docs/essentials/testing/listen_provider.dart +++ b/website/docs/essentials/testing/listen_provider.dart @@ -3,13 +3,11 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:riverpod/riverpod.dart'; -import 'create_container.dart'; - final provider = Provider((_) => 'Hello world'); void main() { test('Some description', () { - final container = createContainer(); + final container = ProviderContainer.test(); /* SNIPPET START */ container.listen( provider, diff --git a/website/docs/essentials/testing/mock_provider.dart b/website/docs/essentials/testing/mock_provider.dart index 7c71ca802..cea4240bd 100644 --- a/website/docs/essentials/testing/mock_provider.dart +++ b/website/docs/essentials/testing/mock_provider.dart @@ -3,7 +3,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'create_container.dart'; import 'full_widget_test.dart'; import 'provider_to_mock/raw.dart'; @@ -16,7 +15,7 @@ void main() { // {@template container} // In unit tests, by reusing our previous "createContainer" utility. // {@endtemplate} - final container = createContainer( + final container = ProviderContainer.test( // {@template providers} // We can specify a list of providers to mock: // {@endtemplate} diff --git a/website/docs/essentials/testing/notifier_mock/codegen.g.dart b/website/docs/essentials/testing/notifier_mock/codegen.g.dart index 7503232d8..cdf362639 100644 --- a/website/docs/essentials/testing/notifier_mock/codegen.g.dart +++ b/website/docs/essentials/testing/notifier_mock/codegen.g.dart @@ -8,20 +8,74 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(MyNotifier) +const myNotifierProvider = MyNotifierProvider._(); + +final class MyNotifierProvider extends $NotifierProvider { + const MyNotifierProvider._( + {super.runNotifierBuildOverride, MyNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$myNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + MyNotifier create() => _createCb?.call() ?? MyNotifier(); + + @$internal + @override + MyNotifierProvider $copyWithCreate( + MyNotifier Function() create, + ) { + return MyNotifierProvider._(create: create); + } + + @$internal + @override + MyNotifierProvider $copyWithBuild( + int Function( + Ref, + MyNotifier, + ) build, + ) { + return MyNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$myNotifierHash() => r'912fa35c2296626fc0825bcbcfc6b6c85958be02'; -/// See also [MyNotifier]. -@ProviderFor(MyNotifier) -final myNotifierProvider = - AutoDisposeNotifierProvider.internal( - MyNotifier.new, - name: r'myNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$myNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$MyNotifier = AutoDisposeNotifier; +abstract class _$MyNotifier extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/testing/notifier_usage.dart b/website/docs/essentials/testing/notifier_usage.dart index 26fb960bd..ba775c239 100644 --- a/website/docs/essentials/testing/notifier_usage.dart +++ b/website/docs/essentials/testing/notifier_usage.dart @@ -1,14 +1,14 @@ // ignore_for_file: unused_local_variable import 'package:flutter_test/flutter_test.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; -import 'create_container.dart'; import 'notifier_mock/codegen.dart'; /* SNIPPET START */ void main() { test('Some description', () { - final container = createContainer( + final container = ProviderContainer.test( // {@template overrides} // Override the provider to have it create our mock Notifier. // {@endtemplate} diff --git a/website/docs/essentials/testing/provider_to_mock/codegen.dart b/website/docs/essentials/testing/provider_to_mock/codegen.dart index 078148729..073779b2d 100644 --- a/website/docs/essentials/testing/provider_to_mock/codegen.dart +++ b/website/docs/essentials/testing/provider_to_mock/codegen.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/essentials/testing/provider_to_mock/codegen.g.dart b/website/docs/essentials/testing/provider_to_mock/codegen.g.dart index 597d3064c..d317584cf 100644 --- a/website/docs/essentials/testing/provider_to_mock/codegen.g.dart +++ b/website/docs/essentials/testing/provider_to_mock/codegen.g.dart @@ -8,21 +8,56 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(example) +const exampleProvider = ExampleProvider._(); + +final class ExampleProvider + extends $FunctionalProvider, FutureOr> + with $FutureModifier, $FutureProvider { + const ExampleProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'exampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$exampleHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ExampleProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return ExampleProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? example; + return _$cb(ref); + } +} + String _$exampleHash() => r'8768b72378809f5cb6ae94b65493cbb49bd23b77'; -/// See also [example]. -@ProviderFor(example) -final exampleProvider = AutoDisposeFutureProvider.internal( - example, - name: r'exampleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$exampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ExampleRef = AutoDisposeFutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/testing/unit_test.dart b/website/docs/essentials/testing/unit_test.dart index 5662ba5a6..8bfe145be 100644 --- a/website/docs/essentials/testing/unit_test.dart +++ b/website/docs/essentials/testing/unit_test.dart @@ -3,8 +3,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:riverpod/riverpod.dart'; -import 'create_container.dart'; - final provider = Provider((_) => 42); /* SNIPPET START */ @@ -14,7 +12,7 @@ void main() { // Create a ProviderContainer for this test. // DO NOT share ProviderContainers between tests. // {@endtemplate} - final container = createContainer(); + final container = ProviderContainer.test(); // {@template useProvider} // TODO: use the container to test your application. diff --git a/website/docs/essentials/websockets_sync/change_notifier_provider.dart b/website/docs/essentials/websockets_sync/change_notifier_provider.dart index c4612aa10..a16a8e6e3 100644 --- a/website/docs/essentials/websockets_sync/change_notifier_provider.dart +++ b/website/docs/essentials/websockets_sync/change_notifier_provider.dart @@ -1,7 +1,7 @@ // ignore_for_file: omit_local_variable_types import 'package:flutter/widgets.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ final myProvider = ChangeNotifierProvider>((ref) { diff --git a/website/docs/essentials/websockets_sync/pipe_change_notifier.dart b/website/docs/essentials/websockets_sync/pipe_change_notifier.dart index 8d4a45613..0cc75bea6 100644 --- a/website/docs/essentials/websockets_sync/pipe_change_notifier.dart +++ b/website/docs/essentials/websockets_sync/pipe_change_notifier.dart @@ -1,7 +1,6 @@ // ignore_for_file: omit_local_variable_types import 'package:flutter/widgets.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'pipe_change_notifier.g.dart'; @@ -12,7 +11,7 @@ part 'pipe_change_notifier.g.dart'; /// whenever the value changes. // {@endtemplate} @riverpod -ValueNotifier myListenable(Ref ref) { +Raw> myListenable(Ref ref) { final notifier = ValueNotifier(0); // {@template onDispose} diff --git a/website/docs/essentials/websockets_sync/pipe_change_notifier.g.dart b/website/docs/essentials/websockets_sync/pipe_change_notifier.g.dart index 6ae36a251..f4b759774 100644 --- a/website/docs/essentials/websockets_sync/pipe_change_notifier.g.dart +++ b/website/docs/essentials/websockets_sync/pipe_change_notifier.g.dart @@ -8,24 +8,74 @@ part of 'pipe_change_notifier.dart'; // RiverpodGenerator // ************************************************************************** -String _$myListenableHash() => r'83341786f6ed7986544648ddf718cf8b03a18af9'; - /// A provider which creates a ValueNotifier and update its listeners /// whenever the value changes. -/// -/// Copied from [myListenable]. +// {@endtemplate} @ProviderFor(myListenable) -final myListenableProvider = AutoDisposeProvider>.internal( - myListenable, - name: r'myListenableProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$myListenableHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef MyListenableRef = AutoDisposeProviderRef>; +const myListenableProvider = MyListenableProvider._(); + +/// A provider which creates a ValueNotifier and update its listeners +/// whenever the value changes. +// {@endtemplate} +final class MyListenableProvider extends $FunctionalProvider< + Raw>, + Raw>> with $Provider>> { + /// A provider which creates a ValueNotifier and update its listeners + /// whenever the value changes. +// {@endtemplate} + const MyListenableProvider._( + {Raw> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myListenableProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Raw> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$myListenableHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>>(value), + ); + } + + @$internal + @override + $ProviderElement>> $createElement( + $ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + MyListenableProvider $copyWithCreate( + Raw> Function( + Ref ref, + ) create, + ) { + return MyListenableProvider._(create: create); + } + + @override + Raw> create(Ref ref) { + final _$cb = _createCb ?? myListenable; + return _$cb(ref); + } +} + +String _$myListenableHash() => r'11b973997ad9787b8f775746d7a87211df2cb6bb'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/websockets_sync/raw_usage.g.dart b/website/docs/essentials/websockets_sync/raw_usage.g.dart index 041d8dab1..c971179f0 100644 --- a/website/docs/essentials/websockets_sync/raw_usage.g.dart +++ b/website/docs/essentials/websockets_sync/raw_usage.g.dart @@ -8,21 +8,64 @@ part of 'raw_usage.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(rawStream) +const rawStreamProvider = RawStreamProvider._(); + +final class RawStreamProvider + extends $FunctionalProvider>, Raw>> + with $Provider>> { + const RawStreamProvider._( + {Raw> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'rawStreamProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Raw> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$rawStreamHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>>(value), + ); + } + + @$internal + @override + $ProviderElement>> $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + RawStreamProvider $copyWithCreate( + Raw> Function( + Ref ref, + ) create, + ) { + return RawStreamProvider._(create: create); + } + + @override + Raw> create(Ref ref) { + final _$cb = _createCb ?? rawStream; + return _$cb(ref); + } +} + String _$rawStreamHash() => r'9ce48e3afce64329958af139c77f5e271e0bf04f'; -/// See also [rawStream]. -@ProviderFor(rawStream) -final rawStreamProvider = AutoDisposeProvider>>.internal( - rawStream, - name: r'rawStreamProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$rawStreamHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef RawStreamRef = AutoDisposeProviderRef>>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/websockets_sync/shared_pipe_change_notifier.dart b/website/docs/essentials/websockets_sync/shared_pipe_change_notifier.dart index e979240f8..8b94a0d55 100644 --- a/website/docs/essentials/websockets_sync/shared_pipe_change_notifier.dart +++ b/website/docs/essentials/websockets_sync/shared_pipe_change_notifier.dart @@ -1,7 +1,6 @@ // ignore_for_file: omit_local_variable_types import 'package:flutter/widgets.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'shared_pipe_change_notifier.g.dart'; @@ -23,11 +22,11 @@ extension on Ref { } @riverpod -ValueNotifier myListenable(Ref ref) { +Raw> myListenable(Ref ref) { return ref.disposeAndListenChangeNotifier(ValueNotifier(0)); } @riverpod -ValueNotifier anotherListenable(Ref ref) { +Raw> anotherListenable(Ref ref) { return ref.disposeAndListenChangeNotifier(ValueNotifier(42)); } diff --git a/website/docs/essentials/websockets_sync/shared_pipe_change_notifier.g.dart b/website/docs/essentials/websockets_sync/shared_pipe_change_notifier.g.dart index b29de01a7..59ea67a73 100644 --- a/website/docs/essentials/websockets_sync/shared_pipe_change_notifier.g.dart +++ b/website/docs/essentials/websockets_sync/shared_pipe_change_notifier.g.dart @@ -8,39 +8,125 @@ part of 'shared_pipe_change_notifier.dart'; // RiverpodGenerator // ************************************************************************** -String _$myListenableHash() => r'291bdd4fa77ef8b268c050be5ecb94d848e32292'; - -/// See also [myListenable]. @ProviderFor(myListenable) -final myListenableProvider = AutoDisposeProvider>.internal( - myListenable, - name: r'myListenableProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$myListenableHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef MyListenableRef = AutoDisposeProviderRef>; -String _$anotherListenableHash() => r'd967ed586ddcb57a4dc1cca3c380a299c5affc5d'; - -/// See also [anotherListenable]. +const myListenableProvider = MyListenableProvider._(); + +final class MyListenableProvider extends $FunctionalProvider< + Raw>, + Raw>> with $Provider>> { + const MyListenableProvider._( + {Raw> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myListenableProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Raw> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$myListenableHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>>(value), + ); + } + + @$internal + @override + $ProviderElement>> $createElement( + $ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + MyListenableProvider $copyWithCreate( + Raw> Function( + Ref ref, + ) create, + ) { + return MyListenableProvider._(create: create); + } + + @override + Raw> create(Ref ref) { + final _$cb = _createCb ?? myListenable; + return _$cb(ref); + } +} + +String _$myListenableHash() => r'a28ce39430582e0d7be5f8303a31477569153193'; + @ProviderFor(anotherListenable) -final anotherListenableProvider = - AutoDisposeProvider>.internal( - anotherListenable, - name: r'anotherListenableProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$anotherListenableHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef AnotherListenableRef = AutoDisposeProviderRef>; +const anotherListenableProvider = AnotherListenableProvider._(); + +final class AnotherListenableProvider extends $FunctionalProvider< + Raw>, + Raw>> with $Provider>> { + const AnotherListenableProvider._( + {Raw> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'anotherListenableProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Raw> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$anotherListenableHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Raw> value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>>(value), + ); + } + + @$internal + @override + $ProviderElement>> $createElement( + $ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + AnotherListenableProvider $copyWithCreate( + Raw> Function( + Ref ref, + ) create, + ) { + return AnotherListenableProvider._(create: create); + } + + @override + Raw> create(Ref ref) { + final _$cb = _createCb ?? anotherListenable; + return _$cb(ref); + } +} + +String _$anotherListenableHash() => r'49aab48c26d8596262c3d89e0190baeaf9d7ac4a'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/websockets_sync/stream_provider/codegen.dart b/website/docs/essentials/websockets_sync/stream_provider/codegen.dart index aa0f6ca36..e80ec7478 100644 --- a/website/docs/essentials/websockets_sync/stream_provider/codegen.dart +++ b/website/docs/essentials/websockets_sync/stream_provider/codegen.dart @@ -32,9 +32,9 @@ class Consumer extends ConsumerWidget { // {@endtemplate} return switch (value) { AsyncValue(:final error?) => Text('Error: $error'), - AsyncValue(:final valueOrNull?) => Text('$valueOrNull'), + AsyncValue(:final value?) => Text('$value'), _ => const CircularProgressIndicator(), }; } } -/* SNIPPET END */ \ No newline at end of file +/* SNIPPET END */ diff --git a/website/docs/essentials/websockets_sync/stream_provider/codegen.g.dart b/website/docs/essentials/websockets_sync/stream_provider/codegen.g.dart index 935c7442f..0e31026e8 100644 --- a/website/docs/essentials/websockets_sync/stream_provider/codegen.g.dart +++ b/website/docs/essentials/websockets_sync/stream_provider/codegen.g.dart @@ -8,22 +8,56 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(streamExample) +const streamExampleProvider = StreamExampleProvider._(); + +final class StreamExampleProvider + extends $FunctionalProvider, Stream> + with $FutureModifier, $StreamProvider { + const StreamExampleProvider._( + {Stream Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'streamExampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Stream Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$streamExampleHash(); + + @$internal + @override + $StreamProviderElement $createElement($ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + + @override + StreamExampleProvider $copyWithCreate( + Stream Function( + Ref ref, + ) create, + ) { + return StreamExampleProvider._(create: create); + } + + @override + Stream create(Ref ref) { + final _$cb = _createCb ?? streamExample; + return _$cb(ref); + } +} + String _$streamExampleHash() => r'5f0e824562e820b85cc0d031a7fcce1d381023a5'; -/// See also [streamExample]. -@ProviderFor(streamExample) -final streamExampleProvider = AutoDisposeStreamProvider.internal( - streamExample, - name: r'streamExampleProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$streamExampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef StreamExampleRef = AutoDisposeStreamProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/websockets_sync/stream_provider/raw.dart b/website/docs/essentials/websockets_sync/stream_provider/raw.dart index f779ef5a8..ee0191066 100644 --- a/website/docs/essentials/websockets_sync/stream_provider/raw.dart +++ b/website/docs/essentials/websockets_sync/stream_provider/raw.dart @@ -28,9 +28,9 @@ class Consumer extends ConsumerWidget { // {@endtemplate} return switch (value) { AsyncValue(:final error?) => Text('Error: $error'), - AsyncValue(:final valueOrNull?) => Text('$valueOrNull'), + AsyncValue(:final value?) => Text('$value'), _ => const CircularProgressIndicator(), }; } } -/* SNIPPET END */ \ No newline at end of file +/* SNIPPET END */ diff --git a/website/docs/essentials/websockets_sync/sync_definition/codegen.dart b/website/docs/essentials/websockets_sync/sync_definition/codegen.dart index e2a13c489..cdddcb32d 100644 --- a/website/docs/essentials/websockets_sync/sync_definition/codegen.dart +++ b/website/docs/essentials/websockets_sync/sync_definition/codegen.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; @@ -8,4 +7,4 @@ part 'codegen.g.dart'; int synchronousExample(Ref ref) { return 0; } -/* SNIPPET END */ \ No newline at end of file +/* SNIPPET END */ diff --git a/website/docs/essentials/websockets_sync/sync_definition/codegen.g.dart b/website/docs/essentials/websockets_sync/sync_definition/codegen.g.dart index 075c64313..287ae347d 100644 --- a/website/docs/essentials/websockets_sync/sync_definition/codegen.g.dart +++ b/website/docs/essentials/websockets_sync/sync_definition/codegen.g.dart @@ -8,23 +8,64 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(synchronousExample) +const synchronousExampleProvider = SynchronousExampleProvider._(); + +final class SynchronousExampleProvider extends $FunctionalProvider + with $Provider { + const SynchronousExampleProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'synchronousExampleProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$synchronousExampleHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + SynchronousExampleProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return SynchronousExampleProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? synchronousExample; + return _$cb(ref); + } +} + String _$synchronousExampleHash() => r'a12577c395d5a639fdad88b28309f378a64bd2a7'; -/// See also [synchronousExample]. -@ProviderFor(synchronousExample) -final synchronousExampleProvider = AutoDisposeProvider.internal( - synchronousExample, - name: r'synchronousExampleProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$synchronousExampleHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef SynchronousExampleRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/essentials/websockets_sync/sync_definition/raw.dart b/website/docs/essentials/websockets_sync/sync_definition/raw.dart index 9c64294a0..193c5eeff 100644 --- a/website/docs/essentials/websockets_sync/sync_definition/raw.dart +++ b/website/docs/essentials/websockets_sync/sync_definition/raw.dart @@ -4,4 +4,4 @@ import 'package:riverpod/riverpod.dart'; final synchronousExampleProvider = Provider.autoDispose((ref) { return 0; }); -/* SNIPPET END */ \ No newline at end of file +/* SNIPPET END */ diff --git a/website/docs/from_provider/family/family.dart b/website/docs/from_provider/family/family.dart index 88012fd24..61cf268fc 100644 --- a/website/docs/from_provider/family/family.dart +++ b/website/docs/from_provider/family/family.dart @@ -1,6 +1,5 @@ import 'dart:math'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'family.g.dart'; diff --git a/website/docs/from_provider/family/family.g.dart b/website/docs/from_provider/family/family.g.dart index 0bee73fa6..8c2fc95a0 100644 --- a/website/docs/from_provider/family/family.g.dart +++ b/website/docs/from_provider/family/family.g.dart @@ -8,169 +8,159 @@ part of 'family.dart'; // RiverpodGenerator // ************************************************************************** -String _$randomHash() => r'ab69799dce84746b22880feae0f1db6dea906f6a'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// See also [random]. @ProviderFor(random) -const randomProvider = RandomFamily(); - -/// See also [random]. -class RandomFamily extends Family { - /// See also [random]. - const RandomFamily(); +const randomProvider = RandomFamily._(); + +final class RandomProvider extends $FunctionalProvider + with $Provider { + const RandomProvider._( + {required RandomFamily super.from, + required ({ + int seed, + int max, + }) + super.argument, + int Function( + Ref ref, { + required int seed, + required int max, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'randomProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// See also [random]. - RandomProvider call({ + final int Function( + Ref ref, { required int seed, required int max, - }) { - return RandomProvider( - seed: seed, - max: max, - ); - } + })? _createCb; @override - RandomProvider getProviderOverride( - covariant RandomProvider provider, - ) { - return call( - seed: provider.seed, - max: provider.max, - ); - } - - static const Iterable? _dependencies = null; + String debugGetCreateSourceHash() => _$randomHash(); @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; + String toString() { + return r'randomProvider' + '' + '$argument'; + } - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + @$internal @override - String? get name => r'randomProvider'; -} - -/// See also [random]. -class RandomProvider extends AutoDisposeProvider { - /// See also [random]. - RandomProvider({ - required int seed, - required int max, - }) : this._internal( - (ref) => random( - ref as RandomRef, - seed: seed, - max: max, - ), - from: randomProvider, - name: r'randomProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$randomHash, - dependencies: RandomFamily._dependencies, - allTransitiveDependencies: RandomFamily._allTransitiveDependencies, - seed: seed, - max: max, - ); - - RandomProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.seed, - required this.max, - }) : super.internal(); - - final int seed; - final int max; + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); @override - Override overrideWith( - int Function(RandomRef provider) create, + RandomProvider $copyWithCreate( + int Function( + Ref ref, + ) create, ) { - return ProviderOverride( - origin: this, - override: RandomProvider._internal( - (ref) => create(ref as RandomRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - seed: seed, - max: max, - ), - ); + return RandomProvider._( + argument: argument as ({ + int seed, + int max, + }), + from: from! as RandomFamily, + create: ( + ref, { + required int seed, + required int max, + }) => + create(ref)); } @override - AutoDisposeProviderElement createElement() { - return _RandomProviderElement(this); + int create(Ref ref) { + final _$cb = _createCb ?? random; + final argument = this.argument as ({ + int seed, + int max, + }); + return _$cb( + ref, + seed: argument.seed, + max: argument.max, + ); } @override bool operator ==(Object other) { - return other is RandomProvider && other.seed == seed && other.max == max; + return other is RandomProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, seed.hashCode); - hash = _SystemHash.combine(hash, max.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin RandomRef on AutoDisposeProviderRef { - /// The parameter `seed` of this provider. - int get seed; +String _$randomHash() => r'ab69799dce84746b22880feae0f1db6dea906f6a'; - /// The parameter `max` of this provider. - int get max; -} +final class RandomFamily extends Family { + const RandomFamily._() + : super( + retry: null, + name: r'randomProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); -class _RandomProviderElement extends AutoDisposeProviderElement - with RandomRef { - _RandomProviderElement(super.provider); + RandomProvider call({ + required int seed, + required int max, + }) => + RandomProvider._(argument: ( + seed: seed, + max: max, + ), from: this); @override - int get seed => (origin as RandomProvider).seed; + String debugGetCreateSourceHash() => _$randomHash(); + @override - int get max => (origin as RandomProvider).max; + String toString() => r'randomProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + int Function( + Ref ref, + ({ + int seed, + int max, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as RandomProvider; + + final argument = provider.argument as ({ + int seed, + int max, + }); + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/from_provider/family/raw.dart b/website/docs/from_provider/family/raw.dart index 68b84d40d..e1977b568 100644 --- a/website/docs/from_provider/family/raw.dart +++ b/website/docs/from_provider/family/raw.dart @@ -1,7 +1,7 @@ import 'dart:math'; import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; @immutable abstract class Equatable { diff --git a/website/docs/from_provider/motivation/async_values/async_values.dart b/website/docs/from_provider/motivation/async_values/async_values.dart index 96f694e2a..0518dc0a7 100644 --- a/website/docs/from_provider/motivation/async_values/async_values.dart +++ b/website/docs/from_provider/motivation/async_values/async_values.dart @@ -1,6 +1,5 @@ import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../helpers/item.dart'; diff --git a/website/docs/from_provider/motivation/async_values/async_values.g.dart b/website/docs/from_provider/motivation/async_values/async_values.g.dart index 9468dc57a..1b78c2ed0 100644 --- a/website/docs/from_provider/motivation/async_values/async_values.g.dart +++ b/website/docs/from_provider/motivation/async_values/async_values.g.dart @@ -8,37 +8,115 @@ part of 'async_values.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(itemsApi) +const itemsApiProvider = ItemsApiProvider._(); + +final class ItemsApiProvider + extends $FunctionalProvider>, FutureOr>> + with $FutureModifier>, $FutureProvider> { + const ItemsApiProvider._( + {FutureOr> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'itemsApiProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$itemsApiHash(); + + @$internal + @override + $FutureProviderElement> $createElement($ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + ItemsApiProvider $copyWithCreate( + FutureOr> Function( + Ref ref, + ) create, + ) { + return ItemsApiProvider._(create: create); + } + + @override + FutureOr> create(Ref ref) { + final _$cb = _createCb ?? itemsApi; + return _$cb(ref); + } +} + String _$itemsApiHash() => r'fa5a8f7e93ac048d9bd5dfc1744749995cf154af'; -/// See also [itemsApi]. -@ProviderFor(itemsApi) -final itemsApiProvider = AutoDisposeFutureProvider>.internal( - itemsApi, - name: r'itemsApiProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$itemsApiHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ItemsApiRef = AutoDisposeFutureProviderRef>; +@ProviderFor(evenItems) +const evenItemsProvider = EvenItemsProvider._(); + +final class EvenItemsProvider + extends $FunctionalProvider, List> + with $Provider> { + const EvenItemsProvider._( + {List Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'evenItemsProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final List Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$evenItemsHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + $ProviderElement> $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + EvenItemsProvider $copyWithCreate( + List Function( + Ref ref, + ) create, + ) { + return EvenItemsProvider._(create: create); + } + + @override + List create(Ref ref) { + final _$cb = _createCb ?? evenItems; + return _$cb(ref); + } +} + String _$evenItemsHash() => r'22297e33c5f55ff99fb49747c203be595a28fabf'; -/// See also [evenItems]. -@ProviderFor(evenItems) -final evenItemsProvider = AutoDisposeProvider>.internal( - evenItems, - name: r'evenItemsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$evenItemsHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef EvenItemsRef = AutoDisposeProviderRef>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/from_provider/motivation/async_values/raw.dart b/website/docs/from_provider/motivation/async_values/raw.dart index 1ca8987ef..f21a954e5 100644 --- a/website/docs/from_provider/motivation/async_values/raw.dart +++ b/website/docs/from_provider/motivation/async_values/raw.dart @@ -1,6 +1,6 @@ import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; import '../../helpers/item.dart'; import '../../helpers/json.dart'; diff --git a/website/docs/from_provider/motivation/auto_dispose/auto_dispose.dart b/website/docs/from_provider/motivation/auto_dispose/auto_dispose.dart index fcb26dd6c..052686f61 100644 --- a/website/docs/from_provider/motivation/auto_dispose/auto_dispose.dart +++ b/website/docs/from_provider/motivation/auto_dispose/auto_dispose.dart @@ -1,6 +1,5 @@ import 'dart:math'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'auto_dispose.g.dart'; diff --git a/website/docs/from_provider/motivation/auto_dispose/auto_dispose.g.dart b/website/docs/from_provider/motivation/auto_dispose/auto_dispose.g.dart index 23d7cd529..2c1ffb3ea 100644 --- a/website/docs/from_provider/motivation/auto_dispose/auto_dispose.g.dart +++ b/website/docs/from_provider/motivation/auto_dispose/auto_dispose.g.dart @@ -8,38 +8,121 @@ part of 'auto_dispose.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(diceRoll) +const diceRollProvider = DiceRollProvider._(); + +final class DiceRollProvider extends $FunctionalProvider + with $Provider { + const DiceRollProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'diceRollProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$diceRollHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + DiceRollProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return DiceRollProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? diceRoll; + return _$cb(ref); + } +} + String _$diceRollHash() => r'58d43e5143bb64e855939d55a3be3ee81d66c518'; -/// See also [diceRoll]. -@ProviderFor(diceRoll) -final diceRollProvider = AutoDisposeProvider.internal( - diceRoll, - name: r'diceRollProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$diceRollHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef DiceRollRef = AutoDisposeProviderRef; +@ProviderFor(cachedDiceRoll) +const cachedDiceRollProvider = CachedDiceRollProvider._(); + +final class CachedDiceRollProvider extends $FunctionalProvider + with $Provider { + const CachedDiceRollProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'cachedDiceRollProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$cachedDiceRollHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CachedDiceRollProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return CachedDiceRollProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? cachedDiceRoll; + return _$cb(ref); + } +} + String _$cachedDiceRollHash() => r'eaf5bb809278298f16e2eda8972b1876921f66f5'; -/// See also [cachedDiceRoll]. -@ProviderFor(cachedDiceRoll) -final cachedDiceRollProvider = AutoDisposeProvider.internal( - cachedDiceRoll, - name: r'cachedDiceRollProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$cachedDiceRollHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CachedDiceRollRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/from_provider/motivation/auto_dispose/raw.dart b/website/docs/from_provider/motivation/auto_dispose/raw.dart index e27d1903a..7fd1edd57 100644 --- a/website/docs/from_provider/motivation/auto_dispose/raw.dart +++ b/website/docs/from_provider/motivation/auto_dispose/raw.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; /* SNIPPET START */ diff --git a/website/docs/from_provider/motivation/combine/combine.dart b/website/docs/from_provider/motivation/combine/combine.dart index f26e5d285..521d231b7 100644 --- a/website/docs/from_provider/motivation/combine/combine.dart +++ b/website/docs/from_provider/motivation/combine/combine.dart @@ -1,6 +1,5 @@ import 'dart:math'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'combine.g.dart'; diff --git a/website/docs/from_provider/motivation/combine/combine.g.dart b/website/docs/from_provider/motivation/combine/combine.g.dart index dc4a9780e..d8ed187a2 100644 --- a/website/docs/from_provider/motivation/combine/combine.g.dart +++ b/website/docs/from_provider/motivation/combine/combine.g.dart @@ -8,37 +8,121 @@ part of 'combine.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(number) +const numberProvider = NumberProvider._(); + +final class NumberProvider extends $FunctionalProvider + with $Provider { + const NumberProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'numberProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$numberHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + NumberProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return NumberProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? number; + return _$cb(ref); + } +} + String _$numberHash() => r'03ac91d5904c18f04321b140fd263ed6bc85d3c1'; -/// See also [number]. -@ProviderFor(number) -final numberProvider = AutoDisposeProvider.internal( - number, - name: r'numberProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$numberHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef NumberRef = AutoDisposeProviderRef; +@ProviderFor(doubled) +const doubledProvider = DoubledProvider._(); + +final class DoubledProvider extends $FunctionalProvider + with $Provider { + const DoubledProvider._( + {int Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'doubledProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final int Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$doubledHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + DoubledProvider $copyWithCreate( + int Function( + Ref ref, + ) create, + ) { + return DoubledProvider._(create: create); + } + + @override + int create(Ref ref) { + final _$cb = _createCb ?? doubled; + return _$cb(ref); + } +} + String _$doubledHash() => r'2a7f7fadb89e55d6adcf11aaa21943c66b10df5e'; -/// See also [doubled]. -@ProviderFor(doubled) -final doubledProvider = AutoDisposeProvider.internal( - doubled, - name: r'doubledProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$doubledHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef DoubledRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/from_provider/motivation/combine/raw.dart b/website/docs/from_provider/motivation/combine/raw.dart index ad33636e7..1b8412dd0 100644 --- a/website/docs/from_provider/motivation/combine/raw.dart +++ b/website/docs/from_provider/motivation/combine/raw.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; /* SNIPPET START */ diff --git a/website/docs/from_provider/motivation/same_type/raw.dart b/website/docs/from_provider/motivation/same_type/raw.dart index dacfe9b9d..1a2456978 100644 --- a/website/docs/from_provider/motivation/same_type/raw.dart +++ b/website/docs/from_provider/motivation/same_type/raw.dart @@ -1,5 +1,5 @@ import 'package:collection/collection.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; import '../../helpers/item.dart'; diff --git a/website/docs/from_provider/motivation/same_type/same_type.dart b/website/docs/from_provider/motivation/same_type/same_type.dart index 1094a6950..a46db40cf 100644 --- a/website/docs/from_provider/motivation/same_type/same_type.dart +++ b/website/docs/from_provider/motivation/same_type/same_type.dart @@ -1,5 +1,4 @@ import 'package:collection/collection.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../helpers/item.dart'; diff --git a/website/docs/from_provider/motivation/same_type/same_type.g.dart b/website/docs/from_provider/motivation/same_type/same_type.g.dart index e8cdf55aa..30bc5f7ec 100644 --- a/website/docs/from_provider/motivation/same_type/same_type.g.dart +++ b/website/docs/from_provider/motivation/same_type/same_type.g.dart @@ -8,37 +8,122 @@ part of 'same_type.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(items) +const itemsProvider = ItemsProvider._(); + +final class ItemsProvider extends $FunctionalProvider, List> + with $Provider> { + const ItemsProvider._( + {List Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'itemsProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final List Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$itemsHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + $ProviderElement> $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + ItemsProvider $copyWithCreate( + List Function( + Ref ref, + ) create, + ) { + return ItemsProvider._(create: create); + } + + @override + List create(Ref ref) { + final _$cb = _createCb ?? items; + return _$cb(ref); + } +} + String _$itemsHash() => r'8dafed1afc3fc52651c24445640d8b57ff080f66'; -/// See also [items]. -@ProviderFor(items) -final itemsProvider = AutoDisposeProvider>.internal( - items, - name: r'itemsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$itemsHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ItemsRef = AutoDisposeProviderRef>; +@ProviderFor(evenItems) +const evenItemsProvider = EvenItemsProvider._(); + +final class EvenItemsProvider + extends $FunctionalProvider, List> + with $Provider> { + const EvenItemsProvider._( + {List Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'evenItemsProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final List Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$evenItemsHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + $ProviderElement> $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + EvenItemsProvider $copyWithCreate( + List Function( + Ref ref, + ) create, + ) { + return EvenItemsProvider._(create: create); + } + + @override + List create(Ref ref) { + final _$cb = _createCb ?? evenItems; + return _$cb(ref); + } +} + String _$evenItemsHash() => r'83ef608e2e1ec6926495f7a4dd4bac3e6b1f16e1'; -/// See also [evenItems]. -@ProviderFor(evenItems) -final evenItemsProvider = AutoDisposeProvider>.internal( - evenItems, - name: r'evenItemsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$evenItemsHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef EvenItemsRef = AutoDisposeProviderRef>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/introduction/getting_started/dart_hello_world/main.dart b/website/docs/introduction/getting_started/dart_hello_world/main.dart index b989c0de1..c265b837a 100644 --- a/website/docs/introduction/getting_started/dart_hello_world/main.dart +++ b/website/docs/introduction/getting_started/dart_hello_world/main.dart @@ -1,8 +1,7 @@ -// ignore_for_file: avoid_print +// ignore_for_file: avoid_print, unreachable_from_main /* SNIPPET START */ -import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'main.g.dart'; diff --git a/website/docs/introduction/getting_started/dart_hello_world/main.g.dart b/website/docs/introduction/getting_started/dart_hello_world/main.g.dart index 9dd4f5ad3..c197b77fe 100644 --- a/website/docs/introduction/getting_started/dart_hello_world/main.g.dart +++ b/website/docs/introduction/getting_started/dart_hello_world/main.g.dart @@ -8,21 +8,63 @@ part of 'main.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(helloWorld) +const helloWorldProvider = HelloWorldProvider._(); + +final class HelloWorldProvider extends $FunctionalProvider + with $Provider { + const HelloWorldProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'helloWorldProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$helloWorldHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + HelloWorldProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return HelloWorldProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? helloWorld; + return _$cb(ref); + } +} + String _$helloWorldHash() => r'9abaa5ab530c55186861f2debdaa218aceacb7eb'; -/// See also [helloWorld]. -@ProviderFor(helloWorld) -final helloWorldProvider = AutoDisposeProvider.internal( - helloWorld, - name: r'helloWorldProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$helloWorldHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef HelloWorldRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/introduction/getting_started/hello_world/hooks_codegen/main.dart b/website/docs/introduction/getting_started/hello_world/hooks_codegen/main.dart index 5c6766ba3..cb84e0e85 100644 --- a/website/docs/introduction/getting_started/hello_world/hooks_codegen/main.dart +++ b/website/docs/introduction/getting_started/hello_world/hooks_codegen/main.dart @@ -1,4 +1,4 @@ -// ignore_for_file: use_key_in_widget_constructors, omit_local_variable_types +// ignore_for_file: use_key_in_widget_constructors, omit_local_variable_types, unreachable_from_main /* SNIPPET START */ diff --git a/website/docs/introduction/getting_started/hello_world/hooks_codegen/main.g.dart b/website/docs/introduction/getting_started/hello_world/hooks_codegen/main.g.dart index 9dd4f5ad3..c197b77fe 100644 --- a/website/docs/introduction/getting_started/hello_world/hooks_codegen/main.g.dart +++ b/website/docs/introduction/getting_started/hello_world/hooks_codegen/main.g.dart @@ -8,21 +8,63 @@ part of 'main.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(helloWorld) +const helloWorldProvider = HelloWorldProvider._(); + +final class HelloWorldProvider extends $FunctionalProvider + with $Provider { + const HelloWorldProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'helloWorldProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$helloWorldHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + HelloWorldProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return HelloWorldProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? helloWorld; + return _$cb(ref); + } +} + String _$helloWorldHash() => r'9abaa5ab530c55186861f2debdaa218aceacb7eb'; -/// See also [helloWorld]. -@ProviderFor(helloWorld) -final helloWorldProvider = AutoDisposeProvider.internal( - helloWorld, - name: r'helloWorldProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$helloWorldHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef HelloWorldRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/introduction/getting_started/hello_world/main.dart b/website/docs/introduction/getting_started/hello_world/main.dart index 710a3627b..622956be6 100644 --- a/website/docs/introduction/getting_started/hello_world/main.dart +++ b/website/docs/introduction/getting_started/hello_world/main.dart @@ -1,4 +1,4 @@ -// ignore_for_file: use_key_in_widget_constructors, omit_local_variable_types +// ignore_for_file: use_key_in_widget_constructors, omit_local_variable_types, unreachable_from_main /* SNIPPET START */ diff --git a/website/docs/introduction/getting_started/hello_world/main.g.dart b/website/docs/introduction/getting_started/hello_world/main.g.dart index 9dd4f5ad3..c197b77fe 100644 --- a/website/docs/introduction/getting_started/hello_world/main.g.dart +++ b/website/docs/introduction/getting_started/hello_world/main.g.dart @@ -8,21 +8,63 @@ part of 'main.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(helloWorld) +const helloWorldProvider = HelloWorldProvider._(); + +final class HelloWorldProvider extends $FunctionalProvider + with $Provider { + const HelloWorldProvider._( + {String Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'helloWorldProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final String Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$helloWorldHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(String value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + HelloWorldProvider $copyWithCreate( + String Function( + Ref ref, + ) create, + ) { + return HelloWorldProvider._(create: create); + } + + @override + String create(Ref ref) { + final _$cb = _createCb ?? helloWorld; + return _$cb(ref); + } +} + String _$helloWorldHash() => r'9abaa5ab530c55186861f2debdaa218aceacb7eb'; -/// See also [helloWorld]. -@ProviderFor(helloWorld) -final helloWorldProvider = AutoDisposeProvider.internal( - helloWorld, - name: r'helloWorldProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$helloWorldHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef HelloWorldRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/introduction/why_riverpod/codegen.dart b/website/docs/introduction/why_riverpod/codegen.dart index 5205e623c..bd784d449 100644 --- a/website/docs/introduction/why_riverpod/codegen.dart +++ b/website/docs/introduction/why_riverpod/codegen.dart @@ -1,7 +1,6 @@ // ignore_for_file: use_key_in_widget_constructors, omit_local_variable_types import 'package:dio/dio.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/introduction/why_riverpod/codegen.g.dart b/website/docs/introduction/why_riverpod/codegen.g.dart index 7df9e597b..c95e4a048 100644 --- a/website/docs/introduction/why_riverpod/codegen.g.dart +++ b/website/docs/introduction/why_riverpod/codegen.g.dart @@ -8,173 +8,153 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** -String _$fetchPackagesHash() => r'4b2c6ea2cd702ab0f9846ba19c945d2c43161605'; - -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); - - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } - - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); - } -} - -/// See also [fetchPackages]. @ProviderFor(fetchPackages) -const fetchPackagesProvider = FetchPackagesFamily(); - -/// See also [fetchPackages]. -class FetchPackagesFamily extends Family>> { - /// See also [fetchPackages]. - const FetchPackagesFamily(); +const fetchPackagesProvider = FetchPackagesFamily._(); + +final class FetchPackagesProvider extends $FunctionalProvider< + AsyncValue>, FutureOr>> + with $FutureModifier>, $FutureProvider> { + const FetchPackagesProvider._( + {required FetchPackagesFamily super.from, + required ({ + int page, + String search, + }) + super.argument, + FutureOr> Function( + Ref ref, { + required int page, + String search, + })? create}) + : _createCb = create, + super( + retry: null, + name: r'fetchPackagesProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - /// See also [fetchPackages]. - FetchPackagesProvider call({ + final FutureOr> Function( + Ref ref, { required int page, - String search = '', - }) { - return FetchPackagesProvider( - page: page, - search: search, - ); - } + String search, + })? _createCb; @override - FetchPackagesProvider getProviderOverride( - covariant FetchPackagesProvider provider, - ) { - return call( - page: provider.page, - search: provider.search, - ); - } - - static const Iterable? _dependencies = null; + String debugGetCreateSourceHash() => _$fetchPackagesHash(); @override - Iterable? get dependencies => _dependencies; - - static const Iterable? _allTransitiveDependencies = null; - - @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + String toString() { + return r'fetchPackagesProvider' + '' + '$argument'; + } + @$internal @override - String? get name => r'fetchPackagesProvider'; -} - -/// See also [fetchPackages]. -class FetchPackagesProvider extends AutoDisposeFutureProvider> { - /// See also [fetchPackages]. - FetchPackagesProvider({ - required int page, - String search = '', - }) : this._internal( - (ref) => fetchPackages( - ref as FetchPackagesRef, - page: page, - search: search, - ), - from: fetchPackagesProvider, - name: r'fetchPackagesProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$fetchPackagesHash, - dependencies: FetchPackagesFamily._dependencies, - allTransitiveDependencies: - FetchPackagesFamily._allTransitiveDependencies, - page: page, - search: search, - ); - - FetchPackagesProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.page, - required this.search, - }) : super.internal(); - - final int page; - final String search; + $FutureProviderElement> $createElement( + $ProviderPointer pointer) => + $FutureProviderElement(this, pointer); @override - Override overrideWith( - FutureOr> Function(FetchPackagesRef provider) create, + FetchPackagesProvider $copyWithCreate( + FutureOr> Function( + Ref ref, + ) create, ) { - return ProviderOverride( - origin: this, - override: FetchPackagesProvider._internal( - (ref) => create(ref as FetchPackagesRef), - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - page: page, - search: search, - ), - ); + return FetchPackagesProvider._( + argument: argument as ({ + int page, + String search, + }), + from: from! as FetchPackagesFamily, + create: ( + ref, { + required int page, + String search = '', + }) => + create(ref)); } @override - AutoDisposeFutureProviderElement> createElement() { - return _FetchPackagesProviderElement(this); + FutureOr> create(Ref ref) { + final _$cb = _createCb ?? fetchPackages; + final argument = this.argument as ({ + int page, + String search, + }); + return _$cb( + ref, + page: argument.page, + search: argument.search, + ); } @override bool operator ==(Object other) { - return other is FetchPackagesProvider && - other.page == page && - other.search == search; + return other is FetchPackagesProvider && other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, page.hashCode); - hash = _SystemHash.combine(hash, search.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin FetchPackagesRef on AutoDisposeFutureProviderRef> { - /// The parameter `page` of this provider. - int get page; +String _$fetchPackagesHash() => r'4b2c6ea2cd702ab0f9846ba19c945d2c43161605'; - /// The parameter `search` of this provider. - String get search; -} +final class FetchPackagesFamily extends Family { + const FetchPackagesFamily._() + : super( + retry: null, + name: r'fetchPackagesProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); -class _FetchPackagesProviderElement - extends AutoDisposeFutureProviderElement> - with FetchPackagesRef { - _FetchPackagesProviderElement(super.provider); + FetchPackagesProvider call({ + required int page, + String search = '', + }) => + FetchPackagesProvider._(argument: ( + page: page, + search: search, + ), from: this); @override - int get page => (origin as FetchPackagesProvider).page; + String debugGetCreateSourceHash() => _$fetchPackagesHash(); + @override - String get search => (origin as FetchPackagesProvider).search; + String toString() => r'fetchPackagesProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + FutureOr> Function( + Ref ref, + ({ + int page, + String search, + }) args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as FetchPackagesProvider; + + final argument = provider.argument as ({ + int page, + String search, + }); + + return provider + .$copyWithCreate((ref) => create(ref, argument)) + .$createElement(pointer); + }, + ); + } } // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/migration/from_change_notifier.mdx b/website/docs/migration/from_change_notifier.mdx index 36edb8c22..453c5deb4 100644 --- a/website/docs/migration/from_change_notifier.mdx +++ b/website/docs/migration/from_change_notifier.mdx @@ -71,7 +71,7 @@ There's no need to think about the right class names and their *specific* APIs. `@riverpod` only asks you to write a class with its return type, and you're good to go. ::: -Technically, the best fit here is to define a `AutoDisposeAsyncNotifier>`, +Technically, the best fit here is to define a `AsyncNotifier>`, which meets all the above requirements. Let's write some pseudocode first. diff --git a/website/docs/migration/from_change_notifier/declaration/declaration.g.dart b/website/docs/migration/from_change_notifier/declaration/declaration.g.dart index 748943534..1632a0375 100644 --- a/website/docs/migration/from_change_notifier/declaration/declaration.g.dart +++ b/website/docs/migration/from_change_notifier/declaration/declaration.g.dart @@ -8,20 +8,67 @@ part of 'declaration.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(MyNotifier) +const myNotifierProvider = MyNotifierProvider._(); + +final class MyNotifierProvider + extends $AsyncNotifierProvider> { + const MyNotifierProvider._( + {super.runNotifierBuildOverride, MyNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$myNotifierHash(); + + @$internal + @override + MyNotifier create() => _createCb?.call() ?? MyNotifier(); + + @$internal + @override + MyNotifierProvider $copyWithCreate( + MyNotifier Function() create, + ) { + return MyNotifierProvider._(create: create); + } + + @$internal + @override + MyNotifierProvider $copyWithBuild( + FutureOr> Function( + Ref, + MyNotifier, + ) build, + ) { + return MyNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $AsyncNotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); +} + String _$myNotifierHash() => r'fc9a07f8ef9f792da2ac660d76ea0a809335ba18'; -/// See also [MyNotifier]. -@ProviderFor(MyNotifier) -final myNotifierProvider = - AutoDisposeAsyncNotifierProvider>.internal( - MyNotifier.new, - name: r'myNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$myNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$MyNotifier = AutoDisposeAsyncNotifier>; +abstract class _$MyNotifier extends $AsyncNotifier> { + FutureOr> build(); + @$internal + @override + FutureOr> runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/migration/from_change_notifier/declaration/raw.dart b/website/docs/migration/from_change_notifier/declaration/raw.dart index c7485bbba..0c723f63c 100644 --- a/website/docs/migration/from_change_notifier/declaration/raw.dart +++ b/website/docs/migration/from_change_notifier/declaration/raw.dart @@ -1,5 +1,6 @@ // ignore_for_file: avoid_print, avoid_unused_constructor_parameters +import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; class Todo { @@ -17,8 +18,7 @@ class Http { final http = Http(); /* SNIPPET START */ -@riverpod -class MyNotifier extends AutoDisposeAsyncNotifier> { +class MyNotifier extends AsyncNotifier> { @override FutureOr> build() { // TODO ... @@ -30,4 +30,5 @@ class MyNotifier extends AutoDisposeAsyncNotifier> { } } -final myNotifierProvider = AsyncNotifierProvider.autoDispose(MyNotifier.new); +final myNotifierProvider = + AsyncNotifierProvider.autoDispose(MyNotifier.new); diff --git a/website/docs/migration/from_change_notifier/initialization/initialization.g.dart b/website/docs/migration/from_change_notifier/initialization/initialization.g.dart index 5a3b20384..52f915fbb 100644 --- a/website/docs/migration/from_change_notifier/initialization/initialization.g.dart +++ b/website/docs/migration/from_change_notifier/initialization/initialization.g.dart @@ -8,20 +8,67 @@ part of 'initialization.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(MyNotifier) +const myNotifierProvider = MyNotifierProvider._(); + +final class MyNotifierProvider + extends $AsyncNotifierProvider> { + const MyNotifierProvider._( + {super.runNotifierBuildOverride, MyNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$myNotifierHash(); + + @$internal + @override + MyNotifier create() => _createCb?.call() ?? MyNotifier(); + + @$internal + @override + MyNotifierProvider $copyWithCreate( + MyNotifier Function() create, + ) { + return MyNotifierProvider._(create: create); + } + + @$internal + @override + MyNotifierProvider $copyWithBuild( + FutureOr> Function( + Ref, + MyNotifier, + ) build, + ) { + return MyNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $AsyncNotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); +} + String _$myNotifierHash() => r'1c67c12443102cf8c43efbf6c630d3028d9847c3'; -/// See also [MyNotifier]. -@ProviderFor(MyNotifier) -final myNotifierProvider = - AutoDisposeAsyncNotifierProvider>.internal( - MyNotifier.new, - name: r'myNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$myNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$MyNotifier = AutoDisposeAsyncNotifier>; +abstract class _$MyNotifier extends $AsyncNotifier> { + FutureOr> build(); + @$internal + @override + FutureOr> runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/migration/from_change_notifier/initialization/raw.dart b/website/docs/migration/from_change_notifier/initialization/raw.dart index 24ab265f7..5ef9e85c1 100644 --- a/website/docs/migration/from_change_notifier/initialization/raw.dart +++ b/website/docs/migration/from_change_notifier/initialization/raw.dart @@ -1,5 +1,6 @@ // ignore_for_file: avoid_print, avoid_unused_constructor_parameters +import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; class Todo { @@ -17,8 +18,7 @@ class Http { final http = Http(); /* SNIPPET START */ -@riverpod -class MyNotifier extends AutoDisposeAsyncNotifier> { +class MyNotifier extends AsyncNotifier> { @override FutureOr> build() async { final json = await http.get('api/todos'); @@ -26,4 +26,5 @@ class MyNotifier extends AutoDisposeAsyncNotifier> { } } -final myNotifierProvider = AsyncNotifierProvider.autoDispose(MyNotifier.new); +final myNotifierProvider = + AsyncNotifierProvider.autoDispose(MyNotifier.new); diff --git a/website/docs/migration/from_change_notifier/migrated/migrated.g.dart b/website/docs/migration/from_change_notifier/migrated/migrated.g.dart index f7bd069c3..49889dd55 100644 --- a/website/docs/migration/from_change_notifier/migrated/migrated.g.dart +++ b/website/docs/migration/from_change_notifier/migrated/migrated.g.dart @@ -8,20 +8,67 @@ part of 'migrated.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(MyNotifier) +const myNotifierProvider = MyNotifierProvider._(); + +final class MyNotifierProvider + extends $AsyncNotifierProvider> { + const MyNotifierProvider._( + {super.runNotifierBuildOverride, MyNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$myNotifierHash(); + + @$internal + @override + MyNotifier create() => _createCb?.call() ?? MyNotifier(); + + @$internal + @override + MyNotifierProvider $copyWithCreate( + MyNotifier Function() create, + ) { + return MyNotifierProvider._(create: create); + } + + @$internal + @override + MyNotifierProvider $copyWithBuild( + FutureOr> Function( + Ref, + MyNotifier, + ) build, + ) { + return MyNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $AsyncNotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); +} + String _$myNotifierHash() => r'bde95c56aa12eff7c8c01ede57ae4ad2b616c225'; -/// See also [MyNotifier]. -@ProviderFor(MyNotifier) -final myNotifierProvider = - AutoDisposeAsyncNotifierProvider>.internal( - MyNotifier.new, - name: r'myNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$myNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$MyNotifier = AutoDisposeAsyncNotifier>; +abstract class _$MyNotifier extends $AsyncNotifier> { + FutureOr> build(); + @$internal + @override + FutureOr> runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/migration/from_change_notifier/migrated/raw.dart b/website/docs/migration/from_change_notifier/migrated/raw.dart index 8572db71d..6927bd057 100644 --- a/website/docs/migration/from_change_notifier/migrated/raw.dart +++ b/website/docs/migration/from_change_notifier/migrated/raw.dart @@ -1,5 +1,6 @@ // ignore_for_file: avoid_print, avoid_unused_constructor_parameters +import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; class Todo { @@ -17,8 +18,7 @@ class Http { final http = Http(); /* SNIPPET START */ -@riverpod -class MyNotifier extends AutoDisposeAsyncNotifier> { +class MyNotifier extends AsyncNotifier> { @override FutureOr> build() async { final json = await http.get('api/todos'); @@ -34,4 +34,5 @@ class MyNotifier extends AutoDisposeAsyncNotifier> { } } -final myNotifierProvider = AsyncNotifierProvider.autoDispose(MyNotifier.new); +final myNotifierProvider = + AsyncNotifierProvider.autoDispose(MyNotifier.new); diff --git a/website/docs/migration/from_change_notifier/old.dart b/website/docs/migration/from_change_notifier/old.dart index 4e65e823d..932804174 100644 --- a/website/docs/migration/from_change_notifier/old.dart +++ b/website/docs/migration/from_change_notifier/old.dart @@ -1,7 +1,7 @@ // ignore_for_file: avoid_print, avoid_unused_constructor_parameters import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; class Todo { const Todo(this.id); diff --git a/website/docs/migration/from_state_notifier.mdx b/website/docs/migration/from_state_notifier.mdx index 6d6f1a141..e6bedd1cc 100644 --- a/website/docs/migration/from_state_notifier.mdx +++ b/website/docs/migration/from_state_notifier.mdx @@ -1,6 +1,6 @@ --- title: From `StateNotifier` -version: 1 +version: 2 --- import buildInit from "./from_state_notifier/build_init"; @@ -22,6 +22,12 @@ import obtainNotifierOnTests from "!!raw-loader!./from_state_notifier/obtain_not import { Link } from "/src/components/Link"; import { AutoSnippet } from "/src/components/CodeSnippet"; +:::caution +The content of this page may be outdated. +It will be updated in the future, but for now you may want to refer to the content +in the top of the sidebar instead (introduction/essentials/case-studies/...) +::: + Along with [Riverpod 2.0](https://pub.dev/packages/flutter_riverpod/changelog#200), new classes were introduced: `Notifier` / `AsyncNotifer`. `StateNotifier` is now discouraged in favor of those new APIs. @@ -77,8 +83,8 @@ Here's the above example, rewritten with the new `AsyncNotifier` APIs: Here, it's easy to see `AsyncNotifer` as a `FutureProvider` with methods. `AsyncNotifer` comes with a set of utilities and getters that `StateNotifier` doesn't have, such as e.g. -[`future`](https://pub.dev/documentation/riverpod/latest/riverpod/AutoDisposeAsyncNotifier/future.html) -and [`update`](https://pub.dev/documentation/riverpod/latest/riverpod/AutoDisposeAsyncNotifier/update.html). +[`future`](https://pub.dev/documentation/riverpod/latest/riverpod/AsyncNotifier/future.html) +and [`update`](https://pub.dev/documentation/riverpod/latest/riverpod/AsyncNotifier/update.html). This enables us to write much simpler logic when handling asynchronous mutations and side-effects. See also . :::tip @@ -105,9 +111,9 @@ Let's go further down and highlight more differences and similarities. Another important difference is how families and auto dispose is handled with the new APIs. `Notifier`, has its own `.family` and `.autoDispose` counterparts, such as `FamilyNotifier` -and `AutoDisposeNotifier`. +and `Notifier`. As always, such modifications can be combined (aka `AutoDisposeFamilyNotifier`). -`AsyncNotifer` has its asynchronous equivalent, too (e.g. `AutoDisposeFamilyAsyncNotifier`). +`AsyncNotifer` has its asynchronous equivalent, too (e.g. `FamilyAsyncNotifier`). Modifications are explicitly stated inside the class; any parameters are directly injected in the `build` method, so that they're available to the initialization logic. diff --git a/website/docs/migration/from_state_notifier/add_listener/add_listener.g.dart b/website/docs/migration/from_state_notifier/add_listener/add_listener.g.dart index d1bde1263..dff85bbe7 100644 --- a/website/docs/migration/from_state_notifier/add_listener/add_listener.g.dart +++ b/website/docs/migration/from_state_notifier/add_listener/add_listener.g.dart @@ -8,20 +8,74 @@ part of 'add_listener.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(MyNotifier) +const myNotifierProvider = MyNotifierProvider._(); + +final class MyNotifierProvider extends $NotifierProvider { + const MyNotifierProvider._( + {super.runNotifierBuildOverride, MyNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$myNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + MyNotifier create() => _createCb?.call() ?? MyNotifier(); + + @$internal + @override + MyNotifierProvider $copyWithCreate( + MyNotifier Function() create, + ) { + return MyNotifierProvider._(create: create); + } + + @$internal + @override + MyNotifierProvider $copyWithBuild( + int Function( + Ref, + MyNotifier, + ) build, + ) { + return MyNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$myNotifierHash() => r'7ff4b7b8f822ca720b55e2d29ed07d7f0b3485e8'; -/// See also [MyNotifier]. -@ProviderFor(MyNotifier) -final myNotifierProvider = - AutoDisposeNotifierProvider.internal( - MyNotifier.new, - name: r'myNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$myNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$MyNotifier = AutoDisposeNotifier; +abstract class _$MyNotifier extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/migration/from_state_notifier/add_listener/raw.dart b/website/docs/migration/from_state_notifier/add_listener/raw.dart index 5dae29c44..c2c30b288 100644 --- a/website/docs/migration/from_state_notifier/add_listener/raw.dart +++ b/website/docs/migration/from_state_notifier/add_listener/raw.dart @@ -1,7 +1,7 @@ // ignore_for_file: avoid_print import 'package:flutter/material.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; /* SNIPPET START */ class MyNotifier extends Notifier { diff --git a/website/docs/migration/from_state_notifier/add_listener_old.dart b/website/docs/migration/from_state_notifier/add_listener_old.dart index 98fd0dde2..29d4320f5 100644 --- a/website/docs/migration/from_state_notifier/add_listener_old.dart +++ b/website/docs/migration/from_state_notifier/add_listener_old.dart @@ -1,7 +1,7 @@ // ignore_for_file: avoid_print import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ class MyNotifier extends StateNotifier { diff --git a/website/docs/migration/from_state_notifier/async_notifier/async_notifier.g.dart b/website/docs/migration/from_state_notifier/async_notifier/async_notifier.g.dart index 144cfb22b..7c9f86c59 100644 --- a/website/docs/migration/from_state_notifier/async_notifier/async_notifier.g.dart +++ b/website/docs/migration/from_state_notifier/async_notifier/async_notifier.g.dart @@ -8,22 +8,68 @@ part of 'async_notifier.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(AsyncTodosNotifier) +const asyncTodosNotifierProvider = AsyncTodosNotifierProvider._(); + +final class AsyncTodosNotifierProvider + extends $AsyncNotifierProvider> { + const AsyncTodosNotifierProvider._( + {super.runNotifierBuildOverride, AsyncTodosNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'asyncTodosNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final AsyncTodosNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$asyncTodosNotifierHash(); + + @$internal + @override + AsyncTodosNotifier create() => _createCb?.call() ?? AsyncTodosNotifier(); + + @$internal + @override + AsyncTodosNotifierProvider $copyWithCreate( + AsyncTodosNotifier Function() create, + ) { + return AsyncTodosNotifierProvider._(create: create); + } + + @$internal + @override + AsyncTodosNotifierProvider $copyWithBuild( + FutureOr> Function( + Ref, + AsyncTodosNotifier, + ) build, + ) { + return AsyncTodosNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $AsyncNotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); +} + String _$asyncTodosNotifierHash() => r'10207327c7dee180e9da8beece5bfffedcf86e98'; -/// See also [AsyncTodosNotifier]. -@ProviderFor(AsyncTodosNotifier) -final asyncTodosNotifierProvider = - AutoDisposeAsyncNotifierProvider>.internal( - AsyncTodosNotifier.new, - name: r'asyncTodosNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$asyncTodosNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$AsyncTodosNotifier = AutoDisposeAsyncNotifier>; +abstract class _$AsyncTodosNotifier extends $AsyncNotifier> { + FutureOr> build(); + @$internal + @override + FutureOr> runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/migration/from_state_notifier/async_notifier/raw.dart b/website/docs/migration/from_state_notifier/async_notifier/raw.dart index 52da61d76..ac91d2f88 100644 --- a/website/docs/migration/from_state_notifier/async_notifier/raw.dart +++ b/website/docs/migration/from_state_notifier/async_notifier/raw.dart @@ -1,5 +1,6 @@ // ignore_for_file: avoid_print, avoid_unused_constructor_parameters +import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; class Todo { @@ -24,6 +25,7 @@ class AsyncTodosNotifier extends AsyncNotifier> { // ... } -final asyncTodosNotifier = AsyncNotifierProvider>( +final asyncTodosNotifier = + AsyncNotifierProvider>( AsyncTodosNotifier.new, ); diff --git a/website/docs/migration/from_state_notifier/async_notifier_old.dart b/website/docs/migration/from_state_notifier/async_notifier_old.dart index 50d7f4aed..01e4ec6e9 100644 --- a/website/docs/migration/from_state_notifier/async_notifier_old.dart +++ b/website/docs/migration/from_state_notifier/async_notifier_old.dart @@ -1,5 +1,6 @@ // ignore_for_file: avoid_print, avoid_unused_constructor_parameters +import 'package:flutter_riverpod/legacy.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class Todo { diff --git a/website/docs/migration/from_state_notifier/build_init/build_init.g.dart b/website/docs/migration/from_state_notifier/build_init/build_init.g.dart index 6615d2027..d91084e2d 100644 --- a/website/docs/migration/from_state_notifier/build_init/build_init.g.dart +++ b/website/docs/migration/from_state_notifier/build_init/build_init.g.dart @@ -8,21 +8,75 @@ part of 'build_init.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(CounterNotifier) +const counterNotifierProvider = CounterNotifierProvider._(); + +final class CounterNotifierProvider + extends $NotifierProvider { + const CounterNotifierProvider._( + {super.runNotifierBuildOverride, CounterNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counterNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final CounterNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$counterNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + CounterNotifier create() => _createCb?.call() ?? CounterNotifier(); + + @$internal + @override + CounterNotifierProvider $copyWithCreate( + CounterNotifier Function() create, + ) { + return CounterNotifierProvider._(create: create); + } + + @$internal + @override + CounterNotifierProvider $copyWithBuild( + int Function( + Ref, + CounterNotifier, + ) build, + ) { + return CounterNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$counterNotifierHash() => r'4a971b45b66819f429f205806499527353d1a78e'; -/// See also [CounterNotifier]. -@ProviderFor(CounterNotifier) -final counterNotifierProvider = - AutoDisposeNotifierProvider.internal( - CounterNotifier.new, - name: r'counterNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$counterNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$CounterNotifier = AutoDisposeNotifier; +abstract class _$CounterNotifier extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/migration/from_state_notifier/build_init/raw.dart b/website/docs/migration/from_state_notifier/build_init/raw.dart index 0ba8eebed..9456a84da 100644 --- a/website/docs/migration/from_state_notifier/build_init/raw.dart +++ b/website/docs/migration/from_state_notifier/build_init/raw.dart @@ -1,7 +1,6 @@ // ignore_for_file: avoid_print import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; /* SNIPPET START */ class CounterNotifier extends Notifier { diff --git a/website/docs/migration/from_state_notifier/build_init_old.dart b/website/docs/migration/from_state_notifier/build_init_old.dart index 846355389..26b71af34 100644 --- a/website/docs/migration/from_state_notifier/build_init_old.dart +++ b/website/docs/migration/from_state_notifier/build_init_old.dart @@ -1,4 +1,4 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ class CounterNotifier extends StateNotifier { @@ -8,6 +8,7 @@ class CounterNotifier extends StateNotifier { void decrement() => state--; } -final counterNotifierProvider = StateNotifierProvider((ref) { +final counterNotifierProvider = + StateNotifierProvider((ref) { return CounterNotifier(); }); diff --git a/website/docs/migration/from_state_notifier/consumers_dont_change.dart b/website/docs/migration/from_state_notifier/consumers_dont_change.dart index 6a964a5a6..51829e903 100644 --- a/website/docs/migration/from_state_notifier/consumers_dont_change.dart +++ b/website/docs/migration/from_state_notifier/consumers_dont_change.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class CounterNotifier extends StateNotifier { @@ -8,7 +9,8 @@ class CounterNotifier extends StateNotifier { void decrement() => state++; } -final counterNotifierProvider = StateNotifierProvider((ref) { +final counterNotifierProvider = + StateNotifierProvider((ref) { return CounterNotifier(); }); @@ -26,10 +28,11 @@ class SomeConsumer extends ConsumerWidget { Text("You've counted up until $counter, good job!"), TextButton( /* highlight-start */ - onPressed: ref.read(counterNotifierProvider.notifier).increment, + onPressed: () => + ref.read(counterNotifierProvider.notifier).increment(), /* highlight-end */ child: const Text('Count even more!'), - ) + ), ], ); } diff --git a/website/docs/migration/from_state_notifier/family_and_dispose/family_and_dispose.dart b/website/docs/migration/from_state_notifier/family_and_dispose/family_and_dispose.dart index 9a695614c..0bc88af1f 100644 --- a/website/docs/migration/from_state_notifier/family_and_dispose/family_and_dispose.dart +++ b/website/docs/migration/from_state_notifier/family_and_dispose/family_and_dispose.dart @@ -8,6 +8,9 @@ import '../../utils.dart'; part 'family_and_dispose.g.dart'; +@riverpod +TaskTrackerRepo taskTracker(Ref ref) => TaskTrackerRepo(); + /* SNIPPET START */ @riverpod class BugsEncounteredNotifier extends _$BugsEncounteredNotifier { @@ -18,7 +21,9 @@ class BugsEncounteredNotifier extends _$BugsEncounteredNotifier { Future fix(int amount) async { final old = await future; - final result = await ref.read(taskTrackerProvider).fix(id: this.featureId, fixed: amount); + final result = await ref + .read(taskTrackerProvider) + .fix(id: this.featureId, fixed: amount); state = AsyncData(max(old - result, 0)); } } diff --git a/website/docs/migration/from_state_notifier/family_and_dispose/family_and_dispose.g.dart b/website/docs/migration/from_state_notifier/family_and_dispose/family_and_dispose.g.dart index 88d54326b..e6db0d173 100644 --- a/website/docs/migration/from_state_notifier/family_and_dispose/family_and_dispose.g.dart +++ b/website/docs/migration/from_state_notifier/family_and_dispose/family_and_dispose.g.dart @@ -8,173 +8,222 @@ part of 'family_and_dispose.dart'; // RiverpodGenerator // ************************************************************************** -String _$bugsEncounteredNotifierHash() => - r'c76e924f84db91c57d226896b062d9f4e8ab79e5'; +@ProviderFor(taskTracker) +const taskTrackerProvider = TaskTrackerProvider._(); + +final class TaskTrackerProvider + extends $FunctionalProvider + with $Provider { + const TaskTrackerProvider._( + {TaskTrackerRepo Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'taskTrackerProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); -/// Copied from Dart SDK -class _SystemHash { - _SystemHash._(); + final TaskTrackerRepo Function( + Ref ref, + )? _createCb; - static int combine(int hash, int value) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + value); - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); - return hash ^ (hash >> 6); - } + @override + String debugGetCreateSourceHash() => _$taskTrackerHash(); - static int finish(int hash) { - // ignore: parameter_assignments - hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); - // ignore: parameter_assignments - hash = hash ^ (hash >> 11); - return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + /// {@macro riverpod.override_with_value} + Override overrideWithValue(TaskTrackerRepo value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); } -} - -abstract class _$BugsEncounteredNotifier - extends BuildlessAutoDisposeAsyncNotifier { - late final String featureId; - FutureOr build( - String featureId, - ); -} - -/// See also [BugsEncounteredNotifier]. -@ProviderFor(BugsEncounteredNotifier) -const bugsEncounteredNotifierProvider = BugsEncounteredNotifierFamily(); - -/// See also [BugsEncounteredNotifier]. -class BugsEncounteredNotifierFamily extends Family> { - /// See also [BugsEncounteredNotifier]. - const BugsEncounteredNotifierFamily(); + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); - /// See also [BugsEncounteredNotifier]. - BugsEncounteredNotifierProvider call( - String featureId, + @override + TaskTrackerProvider $copyWithCreate( + TaskTrackerRepo Function( + Ref ref, + ) create, ) { - return BugsEncounteredNotifierProvider( - featureId, - ); + return TaskTrackerProvider._(create: create); } @override - BugsEncounteredNotifierProvider getProviderOverride( - covariant BugsEncounteredNotifierProvider provider, - ) { - return call( - provider.featureId, - ); + TaskTrackerRepo create(Ref ref) { + final _$cb = _createCb ?? taskTracker; + return _$cb(ref); } +} - static const Iterable? _dependencies = null; +String _$taskTrackerHash() => r'004d4554b37d841c6f668e298067dd39611a453a'; - @override - Iterable? get dependencies => _dependencies; +@ProviderFor(BugsEncounteredNotifier) +const bugsEncounteredNotifierProvider = BugsEncounteredNotifierFamily._(); + +final class BugsEncounteredNotifierProvider + extends $AsyncNotifierProvider { + const BugsEncounteredNotifierProvider._( + {required BugsEncounteredNotifierFamily super.from, + required String super.argument, + super.runNotifierBuildOverride, + BugsEncounteredNotifier Function()? create}) + : _createCb = create, + super( + retry: null, + name: r'bugsEncounteredNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); - static const Iterable? _allTransitiveDependencies = null; + final BugsEncounteredNotifier Function()? _createCb; @override - Iterable? get allTransitiveDependencies => - _allTransitiveDependencies; + String debugGetCreateSourceHash() => _$bugsEncounteredNotifierHash(); @override - String? get name => r'bugsEncounteredNotifierProvider'; -} - -/// See also [BugsEncounteredNotifier]. -class BugsEncounteredNotifierProvider - extends AutoDisposeAsyncNotifierProviderImpl { - /// See also [BugsEncounteredNotifier]. - BugsEncounteredNotifierProvider( - String featureId, - ) : this._internal( - () => BugsEncounteredNotifier()..featureId = featureId, - from: bugsEncounteredNotifierProvider, - name: r'bugsEncounteredNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$bugsEncounteredNotifierHash, - dependencies: BugsEncounteredNotifierFamily._dependencies, - allTransitiveDependencies: - BugsEncounteredNotifierFamily._allTransitiveDependencies, - featureId: featureId, - ); - - BugsEncounteredNotifierProvider._internal( - super._createNotifier, { - required super.name, - required super.dependencies, - required super.allTransitiveDependencies, - required super.debugGetCreateSourceHash, - required super.from, - required this.featureId, - }) : super.internal(); + String toString() { + return r'bugsEncounteredNotifierProvider' + '' + '($argument)'; + } - final String featureId; + @$internal + @override + BugsEncounteredNotifier create() => + _createCb?.call() ?? BugsEncounteredNotifier(); + @$internal @override - FutureOr runNotifierBuild( - covariant BugsEncounteredNotifier notifier, + BugsEncounteredNotifierProvider $copyWithCreate( + BugsEncounteredNotifier Function() create, ) { - return notifier.build( - featureId, - ); + return BugsEncounteredNotifierProvider._( + argument: argument as String, + from: from! as BugsEncounteredNotifierFamily, + create: create); } + @$internal @override - Override overrideWith(BugsEncounteredNotifier Function() create) { - return ProviderOverride( - origin: this, - override: BugsEncounteredNotifierProvider._internal( - () => create()..featureId = featureId, - from: from, - name: null, - dependencies: null, - allTransitiveDependencies: null, - debugGetCreateSourceHash: null, - featureId: featureId, - ), - ); + BugsEncounteredNotifierProvider $copyWithBuild( + FutureOr Function( + Ref, + BugsEncounteredNotifier, + ) build, + ) { + return BugsEncounteredNotifierProvider._( + argument: argument as String, + from: from! as BugsEncounteredNotifierFamily, + runNotifierBuildOverride: build); } + @$internal @override - AutoDisposeAsyncNotifierProviderElement - createElement() { - return _BugsEncounteredNotifierProviderElement(this); - } + $AsyncNotifierProviderElement $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); @override bool operator ==(Object other) { return other is BugsEncounteredNotifierProvider && - other.featureId == featureId; + other.argument == argument; } @override int get hashCode { - var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, featureId.hashCode); - - return _SystemHash.finish(hash); + return argument.hashCode; } } -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -mixin BugsEncounteredNotifierRef on AutoDisposeAsyncNotifierProviderRef { - /// The parameter `featureId` of this provider. - String get featureId; +String _$bugsEncounteredNotifierHash() => + r'c76e924f84db91c57d226896b062d9f4e8ab79e5'; + +final class BugsEncounteredNotifierFamily extends Family { + const BugsEncounteredNotifierFamily._() + : super( + retry: null, + name: r'bugsEncounteredNotifierProvider', + dependencies: null, + allTransitiveDependencies: null, + isAutoDispose: true, + ); + + BugsEncounteredNotifierProvider call( + String featureId, + ) => + BugsEncounteredNotifierProvider._(argument: featureId, from: this); + + @override + String debugGetCreateSourceHash() => _$bugsEncounteredNotifierHash(); + + @override + String toString() => r'bugsEncounteredNotifierProvider'; + + /// {@macro riverpod.override_with} + Override overrideWith( + BugsEncounteredNotifier Function( + String args, + ) create, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as BugsEncounteredNotifierProvider; + + final argument = provider.argument as String; + + return provider + .$copyWithCreate(() => create(argument)) + .$createElement(pointer); + }, + ); + } + + /// {@macro riverpod.override_with_build} + Override overrideWithBuild( + FutureOr Function( + Ref ref, BugsEncounteredNotifier notifier, String argument) + build, + ) { + return $FamilyOverride( + from: this, + createElement: (pointer) { + final provider = pointer.origin as BugsEncounteredNotifierProvider; + + final argument = provider.argument as String; + + return provider + .$copyWithBuild((ref, notifier) => build(ref, notifier, argument)) + .$createElement(pointer); + }, + ); + } } -class _BugsEncounteredNotifierProviderElement - extends AutoDisposeAsyncNotifierProviderElement with BugsEncounteredNotifierRef { - _BugsEncounteredNotifierProviderElement(super.provider); +abstract class _$BugsEncounteredNotifier extends $AsyncNotifier { + late final _$args = ref.$arg as String; + String get featureId => _$args; + FutureOr build( + String featureId, + ); + @$internal @override - String get featureId => (origin as BugsEncounteredNotifierProvider).featureId; + FutureOr runBuild() => build( + _$args, + ); } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/migration/from_state_notifier/family_and_dispose/raw.dart b/website/docs/migration/from_state_notifier/family_and_dispose/raw.dart index 8dfce9447..b7068013a 100644 --- a/website/docs/migration/from_state_notifier/family_and_dispose/raw.dart +++ b/website/docs/migration/from_state_notifier/family_and_dispose/raw.dart @@ -8,7 +8,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../utils.dart'; /* SNIPPET START */ -class BugsEncounteredNotifier extends AutoDisposeFamilyAsyncNotifier { +class BugsEncounteredNotifier extends FamilyAsyncNotifier { @override FutureOr build(String featureId) { return 99; @@ -16,12 +16,13 @@ class BugsEncounteredNotifier extends AutoDisposeFamilyAsyncNotifier fix(int amount) async { final old = await future; - final result = await ref.read(taskTrackerProvider).fix(id: this.arg, fixed: amount); + final result = + await ref.read(taskTrackerProvider).fix(id: this.arg, fixed: amount); state = AsyncData(max(old - result, 0)); } } -final bugsEncounteredNotifierProvider = - AsyncNotifierProvider.family.autoDispose( +final bugsEncounteredNotifierProvider = AsyncNotifierProvider.family + .autoDispose( BugsEncounteredNotifier.new, ); diff --git a/website/docs/migration/from_state_notifier/family_and_dispose_old.dart b/website/docs/migration/from_state_notifier/family_and_dispose_old.dart index 28f93a65c..113594ef1 100644 --- a/website/docs/migration/from_state_notifier/family_and_dispose_old.dart +++ b/website/docs/migration/from_state_notifier/family_and_dispose_old.dart @@ -2,6 +2,7 @@ import 'dart:math'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../utils.dart'; @@ -18,13 +19,14 @@ class BugsEncounteredNotifier extends StateNotifier> { Future fix(int amount) async { state = await AsyncValue.guard(() async { final old = state.requireValue; - final result = await ref.read(taskTrackerProvider).fix(id: featureId, fixed: amount); + final result = + await ref.read(taskTrackerProvider).fix(id: featureId, fixed: amount); return max(old - result, 0); }); } } -final bugsEncounteredNotifierProvider = - StateNotifierProvider.family.autoDispose((ref, id) { +final bugsEncounteredNotifierProvider = StateNotifierProvider.family + .autoDispose, String>((ref, id) { return BugsEncounteredNotifier(ref: ref, featureId: id); }); diff --git a/website/docs/migration/from_state_notifier/from_state_provider/from_state_provider.g.dart b/website/docs/migration/from_state_notifier/from_state_provider/from_state_provider.g.dart index 51c7cef14..0f1fdaa0f 100644 --- a/website/docs/migration/from_state_notifier/from_state_provider/from_state_provider.g.dart +++ b/website/docs/migration/from_state_notifier/from_state_provider/from_state_provider.g.dart @@ -8,21 +8,75 @@ part of 'from_state_provider.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(CounterNotifier) +const counterNotifierProvider = CounterNotifierProvider._(); + +final class CounterNotifierProvider + extends $NotifierProvider { + const CounterNotifierProvider._( + {super.runNotifierBuildOverride, CounterNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'counterNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final CounterNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$counterNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + CounterNotifier create() => _createCb?.call() ?? CounterNotifier(); + + @$internal + @override + CounterNotifierProvider $copyWithCreate( + CounterNotifier Function() create, + ) { + return CounterNotifierProvider._(create: create); + } + + @$internal + @override + CounterNotifierProvider $copyWithBuild( + int Function( + Ref, + CounterNotifier, + ) build, + ) { + return CounterNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$counterNotifierHash() => r'b32033040f0fff627f1a6dfd9cfb4e93a842390b'; -/// See also [CounterNotifier]. -@ProviderFor(CounterNotifier) -final counterNotifierProvider = - AutoDisposeNotifierProvider.internal( - CounterNotifier.new, - name: r'counterNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$counterNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$CounterNotifier = AutoDisposeNotifier; +abstract class _$CounterNotifier extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/migration/from_state_notifier/from_state_provider/raw.dart b/website/docs/migration/from_state_notifier/from_state_provider/raw.dart index 97e4564f3..ee7eb761e 100644 --- a/website/docs/migration/from_state_notifier/from_state_provider/raw.dart +++ b/website/docs/migration/from_state_notifier/from_state_provider/raw.dart @@ -1,4 +1,4 @@ -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; /* SNIPPET START */ class CounterNotifier extends Notifier { @@ -10,4 +10,5 @@ class CounterNotifier extends Notifier { int update(int Function(int state) cb) => state = cb(state); } -final counterNotifierProvider = NotifierProvider(CounterNotifier.new); +final counterNotifierProvider = + NotifierProvider(CounterNotifier.new); diff --git a/website/docs/migration/from_state_notifier/from_state_provider_old.dart b/website/docs/migration/from_state_notifier/from_state_provider_old.dart index 246f44a0c..4b4ec6aa4 100644 --- a/website/docs/migration/from_state_notifier/from_state_provider_old.dart +++ b/website/docs/migration/from_state_notifier/from_state_provider_old.dart @@ -1,4 +1,4 @@ -import 'package:riverpod/riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ final counterProvider = StateProvider((ref) { diff --git a/website/docs/migration/from_state_notifier/obtain_notifier_on_tests.dart b/website/docs/migration/from_state_notifier/obtain_notifier_on_tests.dart index b4395dc1a..dae163a73 100644 --- a/website/docs/migration/from_state_notifier/obtain_notifier_on_tests.dart +++ b/website/docs/migration/from_state_notifier/obtain_notifier_on_tests.dart @@ -3,14 +3,15 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -class MyNotifier extends AutoDisposeNotifier { +class MyNotifier extends Notifier { @override int build() { return 0; } } -final myNotifierProvider = NotifierProvider.autoDispose(MyNotifier.new); +final myNotifierProvider = + NotifierProvider.autoDispose(MyNotifier.new); /* SNIPPET START */ void main(List args) { @@ -22,7 +23,7 @@ void main(List args) { // Obtaining a notifier // {@endtemplate} /* highlight-start */ - final AutoDisposeNotifier notifier = container.read(myNotifierProvider.notifier); + final Notifier notifier = container.read(myNotifierProvider.notifier); /* highlight-end */ // {@template state} diff --git a/website/docs/migration/from_state_notifier/old_lifecycles/old_lifecycles.dart b/website/docs/migration/from_state_notifier/old_lifecycles/old_lifecycles.dart index 30bd20cc3..4e8cf8d62 100644 --- a/website/docs/migration/from_state_notifier/old_lifecycles/old_lifecycles.dart +++ b/website/docs/migration/from_state_notifier/old_lifecycles/old_lifecycles.dart @@ -1,20 +1,11 @@ import 'dart:async'; -import 'package:dio/dio.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; -import '../../utils.dart'; +import '../old_lifecycles_final/old_lifecycles_final.dart'; part 'old_lifecycles.g.dart'; -final repositoryProvider = Provider<_MyRepo>((ref) { - return _MyRepo(); -}); - -class _MyRepo { - Future update(int i, {CancelToken? token}) async {} -} - /* SNIPPET START */ @riverpod class MyNotifier extends _$MyNotifier { diff --git a/website/docs/migration/from_state_notifier/old_lifecycles/old_lifecycles.g.dart b/website/docs/migration/from_state_notifier/old_lifecycles/old_lifecycles.g.dart index 4bcfde6df..3ca538354 100644 --- a/website/docs/migration/from_state_notifier/old_lifecycles/old_lifecycles.g.dart +++ b/website/docs/migration/from_state_notifier/old_lifecycles/old_lifecycles.g.dart @@ -8,20 +8,74 @@ part of 'old_lifecycles.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(MyNotifier) +const myNotifierProvider = MyNotifierProvider._(); + +final class MyNotifierProvider extends $NotifierProvider { + const MyNotifierProvider._( + {super.runNotifierBuildOverride, MyNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$myNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + MyNotifier create() => _createCb?.call() ?? MyNotifier(); + + @$internal + @override + MyNotifierProvider $copyWithCreate( + MyNotifier Function() create, + ) { + return MyNotifierProvider._(create: create); + } + + @$internal + @override + MyNotifierProvider $copyWithBuild( + int Function( + Ref, + MyNotifier, + ) build, + ) { + return MyNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$myNotifierHash() => r'0495c52ce893ee0304d4d5ac5648c634ed4a241e'; -/// See also [MyNotifier]. -@ProviderFor(MyNotifier) -final myNotifierProvider = - AutoDisposeNotifierProvider.internal( - MyNotifier.new, - name: r'myNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$myNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$MyNotifier = AutoDisposeNotifier; +abstract class _$MyNotifier extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/migration/from_state_notifier/old_lifecycles/raw.dart b/website/docs/migration/from_state_notifier/old_lifecycles/raw.dart index 046dc07ef..ace2dce58 100644 --- a/website/docs/migration/from_state_notifier/old_lifecycles/raw.dart +++ b/website/docs/migration/from_state_notifier/old_lifecycles/raw.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:dio/dio.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; import '../../utils.dart'; diff --git a/website/docs/migration/from_state_notifier/old_lifecycles_final/old_lifecycles_final.dart b/website/docs/migration/from_state_notifier/old_lifecycles_final/old_lifecycles_final.dart index de4b1bdd7..7c0233912 100644 --- a/website/docs/migration/from_state_notifier/old_lifecycles_final/old_lifecycles_final.dart +++ b/website/docs/migration/from_state_notifier/old_lifecycles_final/old_lifecycles_final.dart @@ -3,13 +3,14 @@ import 'dart:async'; import 'package:dio/dio.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; -import '../../utils.dart'; - part 'old_lifecycles_final.g.dart'; -final repositoryProvider = Provider<_MyRepo>((ref) { - return _MyRepo(); -}); +@riverpod +Duration duration(Ref ref) => const Duration(seconds: 1); + +@riverpod +// ignore: library_private_types_in_public_api +_MyRepo repository(Ref ref) => _MyRepo(); class _MyRepo { Future update(int i, {CancelToken? token}) async {} diff --git a/website/docs/migration/from_state_notifier/old_lifecycles_final/old_lifecycles_final.g.dart b/website/docs/migration/from_state_notifier/old_lifecycles_final/old_lifecycles_final.g.dart index b6d29ddd4..02f0ae7a2 100644 --- a/website/docs/migration/from_state_notifier/old_lifecycles_final/old_lifecycles_final.g.dart +++ b/website/docs/migration/from_state_notifier/old_lifecycles_final/old_lifecycles_final.g.dart @@ -8,20 +8,190 @@ part of 'old_lifecycles_final.dart'; // RiverpodGenerator // ************************************************************************** -String _$myNotifierHash() => r'8ea2586ea29d12306efd4b8b847142136dd20338'; +@ProviderFor(duration) +const durationProvider = DurationProvider._(); + +final class DurationProvider extends $FunctionalProvider + with $Provider { + const DurationProvider._( + {Duration Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'durationProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Duration Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$durationHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Duration value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + DurationProvider $copyWithCreate( + Duration Function( + Ref ref, + ) create, + ) { + return DurationProvider._(create: create); + } + + @override + Duration create(Ref ref) { + final _$cb = _createCb ?? duration; + return _$cb(ref); + } +} + +String _$durationHash() => r'997cacfb78da8107053428dfc5515497354b50c6'; + +@ProviderFor(repository) +const repositoryProvider = RepositoryProvider._(); + +final class RepositoryProvider extends $FunctionalProvider<_MyRepo, _MyRepo> + with $Provider<_MyRepo> { + const RepositoryProvider._( + {_MyRepo Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'repositoryProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final _MyRepo Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$repositoryHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(_MyRepo value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider<_MyRepo>(value), + ); + } + + @$internal + @override + $ProviderElement<_MyRepo> $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + RepositoryProvider $copyWithCreate( + _MyRepo Function( + Ref ref, + ) create, + ) { + return RepositoryProvider._(create: create); + } + + @override + _MyRepo create(Ref ref) { + final _$cb = _createCb ?? repository; + return _$cb(ref); + } +} + +String _$repositoryHash() => r'8c1b035ba722660550674e92444db7b6f25ac2a3'; -/// See also [MyNotifier]. @ProviderFor(MyNotifier) -final myNotifierProvider = - AutoDisposeNotifierProvider.internal( - MyNotifier.new, - name: r'myNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$myNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$MyNotifier = AutoDisposeNotifier; +const myNotifierProvider = MyNotifierProvider._(); + +final class MyNotifierProvider extends $NotifierProvider { + const MyNotifierProvider._( + {super.runNotifierBuildOverride, MyNotifier Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'myNotifierProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final MyNotifier Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$myNotifierHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + MyNotifier create() => _createCb?.call() ?? MyNotifier(); + + @$internal + @override + MyNotifierProvider $copyWithCreate( + MyNotifier Function() create, + ) { + return MyNotifierProvider._(create: create); + } + + @$internal + @override + MyNotifierProvider $copyWithBuild( + int Function( + Ref, + MyNotifier, + ) build, + ) { + return MyNotifierProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + +String _$myNotifierHash() => r'8ea2586ea29d12306efd4b8b847142136dd20338'; + +abstract class _$MyNotifier extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/migration/from_state_notifier/old_lifecycles_final/raw.dart b/website/docs/migration/from_state_notifier/old_lifecycles_final/raw.dart index 0655b4008..cf7294676 100644 --- a/website/docs/migration/from_state_notifier/old_lifecycles_final/raw.dart +++ b/website/docs/migration/from_state_notifier/old_lifecycles_final/raw.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:dio/dio.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod/riverpod.dart'; import '../../utils.dart'; diff --git a/website/docs/migration/from_state_notifier/old_lifecycles_old.dart b/website/docs/migration/from_state_notifier/old_lifecycles_old.dart index 8f6eaa485..2b6e8b335 100644 --- a/website/docs/migration/from_state_notifier/old_lifecycles_old.dart +++ b/website/docs/migration/from_state_notifier/old_lifecycles_old.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:dio/dio.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import '../utils.dart'; diff --git a/website/docs/providers/change_notifier_provider/todos.dart b/website/docs/providers/change_notifier_provider/todos.dart index d766920b1..a88c25c71 100644 --- a/website/docs/providers/change_notifier_provider/todos.dart +++ b/website/docs/providers/change_notifier_provider/todos.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/docs/providers/future_provider/config_provider/codegen.dart b/website/docs/providers/future_provider/config_provider/codegen.dart index f8520a4ec..e862b79f8 100644 --- a/website/docs/providers/future_provider/config_provider/codegen.dart +++ b/website/docs/providers/future_provider/config_provider/codegen.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'package:flutter/services.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/providers/future_provider/config_provider/codegen.g.dart b/website/docs/providers/future_provider/config_provider/codegen.g.dart index 5e2d34c20..4464c19c8 100644 --- a/website/docs/providers/future_provider/config_provider/codegen.g.dart +++ b/website/docs/providers/future_provider/config_provider/codegen.g.dart @@ -8,24 +8,58 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(fetchConfiguration) +const fetchConfigurationProvider = FetchConfigurationProvider._(); + +final class FetchConfigurationProvider extends $FunctionalProvider< + AsyncValue, FutureOr> + with $FutureModifier, $FutureProvider { + const FetchConfigurationProvider._( + {FutureOr Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'fetchConfigurationProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final FutureOr Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$fetchConfigurationHash(); + + @$internal + @override + $FutureProviderElement $createElement( + $ProviderPointer pointer) => + $FutureProviderElement(this, pointer); + + @override + FetchConfigurationProvider $copyWithCreate( + FutureOr Function( + Ref ref, + ) create, + ) { + return FetchConfigurationProvider._(create: create); + } + + @override + FutureOr create(Ref ref) { + final _$cb = _createCb ?? fetchConfiguration; + return _$cb(ref); + } +} + String _$fetchConfigurationHash() => r'f18dd06ced5e58734c6fd925e5614c34e94d1b9e'; -/// See also [fetchConfiguration]. -@ProviderFor(fetchConfiguration) -final fetchConfigurationProvider = - AutoDisposeFutureProvider.internal( - fetchConfiguration, - name: r'fetchConfigurationProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$fetchConfigurationHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef FetchConfigurationRef = AutoDisposeFutureProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/providers/notifier_provider/remote_todos/codegen.g.dart b/website/docs/providers/notifier_provider/remote_todos/codegen.g.dart index d6af18b53..3f847060c 100644 --- a/website/docs/providers/notifier_provider/remote_todos/codegen.g.dart +++ b/website/docs/providers/notifier_provider/remote_todos/codegen.g.dart @@ -25,20 +25,67 @@ Map _$$TodoImplToJson(_$TodoImpl instance) => // RiverpodGenerator // ************************************************************************** +@ProviderFor(AsyncTodos) +const asyncTodosProvider = AsyncTodosProvider._(); + +final class AsyncTodosProvider + extends $AsyncNotifierProvider> { + const AsyncTodosProvider._( + {super.runNotifierBuildOverride, AsyncTodos Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'asyncTodosProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final AsyncTodos Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$asyncTodosHash(); + + @$internal + @override + AsyncTodos create() => _createCb?.call() ?? AsyncTodos(); + + @$internal + @override + AsyncTodosProvider $copyWithCreate( + AsyncTodos Function() create, + ) { + return AsyncTodosProvider._(create: create); + } + + @$internal + @override + AsyncTodosProvider $copyWithBuild( + FutureOr> Function( + Ref, + AsyncTodos, + ) build, + ) { + return AsyncTodosProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $AsyncNotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $AsyncNotifierProviderElement(this, pointer); +} + String _$asyncTodosHash() => r'fd0d7502a1c17b7cedd2350519649dd680fc48cd'; -/// See also [AsyncTodos]. -@ProviderFor(AsyncTodos) -final asyncTodosProvider = - AutoDisposeAsyncNotifierProvider>.internal( - AsyncTodos.new, - name: r'asyncTodosProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$asyncTodosHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$AsyncTodos = AutoDisposeAsyncNotifier>; +abstract class _$AsyncTodos extends $AsyncNotifier> { + FutureOr> build(); + @$internal + @override + FutureOr> runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/providers/notifier_provider/todos/codegen.g.dart b/website/docs/providers/notifier_provider/todos/codegen.g.dart index 52bf0eda1..efd558a2c 100644 --- a/website/docs/providers/notifier_provider/todos/codegen.g.dart +++ b/website/docs/providers/notifier_provider/todos/codegen.g.dart @@ -8,19 +8,74 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(Todos) +const todosProvider = TodosProvider._(); + +final class TodosProvider extends $NotifierProvider> { + const TodosProvider._( + {super.runNotifierBuildOverride, Todos Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'todosProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Todos Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$todosHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + Todos create() => _createCb?.call() ?? Todos(); + + @$internal + @override + TodosProvider $copyWithCreate( + Todos Function() create, + ) { + return TodosProvider._(create: create); + } + + @$internal + @override + TodosProvider $copyWithBuild( + List Function( + Ref, + Todos, + ) build, + ) { + return TodosProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$todosHash() => r'3485c14ec4db07efe5fe52243258a66e6f99b2b4'; -/// See also [Todos]. -@ProviderFor(Todos) -final todosProvider = AutoDisposeNotifierProvider>.internal( - Todos.new, - name: r'todosProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$todosHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Todos = AutoDisposeNotifier>; +abstract class _$Todos extends $Notifier> { + List build(); + @$internal + @override + List runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/providers/provider/completed_todos/completed_todos.dart b/website/docs/providers/provider/completed_todos/completed_todos.dart index bc04a41b9..720a034ba 100644 --- a/website/docs/providers/provider/completed_todos/completed_todos.dart +++ b/website/docs/providers/provider/completed_todos/completed_todos.dart @@ -1,4 +1,3 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../todo/todo.dart'; diff --git a/website/docs/providers/provider/completed_todos/completed_todos.g.dart b/website/docs/providers/provider/completed_todos/completed_todos.g.dart index 3d248ae8b..41474e4a3 100644 --- a/website/docs/providers/provider/completed_todos/completed_todos.g.dart +++ b/website/docs/providers/provider/completed_todos/completed_todos.g.dart @@ -8,22 +8,64 @@ part of 'completed_todos.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(completedTodos) +const completedTodosProvider = CompletedTodosProvider._(); + +final class CompletedTodosProvider + extends $FunctionalProvider, List> + with $Provider> { + const CompletedTodosProvider._( + {List Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'completedTodosProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final List Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$completedTodosHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + $ProviderElement> $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CompletedTodosProvider $copyWithCreate( + List Function( + Ref ref, + ) create, + ) { + return CompletedTodosProvider._(create: create); + } + + @override + List create(Ref ref) { + final _$cb = _createCb ?? completedTodos; + return _$cb(ref); + } +} + String _$completedTodosHash() => r'0a6a67db7f22556b2cd64236815fdd4d2e72a72b'; -/// See also [completedTodos]. -@ProviderFor(completedTodos) -final completedTodosProvider = AutoDisposeProvider>.internal( - completedTodos, - name: r'completedTodosProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$completedTodosHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CompletedTodosRef = AutoDisposeProviderRef>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/providers/provider/optimized_previous_button/optimized_previous_button.g.dart b/website/docs/providers/provider/optimized_previous_button/optimized_previous_button.g.dart index bc74b696a..8d7ca5e1f 100644 --- a/website/docs/providers/provider/optimized_previous_button/optimized_previous_button.g.dart +++ b/website/docs/providers/provider/optimized_previous_button/optimized_previous_button.g.dart @@ -8,37 +8,133 @@ part of 'optimized_previous_button.dart'; // RiverpodGenerator // ************************************************************************** -String _$canGoToPreviousPageHash() => - r'1cb9c497aa7e5e8ee03c5711f079c2b68a4c28c5'; +@ProviderFor(PageIndex) +const pageIndexProvider = PageIndexProvider._(); + +final class PageIndexProvider extends $NotifierProvider { + const PageIndexProvider._( + {super.runNotifierBuildOverride, PageIndex Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'pageIndexProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final PageIndex Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$pageIndexHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + PageIndex create() => _createCb?.call() ?? PageIndex(); + + @$internal + @override + PageIndexProvider $copyWithCreate( + PageIndex Function() create, + ) { + return PageIndexProvider._(create: create); + } + + @$internal + @override + PageIndexProvider $copyWithBuild( + int Function( + Ref, + PageIndex, + ) build, + ) { + return PageIndexProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} -/// See also [canGoToPreviousPage]. -@ProviderFor(canGoToPreviousPage) -final canGoToPreviousPageProvider = AutoDisposeProvider.internal( - canGoToPreviousPage, - name: r'canGoToPreviousPageProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$canGoToPreviousPageHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef CanGoToPreviousPageRef = AutoDisposeProviderRef; String _$pageIndexHash() => r'59307ecf23b5b2432833da5ad6b312bf36435d0e'; -/// See also [PageIndex]. -@ProviderFor(PageIndex) -final pageIndexProvider = AutoDisposeNotifierProvider.internal( - PageIndex.new, - name: r'pageIndexProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$pageIndexHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$PageIndex = AutoDisposeNotifier; +abstract class _$PageIndex extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + +@ProviderFor(canGoToPreviousPage) +const canGoToPreviousPageProvider = CanGoToPreviousPageProvider._(); + +final class CanGoToPreviousPageProvider extends $FunctionalProvider + with $Provider { + const CanGoToPreviousPageProvider._( + {bool Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'canGoToPreviousPageProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final bool Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$canGoToPreviousPageHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(bool value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + $ProviderElement $createElement($ProviderPointer pointer) => + $ProviderElement(this, pointer); + + @override + CanGoToPreviousPageProvider $copyWithCreate( + bool Function( + Ref ref, + ) create, + ) { + return CanGoToPreviousPageProvider._(create: create); + } + + @override + bool create(Ref ref) { + final _$cb = _createCb ?? canGoToPreviousPage; + return _$cb(ref); + } +} + +String _$canGoToPreviousPageHash() => + r'1cb9c497aa7e5e8ee03c5711f079c2b68a4c28c5'; + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/providers/provider/optimized_previous_button/raw.dart b/website/docs/providers/provider/optimized_previous_button/raw.dart index 30b1cc673..a4d3cb4b9 100644 --- a/website/docs/providers/provider/optimized_previous_button/raw.dart +++ b/website/docs/providers/provider/optimized_previous_button/raw.dart @@ -1,6 +1,7 @@ // A provider that controls the current page import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/docs/providers/provider/todo/todo.g.dart b/website/docs/providers/provider/todo/todo.g.dart index 406afcd46..cb2d43165 100644 --- a/website/docs/providers/provider/todo/todo.g.dart +++ b/website/docs/providers/provider/todo/todo.g.dart @@ -8,19 +8,74 @@ part of 'todo.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(Todos) +const todosProvider = TodosProvider._(); + +final class TodosProvider extends $NotifierProvider> { + const TodosProvider._( + {super.runNotifierBuildOverride, Todos Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'todosProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Todos Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$todosHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + Todos create() => _createCb?.call() ?? Todos(); + + @$internal + @override + TodosProvider $copyWithCreate( + Todos Function() create, + ) { + return TodosProvider._(create: create); + } + + @$internal + @override + TodosProvider $copyWithBuild( + List Function( + Ref, + Todos, + ) build, + ) { + return TodosProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement> $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$todosHash() => r'4bd25c3c15bfff56ad6e733bd17ecb7284c4ceb2'; -/// See also [Todos]. -@ProviderFor(Todos) -final todosProvider = AutoDisposeNotifierProvider>.internal( - Todos.new, - name: r'todosProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$todosHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Todos = AutoDisposeNotifier>; +abstract class _$Todos extends $Notifier> { + List build(); + @$internal + @override + List runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/providers/provider/todos_consumer.dart b/website/docs/providers/provider/todos_consumer.dart index 9b93e9185..96471e694 100644 --- a/website/docs/providers/provider/todos_consumer.dart +++ b/website/docs/providers/provider/todos_consumer.dart @@ -13,6 +13,6 @@ Consumer(builder: (context, ref, child) { // TODO show the todos using a ListView/GridView/.../* SKIP */ return Container(); /* SKIP END */ -}); +},); /* SNIPPET END */ } diff --git a/website/docs/providers/provider/unoptimized_previous_button/raw.dart b/website/docs/providers/provider/unoptimized_previous_button/raw.dart index 828d382d5..b670eaae3 100644 --- a/website/docs/providers/provider/unoptimized_previous_button/raw.dart +++ b/website/docs/providers/provider/unoptimized_previous_button/raw.dart @@ -1,6 +1,7 @@ // A provider that controls the current page import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/docs/providers/provider/unoptimized_previous_button/unoptimized_previous_button.g.dart b/website/docs/providers/provider/unoptimized_previous_button/unoptimized_previous_button.g.dart index 633727343..f56618fca 100644 --- a/website/docs/providers/provider/unoptimized_previous_button/unoptimized_previous_button.g.dart +++ b/website/docs/providers/provider/unoptimized_previous_button/unoptimized_previous_button.g.dart @@ -8,19 +8,74 @@ part of 'unoptimized_previous_button.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(PageIndex) +const pageIndexProvider = PageIndexProvider._(); + +final class PageIndexProvider extends $NotifierProvider { + const PageIndexProvider._( + {super.runNotifierBuildOverride, PageIndex Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'pageIndexProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final PageIndex Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$pageIndexHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(int value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider(value), + ); + } + + @$internal + @override + PageIndex create() => _createCb?.call() ?? PageIndex(); + + @$internal + @override + PageIndexProvider $copyWithCreate( + PageIndex Function() create, + ) { + return PageIndexProvider._(create: create); + } + + @$internal + @override + PageIndexProvider $copyWithBuild( + int Function( + Ref, + PageIndex, + ) build, + ) { + return PageIndexProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + $NotifierProviderElement $createElement( + $ProviderPointer pointer) => + $NotifierProviderElement(this, pointer); +} + String _$pageIndexHash() => r'59307ecf23b5b2432833da5ad6b312bf36435d0e'; -/// See also [PageIndex]. -@ProviderFor(PageIndex) -final pageIndexProvider = AutoDisposeNotifierProvider.internal( - PageIndex.new, - name: r'pageIndexProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$pageIndexHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$PageIndex = AutoDisposeNotifier; +abstract class _$PageIndex extends $Notifier { + int build(); + @$internal + @override + int runBuild() => build(); +} + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/docs/providers/state_notifier_provider/todos.dart b/website/docs/providers/state_notifier_provider/todos.dart index 1318bbcbf..f39051948 100644 --- a/website/docs/providers/state_notifier_provider/todos.dart +++ b/website/docs/providers/state_notifier_provider/todos.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -7,7 +7,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; // We could also use packages like Freezed to help with the implementation. @immutable class Todo { - const Todo({required this.id, required this.description, required this.completed}); + const Todo({ + required this.id, + required this.description, + required this.completed, + }); // All properties should be `final` on our class. final String id; @@ -31,7 +35,7 @@ class Todo { // The public methods on this class will be what allow the UI to modify the state. class TodosNotifier extends StateNotifier> { // We initialize the list of todos to an empty list - TodosNotifier(): super([]); + TodosNotifier() : super([]); // Let's allow the UI to add todos. void addTodo(Todo todo) { @@ -75,4 +79,4 @@ class TodosNotifier extends StateNotifier> { // our TodosNotifier class. final todosProvider = StateNotifierProvider>((ref) { return TodosNotifier(); -}); \ No newline at end of file +}); diff --git a/website/docs/providers/state_notifier_provider/todos_consumer.dart b/website/docs/providers/state_notifier_provider/todos_consumer.dart index 8722a542f..0b60d4eaf 100644 --- a/website/docs/providers/state_notifier_provider/todos_consumer.dart +++ b/website/docs/providers/state_notifier_provider/todos_consumer.dart @@ -22,10 +22,11 @@ class TodoListView extends ConsumerWidget { CheckboxListTile( value: todo.completed, // When tapping on the todo, change its completed status - onChanged: (value) => ref.read(todosProvider.notifier).toggle(todo.id), + onChanged: (value) => + ref.read(todosProvider.notifier).toggle(todo.id), title: Text(todo.description), ), ], ); } -} \ No newline at end of file +} diff --git a/website/docs/providers/state_provider/connected_dropdown.dart b/website/docs/providers/state_provider/connected_dropdown.dart index 189ff3c6a..31566a52a 100644 --- a/website/docs/providers/state_provider/connected_dropdown.dart +++ b/website/docs/providers/state_provider/connected_dropdown.dart @@ -21,5 +21,5 @@ DropdownButton( ], ), /* SNIPPET END */ - ]); + ],); } diff --git a/website/docs/providers/state_provider/full.dart b/website/docs/providers/state_provider/full.dart index 4c598055f..28eb1f863 100644 --- a/website/docs/providers/state_provider/full.dart +++ b/website/docs/providers/state_provider/full.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; void main() { runApp(const ProviderScope(child: MyApp())); diff --git a/website/docs/providers/state_provider/sort_provider.dart b/website/docs/providers/state_provider/sort_provider.dart index 60a5f60c1..d42eb8cc5 100644 --- a/website/docs/providers/state_provider/sort_provider.dart +++ b/website/docs/providers/state_provider/sort_provider.dart @@ -1,4 +1,4 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'dropdown.dart'; diff --git a/website/docs/providers/state_provider/sorted_product_provider.dart b/website/docs/providers/state_provider/sorted_product_provider.dart index 6725d0282..b74c3adf8 100644 --- a/website/docs/providers/state_provider/sorted_product_provider.dart +++ b/website/docs/providers/state_provider/sorted_product_provider.dart @@ -21,4 +21,4 @@ final productsProvider = Provider>((ref) { case ProductSortType.price: return _products.sorted((a, b) => a.price.compareTo(b.price)); } -}); \ No newline at end of file +}); diff --git a/website/docs/providers/state_provider/update_read_once.dart b/website/docs/providers/state_provider/update_read_once.dart index 825df6d0f..3cd8e6878 100644 --- a/website/docs/providers/state_provider/update_read_once.dart +++ b/website/docs/providers/state_provider/update_read_once.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -20,4 +21,4 @@ class HomeView extends ConsumerWidget { ), ); } -} \ No newline at end of file +} diff --git a/website/docs/providers/state_provider/update_read_twice.dart b/website/docs/providers/state_provider/update_read_twice.dart index c029e0730..58f866fc9 100644 --- a/website/docs/providers/state_provider/update_read_twice.dart +++ b/website/docs/providers/state_provider/update_read_twice.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -15,9 +16,10 @@ class HomeView extends ConsumerWidget { onPressed: () { // We're updating the state from the previous value, we ended-up reading // the provider twice! - ref.read(counterProvider.notifier).state = ref.read(counterProvider.notifier).state + 1; + ref.read(counterProvider.notifier).state = + ref.read(counterProvider.notifier).state + 1; }, ), ); } -} \ No newline at end of file +} diff --git a/website/docs/providers/stream_provider/live_stream_chat_provider/codegen.dart b/website/docs/providers/stream_provider/live_stream_chat_provider/codegen.dart index d5247516f..6ac7b13c4 100644 --- a/website/docs/providers/stream_provider/live_stream_chat_provider/codegen.dart +++ b/website/docs/providers/stream_provider/live_stream_chat_provider/codegen.dart @@ -2,7 +2,6 @@ import 'dart:convert'; import 'dart:io'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'codegen.g.dart'; diff --git a/website/docs/providers/stream_provider/live_stream_chat_provider/codegen.g.dart b/website/docs/providers/stream_provider/live_stream_chat_provider/codegen.g.dart index 931c15b0a..4c5c4e59b 100644 --- a/website/docs/providers/stream_provider/live_stream_chat_provider/codegen.g.dart +++ b/website/docs/providers/stream_provider/live_stream_chat_provider/codegen.g.dart @@ -8,21 +8,57 @@ part of 'codegen.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(chat) +const chatProvider = ChatProvider._(); + +final class ChatProvider + extends $FunctionalProvider>, Stream>> + with $FutureModifier>, $StreamProvider> { + const ChatProvider._( + {Stream> Function( + Ref ref, + )? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'chatProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final Stream> Function( + Ref ref, + )? _createCb; + + @override + String debugGetCreateSourceHash() => _$chatHash(); + + @$internal + @override + $StreamProviderElement> $createElement( + $ProviderPointer pointer) => + $StreamProviderElement(this, pointer); + + @override + ChatProvider $copyWithCreate( + Stream> Function( + Ref ref, + ) create, + ) { + return ChatProvider._(create: create); + } + + @override + Stream> create(Ref ref) { + final _$cb = _createCb ?? chat; + return _$cb(ref); + } +} + String _$chatHash() => r'bad093d5344471463a1e71688281924642f3a58c'; -/// See also [chat]. -@ProviderFor(chat) -final chatProvider = AutoDisposeStreamProvider>.internal( - chat, - name: r'chatProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$chatHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef ChatRef = AutoDisposeStreamProviderRef>; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart index 1ce474d55..c8abd8706 100644 --- a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart +++ b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; final repositoryProvider = Provider((ref) => Repository()); diff --git a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart index 4ed90e7af..620d39d3d 100644 --- a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart +++ b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart @@ -1,5 +1,6 @@ // ignore_for_file: omit_local_variable_types, avoid_types_on_closure_parameters, avoid_print +import 'package:flutter_riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; import 'reading_counter.dart'; @@ -12,4 +13,4 @@ final anotherProvider = Provider((ref) { print('The counter changed $newCount'); }); // ... -}); \ No newline at end of file +}); diff --git a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart index 066565b67..ec3488a7f 100644 --- a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart +++ b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'reading_counter.dart'; /* SNIPPET START */ @@ -16,7 +17,7 @@ class HomeView extends ConsumerWidget { ref.listen(counterProvider, (int? previousCount, int newCount) { print('The counter changed $newCount'); }); - + return Container(); } -} \ No newline at end of file +} diff --git a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_read.dart b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_read.dart index 91be089df..471610860 100644 --- a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_read.dart +++ b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_read.dart @@ -2,13 +2,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'reading_counter.dart'; /* SNIPPET START */ -final counterProvider = - StateNotifierProvider(Counter.new); +final counterProvider = StateNotifierProvider(Counter.new); class HomeView extends ConsumerWidget { const HomeView({super.key}); diff --git a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart index 685d5ac4d..4a17f4645 100644 --- a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart +++ b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart index 34104aead..95bdab6c5 100644 --- a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart +++ b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -13,4 +14,4 @@ Widget build(BuildContext context, WidgetRef ref) { onPressed: () => counter.state++, child: const Text('button'), ); -} \ No newline at end of file +} diff --git a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart index 1bc535f2e..17eb632ef 100644 --- a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart +++ b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart @@ -1,6 +1,7 @@ // ignore_for_file: omit_local_variable_types import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; enum FilterType { none, diff --git a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart index bc0c7bdf4..1e60d2751 100644 --- a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart +++ b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; enum FilterType { none, diff --git a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart index 66c630717..94d77b5e3 100644 --- a/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart +++ b/website/i18n/bn/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart b/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart index 2848a2a91..147b1b2ca 100644 --- a/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart +++ b/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart @@ -1,6 +1,7 @@ // A provider that controls the current page import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/provider/todo.dart b/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/provider/todo.dart index 1922735f5..927f2883c 100644 --- a/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/provider/todo.dart +++ b/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/provider/todo.dart @@ -1,6 +1,6 @@ // ignore_for_file: avoid_positional_boolean_parameters -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart b/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart index 9ae43a3eb..0a04b3bb9 100644 --- a/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart +++ b/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart @@ -1,6 +1,7 @@ // A provider that controls the current page import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart b/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart index 3c59ff2da..df714ea54 100644 --- a/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart +++ b/website/i18n/bn/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; void main() { runApp(const ProviderScope(child: MyApp())); diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart index 238d1eaf8..cf052b3e9 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart index 21a7accf8..103dd40d4 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; final repositoryProvider = Provider((ref) => Repository()); diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart index 4ed90e7af..620d39d3d 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart @@ -1,5 +1,6 @@ // ignore_for_file: omit_local_variable_types, avoid_types_on_closure_parameters, avoid_print +import 'package:flutter_riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; import 'reading_counter.dart'; @@ -12,4 +13,4 @@ final anotherProvider = Provider((ref) { print('The counter changed $newCount'); }); // ... -}); \ No newline at end of file +}); diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart index 066565b67..ec3488a7f 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'reading_counter.dart'; /* SNIPPET START */ @@ -16,7 +17,7 @@ class HomeView extends ConsumerWidget { ref.listen(counterProvider, (int? previousCount, int newCount) { print('The counter changed $newCount'); }); - + return Container(); } -} \ No newline at end of file +} diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_read.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_read.dart index 1c42dd23f..1628a02ec 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_read.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_read.dart @@ -2,13 +2,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'reading_counter.dart'; /* SNIPPET START */ -final counterProvider = - StateNotifierProvider(Counter.new); +final counterProvider = StateNotifierProvider(Counter.new); class HomeView extends ConsumerWidget { const HomeView({super.key}); diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart index 0ebe6a990..059b28e97 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart index 34104aead..95bdab6c5 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -13,4 +14,4 @@ Widget build(BuildContext context, WidgetRef ref) { onPressed: () => counter.state++, child: const Text('button'), ); -} \ No newline at end of file +} diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart index 5c688ee3f..35ae259c0 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart @@ -1,6 +1,7 @@ // ignore_for_file: omit_local_variable_types import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; enum FilterType { none, diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart index d7fc059f6..9f0547f55 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; enum FilterType { none, diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart index 66c630717..94d77b5e3 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart index aa6ab759a..2b0eb7c2b 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart @@ -22,7 +22,7 @@ void main() { // Überschreiben Sie das Verhalten von repositoryProvider, um // FakeRepository anstelle von Repository zurückzugeben. /* highlight-start */ - repositoryProvider.overrideWithValue(FakeRepository()) + repositoryProvider.overrideWithValue(FakeRepository()), /* highlight-end */ // Wir müssen den `todoListProvider` nicht überschreiben, er wird // automatisch den überschriebenen repositoryProvider verwenden diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart index 58b425278..56280727e 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart @@ -26,7 +26,7 @@ void main() { // Überschreiben Sie das Verhalten von repositoryProvider, um // FakeRepository anstelle von Repository zurückzugeben. /* highlight-start */ - repositoryProvider.overrideWithValue(FakeRepository()) + repositoryProvider.overrideWithValue(FakeRepository()), /* highlight-end */ // Wir müssen den `todoListProvider` nicht überschreiben, er wird // automatisch den überschriebenen repositoryProvider verwenden diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart index 4bfe5b24a..a6feb44ba 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart @@ -68,10 +68,10 @@ void main() { } return ListView( children: [ - for (final todo in todos.asData!.value) TodoItem(todo: todo) + for (final todo in todos.asData!.value) TodoItem(todo: todo), ], ); - }), + },), ), ), ), diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart index 256d26f0a..4ca727bed 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:riverpod/riverpod.dart'; diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart index db1ba949f..0996f4110 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; /* SNIPPET START */ @@ -25,7 +26,7 @@ class MyApp extends StatelessWidget { onPressed: () => ref.read(counterProvider.notifier).state++, child: Text('$counter'), ); - }), + },), ); } } diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart index 4e04ebdb7..ac23258a2 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart @@ -1,6 +1,7 @@ // A provider that controls the current page import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/provider/todo.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/provider/todo.dart index 1922735f5..927f2883c 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/provider/todo.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/provider/todo.dart @@ -1,6 +1,6 @@ // ignore_for_file: avoid_positional_boolean_parameters -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart index 99efdd26b..c2b762562 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart @@ -1,6 +1,7 @@ // Ein Provider, der die aktuelle Seite kontrolliert import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart index 3f2b4e839..be7c92fa1 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -7,8 +7,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; // Wir könnten auch Pakete wie Freezed verwenden, um bei der Implementierung zu helfen. @immutable class Todo { - const Todo( - {required this.id, required this.description, required this.completed}); + const Todo({ + required this.id, + required this.description, + required this.completed, + }); // Alle variablen in unserer Klasse sollten `final` sein. final String id; diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart index f37d6998d..e0f947f73 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart @@ -21,5 +21,5 @@ Widget build(BuildContext context, WidgetRef ref) { ], ), /* SNIPPET END */ - ]); + ],); } diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart index d47fb2427..22ca5c5f2 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; void main() { runApp(const ProviderScope(child: MyApp())); diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart index 49d416646..de83f8cee 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart @@ -1,4 +1,4 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'dropdown.dart'; diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart index 6725d0282..b74c3adf8 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart @@ -21,4 +21,4 @@ final productsProvider = Provider>((ref) { case ProductSortType.price: return _products.sorted((a, b) => a.price.compareTo(b.price)); } -}); \ No newline at end of file +}); diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart index 825df6d0f..3cd8e6878 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -20,4 +21,4 @@ class HomeView extends ConsumerWidget { ), ); } -} \ No newline at end of file +} diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart index 91b877290..6d7df059f 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart index 397c8dc1b..3ac1127d0 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart @@ -1,6 +1,7 @@ // A provider that controls the current page import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/provider/todo.dart b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/provider/todo.dart index 3770a91d8..b296e2fde 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/provider/todo.dart +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/provider/todo.dart @@ -1,6 +1,6 @@ // ignore_for_file: avoid_positional_boolean_parameters -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart index 24f11a845..bfab931e1 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart @@ -1,6 +1,7 @@ // A provider that controls the current page import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart index bf6761114..1c3e04f4a 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -7,14 +7,18 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; // También podríamos usar paquetes como Freezed para ayudar con la implementación. @immutable class Todo { - const Todo({required this.id, required this.description, required this.completed}); + const Todo({ + required this.id, + required this.description, + required this.completed, + }); // Todas las propiedades deben ser `final` en nuestra clase. final String id; final String description; final bool completed; - // Como `Todo` es inmutable, implementamos un método que permite clonar el + // Como `Todo` es inmutable, implementamos un método que permite clonar el // `Todo` con un contenido ligeramente diferente. Todo copyWith({String? id, String? description, bool? completed}) { return Todo( @@ -26,17 +30,17 @@ class Todo { } // La clase StateNotifier que se pasará a nuestro StateNotifierProvider. -// Esta clase no debe exponer el estado fuera de su propiedad "estado", lo que significa +// Esta clase no debe exponer el estado fuera de su propiedad "estado", lo que significa // ¡sin getters/propiedades públicas! // Los métodos públicos en esta clase serán los que permitirán // que la interfaz de usuario modifique el estado. class TodosNotifier extends StateNotifier> { // Inicializamos la lista de `todos` como una lista vacía - TodosNotifier(): super([]); + TodosNotifier() : super([]); // Permitamos que la interfaz de usuario agregue todos. void addTodo(Todo todo) { - // Ya que nuestro estado es inmutable, no podemos hacer `state.add(todo)`. + // Ya que nuestro estado es inmutable, no podemos hacer `state.add(todo)`. // En su lugar, debemos crear una nueva lista de todos que contenga la anterior // elementos y el nuevo. // ¡Usar el spread operator de Dart aquí es útil! @@ -47,7 +51,7 @@ class TodosNotifier extends StateNotifier> { // Permitamos eliminar `todos` void removeTodo(String todoId) { - // Nuevamente, nuestro estado es inmutable. Así que estamos haciendo + // Nuevamente, nuestro estado es inmutable. Así que estamos haciendo // una nueva lista en lugar de cambiar la lista existente. state = [ for (final todo in state) @@ -61,8 +65,8 @@ class TodosNotifier extends StateNotifier> { for (final todo in state) // Estamos marcando solo el `todo` coincidente como completada if (todo.id == todoId) - // Una vez más, dado que nuestro estado es inmutable, necesitamos hacer una copia - // del `todo`. Estamos usando nuestro método `copyWith` implementado antes + // Una vez más, dado que nuestro estado es inmutable, necesitamos hacer una copia + // del `todo`. Estamos usando nuestro método `copyWith` implementado antes // para ayudar con eso. todo.copyWith(completed: !todo.completed) else @@ -72,7 +76,7 @@ class TodosNotifier extends StateNotifier> { } } -// Finalmente, estamos usando StateNotifierProvider para permitir que la +// Finalmente, estamos usando StateNotifierProvider para permitir que la // interfaz de usuario interactúe con nuestra clase TodosNotifier. final todosProvider = StateNotifierProvider>((ref) { return TodosNotifier(); diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart index dfe9372f6..e3076af8d 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart @@ -21,5 +21,5 @@ DropdownButton( ], ), /* SNIPPET END */ - ]); + ],); } diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart index 283d4c4bd..41a3ec1dc 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; void main() { runApp(const ProviderScope(child: MyApp())); diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart index 6a1821d68..c3bccd1e8 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart @@ -1,4 +1,4 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'dropdown.dart'; diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart index 6725d0282..b74c3adf8 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart @@ -21,4 +21,4 @@ final productsProvider = Provider>((ref) { case ProductSortType.price: return _products.sorted((a, b) => a.price.compareTo(b.price)); } -}); \ No newline at end of file +}); diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart index 825df6d0f..3cd8e6878 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -20,4 +21,4 @@ class HomeView extends ConsumerWidget { ), ); } -} \ No newline at end of file +} diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart index b60695d94..c6c124a45 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart index 8fcbf1d1a..7a8f04444 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:shared_preferences/shared_preferences.dart'; class LoadingScreen extends StatelessWidget { @@ -31,7 +32,7 @@ final sharedPreferencesProvider = Future main() async { // Affiche un indicateur de chargement avant de lancer l'application complète (facultatif) // L'écran de chargement de la plate-forme sera utilisé pendant l'attente si vous omettez cette option. - runApp(const LoadingScreen()); + runApp(const ProviderScope(child: LoadingScreen())); // Obtention de l'instance de shared preferences final prefs = await SharedPreferences.getInstance(); diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart index 71ccc1557..52e334029 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook_widget.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook_widget.dart index 194bec8dc..600c980a7 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook_widget.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook_widget.dart @@ -19,4 +19,4 @@ class HomeView extends HookConsumerWidget { final counter = ref.watch(counterProvider); return Text('$counter'); } -} \ No newline at end of file +} diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_consumer_stateful_widget.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_consumer_stateful_widget.dart index a685b7bac..09a1de9c6 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_consumer_stateful_widget.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_consumer_stateful_widget.dart @@ -25,4 +25,4 @@ class HomeViewState extends ConsumerState { final counter = ref.watch(counterProvider); return Text('$counter'); } -} \ No newline at end of file +} diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart index 236575f6d..87f981849 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; final repositoryProvider = Provider((ref) => Repository()); @@ -13,7 +14,7 @@ final counterProvider = StateNotifierProvider((ref) { }); class Counter extends StateNotifier { - Counter(this.ref): super(0); + Counter(this.ref) : super(0); final Ref ref; @@ -22,4 +23,4 @@ class Counter extends StateNotifier { final repository = ref.read(repositoryProvider); repository.post('...'); } -} \ No newline at end of file +} diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart index 4ed90e7af..4f6829a9e 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; // ignore_for_file: omit_local_variable_types, avoid_types_on_closure_parameters, avoid_print import 'package:riverpod/riverpod.dart'; @@ -12,4 +13,4 @@ final anotherProvider = Provider((ref) { print('The counter changed $newCount'); }); // ... -}); \ No newline at end of file +}); diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart index 066565b67..0d94f0d08 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; + import 'reading_counter.dart'; /* SNIPPET START */ @@ -16,7 +18,7 @@ class HomeView extends ConsumerWidget { ref.listen(counterProvider, (int? previousCount, int newCount) { print('The counter changed $newCount'); }); - + return Container(); } -} \ No newline at end of file +} diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_read.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_read.dart index 23a1a9a97..7b30e6e71 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_read.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_read.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'reading_counter.dart'; @@ -23,4 +24,4 @@ class HomeView extends ConsumerWidget { ), ); } -} \ No newline at end of file +} diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart index c51b7cb52..11e5fd3e3 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -14,4 +15,4 @@ Widget build(BuildContext context, WidgetRef ref) { onPressed: () => counter.state++, child: const Text('button'), ); -} \ No newline at end of file +} diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart index 34104aead..95bdab6c5 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -13,4 +14,4 @@ Widget build(BuildContext context, WidgetRef ref) { onPressed: () => counter.state++, child: const Text('button'), ); -} \ No newline at end of file +} diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_stateful_hook_consumer_widget.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_stateful_hook_consumer_widget.dart index 18193caee..f57624be5 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_stateful_hook_consumer_widget.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_stateful_hook_consumer_widget.dart @@ -31,4 +31,4 @@ class HomeViewState extends ConsumerState { final counter = ref.watch(counterProvider); return Text('$counter'); } -} \ No newline at end of file +} diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart index 545d7832e..cc43d617e 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart @@ -1,6 +1,7 @@ // ignore_for_file: omit_local_variable_types import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; enum FilterType { none, @@ -12,13 +13,14 @@ abstract class Todo { } class TodoList extends StateNotifier> { - TodoList(): super([]); + TodoList() : super([]); } /* SNIPPET START */ final filterTypeProvider = StateProvider((ref) => FilterType.none); -final todosProvider = StateNotifierProvider>((ref) => TodoList()); +final todosProvider = + StateNotifierProvider>((ref) => TodoList()); final filteredTodoListProvider = Provider((ref) { // récupère à la fois le filtre et la liste des todos @@ -33,4 +35,4 @@ final filteredTodoListProvider = Provider((ref) { // renvoie la liste non filtrée des todos return todos; } -}); \ No newline at end of file +}); diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart index e87b2436b..894c3a364 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; enum FilterType { none, @@ -13,7 +14,7 @@ abstract class Todo { } class TodoList extends StateNotifier> { - TodoList(): super([]); + TodoList() : super([]); } /* SNIPPET START */ @@ -30,4 +31,4 @@ class HomeView extends ConsumerWidget { return Text('$counter'); } -} \ No newline at end of file +} diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart index 66c630717..94d77b5e3 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart index cb4fc4046..0e5fbfc67 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -53,7 +54,7 @@ class Home extends ConsumerWidget { // Cet affichage particulier utilisera l'état du provider à partir du ProviderScope racine. const CounterDisplay(), ], - )); + ),); } } diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart index af60e2a4e..f48651071 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -43,7 +44,7 @@ class Home extends ConsumerWidget { child: const Text('Incrémenter le compteur'), ), ], - )); + ),); } } diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart index 92e0a96de..560e2b0ab 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart @@ -16,36 +16,36 @@ final todoListProvider = FutureProvider>((ref) => []); void main() { /* SNIPPET START */ -test('override repositoryProvider', () async { - final container = ProviderContainer( - overrides: [ - // Surcharge le comportement de repositoryProvider pour qu'il renvoie - // FakeRepository au lieu de Repository. - /* highlight-start */ - repositoryProvider.overrideWithValue(FakeRepository()) - /* highlight-end */ - // Il n'est pas nécessaire de surcharger `todoListProvider`, - // il utilisera automatiquement le repositoryProvider surchargé. - ], - ); - - // La première lecture si l'état est en cours de chargement - expect( - container.read(todoListProvider), - const AsyncValue>.loading(), - ); - - /// Attendre la fin de la demande (la requete) - await container.read(todoListProvider.future); - - // Expose les données recherchées - expect(container.read(todoListProvider).value, [ - isA() - .having((s) => s.id, 'id', '42') - .having((s) => s.label, 'label', 'Hello world') - .having((s) => s.completed, 'completed', false), - ]); -}); + test('override repositoryProvider', () async { + final container = ProviderContainer( + overrides: [ + // Surcharge le comportement de repositoryProvider pour qu'il renvoie + // FakeRepository au lieu de Repository. + /* highlight-start */ + repositoryProvider.overrideWithValue(FakeRepository()), + /* highlight-end */ + // Il n'est pas nécessaire de surcharger `todoListProvider`, + // il utilisera automatiquement le repositoryProvider surchargé. + ], + ); + + // La première lecture si l'état est en cours de chargement + expect( + container.read(todoListProvider), + const AsyncValue>.loading(), + ); + + /// Attendre la fin de la demande (la requete) + await container.read(todoListProvider.future); + + // Expose les données recherchées + expect(container.read(todoListProvider).value, [ + isA() + .having((s) => s.id, 'id', '42') + .having((s) => s.label, 'label', 'Hello world') + .having((s) => s.completed, 'completed', false), + ]); + }); /* SNIPPET END */ } diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart index 28c5c624e..7c36d1f93 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart @@ -25,7 +25,7 @@ void main() { // Surcharge le comportement de repositoryProvider pour qu'il renvoie // FakeRepository au lieu de Repository. /* highlight-start */ - repositoryProvider.overrideWithValue(FakeRepository()) + repositoryProvider.overrideWithValue(FakeRepository()), /* highlight-end */ // Il n'est pas nécessaire de surcharger `todoListProvider`, // il utilisera automatiquement le repositoryProvider surchargé. diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart index 6d0835bdb..fad9808c7 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart @@ -61,18 +61,21 @@ void main() { // Vous pouvez l'extraire dans un widget MyApp child: MaterialApp( home: Scaffold( - body: Consumer(builder: (context, ref, _) { - final todos = ref.watch(todoListProvider); - // La liste des todos est en cours de chargement ou en erreur - if (todos.asData == null) { - return const CircularProgressIndicator(); - } - return ListView( - children: [ - for (final todo in todos.asData!.value) TodoItem(todo: todo) - ], - ); - }), + body: Consumer( + builder: (context, ref, _) { + final todos = ref.watch(todoListProvider); + // La liste des todos est en cours de chargement ou en erreur + if (todos.asData == null) { + return const CircularProgressIndicator(); + } + return ListView( + children: [ + for (final todo in todos.asData!.value) + TodoItem(todo: todo), + ], + ); + }, + ), ), ), ), diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart index 30051b690..51249f798 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:riverpod/riverpod.dart'; diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart index d7ac517cc..50931c010 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; /* SNIPPET START */ @@ -18,13 +19,15 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - home: Consumer(builder: (context, ref, _) { - final counter = ref.watch(counterProvider); - return ElevatedButton( - onPressed: () => ref.read(counterProvider.notifier).state++, - child: Text('$counter'), - ); - }), + home: Consumer( + builder: (context, ref, _) { + final counter = ref.watch(counterProvider); + return ElevatedButton( + onPressed: () => ref.read(counterProvider.notifier).state++, + child: Text('$counter'), + ); + }, + ), ); } } diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/dart_hello_world/main.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/dart_hello_world/main.dart index d2e9435dc..4a9361d1d 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/dart_hello_world/main.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/dart_hello_world/main.dart @@ -1,8 +1,7 @@ -// ignore_for_file: avoid_print +// ignore_for_file: avoid_print, unreachable_from_main /* SNIPPET START */ -import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'main.g.dart'; diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/codegen.yaml b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/codegen.yaml index 538c95316..8109a4e5a 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/codegen.yaml +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/codegen.yaml @@ -1,6 +1,6 @@ name: my_app_name environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: riverpod: ^2.1.1 diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/raw.yaml b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/raw.yaml index da0363ab3..c1a60a902 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/raw.yaml +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/raw.yaml @@ -1,6 +1,6 @@ name: my_app_name environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: riverpod: ^2.1.1 \ No newline at end of file diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/hello_world/main.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/hello_world/main.dart index ea2b1fda1..11c0f9cb3 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/hello_world/main.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/hello_world/main.dart @@ -1,4 +1,4 @@ -// ignore_for_file: use_key_in_widget_constructors, omit_local_variable_types +// ignore_for_file: use_key_in_widget_constructors, omit_local_variable_types, unreachable_from_main /* SNIPPET START */ diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/codegen.yaml b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/codegen.yaml index 6c6f2bc15..9f643cfd0 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/codegen.yaml +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/codegen.yaml @@ -1,6 +1,6 @@ name: my_app_name environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=3.0.0" dependencies: diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks.yaml b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks.yaml index 7e0d1aa24..ad77044c5 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks.yaml +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks.yaml @@ -1,6 +1,6 @@ name: my_app_name environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=3.0.0" dependencies: diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks_codegen.yaml b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks_codegen.yaml index 90356238a..5494fa534 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks_codegen.yaml +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks_codegen.yaml @@ -1,6 +1,6 @@ name: my_app_name environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=3.0.0" dependencies: diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/raw.yaml b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/raw.yaml index 4beb7f86b..28d172172 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/raw.yaml +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/getting_started/pubspec/raw.yaml @@ -1,6 +1,6 @@ name: my_app_name environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=3.0.0" dependencies: diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart index a07051956..b043e8744 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart index 0ca446615..38ee03d5b 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart @@ -1,6 +1,7 @@ // A provider that controls the current page import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -35,4 +36,4 @@ class PreviousButton extends ConsumerWidget { child: const Text('previous'), ); } -} \ No newline at end of file +} diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/todo.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/todo.dart index 4e41c2c2b..6c5f0bd4a 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/todo.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/todo.dart @@ -1,6 +1,6 @@ // ignore_for_file: avoid_positional_boolean_parameters -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart index a8b63aed3..38801e499 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart @@ -12,6 +12,6 @@ Widget build() { // TODO afficher les todos en utilisant un ListView/GridView/... /* SKIP */ return Container(); /* SKIP END */ - }); + },); /* SNIPPET END */ } diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart index 9a60b44fc..d3a68541f 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart @@ -1,6 +1,7 @@ // A provider that controls the current page import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -23,4 +24,4 @@ class PreviousButton extends ConsumerWidget { child: const Text('previous'), ); } -} \ No newline at end of file +} diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart index d22ba9169..59457f9d7 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -7,14 +7,18 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; // On peut aussi utiliser des packages comme Freezed pour aider à la mise en œuvre. @immutable class Todo { - const Todo({required this.id, required this.description, required this.completed}); + const Todo({ + required this.id, + required this.description, + required this.completed, + }); // Toutes les propriétés doivent être `finales` sur notre classe. final String id; final String description; final bool completed; - // Puisque Todo est immuable, on implémente une méthode qui permet de cloner le Todo + // Puisque Todo est immuable, on implémente une méthode qui permet de cloner le Todo // avec un contenu légèrement différent. Todo copyWith({String? id, String? description, bool? completed}) { return Todo( @@ -25,29 +29,29 @@ class Todo { } } -// La classe StateNotifier qui sera transmise à notre StateNotifierProvider. -// Cette classe ne doit pas exposer d'état (state) en dehors de sa propriété "state", +// La classe StateNotifier qui sera transmise à notre StateNotifierProvider. +// Cette classe ne doit pas exposer d'état (state) en dehors de sa propriété "state", // ce qui signifie que pas de public getteurs/properietés! -// Les méthodes publiques de cette classe seront celles qui permettront à l'interface +// Les méthodes publiques de cette classe seront celles qui permettront à l'interface // utilisateur de modifier l'état (state). class TodosNotifier extends StateNotifier> { // On initialise la liste des todos à une liste vide. - TodosNotifier(): super([]); + TodosNotifier() : super([]); // Permettre à l'interface utilisateur d'ajouter des todos. void addTodo(Todo todo) { // Comme notre état est immuable, nous n'avons pas le droit de faire `state.add(todo)`. - // Au lieu de cela, on doit créer une nouvelle liste de tâches qui contient + // Au lieu de cela, on doit créer une nouvelle liste de tâches qui contient // les éléments précédents et le nouveau. // L'utilisation de spread de Dart est utile ici ! state = [...state, todo]; - // Il n'est pas nécessaire d'appeler "notifyListeners" ou quelque chose de similaire. + // Il n'est pas nécessaire d'appeler "notifyListeners" ou quelque chose de similaire. // L'appel à "state =" reconstruira automatiquement l'interface utilisateur si nécessaire. } // Autorisation de supprimer les todos void removeTodo(String todoId) { - // Encore une fois, notre état est immuable. + // Encore une fois, notre état est immuable. // Donc on crée une nouvelle liste au lieu de changer la liste existante. state = [ for (final todo in state) @@ -61,8 +65,8 @@ class TodosNotifier extends StateNotifier> { for (final todo in state) // on marque seulement le todo correspondant comme terminé if (todo.id == todoId) - // Encore une fois, puisque notre état est immuable, - // nous devons faire une copie du todo. Nous utilisons la méthode `copyWith` + // Encore une fois, puisque notre état est immuable, + // nous devons faire une copie du todo. Nous utilisons la méthode `copyWith` // implémentée précédemment pour y parvenir. todo.copyWith(completed: !todo.completed) else @@ -72,8 +76,8 @@ class TodosNotifier extends StateNotifier> { } } -// Enfin, on utilise le StateNotifierProvider pour permettre à l'interface utilisateur +// Enfin, on utilise le StateNotifierProvider pour permettre à l'interface utilisateur // d'interagir avec notre classe TodosNotifier. final todosProvider = StateNotifierProvider>((ref) { return TodosNotifier(); -}); \ No newline at end of file +}); diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart index 016ba782b..c3c0d8fd3 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart @@ -22,10 +22,11 @@ class TodoListView extends ConsumerWidget { CheckboxListTile( value: todo.completed, // En appuyant sur le todo, on modifie son état d'achèvement. - onChanged: (value) => ref.read(todosProvider.notifier).toggle(todo.id), + onChanged: (value) => + ref.read(todosProvider.notifier).toggle(todo.id), title: Text(todo.description), ), ], ); } -} \ No newline at end of file +} diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart index 128337612..b75e1435a 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart @@ -22,5 +22,5 @@ Widget build(BuildContext context, WidgetRef ref) { ], ), /* SNIPPET END */ - ]); + ],); } diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart index bb48b1107..ceaaeb4f1 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; void main() { runApp(const ProviderScope(child: MyApp())); diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart index 38f28bb13..0d5ff387e 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart @@ -1,4 +1,4 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'dropdown.dart'; diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart index 6725d0282..b74c3adf8 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart @@ -21,4 +21,4 @@ final productsProvider = Provider>((ref) { case ProductSortType.price: return _products.sorted((a, b) => a.price.compareTo(b.price)); } -}); \ No newline at end of file +}); diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart index 825df6d0f..3cd8e6878 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -20,4 +21,4 @@ class HomeView extends ConsumerWidget { ), ); } -} \ No newline at end of file +} diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart index 37d6d4a19..246eec6c3 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -13,11 +14,12 @@ class HomeView extends ConsumerWidget { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () { - // On et à jour l'état à partir de la valeur précédente, + // On et à jour l'état à partir de la valeur précédente, // on fini par lire le provider deux fois ! - ref.read(counterProvider.notifier).state = ref.read(counterProvider.notifier).state + 1; + ref.read(counterProvider.notifier).state = + ref.read(counterProvider.notifier).state + 1; }, ), ); } -} \ No newline at end of file +} diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart index 18817b776..a006cf77d 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:shared_preferences/shared_preferences.dart'; class LoadingScreen extends StatelessWidget { @@ -31,7 +32,7 @@ final sharedPreferencesProvider = Future main() async { // Show a loading indicator before running the full app (optional) // The platform's loading screen will be used while awaiting if you omit this. - runApp(const LoadingScreen()); + runApp(const ProviderScope(child: LoadingScreen())); // Get the instance of shared preferences final prefs = await SharedPreferences.getInstance(); diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/combining_providers.mdx b/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/combining_providers.mdx index 2298e4d02..e3aacdf73 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/combining_providers.mdx +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/combining_providers.mdx @@ -158,6 +158,39 @@ You can do so by using the [ProviderContainer] class: ```dart final repositoryProvider = Provider((ref) => Repository(ref)); +class Repository { + Repository(this.ref); + + final Ref ref; +} +``` +L'unica differenza che comporta passare `ref.read` è la scrittura di meno codice, inoltre, +garantisce che il nostro oggetto non utilizzi mai `ref.watch`. +::: + +:::danger **NON** usare [read] dentro al body di un provider + +```dart +final myProvider = Provider((ref) { + // É cattiva pratica chiamare `read` qui. + final value = ref.read(anotherProvider); +}); +``` +Se hai utilizzato [read] come tentativo di evitare rebuilds non volute del tuo oggetto, +consulta [Il mio provider si aggiorna troppo spesso, cosa posso fare?](#il-mio-provider-si-aggiorna-troppo-spesso-cosa-posso-fare) +::: + +### Come testare un oggetto che riceve [read] come parametro del suo costruttore? + +Se stai usando il pattern descritto in [Posso leggere un provider senza ascoltarlo?](#posso-leggere-un-provider-senza-ascoltarlo), +ti starai chiedendo come scrivere dei test per il tuo oggetto. + +In questo scenario, considera testare il provider direttamente invece dell'oggetto puro. +Puoi fare ciò usando la classe [ProviderContainer]: + +```dart +final repositoryProvider = Provider((ref) => Repository(ref.read)); + test('fetches catalog', () async { final container = ProviderContainer(); addTearDown(container.dispose); diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart index 40f8120e6..8ab5ecbae 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart index 9aa1903e5..a0358cdfd 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -23,36 +24,37 @@ class Home extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( - body: Column( - children: [ - ProviderScope( - /// Just specify which provider you want to have a copy of in the subtree - /// - /// Note that dependant providers such as [adjustedCountProvider] will - /// also be copied for this subtree. If that is not the behavior you want, - /// consider using families instead - overrides: [counterProvider], - child: const CounterDisplay(), - ), - ProviderScope( - // You can change the provider's behavior in a particular subtree - overrides: [counterProvider.overrideWith((ref) => 1)], - child: const CounterDisplay(), - ), - ProviderScope( - overrides: [ - counterProvider, - // You can also change dependent provider's behaviors - adjustedCountProvider.overrideWith( - (ref) => ref.watch(counterProvider) * 3, - ), - ], - child: const CounterDisplay(), - ), - // This particular display will use the provider state from the root ProviderScope - const CounterDisplay(), - ], - )); + body: Column( + children: [ + ProviderScope( + /// Just specify which provider you want to have a copy of in the subtree + /// + /// Note that dependant providers such as [adjustedCountProvider] will + /// also be copied for this subtree. If that is not the behavior you want, + /// consider using families instead + overrides: [counterProvider], + child: const CounterDisplay(), + ), + ProviderScope( + // You can change the provider's behavior in a particular subtree + overrides: [counterProvider.overrideWith((ref) => 1)], + child: const CounterDisplay(), + ), + ProviderScope( + overrides: [ + counterProvider, + // You can also change dependent provider's behaviors + adjustedCountProvider.overrideWith( + (ref) => ref.watch(counterProvider) * 3, + ), + ], + child: const CounterDisplay(), + ), + // This particular display will use the provider state from the root ProviderScope + const CounterDisplay(), + ], + ), + ); } } diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart index d17f15f02..4d171982b 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -27,23 +28,24 @@ class Home extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( - body: Column( - children: [ - // This counter will have a primary color of green - Theme( - data: Theme.of(context).copyWith(primaryColor: Colors.green), - child: const CounterDisplay(), - ), - // This counter will have a primary color of blue - const CounterDisplay(), - ElevatedButton( - onPressed: () { - ref.read(counterProvider.notifier).state++; - }, - child: const Text('Increment Count'), - ), - ], - )); + body: Column( + children: [ + // This counter will have a primary color of green + Theme( + data: Theme.of(context).copyWith(primaryColor: Colors.green), + child: const CounterDisplay(), + ), + // This counter will have a primary color of blue + const CounterDisplay(), + ElevatedButton( + onPressed: () { + ref.read(counterProvider.notifier).state++; + }, + child: const Text('Increment Count'), + ), + ], + ), + ); } } diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart index 2d25bb8e8..35cf7a54f 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart @@ -16,36 +16,36 @@ final todoListProvider = FutureProvider>((ref) => []); void main() { /* SNIPPET START */ -test('override repositoryProvider', () async { - final container = ProviderContainer( - overrides: [ - // Override the behavior of repositoryProvider to return - // FakeRepository instead of Repository. - /* highlight-start */ - repositoryProvider.overrideWithValue(FakeRepository()) - /* highlight-end */ - // We do not have to override `todoListProvider`, it will automatically - // use the overridden repositoryProvider - ], - ); - - // The first read if the loading state - expect( - container.read(todoListProvider), - const AsyncValue>.loading(), - ); - - /// Wait for the request to finish - await container.read(todoListProvider.future); - - // Exposes the data fetched - expect(container.read(todoListProvider).value, [ - isA() - .having((s) => s.id, 'id', '42') - .having((s) => s.label, 'label', 'Hello world') - .having((s) => s.completed, 'completed', false), - ]); -}); + test('override repositoryProvider', () async { + final container = ProviderContainer( + overrides: [ + // Sovrascrive il comportamento di repositoryProvider per restituire + // FakeRepository al posto di Repository. + /* highlight-start */ + repositoryProvider.overrideWithValue(FakeRepository()), + /* highlight-end */ + // Non dobbiamo sovrascrivere `todoListProvider`, + // utilizzerà automaticamente il repositoryProvider sovrascritto + ], + ); + + // The first read if the loading state + expect( + container.read(todoListProvider), + const AsyncValue>.loading(), + ); + + /// Wait for the request to finish + await container.read(todoListProvider.future); + + // Exposes the data fetched + expect(container.read(todoListProvider).value, [ + isA() + .having((s) => s.id, 'id', '42') + .having((s) => s.label, 'label', 'Hello world') + .having((s) => s.completed, 'completed', false), + ]); + }); /* SNIPPET END */ } diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart index 6685a1bdb..e6a322735 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart @@ -18,23 +18,21 @@ class FakeRepository {} void main() { /* SNIPPET START */ - -testWidgets('override repositoryProvider', (tester) async { - await tester.pumpWidget( - ProviderScope( - overrides: [ - // Override the behavior of repositoryProvider to return - // FakeRepository instead of Repository. - /* highlight-start */ - repositoryProvider.overrideWithValue(FakeRepository()) - /* highlight-end */ - // We do not have to override `todoListProvider`, it will automatically - // use the overridden repositoryProvider - ], - child: MyApp(), - ), - ); -}); - + testWidgets('override repositoryProvider', (tester) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + // Sovrascrive il comportamento di repositoryProvider per restituire + // FakeRepository al posto di Repository. + /* highlight-start */ + repositoryProvider.overrideWithValue(FakeRepository()), + /* highlight-end */ + // Non dobbiamo sovrascrivere `todoListProvider`, + // utilizzerà automaticamente il repositoryProvider sovrascritto + ], + child: MyApp(), + ), + ); + }); /* SNIPPET END */ } diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart index 0f79b4ee8..e75b5747f 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -56,24 +55,27 @@ void main() { await tester.pumpWidget( ProviderScope( overrides: [ - repositoryProvider.overrideWithValue(FakeRepository()) + repositoryProvider.overrideWithValue(FakeRepository()), ], // Our application, which will read from todoListProvider to display the todo-list. // You may extract this into a MyApp widget child: MaterialApp( home: Scaffold( - body: Consumer(builder: (context, ref, _) { - final todos = ref.watch(todoListProvider); - // The list of todos is loading or in error - if (todos.asData == null) { - return const CircularProgressIndicator(); - } - return ListView( - children: [ - for (final todo in todos.asData!.value) TodoItem(todo: todo) - ], - ); - }), + body: Consumer( + builder: (context, ref, _) { + final todos = ref.watch(todoListProvider); + // The list of todos is loading or in error + if (todos.asData == null) { + return const CircularProgressIndicator(); + } + return ListView( + children: [ + for (final todo in todos.asData!.value) + TodoItem(todo: todo), + ], + ); + }, + ), ), ), ), @@ -96,4 +98,4 @@ void main() { .having((s) => s.todo.completed, 'todo.completed', false), ]); }); -} \ No newline at end of file +} diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart index 6995de9b8..d78668653 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:riverpod/riverpod.dart'; diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart index f76abe181..a3044da37 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; /* SNIPPET START */ @@ -24,7 +25,7 @@ class MyApp extends StatelessWidget { onPressed: () => ref.read(counterProvider.notifier).state++, child: Text('$counter'), ); - }), + },), ); } } diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/family.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/family.dart index 21c7aba8c..5d142c144 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/family.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/family.dart @@ -6,7 +6,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../first_request/raw/activity.dart'; -FutureOr fetchActivity(String activityType) => throw UnimplementedError(); +FutureOr fetchActivity(String activityType) => + throw UnimplementedError(); /* SNIPPET START */ // Un provider "funzionale" @@ -28,8 +29,8 @@ final activityProvider2 = AsyncNotifierProvider.autoDispose // Quando si usa ".family" con i notifier abbiamo bisogno di cambiare la sottoclasse del notifier: // AsyncNotifier -> FamilyAsyncNotifier -// AutoDisposeAsyncNotifier -> AutoDisposeFamilyAsyncNotifier -class ActivityNotifier extends AutoDisposeFamilyAsyncNotifier { +// AsyncNotifier -> FamilyAsyncNotifier +class ActivityNotifier extends FamilyAsyncNotifier { /// Gli argomenti della famiglia sono passati al metodo build e accessibili tramite this.arg @override Future build(String activityType) async { diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo.dart index 8e19ed0cc..9d40c436a 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo.dart @@ -39,7 +39,8 @@ class _ExampleState extends ConsumerState { builder: (context, snapshot) { // Calcoliamo se c'è uno stato di errore o meno. // Controlliamo qui lo stato di ConnectionState per gestire quando l'operazione viene ripetuta. - final isErrored = snapshot.hasError && snapshot.connectionState != ConnectionState.waiting; + final isErrored = snapshot.hasError && + snapshot.connectionState != ConnectionState.waiting; return Row( children: [ @@ -67,11 +68,11 @@ class _ExampleState extends ConsumerState { if (snapshot.connectionState == ConnectionState.waiting) ...[ const SizedBox(width: 8), const CircularProgressIndicator(), - ] + ], ], ); }, ); } } -/* SNIPPET END */ \ No newline at end of file +/* SNIPPET END */ diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo_hooks.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo_hooks.dart index 17e20ca22..ab199202b 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo_hooks.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo_hooks.dart @@ -34,7 +34,8 @@ class Example extends HookConsumerWidget { // Calcoliamo se c'è uno stato di errore o meno. // Controlliamo qui lo stato di ConnectionState per gestire quando l'operazione viene ripetuta. - final isErrored = snapshot.hasError && snapshot.connectionState != ConnectionState.waiting; + final isErrored = snapshot.hasError && + snapshot.connectionState != ConnectionState.waiting; return Row( children: [ @@ -60,9 +61,9 @@ class Example extends HookConsumerWidget { if (snapshot.connectionState == ConnectionState.waiting) ...[ const SizedBox(width: 8), const CircularProgressIndicator(), - ] + ], ], ); } } -/* SNIPPET END */ \ No newline at end of file +/* SNIPPET END */ diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier.dart index d02e936d0..51561cec5 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier.dart @@ -24,14 +24,15 @@ class Todo { /* SNIPPET START */ // Ora usiamo AsyncNotifierProvider invece di FutureProvider -final todoListProvider = AsyncNotifierProvider.autoDispose>( +final todoListProvider = + AsyncNotifierProvider.autoDispose>( TodoList.new, ); // Usiamo un AsyncNotifier perché la nostra logica è asincrona. -// Più nello specifico, avremo di AutoDisposeAsyncNotifier +// Più nello specifico, avremo di AsyncNotifier // per fruire del modificatore "autoDispose". -class TodoList extends AutoDisposeAsyncNotifier> { +class TodoList extends AsyncNotifier> { @override Future> build() async { // La logica che in precedenza avevamo nel nostro FutureProvider ora è nel metodo di build. diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier_add_todo.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier_add_todo.dart index 581c9fc81..b2ddf3a1f 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier_add_todo.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier_add_todo.dart @@ -7,12 +7,13 @@ import 'package:http/http.dart' as http; import 'todo_list_notifier.dart'; -final todoListProvider = AsyncNotifierProvider.autoDispose>( +final todoListProvider = + AsyncNotifierProvider.autoDispose>( TodoList.new, ); /* SNIPPET START */ -class TodoList extends AutoDisposeAsyncNotifier> { +class TodoList extends AsyncNotifier> { @override Future> build() async => [/* ... */]; diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/testing.mdx b/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/testing.mdx index d9ff23e0b..0e8fb6e51 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/testing.mdx +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/essentials/testing.mdx @@ -3,7 +3,6 @@ title: Testare i tuoi provider --- import { AutoSnippet, When } from "../../../../../src/components/CodeSnippet"; -import createContainer from "!!raw-loader!/docs/essentials/testing/create_container.dart"; import unitTest from "!!raw-loader!/docs/essentials/testing/unit_test.dart"; import widgetTest from "!!raw-loader!/docs/essentials/testing/widget_test.dart"; import fullWidgetTest from "!!raw-loader!/docs/essentials/testing/full_widget_test.dart"; @@ -43,13 +42,6 @@ I test unitari sono definit usando la funzione `test` da [package:test](https:// La differenza principale con qualsiasi altro test è che creeremo un oggetto `ProviderContainer`. Questo oggetto permetterà al nostro test di interagire con i provider -Si consiglia di creare un'utilità di test sia per la creazione che per l'eliminazione -di un oggetto `ProviderContainer`: - - - -Successivamente, possiamo definire un `test` utilizzando questa utilità: - Ora che abbiamo un ProviderContainer possiamo utilizzarlo per leggere i provider usando: diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/migration/from_change_notifier.mdx b/website/i18n/it/docusaurus-plugin-content-docs/current/migration/from_change_notifier.mdx index 05ac7c1b3..1befb4006 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/migration/from_change_notifier.mdx +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/migration/from_change_notifier.mdx @@ -69,7 +69,7 @@ Non è necessario pensare ai nomi corretti delle classi e alle loro API *specifi `@riverpod` ti chiede solamente di scrivere una classe con il suo tipo da restituire, e questo basta. ::: -Tecnicamente, la mossa migliore qui è definire un `AutoDisposeAsyncNotifier>`, +Tecnicamente, la mossa migliore qui è definire un `AsyncNotifier>`, che coprirà tutti i requisiti richiesti. Scriviamo per prima del pseudocodice. diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/migration/from_state_notifier.mdx b/website/i18n/it/docusaurus-plugin-content-docs/current/migration/from_state_notifier.mdx index 96d6e1411..e0cb0444c 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/migration/from_state_notifier.mdx +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/migration/from_state_notifier.mdx @@ -76,8 +76,8 @@ Di seguito l'esempio precedente riscritto con le nuove API `AsyncNotifier`: Qui è facile osservare come `AsyncNotifer` sia un `FutureProvider` con dei metodi. `AsyncNotifer` viene fornito con una serie di utilità e getters che `StateNotifier` non possiede, come -[`future`](https://pub.dev/documentation/riverpod/latest/riverpod/AutoDisposeAsyncNotifier/future.html) e -[`update`](https://pub.dev/documentation/riverpod/latest/riverpod/AutoDisposeAsyncNotifier/update.html). +[`future`](https://pub.dev/documentation/riverpod/latest/riverpod/AsyncNotifier/future.html) e +[`update`](https://pub.dev/documentation/riverpod/latest/riverpod/AsyncNotifier/update.html). Ciò ci permette di scrivere della logica più semplice quando gestiamo mutazioni asincrone e side-effects. Vedere anche . @@ -104,9 +104,9 @@ Addentriamoci ed evidenziamo più differenze e somiglianze. Un'altra importante differenza è come sono gestite le family e la funzionalità di auto dispose con le nuove API. `Notifier` ha le sue personali controparti di `.family` e `.autoDispose`, come `FamilyNotifier` -e `AutoDisposeNotifier`. +e `Notifier`. Come sempre, queste modifiche possono essere combinate (aka `AutoDisposeFamilyNotifier`). -`AsyncNotifer` ha le sue controparti asincrone equivalenti, (`AutoDisposeFamilyAsyncNotifier`). +`AsyncNotifer` ha le sue controparti asincrone equivalenti, (`FamilyAsyncNotifier`). Questi modificatori sono esplicitamente dichiarati dentro la classe; qualsiasi parametro è iniettato direttamente nel metodo `build` in modo tale da essere disponibile alla logica di inizializzazione. diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/migration/utils.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/migration/utils.dart index e515b6f94..37d1d7f83 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/migration/utils.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/migration/utils.dart @@ -1,23 +1,23 @@ import 'dart:math' as math; -import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; -final randomProvider = Provider((ref) { +part 'utils.g.dart'; + +@riverpod +int random(Ref ref) { return math.Random().nextInt(6); -}); +} -final taskTrackerProvider = Provider((ref) { - return TaskTrackerRepo(); -}); +@riverpod +TaskTrackerRepo taskTracker(Ref ref) => TaskTrackerRepo(); class TaskTrackerRepo { Future fix({required String id, required int fixed}) async => 0; } -final durationProvider = Provider((ref) { - return Duration.zero; -}); +@riverpod +Duration duration(Ref ref) => Duration.zero; -final availableWaterProvider = Provider((ref) { - return 40; -}); +@riverpod +int availableWater(Ref ref) => 40; diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart index d766920b1..a88c25c71 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart index 9b93e9185..bf39558a6 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart @@ -8,11 +8,13 @@ import 'completed_todos/completed_todos.dart'; Widget build() { return /* SNIPPET START */ -Consumer(builder: (context, ref, child) { - final completedTodos = ref.watch(completedTodosProvider); - // TODO show the todos using a ListView/GridView/.../* SKIP */ - return Container(); - /* SKIP END */ -}); + Consumer( + builder: (context, ref, child) { + final completedTodos = ref.watch(completedTodosProvider); + // TODO mostrare i todo usando ListView/GridView/.../* SKIP */ + return Container(); + /* SKIP END */ + }, + ); /* SNIPPET END */ } diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart index 1318bbcbf..f39051948 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -7,7 +7,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; // We could also use packages like Freezed to help with the implementation. @immutable class Todo { - const Todo({required this.id, required this.description, required this.completed}); + const Todo({ + required this.id, + required this.description, + required this.completed, + }); // All properties should be `final` on our class. final String id; @@ -31,7 +35,7 @@ class Todo { // The public methods on this class will be what allow the UI to modify the state. class TodosNotifier extends StateNotifier> { // We initialize the list of todos to an empty list - TodosNotifier(): super([]); + TodosNotifier() : super([]); // Let's allow the UI to add todos. void addTodo(Todo todo) { @@ -75,4 +79,4 @@ class TodosNotifier extends StateNotifier> { // our TodosNotifier class. final todosProvider = StateNotifierProvider>((ref) { return TodosNotifier(); -}); \ No newline at end of file +}); diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart index 8722a542f..6cc8feff1 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart @@ -22,7 +22,8 @@ class TodoListView extends ConsumerWidget { CheckboxListTile( value: todo.completed, // When tapping on the todo, change its completed status - onChanged: (value) => ref.read(todosProvider.notifier).toggle(todo.id), + onChanged: (value) => + ref.read(todosProvider.notifier).toggle(todo.id), title: Text(todo.description), ), ], diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart index 189ff3c6a..31566a52a 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart @@ -21,5 +21,5 @@ DropdownButton( ], ), /* SNIPPET END */ - ]); + ],); } diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart index 4c598055f..28eb1f863 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; void main() { runApp(const ProviderScope(child: MyApp())); diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart index 60a5f60c1..d42eb8cc5 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart @@ -1,4 +1,4 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'dropdown.dart'; diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart index 6725d0282..b74c3adf8 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart @@ -21,4 +21,4 @@ final productsProvider = Provider>((ref) { case ProductSortType.price: return _products.sorted((a, b) => a.price.compareTo(b.price)); } -}); \ No newline at end of file +}); diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart index 825df6d0f..3cd8e6878 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -20,4 +21,4 @@ class HomeView extends ConsumerWidget { ), ); } -} \ No newline at end of file +} diff --git a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart index c029e0730..58f866fc9 100644 --- a/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart +++ b/website/i18n/it/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -15,9 +16,10 @@ class HomeView extends ConsumerWidget { onPressed: () { // We're updating the state from the previous value, we ended-up reading // the provider twice! - ref.read(counterProvider.notifier).state = ref.read(counterProvider.notifier).state + 1; + ref.read(counterProvider.notifier).state = + ref.read(counterProvider.notifier).state + 1; }, ), ); } -} \ No newline at end of file +} diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart index d6e0a046d..610ec29b1 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook.dart index fe3d6ac4f..6cfa75273 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook.dart @@ -5,26 +5,25 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'reading_counter.dart'; - class HomeView extends HookConsumerWidget { const HomeView({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { -return + return /* SNIPPET START */ -Scaffold( - body: HookConsumer( - builder: (context, ref, child) { - // HookConsumerWidget と同様にフックが使えます。 - final state = useState(0); + Scaffold( + body: HookConsumer( + builder: (context, ref, child) { + // HookConsumerWidget と同様にフックが使えます。 + final state = useState(0); - // `ref` オブジェクトを使ってプロバイダを監視することもできます。 - final counter = ref.watch(counterProvider); - return Text('$counter'); - }, - ), -); + // `ref` オブジェクトを使ってプロバイダを監視することもできます。 + final counter = ref.watch(counterProvider); + return Text('$counter'); + }, + ), + ); /* SNIPPET END */ } -} \ No newline at end of file +} diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook_widget.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook_widget.dart index 0668b2510..8fd206890 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook_widget.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook_widget.dart @@ -19,4 +19,4 @@ class HomeView extends HookConsumerWidget { final counter = ref.watch(counterProvider); return Text('$counter'); } -} \ No newline at end of file +} diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_consumer_stateful_widget.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_consumer_stateful_widget.dart index 8fd00f4d8..ab7d1bad4 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_consumer_stateful_widget.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_consumer_stateful_widget.dart @@ -25,4 +25,4 @@ class HomeViewState extends ConsumerState { final counter = ref.watch(counterProvider); return Text('$counter'); } -} \ No newline at end of file +} diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart index 16683eff8..3640d66a6 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; final repositoryProvider = Provider((ref) => Repository()); diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart index 4ed90e7af..4f6829a9e 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; // ignore_for_file: omit_local_variable_types, avoid_types_on_closure_parameters, avoid_print import 'package:riverpod/riverpod.dart'; @@ -12,4 +13,4 @@ final anotherProvider = Provider((ref) { print('The counter changed $newCount'); }); // ... -}); \ No newline at end of file +}); diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart index 066565b67..0d94f0d08 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; + import 'reading_counter.dart'; /* SNIPPET START */ @@ -16,7 +18,7 @@ class HomeView extends ConsumerWidget { ref.listen(counterProvider, (int? previousCount, int newCount) { print('The counter changed $newCount'); }); - + return Container(); } -} \ No newline at end of file +} diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_read.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_read.dart index e9afa56c4..9f9e05797 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_read.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_read.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'reading_counter.dart'; @@ -23,4 +24,4 @@ class HomeView extends ConsumerWidget { ), ); } -} \ No newline at end of file +} diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart index 6c3b9cd94..3bbcdb884 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -14,4 +15,4 @@ Widget build(BuildContext context, WidgetRef ref) { onPressed: () => counter.state++, child: const Text('button'), ); -} \ No newline at end of file +} diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart index 34104aead..95bdab6c5 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -13,4 +14,4 @@ Widget build(BuildContext context, WidgetRef ref) { onPressed: () => counter.state++, child: const Text('button'), ); -} \ No newline at end of file +} diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart index ff93df1b5..d436ec8db 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart @@ -1,6 +1,7 @@ // ignore_for_file: omit_local_variable_types import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; enum FilterType { none, @@ -12,13 +13,14 @@ abstract class Todo { } class TodoList extends StateNotifier> { - TodoList(): super([]); + TodoList() : super([]); } /* SNIPPET START */ final filterTypeProvider = StateProvider((ref) => FilterType.none); -final todosProvider = StateNotifierProvider>((ref) => TodoList()); +final todosProvider = + StateNotifierProvider>((ref) => TodoList()); final filteredTodoListProvider = Provider((ref) { // フィルタの種類と Todo リストを取得、監視する @@ -33,4 +35,4 @@ final filteredTodoListProvider = Provider((ref) { // フィルタ未適用の Todo リストをそのまま返す return todos; } -}); \ No newline at end of file +}); diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart index fc1744463..85c00dafc 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; enum FilterType { none, @@ -13,7 +14,7 @@ abstract class Todo { } class TodoList extends StateNotifier> { - TodoList(): super([]); + TodoList() : super([]); } /* SNIPPET START */ @@ -30,4 +31,4 @@ class HomeView extends ConsumerWidget { return Text('$counter'); } -} \ No newline at end of file +} diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart index 66c630717..94d77b5e3 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart index a8c896a18..bd968a444 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart @@ -22,7 +22,7 @@ test('override repositoryProvider', () async { // repositoryProvider の挙動をオーバーライドして // Repository の代わりに FakeRepository を戻り値とする /* highlight-start */ - repositoryProvider.overrideWithValue(FakeRepository()) + repositoryProvider.overrideWithValue(FakeRepository()), /* highlight-end */ // `todoListProvider` はオーバーライドされた repositoryProvider を // 自動的に利用することになるため、オーバーライド不要 diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart index 823aa3d30..82b2a65e6 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart @@ -26,7 +26,7 @@ testWidgets('override repositoryProvider', (tester) async { // repositoryProvider の挙動をオーバーライドして // Repository の代わりに FakeRepository を戻り値とする /* highlight-start */ - repositoryProvider.overrideWithValue(FakeRepository()) + repositoryProvider.overrideWithValue(FakeRepository()), /* highlight-end */ // `todoListProvider` はオーバーライドされた repositoryProvider を // 自動的に利用することになるため、オーバーライド不要 diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart index ba5140848..31fd81da5 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -56,24 +55,27 @@ void main() { await tester.pumpWidget( ProviderScope( overrides: [ - repositoryProvider.overrideWithValue(FakeRepository()) + repositoryProvider.overrideWithValue(FakeRepository()), ], // todoListProvider の値を監視して Todo リストを表示するアプリ // 以下を抽出して MyApp ウィジェットとしても可 child: MaterialApp( home: Scaffold( - body: Consumer(builder: (context, ref, _) { - final todos = ref.watch(todoListProvider); - // Todo リストのステートが loading か error の場合 - if (todos.asData == null) { - return const CircularProgressIndicator(); - } - return ListView( - children: [ - for (final todo in todos.asData!.value) TodoItem(todo: todo) - ], - ); - }), + body: Consumer( + builder: (context, ref, _) { + final todos = ref.watch(todoListProvider); + // Todo リストのステートが loading か error の場合 + if (todos.asData == null) { + return const CircularProgressIndicator(); + } + return ListView( + children: [ + for (final todo in todos.asData!.value) + TodoItem(todo: todo), + ], + ); + }, + ), ), ), ), @@ -96,4 +98,4 @@ void main() { .having((s) => s.todo.completed, 'todo.completed', false), ]); }); -} \ No newline at end of file +} diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart index 59e077459..b5e5b8024 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:riverpod/riverpod.dart'; diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart index 0cce126ca..7515cacf6 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; /* SNIPPET START */ @@ -24,7 +25,7 @@ class MyApp extends StatelessWidget { onPressed: () => ref.read(counterProvider.notifier).state++, child: Text('$counter'), ); - }), + },), ); } } diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/dart_hello_world/main.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/dart_hello_world/main.dart index d2e9435dc..4a9361d1d 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/dart_hello_world/main.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/dart_hello_world/main.dart @@ -1,8 +1,7 @@ -// ignore_for_file: avoid_print +// ignore_for_file: avoid_print, unreachable_from_main /* SNIPPET START */ -import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'main.g.dart'; diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/codegen.yaml b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/codegen.yaml index 864fd5f97..c2e91a21b 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/codegen.yaml +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/codegen.yaml @@ -1,6 +1,6 @@ name: my_app_name environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: riverpod: ^2.1.3 diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/raw.yaml b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/raw.yaml index d0a5ecbd2..3534eed2b 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/raw.yaml +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/dart_pubspec/raw.yaml @@ -1,6 +1,6 @@ name: my_app_name environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: riverpod: ^2.1.3 \ No newline at end of file diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/hello_world/main.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/hello_world/main.dart index ea2b1fda1..11c0f9cb3 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/hello_world/main.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/hello_world/main.dart @@ -1,4 +1,4 @@ -// ignore_for_file: use_key_in_widget_constructors, omit_local_variable_types +// ignore_for_file: use_key_in_widget_constructors, omit_local_variable_types, unreachable_from_main /* SNIPPET START */ diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/codegen.yaml b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/codegen.yaml index 098fd4088..71cdaeab1 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/codegen.yaml +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/codegen.yaml @@ -1,6 +1,6 @@ name: my_app_name environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=3.0.0" dependencies: diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks.yaml b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks.yaml index cb19518e6..fec867b09 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks.yaml +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks.yaml @@ -1,6 +1,6 @@ name: my_app_name environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=3.0.0" dependencies: diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks_codegen.yaml b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks_codegen.yaml index e7f32ee6a..4d432b74a 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks_codegen.yaml +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/hooks_codegen.yaml @@ -1,6 +1,6 @@ name: my_app_name environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=3.0.0" dependencies: diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/raw.yaml b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/raw.yaml index a00be715d..83b86feed 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/raw.yaml +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/getting_started/pubspec/raw.yaml @@ -1,6 +1,6 @@ name: my_app_name environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=3.0.0" dependencies: diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart index 2dc19b955..fe364b71c 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart index 58ed9ed3e..79c41f21b 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart @@ -1,6 +1,7 @@ // A provider that controls the current page import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/todo.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/todo.dart index 9f400c8e9..f6c7ecfc3 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/todo.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/todo.dart @@ -1,6 +1,6 @@ // ignore_for_file: avoid_positional_boolean_parameters -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart index 8f81a12cf..a93b55e17 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart @@ -13,6 +13,6 @@ Consumer(builder: (context, ref, child) { // TODO ListView/GridView/... を使って Todo リストを表示する /* SKIP */ return Container(); /* SKIP END */ -}); +},); /* SNIPPET END */ } diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart index 0f1f4a503..6458b031f 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart @@ -1,6 +1,7 @@ // A provider that controls the current page import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart index f57f590f8..f34724966 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -7,7 +7,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; // ここは Freezed のようなパッケージを利用してイミュータブルにしても OK です。 @immutable class Todo { - const Todo({required this.id, required this.description, required this.completed}); + const Todo({ + required this.id, + required this.description, + required this.completed, + }); // イミュータブルなクラスのプロパティはすべて `final` にする必要があります。 final String id; @@ -31,7 +35,7 @@ class Todo { // public メソッドを通じて UI 側にステートの操作を許可します。 class TodosNotifier extends StateNotifier> { // Todo リストを空のリストとして初期化します。 - TodosNotifier(): super([]); + TodosNotifier() : super([]); // Todo の追加 void addTodo(Todo todo) { @@ -46,7 +50,7 @@ class TodosNotifier extends StateNotifier> { // Todo の削除 void removeTodo(String todoId) { - // しつこいですが、ステートはイミュータブルです。 + // しつこいですが、ステートはイミュータブルです。 // そのため既存リストを変更するのではなく、新しくリストを作成する必要があります。 state = [ for (final todo in state) @@ -75,4 +79,4 @@ class TodosNotifier extends StateNotifier> { // UI 側から Todo リストを操作することを可能にします。 final todosProvider = StateNotifierProvider>((ref) { return TodosNotifier(); -}); \ No newline at end of file +}); diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart index f1e5a74a0..89a74e8a3 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart @@ -22,10 +22,11 @@ class TodoListView extends ConsumerWidget { CheckboxListTile( value: todo.completed, // 各 Todo をタップすると、完了ステータスを変更できる - onChanged: (value) => ref.read(todosProvider.notifier).toggle(todo.id), + onChanged: (value) => + ref.read(todosProvider.notifier).toggle(todo.id), title: Text(todo.description), ), ], ); } -} \ No newline at end of file +} diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart index 0634004b6..843d03d7e 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart @@ -21,5 +21,5 @@ DropdownButton( ], ), /* SNIPPET END */ - ]); + ],); } diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart index 283d4c4bd..41a3ec1dc 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; void main() { runApp(const ProviderScope(child: MyApp())); diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart index d95c25eca..44be1181f 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart @@ -1,4 +1,4 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'dropdown.dart'; diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart index 6725d0282..b74c3adf8 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart @@ -21,4 +21,4 @@ final productsProvider = Provider>((ref) { case ProductSortType.price: return _products.sorted((a, b) => a.price.compareTo(b.price)); } -}); \ No newline at end of file +}); diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart index 825df6d0f..3cd8e6878 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -20,4 +21,4 @@ class HomeView extends ConsumerWidget { ), ); } -} \ No newline at end of file +} diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart index 645be50ee..de9e5a7a8 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -15,9 +16,10 @@ class HomeView extends ConsumerWidget { onPressed: () { // 直近のステートから新たなステートを算出しようとすると、 // このようにプロバイダを2回利用してしまいがち。 - ref.read(counterProvider.notifier).state = ref.read(counterProvider.notifier).state + 1; + ref.read(counterProvider.notifier).state = + ref.read(counterProvider.notifier).state + 1; }, ), ); } -} \ No newline at end of file +} diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/advanced/select.mdx b/website/i18n/ko/docusaurus-plugin-content-docs/current/advanced/select.mdx index a7bd985e5..e11ebe6b5 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/advanced/select.mdx +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/advanced/select.mdx @@ -3,9 +3,10 @@ title: 성능 최적화하기 version: 1 --- -import { AutoSnippet } from "/src/components/CodeSnippet"; +import { AutoSnippet } from "../../../../../src/components/CodeSnippet"; import select from "/docs/advanced/select/select"; -import selectAsync from "/docs/advanced/select/select_async"; + +import selectAsync from "/docs/advanced/select/select_async"; 지금까지 살펴본 내용을 통해 이미 모든 기능을 갖춘 애플리케이션을 구축할 수 있습니다. 하지만 성능과 관련하여 궁금한 점이 있을 수 있습니다. @@ -68,4 +69,4 @@ import selectAsync from "/docs/advanced/select/select_async"; watch: " // user를 사용할 수 있을 때까지 기다렸다가 \"firstName\" 속성만 수신합니다.", todo: " // TODO 다른 항목을 가져오기위해 \"firstName\"을 사용합니다.", }} -/> +/> \ No newline at end of file diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart index 18817b776..a006cf77d 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:shared_preferences/shared_preferences.dart'; class LoadingScreen extends StatelessWidget { @@ -31,7 +32,7 @@ final sharedPreferencesProvider = Future main() async { // Show a loading indicator before running the full app (optional) // The platform's loading screen will be used while awaiting if you omit this. - runApp(const LoadingScreen()); + runApp(const ProviderScope(child: LoadingScreen())); // Get the instance of shared preferences final prefs = await SharedPreferences.getInstance(); diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/combining_providers.mdx b/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/combining_providers.mdx index d7cdb6fbd..1714af6af 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/combining_providers.mdx +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/combining_providers.mdx @@ -160,6 +160,44 @@ You can do so by using the [ProviderContainer] class: ```dart final repositoryProvider = Provider((ref) => Repository(ref)); +class Repository { + Repository(this.ref); + + final Ref ref; +} +``` + +그러나 `ref.read`를 전달하면 코드가 약간 덜 장황해지고 객체가 `ref.watch`를 사용하지 않을 것입니다. +다시 말해, `ref`대신 `ref.read`를 전달하면 `ref.watch`는 사용할 수 없다는 말과 동일합니다. + +::: + +:::위험 **DON'T** : [read]를 프로바이더 내부에서 호출하지 마세요. + +```dart +final myProvider = Provider((ref) { + // 여기서 'read'를 호출하는 것은 좋지 않습니다. + final value = ref.read(anotherProvider); +}); +``` + +만약 객체의 원치 않는 재빌드을 방지하기 위해 [read]를 사용한 경우, +[프로바이더 갱신이 너무 자주일어나는데 어떻게 개선하면 좋을까요?](#프로바이더-갱신이-너무-자주일어나는데-어떻게-개선하면-좋을까요) +항목을 참고해 주세요. + +::: + +### 생성자의 매개변수로 [read]를 전달 받는 객체는 어떻게 테스트 하면 좋은가요? + +만약 [구독없이 프로바이더를 읽을 수 있나요?](#구독없이-프로바이더를-읽을-수-있나요)에서 사용한 패턴을 사용한다면, +어떻게 객체를 테스트할지 의문이 들 수 있습니다. + +이 시나리오에서는 raw 객체 대신에 프로바이더를 직접 테스트 하는 것이 좋습니다. +[ProviderContainer] 클래스를 사용하여 테스트를 진행할 수 있습니다. + +```dart +final repositoryProvider = Provider((ref) => Repository(ref.read)); + test('fetches catalog', () async { final container = ProviderContainer(); addTearDown(container.dispose); diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart index 40f8120e6..8ab5ecbae 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart index 9aa1903e5..a0358cdfd 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -23,36 +24,37 @@ class Home extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( - body: Column( - children: [ - ProviderScope( - /// Just specify which provider you want to have a copy of in the subtree - /// - /// Note that dependant providers such as [adjustedCountProvider] will - /// also be copied for this subtree. If that is not the behavior you want, - /// consider using families instead - overrides: [counterProvider], - child: const CounterDisplay(), - ), - ProviderScope( - // You can change the provider's behavior in a particular subtree - overrides: [counterProvider.overrideWith((ref) => 1)], - child: const CounterDisplay(), - ), - ProviderScope( - overrides: [ - counterProvider, - // You can also change dependent provider's behaviors - adjustedCountProvider.overrideWith( - (ref) => ref.watch(counterProvider) * 3, - ), - ], - child: const CounterDisplay(), - ), - // This particular display will use the provider state from the root ProviderScope - const CounterDisplay(), - ], - )); + body: Column( + children: [ + ProviderScope( + /// Just specify which provider you want to have a copy of in the subtree + /// + /// Note that dependant providers such as [adjustedCountProvider] will + /// also be copied for this subtree. If that is not the behavior you want, + /// consider using families instead + overrides: [counterProvider], + child: const CounterDisplay(), + ), + ProviderScope( + // You can change the provider's behavior in a particular subtree + overrides: [counterProvider.overrideWith((ref) => 1)], + child: const CounterDisplay(), + ), + ProviderScope( + overrides: [ + counterProvider, + // You can also change dependent provider's behaviors + adjustedCountProvider.overrideWith( + (ref) => ref.watch(counterProvider) * 3, + ), + ], + child: const CounterDisplay(), + ), + // This particular display will use the provider state from the root ProviderScope + const CounterDisplay(), + ], + ), + ); } } diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart index d17f15f02..4d171982b 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -27,23 +28,24 @@ class Home extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( - body: Column( - children: [ - // This counter will have a primary color of green - Theme( - data: Theme.of(context).copyWith(primaryColor: Colors.green), - child: const CounterDisplay(), - ), - // This counter will have a primary color of blue - const CounterDisplay(), - ElevatedButton( - onPressed: () { - ref.read(counterProvider.notifier).state++; - }, - child: const Text('Increment Count'), - ), - ], - )); + body: Column( + children: [ + // This counter will have a primary color of green + Theme( + data: Theme.of(context).copyWith(primaryColor: Colors.green), + child: const CounterDisplay(), + ), + // This counter will have a primary color of blue + const CounterDisplay(), + ElevatedButton( + onPressed: () { + ref.read(counterProvider.notifier).state++; + }, + child: const Text('Increment Count'), + ), + ], + ), + ); } } diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart index 2d25bb8e8..978c7f82b 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart @@ -16,36 +16,36 @@ final todoListProvider = FutureProvider>((ref) => []); void main() { /* SNIPPET START */ -test('override repositoryProvider', () async { - final container = ProviderContainer( - overrides: [ - // Override the behavior of repositoryProvider to return - // FakeRepository instead of Repository. - /* highlight-start */ - repositoryProvider.overrideWithValue(FakeRepository()) - /* highlight-end */ - // We do not have to override `todoListProvider`, it will automatically - // use the overridden repositoryProvider - ], - ); - - // The first read if the loading state - expect( - container.read(todoListProvider), - const AsyncValue>.loading(), - ); - - /// Wait for the request to finish - await container.read(todoListProvider.future); - - // Exposes the data fetched - expect(container.read(todoListProvider).value, [ - isA() - .having((s) => s.id, 'id', '42') - .having((s) => s.label, 'label', 'Hello world') - .having((s) => s.completed, 'completed', false), - ]); -}); + test('override repositoryProvider', () async { + final container = ProviderContainer( + overrides: [ + // repositoryProvider의 행위를 오버라이드하여 + // Repository 대신 FakeRepository를 반환합니다. + /* highlight-start */ + repositoryProvider.overrideWithValue(FakeRepository()), + /* highlight-end */ + // 오버라이드된 repositoryProvider를 자동적으로 사용하기 때문에 + // `todoListProvider`를 override하지 않아도 됩니다. + ], + ); + + // The first read if the loading state + expect( + container.read(todoListProvider), + const AsyncValue>.loading(), + ); + + /// Wait for the request to finish + await container.read(todoListProvider.future); + + // Exposes the data fetched + expect(container.read(todoListProvider).value, [ + isA() + .having((s) => s.id, 'id', '42') + .having((s) => s.label, 'label', 'Hello world') + .having((s) => s.completed, 'completed', false), + ]); + }); /* SNIPPET END */ } diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart index 6685a1bdb..9c1a90bf5 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart @@ -19,22 +19,22 @@ class FakeRepository {} void main() { /* SNIPPET START */ -testWidgets('override repositoryProvider', (tester) async { - await tester.pumpWidget( - ProviderScope( - overrides: [ - // Override the behavior of repositoryProvider to return - // FakeRepository instead of Repository. - /* highlight-start */ - repositoryProvider.overrideWithValue(FakeRepository()) - /* highlight-end */ - // We do not have to override `todoListProvider`, it will automatically - // use the overridden repositoryProvider - ], - child: MyApp(), - ), - ); -}); + testWidgets('override repositoryProvider', (tester) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + // repositoryProvider의 행위를 오버라이드하여 + // Repository 대신 FakeRepository를 반환합니다. + /* highlight-start */ + repositoryProvider.overrideWithValue(FakeRepository()), + /* highlight-end */ + // 오버라이드된 repositoryProvider를 자동적으로 사용하기 때문에 + // `todoListProvider`를 override하지 않아도 됩니다. + ], + child: MyApp(), + ), + ); + }); /* SNIPPET END */ } diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart index 0f79b4ee8..e75b5747f 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -56,24 +55,27 @@ void main() { await tester.pumpWidget( ProviderScope( overrides: [ - repositoryProvider.overrideWithValue(FakeRepository()) + repositoryProvider.overrideWithValue(FakeRepository()), ], // Our application, which will read from todoListProvider to display the todo-list. // You may extract this into a MyApp widget child: MaterialApp( home: Scaffold( - body: Consumer(builder: (context, ref, _) { - final todos = ref.watch(todoListProvider); - // The list of todos is loading or in error - if (todos.asData == null) { - return const CircularProgressIndicator(); - } - return ListView( - children: [ - for (final todo in todos.asData!.value) TodoItem(todo: todo) - ], - ); - }), + body: Consumer( + builder: (context, ref, _) { + final todos = ref.watch(todoListProvider); + // The list of todos is loading or in error + if (todos.asData == null) { + return const CircularProgressIndicator(); + } + return ListView( + children: [ + for (final todo in todos.asData!.value) + TodoItem(todo: todo), + ], + ); + }, + ), ), ), ), @@ -96,4 +98,4 @@ void main() { .having((s) => s.todo.completed, 'todo.completed', false), ]); }); -} \ No newline at end of file +} diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart index 6995de9b8..d78668653 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:riverpod/riverpod.dart'; diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart index f76abe181..a3044da37 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; /* SNIPPET START */ @@ -24,7 +25,7 @@ class MyApp extends StatelessWidget { onPressed: () => ref.read(counterProvider.notifier).state++, child: Text('$counter'), ); - }), + },), ); } } diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/essentials/testing.mdx b/website/i18n/ko/docusaurus-plugin-content-docs/current/essentials/testing.mdx index 095848d0f..0ab8757bf 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/essentials/testing.mdx +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/essentials/testing.mdx @@ -3,8 +3,7 @@ title: providers 테스트하기 version: 2 --- -import { AutoSnippet, When } from "/src/components/CodeSnippet"; -import createContainer from "!!raw-loader!/docs/essentials/testing/create_container.dart"; +import { AutoSnippet, When } from "../../../../../src/components/CodeSnippet"; import unitTest from "!!raw-loader!/docs/essentials/testing/unit_test.dart"; import widgetTest from "!!raw-loader!/docs/essentials/testing/widget_test.dart"; import fullWidgetTest from "!!raw-loader!/docs/essentials/testing/full_widget_test.dart"; @@ -43,17 +42,6 @@ Riverpod로 테스트를 정의할 때는 크게 두 가지 시나리오가 있 다른 테스트와 가장 큰 차이점은 `ProviderContainer` 객체를 생성한다는 점입니다. 이 객체를 사용하면 테스트가 provider와 상호 작용할 수 있습니다. -`ProviderContainer` 객체를 생성하고 폐기하기 위한 테스트 유틸리티를 만드는 것이 좋습니다: - - - 그런 다음 이 유틸리티를 사용하여 `test`를 정의할 수 있습니다: >`를 정의하는 것입니다. +기술적으로 가장 적합한 방법은 위의 모든 요구 사항을 충족하는 `AsyncNotifier>`를 정의하는 것입니다. 먼저 의사 코드를 작성해 봅시다. diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/migration/from_state_notifier.mdx b/website/i18n/ko/docusaurus-plugin-content-docs/current/migration/from_state_notifier.mdx index 477041e96..176722642 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/migration/from_state_notifier.mdx +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/migration/from_state_notifier.mdx @@ -73,8 +73,8 @@ import { AutoSnippet } from "/src/components/CodeSnippet"; 여기서 `AsyncNotifier`는 메서드가 있는 `FutureProvider`로 쉽게 볼 수 있습니다. `AsyncNotifer`에는 `StateNotifier`에는 없는 유틸리티와 게터가 함께 제공됩니다, -예를 들어 [`future`](https://pub.dev/documentation/riverpod/latest/riverpod/AutoDisposeAsyncNotifier/future.html) -및 [`update`](https://pub.dev/documentation/riverpod/latest/riverpod/AutoDisposeAsyncNotifier/update.html). +예를 들어 [`future`](https://pub.dev/documentation/riverpod/latest/riverpod/AsyncNotifier/future.html) +및 [`update`](https://pub.dev/documentation/riverpod/latest/riverpod/AsyncNotifier/update.html). 이를 통해 비동기 변이(mutations)와 부수작업(side-effects)을 처리할 때 훨씬 더 간단한 로직을 작성할 수 있습니다. 를 참고하세요. @@ -100,9 +100,9 @@ import { AutoSnippet } from "/src/components/CodeSnippet"; 또 다른 중요한 차이점은 새로운 API로 패밀리 및 자동폐기가 처리되는 방식입니다. -`Notifier`에는 `FamilyNotifier` 및 `AutoDisposeNotifier`와 같은 자체 `.family` 및 `.autoDispose` 대응 항목이 있습니다. +`Notifier`에는 `FamilyNotifier` 및 `Notifier`와 같은 자체 `.family` 및 `.autoDispose` 대응 항목이 있습니다. 항상 그렇듯이, 이러한 수정 사항을 결합할 수 있습니다 (일명 `AutoDisposeFamilyNotifier`). -`AsyncNotifer`에는 비동기 버전도 있습니다(예: `AutoDisposeFamilyAsyncNotifier`). +`AsyncNotifer`에는 비동기 버전도 있습니다(예: `FamilyAsyncNotifier`). 수정 사항(Modifications)은 클래스 내부에 명시적으로 지정(stated)됩니다; 모든 매개변수는 `build` 메서드에 직접 주입되어 초기화 로직에서 사용할 수 있습니다. diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/migration/utils.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/migration/utils.dart index e515b6f94..37d1d7f83 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/migration/utils.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/migration/utils.dart @@ -1,23 +1,23 @@ import 'dart:math' as math; -import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; -final randomProvider = Provider((ref) { +part 'utils.g.dart'; + +@riverpod +int random(Ref ref) { return math.Random().nextInt(6); -}); +} -final taskTrackerProvider = Provider((ref) { - return TaskTrackerRepo(); -}); +@riverpod +TaskTrackerRepo taskTracker(Ref ref) => TaskTrackerRepo(); class TaskTrackerRepo { Future fix({required String id, required int fixed}) async => 0; } -final durationProvider = Provider((ref) { - return Duration.zero; -}); +@riverpod +Duration duration(Ref ref) => Duration.zero; -final availableWaterProvider = Provider((ref) { - return 40; -}); +@riverpod +int availableWater(Ref ref) => 40; diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart index d766920b1..a88c25c71 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/change_notifier_provider/todos.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart index 9b93e9185..cc3c4783c 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart @@ -8,11 +8,13 @@ import 'completed_todos/completed_todos.dart'; Widget build() { return /* SNIPPET START */ -Consumer(builder: (context, ref, child) { - final completedTodos = ref.watch(completedTodosProvider); - // TODO show the todos using a ListView/GridView/.../* SKIP */ - return Container(); - /* SKIP END */ -}); + Consumer( + builder: (context, ref, child) { + final completedTodos = ref.watch(completedTodosProvider); + // TODO a ListView/GridView/...등을 사용하여 todos를 표시하기/* SKIP */ + return Container(); + /* SKIP END */ + }, + ); /* SNIPPET END */ } diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart index 1318bbcbf..f39051948 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -7,7 +7,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; // We could also use packages like Freezed to help with the implementation. @immutable class Todo { - const Todo({required this.id, required this.description, required this.completed}); + const Todo({ + required this.id, + required this.description, + required this.completed, + }); // All properties should be `final` on our class. final String id; @@ -31,7 +35,7 @@ class Todo { // The public methods on this class will be what allow the UI to modify the state. class TodosNotifier extends StateNotifier> { // We initialize the list of todos to an empty list - TodosNotifier(): super([]); + TodosNotifier() : super([]); // Let's allow the UI to add todos. void addTodo(Todo todo) { @@ -75,4 +79,4 @@ class TodosNotifier extends StateNotifier> { // our TodosNotifier class. final todosProvider = StateNotifierProvider>((ref) { return TodosNotifier(); -}); \ No newline at end of file +}); diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart index 8722a542f..0b60d4eaf 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_notifier_provider/todos_consumer.dart @@ -22,10 +22,11 @@ class TodoListView extends ConsumerWidget { CheckboxListTile( value: todo.completed, // When tapping on the todo, change its completed status - onChanged: (value) => ref.read(todosProvider.notifier).toggle(todo.id), + onChanged: (value) => + ref.read(todosProvider.notifier).toggle(todo.id), title: Text(todo.description), ), ], ); } -} \ No newline at end of file +} diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart index 189ff3c6a..31566a52a 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/connected_dropdown.dart @@ -21,5 +21,5 @@ DropdownButton( ], ), /* SNIPPET END */ - ]); + ],); } diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart index 4c598055f..28eb1f863 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; void main() { runApp(const ProviderScope(child: MyApp())); diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart index 60a5f60c1..d42eb8cc5 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/sort_provider.dart @@ -1,4 +1,4 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'dropdown.dart'; diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart index 6725d0282..b74c3adf8 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/sorted_product_provider.dart @@ -21,4 +21,4 @@ final productsProvider = Provider>((ref) { case ProductSortType.price: return _products.sorted((a, b) => a.price.compareTo(b.price)); } -}); \ No newline at end of file +}); diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart index 825df6d0f..3cd8e6878 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_once.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -20,4 +21,4 @@ class HomeView extends ConsumerWidget { ), ); } -} \ No newline at end of file +} diff --git a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart index c029e0730..58f866fc9 100644 --- a/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart +++ b/website/i18n/ko/docusaurus-plugin-content-docs/current/providers/state_provider/update_read_twice.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -15,9 +16,10 @@ class HomeView extends ConsumerWidget { onPressed: () { // We're updating the state from the previous value, we ended-up reading // the provider twice! - ref.read(counterProvider.notifier).state = ref.read(counterProvider.notifier).state + 1; + ref.read(counterProvider.notifier).state = + ref.read(counterProvider.notifier).state + 1; }, ), ); } -} \ No newline at end of file +} diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook.dart index ac6d3ef63..f5ae7e0de 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook.dart @@ -5,26 +5,25 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'reading_counter.dart'; - class HomeView extends HookConsumerWidget { const HomeView({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { -return + return /* SNIPPET START */ -Scaffold( - body: HookConsumer( - builder: (context, ref, child) { - // Мы можем использовать хуки внутри builder, как и в HookConsumerWidget - final state = useState(0); + Scaffold( + body: HookConsumer( + builder: (context, ref, child) { + // Мы можем использовать хуки внутри builder, как и в HookConsumerWidget + final state = useState(0); - // Также мы можем использовать ref для прослушивания провайдеров. - final counter = ref.watch(counterProvider); - return Text('$counter'); - }, - ), -); + // Также мы можем использовать ref для прослушивания провайдеров. + final counter = ref.watch(counterProvider); + return Text('$counter'); + }, + ), + ); /* SNIPPET END */ } -} \ No newline at end of file +} diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook_widget.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook_widget.dart index 69b5c71ce..7e4065128 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook_widget.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_consumer_hook_widget.dart @@ -19,4 +19,4 @@ class HomeView extends HookConsumerWidget { final counter = ref.watch(counterProvider); return Text('$counter'); } -} \ No newline at end of file +} diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_consumer_stateful_widget.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_consumer_stateful_widget.dart index d176a287a..9f202a8c3 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_consumer_stateful_widget.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_consumer_stateful_widget.dart @@ -21,9 +21,9 @@ class HomeViewState extends ConsumerState { @override Widget build(BuildContext context) { - // Также мы можем использовать ref внутри метода build + // Также мы можем использовать ref внутри метода build // для прослушивания провайдеров. final counter = ref.watch(counterProvider); return Text('$counter'); } -} \ No newline at end of file +} diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart index 848a31f27..815cafe73 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_counter.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; import 'package:riverpod/riverpod.dart'; final repositoryProvider = Provider((ref) => Repository()); @@ -13,7 +14,7 @@ final counterProvider = StateNotifierProvider((ref) { }); class Counter extends StateNotifier { - Counter(this.ref): super(0); + Counter(this.ref) : super(0); final Ref ref; @@ -22,4 +23,4 @@ class Counter extends StateNotifier { final repository = ref.read(repositoryProvider); repository.post('...'); } -} \ No newline at end of file +} diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart index 4ed90e7af..4f6829a9e 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_listen.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; // ignore_for_file: omit_local_variable_types, avoid_types_on_closure_parameters, avoid_print import 'package:riverpod/riverpod.dart'; @@ -12,4 +13,4 @@ final anotherProvider = Provider((ref) { print('The counter changed $newCount'); }); // ... -}); \ No newline at end of file +}); diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart index 066565b67..0d94f0d08 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_listen_build.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; + import 'reading_counter.dart'; /* SNIPPET START */ @@ -16,7 +18,7 @@ class HomeView extends ConsumerWidget { ref.listen(counterProvider, (int? previousCount, int newCount) { print('The counter changed $newCount'); }); - + return Container(); } -} \ No newline at end of file +} diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_read.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_read.dart index 957ccc2cc..1c7ed0bd2 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_read.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_read.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'reading_counter.dart'; @@ -23,4 +24,4 @@ class HomeView extends ConsumerWidget { ), ); } -} \ No newline at end of file +} diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart index 342131974..0211d4bb3 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_read_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -15,4 +16,4 @@ Widget build(BuildContext context, WidgetRef ref) { onPressed: () => counter.state++, child: const Text('button'), ); -} \ No newline at end of file +} diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart index 34104aead..95bdab6c5 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_read_notifier_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -13,4 +14,4 @@ Widget build(BuildContext context, WidgetRef ref) { onPressed: () => counter.state++, child: const Text('button'), ); -} \ No newline at end of file +} diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart index e7724c3a8..16ced4bfc 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_watch.dart @@ -1,6 +1,7 @@ // ignore_for_file: omit_local_variable_types import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; enum FilterType { none, @@ -12,13 +13,14 @@ abstract class Todo { } class TodoList extends StateNotifier> { - TodoList(): super([]); + TodoList() : super([]); } /* SNIPPET START */ final filterTypeProvider = StateProvider((ref) => FilterType.none); -final todosProvider = StateNotifierProvider>((ref) => TodoList()); +final todosProvider = + StateNotifierProvider>((ref) => TodoList()); final filteredTodoListProvider = Provider((ref) { // получение фильтра и полного списка задач @@ -33,4 +35,4 @@ final filteredTodoListProvider = Provider((ref) { // возвращает полный список всех задач return todos; } -}); \ No newline at end of file +}); diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart index 779ffb0a1..39cc348d8 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_watch_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; enum FilterType { none, @@ -13,7 +14,7 @@ abstract class Todo { } class TodoList extends StateNotifier> { - TodoList(): super([]); + TodoList() : super([]); } /* SNIPPET START */ @@ -30,4 +31,4 @@ class HomeView extends ConsumerWidget { return Text('$counter'); } -} \ No newline at end of file +} diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart index 66c630717..94d77b5e3 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/concepts/reading_watch_notifier_build.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart index f1a86304d..a6977481d 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart @@ -1,6 +1,7 @@ // Провайдер, контролирующий текущую страницу import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -20,7 +21,7 @@ class PreviousButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { // Наблюдаем за нашим провайдером - // Наш виджет больше не определяет, можно ли вернуться на + // Наш виджет больше не определяет, можно ли вернуться на // предыдущую страницу или нет /* highlight-start */ final canGoToPreviousPage = ref.watch(canGoToPreviousPageProvider); @@ -35,4 +36,4 @@ class PreviousButton extends ConsumerWidget { child: const Text('previous'), ); } -} \ No newline at end of file +} diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/provider/todo.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/provider/todo.dart index bef9aabd1..6073fa5fd 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/provider/todo.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/provider/todo.dart @@ -1,6 +1,6 @@ // ignore_for_file: avoid_positional_boolean_parameters -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart index 106bdd5af..56dd68fd9 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart @@ -1,6 +1,7 @@ // Провайдер, контролирующий текущую страницу import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -24,4 +25,4 @@ class PreviousButton extends ConsumerWidget { child: const Text('previous'), ); } -} \ No newline at end of file +} diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart b/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart index f96b8f7d3..1431292c4 100644 --- a/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/providers/state_provider/full.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; void main() { runApp(const ProviderScope(child: MyApp())); diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart index 18817b776..a006cf77d 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/async_initialization.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:shared_preferences/shared_preferences.dart'; class LoadingScreen extends StatelessWidget { @@ -31,7 +32,7 @@ final sharedPreferencesProvider = Future main() async { // Show a loading indicator before running the full app (optional) // The platform's loading screen will be used while awaiting if you omit this. - runApp(const LoadingScreen()); + runApp(const ProviderScope(child: LoadingScreen())); // Get the instance of shared preferences final prefs = await SharedPreferences.getInstance(); diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart index 40f8120e6..8ab5ecbae 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/provider_observer_logger.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart index 9aa1903e5..a0358cdfd 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/subtree_scope.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -23,36 +24,37 @@ class Home extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( - body: Column( - children: [ - ProviderScope( - /// Just specify which provider you want to have a copy of in the subtree - /// - /// Note that dependant providers such as [adjustedCountProvider] will - /// also be copied for this subtree. If that is not the behavior you want, - /// consider using families instead - overrides: [counterProvider], - child: const CounterDisplay(), - ), - ProviderScope( - // You can change the provider's behavior in a particular subtree - overrides: [counterProvider.overrideWith((ref) => 1)], - child: const CounterDisplay(), - ), - ProviderScope( - overrides: [ - counterProvider, - // You can also change dependent provider's behaviors - adjustedCountProvider.overrideWith( - (ref) => ref.watch(counterProvider) * 3, - ), - ], - child: const CounterDisplay(), - ), - // This particular display will use the provider state from the root ProviderScope - const CounterDisplay(), - ], - )); + body: Column( + children: [ + ProviderScope( + /// Just specify which provider you want to have a copy of in the subtree + /// + /// Note that dependant providers such as [adjustedCountProvider] will + /// also be copied for this subtree. If that is not the behavior you want, + /// consider using families instead + overrides: [counterProvider], + child: const CounterDisplay(), + ), + ProviderScope( + // You can change the provider's behavior in a particular subtree + overrides: [counterProvider.overrideWith((ref) => 1)], + child: const CounterDisplay(), + ), + ProviderScope( + overrides: [ + counterProvider, + // You can also change dependent provider's behaviors + adjustedCountProvider.overrideWith( + (ref) => ref.watch(counterProvider) * 3, + ), + ], + child: const CounterDisplay(), + ), + // This particular display will use the provider state from the root ProviderScope + const CounterDisplay(), + ], + ), + ); } } diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart index d17f15f02..4d171982b 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/concepts/theme_scope.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; /* SNIPPET START */ @@ -27,23 +28,24 @@ class Home extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( - body: Column( - children: [ - // This counter will have a primary color of green - Theme( - data: Theme.of(context).copyWith(primaryColor: Colors.green), - child: const CounterDisplay(), - ), - // This counter will have a primary color of blue - const CounterDisplay(), - ElevatedButton( - onPressed: () { - ref.read(counterProvider.notifier).state++; - }, - child: const Text('Increment Count'), - ), - ], - )); + body: Column( + children: [ + // This counter will have a primary color of green + Theme( + data: Theme.of(context).copyWith(primaryColor: Colors.green), + child: const CounterDisplay(), + ), + // This counter will have a primary color of blue + const CounterDisplay(), + ElevatedButton( + onPressed: () { + ref.read(counterProvider.notifier).state++; + }, + child: const Text('Increment Count'), + ), + ], + ), + ); } } diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart index 2d25bb8e8..fe5b0b89d 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_dart.dart @@ -16,36 +16,36 @@ final todoListProvider = FutureProvider>((ref) => []); void main() { /* SNIPPET START */ -test('override repositoryProvider', () async { - final container = ProviderContainer( - overrides: [ - // Override the behavior of repositoryProvider to return - // FakeRepository instead of Repository. - /* highlight-start */ - repositoryProvider.overrideWithValue(FakeRepository()) - /* highlight-end */ - // We do not have to override `todoListProvider`, it will automatically - // use the overridden repositoryProvider - ], - ); - - // The first read if the loading state - expect( - container.read(todoListProvider), - const AsyncValue>.loading(), - ); - - /// Wait for the request to finish - await container.read(todoListProvider.future); - - // Exposes the data fetched - expect(container.read(todoListProvider).value, [ - isA() - .having((s) => s.id, 'id', '42') - .having((s) => s.label, 'label', 'Hello world') - .having((s) => s.completed, 'completed', false), - ]); -}); + test('override repositoryProvider', () async { + final container = ProviderContainer( + overrides: [ + // Override the behavior of repositoryProvider to return + // FakeRepository instead of Repository. + /* highlight-start */ + repositoryProvider.overrideWithValue(FakeRepository()), + /* highlight-end */ + // We do not have to override `todoListProvider`, it will automatically + // use the overridden repositoryProvider + ], + ); + + // The first read if the loading state + expect( + container.read(todoListProvider), + const AsyncValue>.loading(), + ); + + /// Wait for the request to finish + await container.read(todoListProvider.future); + + // Exposes the data fetched + expect(container.read(todoListProvider).value, [ + isA() + .having((s) => s.id, 'id', '42') + .having((s) => s.label, 'label', 'Hello world') + .having((s) => s.completed, 'completed', false), + ]); + }); /* SNIPPET END */ } diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart index 6685a1bdb..8deed4397 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_flutter.dart @@ -19,22 +19,22 @@ class FakeRepository {} void main() { /* SNIPPET START */ -testWidgets('override repositoryProvider', (tester) async { - await tester.pumpWidget( - ProviderScope( - overrides: [ - // Override the behavior of repositoryProvider to return - // FakeRepository instead of Repository. - /* highlight-start */ - repositoryProvider.overrideWithValue(FakeRepository()) - /* highlight-end */ - // We do not have to override `todoListProvider`, it will automatically - // use the overridden repositoryProvider - ], - child: MyApp(), - ), - ); -}); + testWidgets('override repositoryProvider', (tester) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + // Override the behavior of repositoryProvider to return + // FakeRepository instead of Repository. + /* highlight-start */ + repositoryProvider.overrideWithValue(FakeRepository()), + /* highlight-end */ + // We do not have to override `todoListProvider`, it will automatically + // use the overridden repositoryProvider + ], + child: MyApp(), + ), + ); + }); /* SNIPPET END */ } diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart index 0f79b4ee8..e75b5747f 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_full.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -56,24 +55,27 @@ void main() { await tester.pumpWidget( ProviderScope( overrides: [ - repositoryProvider.overrideWithValue(FakeRepository()) + repositoryProvider.overrideWithValue(FakeRepository()), ], // Our application, which will read from todoListProvider to display the todo-list. // You may extract this into a MyApp widget child: MaterialApp( home: Scaffold( - body: Consumer(builder: (context, ref, _) { - final todos = ref.watch(todoListProvider); - // The list of todos is loading or in error - if (todos.asData == null) { - return const CircularProgressIndicator(); - } - return ListView( - children: [ - for (final todo in todos.asData!.value) TodoItem(todo: todo) - ], - ); - }), + body: Consumer( + builder: (context, ref, _) { + final todos = ref.watch(todoListProvider); + // The list of todos is loading or in error + if (todos.asData == null) { + return const CircularProgressIndicator(); + } + return ListView( + children: [ + for (final todo in todos.asData!.value) + TodoItem(todo: todo), + ], + ); + }, + ), ), ), ), @@ -96,4 +98,4 @@ void main() { .having((s) => s.todo.completed, 'todo.completed', false), ]); }); -} \ No newline at end of file +} diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart index 6995de9b8..d78668653 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_dart.dart @@ -1,3 +1,4 @@ +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:riverpod/riverpod.dart'; diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart index f76abe181..05fca7a8c 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cookbooks/testing_original_test_flutter.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; import 'package:flutter_test/flutter_test.dart'; /* SNIPPET START */ @@ -18,13 +19,15 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - home: Consumer(builder: (context, ref, _) { - final counter = ref.watch(counterProvider); - return ElevatedButton( - onPressed: () => ref.read(counterProvider.notifier).state++, - child: Text('$counter'), - ); - }), + home: Consumer( + builder: (context, ref, _) { + final counter = ref.watch(counterProvider); + return ElevatedButton( + onPressed: () => ref.read(counterProvider.notifier).state++, + child: Text('$counter'), + ); + }, + ), ); } } diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/family.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/family.dart index 7eb1af657..21f643dbd 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/family.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/passing_args/raw/family.dart @@ -27,9 +27,8 @@ final activityProvider2 = AsyncNotifierProvider.autoDispose // 当将 ".family" 与通知者程序一起使用时,我们需要更改通知者程序子类: // AsyncNotifier -> FamilyAsyncNotifier -// AutoDisposeAsyncNotifier -> AutoDisposeFamilyAsyncNotifier -class ActivityNotifier - extends AutoDisposeFamilyAsyncNotifier { +// AsyncNotifier -> FamilyAsyncNotifier +class ActivityNotifier extends FamilyAsyncNotifier { /// Family 参数传递给构建方法并可通过 this.arg 访问 @override Future build(String activityType) async { diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo.dart index a909c2da0..2ad21af6a 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo.dart @@ -68,10 +68,11 @@ class _ExampleState extends ConsumerState { if (snapshot.connectionState == ConnectionState.waiting) ...[ const SizedBox(width: 8), const CircularProgressIndicator(), - ] + ], ], ); }, ); } -} /* SNIPPET END */ +} +/* SNIPPET END */ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo_hooks.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo_hooks.dart index 1a7bd0718..c1ac8caba 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo_hooks.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/render_add_todo_hooks.dart @@ -60,9 +60,9 @@ class Example extends HookConsumerWidget { if (snapshot.connectionState == ConnectionState.waiting) ...[ const SizedBox(width: 8), const CircularProgressIndicator(), - ] + ], ], ); } } -/* SNIPPET END */ \ No newline at end of file +/* SNIPPET END */ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier.dart index c3f0aff34..6eb0dd52f 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier.dart @@ -31,8 +31,8 @@ final todoListProvider = // 因为我们的逻辑是异步的,所以我们需要使用 AsyncNotifier。 // 特别的,由于使用“autoDispose”修饰符, -// 我们需要 AutoDisposeAsyncNotifier。 -class TodoList extends AutoDisposeAsyncNotifier> { +// 我们需要 AsyncNotifier。 +class TodoList extends AsyncNotifier> { @override Future> build() async { // 我们之前在 FutureProvider 中的业务逻辑现在位于 build 方法中。 diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier_add_todo.dart b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier_add_todo.dart index 81e32048f..d4aa8973e 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier_add_todo.dart +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/side_effects/raw/todo_list_notifier_add_todo.dart @@ -13,7 +13,7 @@ final todoListProvider = ); /* SNIPPET START */ -class TodoList extends AutoDisposeAsyncNotifier> { +class TodoList extends AsyncNotifier> { @override Future> build() async => [/* ... */]; diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/testing.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/testing.mdx index 670e5e237..c92da6863 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/testing.mdx +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/essentials/testing.mdx @@ -3,7 +3,6 @@ title: 测试你的提供者程序 --- import { AutoSnippet, When } from "@site/src/components/CodeSnippet"; -import createContainer from "!!raw-loader!/docs/essentials/testing/create_container.dart"; import unitTest from "!!raw-loader!/docs/essentials/testing/unit_test.dart"; import widgetTest from "!!raw-loader!/docs/essentials/testing/widget_test.dart"; import fullWidgetTest from "!!raw-loader!/docs/essentials/testing/full_widget_test.dart"; @@ -81,14 +80,6 @@ with providers. 与任何其他测试的主要区别在于,我们想要创建一个 `ProviderContainer` 对象。 此对象将使我们的测试能够与提供者程序进行交互。 - -建议创建一个测试实用程序来创建和处置对象 `ProviderContainer`: - - - diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/migration/from_change_notifier.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/migration/from_change_notifier.mdx index 668f01510..901723c36 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/migration/from_change_notifier.mdx +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/migration/from_change_notifier.mdx @@ -134,10 +134,10 @@ There's no need to think about the right class names and their *specific* APIs. ::: -从技术上讲,这里最合适的是定义一个 `AutoDisposeAsyncNotifier>`, +从技术上讲,这里最合适的是定义一个 `AsyncNotifier>`, 它满足上述所有要求。让我们先写一些伪代码。 diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/migration/from_state_notifier.mdx b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/migration/from_state_notifier.mdx index db03cac03..131b8e0cd 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/migration/from_state_notifier.mdx +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/migration/from_state_notifier.mdx @@ -142,14 +142,14 @@ Here, it's easy to see `AsyncNotifer` as a `FutureProvider` with methods. `AsyncNotifer` 附带了一组 `StateNotifier` 没有的实用程序和 getter,例如 -[`future`](https://pub.dev/documentation/riverpod/latest/riverpod/AutoDisposeAsyncNotifier/future.html) -和 [`update`](https://pub.dev/documentation/riverpod/latest/riverpod/AutoDisposeAsyncNotifier/update.html)。 +[`future`](https://pub.dev/documentation/riverpod/latest/riverpod/AsyncNotifier/future.html) +和 [`update`](https://pub.dev/documentation/riverpod/latest/riverpod/AsyncNotifier/update.html)。 这使我们能够在处理异步突变和副作用时编写更简单的逻辑。 另请参阅。 :::tip @@ -203,14 +203,14 @@ Another important difference is how families and auto dispose is handled with th `Notifier`,有其自己的 `.family` 和 `.autoDispose` 对应项, -例如 `FamilyNotifier` 和 `AutoDisposeNotifier`。 +例如 `FamilyNotifier` 和 `Notifier`。 与往常一样,此类修改可以组合使用(又名 `AutoDisposeFamilyNotifier`)。 -`AsyncNotifer` 也有其异步等效项(例如 `AutoDisposeFamilyAsyncNotifier`)。 +`AsyncNotifer` 也有其异步等效项(例如 `FamilyAsyncNotifier`)。