diff --git a/website/docs/migration/0.14.0_to_1.0.0.mdx b/website/docs/migration/0.14.0_to_1.0.0.mdx
index f7254f460..37320ec1d 100644
--- a/website/docs/migration/0.14.0_to_1.0.0.mdx
+++ b/website/docs/migration/0.14.0_to_1.0.0.mdx
@@ -2,6 +2,9 @@
title: ^0.14.0 to ^1.0.0
---
+import { Link } from "../../src/components/Link";
+
+
After a long wait, the first stable version of Riverpod is finally released 👏
To see the full list of changes, consult the [Changelog](https://pub.dev/packages/flutter_riverpod/changelog#100).
diff --git a/website/docs/migration/from_change_notifier.mdx b/website/docs/migration/from_change_notifier.mdx
new file mode 100644
index 000000000..9af75d89e
--- /dev/null
+++ b/website/docs/migration/from_change_notifier.mdx
@@ -0,0 +1,131 @@
+---
+title: From `ChangeNotifier`
+---
+
+import old from "!!raw-loader!./from_change_notifier/old.dart";
+import declaration from "./from_change_notifier/declaration";
+import initialization from "./from_change_notifier/initialization";
+import migrated from "./from_change_notifier/migrated";
+
+import { Link } from "../../src/components/Link";
+import { AutoSnippet } from "../../src/components/CodeSnippet";
+
+
+Within Riverpod, `ChangeNotifierProvider` is meant to be used to offer a smooth transition from
+pkg:provider.
+
+If you've just started a migration to pkg:riverpod, make sure you read the dedicated guide
+(see ).
+This article is meant for folks that already transitioned to riverpod, but want to move away from
+`ChangeNotifier` definetively.
+
+All in all, migrating from `ChangeNotifier` to `AsyncNotifer` requires a
+paradigm shift, but it brings great simplification with the resulting migrated
+code. See also .
+
+Take this (faulty) example:
+
+
+This implementation shows several weak design choices such as:
+- The usage of `isLoading` and `hasError` to handle different asynchronous cases
+- The need to carefully handle requests with tedious `try`/`catch`/`finally` expressions
+- The need to inkove `notifyListeners` at the right times to make this implementation work
+- The presence of inconsistent or possibly undesirable states, e.g. initialization with an empty list
+
+Note how this example has been crafted to show how `ChangeNotifier` can lead to faulty design choices
+for newbie developers; also, another takeaway is that mutable state might be way harder than it
+initially promises.
+
+`Notifier`/`AsyncNotifer`, in combination with immutable state, can lead to better design choices
+and less errors.
+
+Let's see how to migrate the above snippet, one step at a time, towards the newest APIs.
+
+
+## Start your migration
+First, we should declare the new provider / notifier: this requires some thought process which
+depends on your unique business logic.
+
+Let's summarize the above requirements:
+- State is represented with `List`, which obtained via a network call, with no parameters
+- State should *also* expose info about its `loading`, `error` and `data` state
+- State can be mutated via some exposed methods, thus a function isn't enough
+
+:::tip
+The above thought process boils down to answering the following questions:
+1. Are some side effects required?
+ - `y`: Use riverpod's class-based API
+ - `n`: Use riverpod's function-based API
+2. Does state need to be loaded asynchronously?
+ - `y`: Let `build` return a `Future`
+ - `n`: Let `build` simply return `T`
+3. Are some parameters required?
+ - `y`: Let `build` (or your function) accept them
+ - `n`: Let `build` (or your function) accept no extra parameters
+:::
+
+:::info
+If you're using codegen, the above thought process is enough.
+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>`,
+which meets all the above requirements. Let's write some pseudocode first.
+
+
+
+:::tip
+Remember: use snippets in your IDE to get some guidance, or just to speed up your code writing.
+See .
+:::
+
+With respect with `ChangeNotifier`'s implementation, we don't need to declare `todos` anymore;
+such variable is `state`, which is implicitly loaded with `build`.
+
+Indeed, riverpod's notifiers can expose *one* entity at a time.
+
+:::tip
+Riverpod's API is meant to be granular; nonetheless, when migrating, you can still define a custom
+entity to hold multiple values. Consider using [Dart 3's records](https://dart.dev/language/records)
+to smooth out the migration at first.
+:::
+
+
+### Initialization
+Initalizing a notifier is easy: just write initialization logic inside `build`.
+We can now get rid of the old `_init` function.
+
+
+
+With respect of the old `_init`, the new `build` isn't missing anything: there is no need to
+initialize variables such as `isLoading` or `hasError` anymore.
+
+Riverpod will automatically translate any asynchronous provider, via exposing an `AsyncValue>`
+and handles the intricacies of asynchronous state way better than what two simple boolean flags can do.
+
+Indeed, any `AsyncNotifier` effectively makes writing additional `try`/`catch`/`finally` an anti-pattern
+for handling asynchronous state.
+
+
+### Mutations and Side Effects
+Just like initialization, when performing side effects there's no need to manipulate boolean flags
+such as `hasError`, or to write additional `try`/`catch`/`finally` blocks.
+
+Below, we've cut down all the boilerplate and successfully fully migrated the above example:
+
+
+:::tip
+Syntax and design choices may vary, but in the end we just need to write our request and update
+state afterwards. See .
+:::
+
+## Migration Process Summary
+
+Let's review the whole migration process applied above, from a operational point of view.
+
+1. We've moved the initialization, away from a custom method invoked in a constructor, to `build`
+2. We've removed `todos`, `isLoading` and `hasError` properties: internal `state` will suffice
+3. We've removed any `try`-`catch`-`finally` blocks: returning the future is enough
+4. We've applied the same simplification on the side effects (`addTodo`)
+5. We've applied the mutations, via simply reassign `state`
diff --git a/website/docs/migration/from_change_notifier/declaration/declaration.dart b/website/docs/migration/from_change_notifier/declaration/declaration.dart
new file mode 100644
index 000000000..b3ca06ef2
--- /dev/null
+++ b/website/docs/migration/from_change_notifier/declaration/declaration.dart
@@ -0,0 +1,33 @@
+// ignore_for_file: avoid_print, avoid_unused_constructor_parameters
+
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+part 'declaration.g.dart';
+
+class Todo {
+ const Todo(this.id);
+ Todo.fromJson(Object obj) : id = 0;
+
+ final int id;
+}
+
+class Http {
+ Future> get(String str) async => [str];
+ Future> post(String str) async => [str];
+}
+
+final http = Http();
+
+/* SNIPPET START */
+@riverpod
+class MyNotifier extends _$MyNotifier {
+ @override
+ FutureOr> build() {
+ // TODO ...
+ return [];
+ }
+
+ Future addTodo(Todo todo) async {
+ // TODO
+ }
+}
diff --git a/website/docs/migration/from_change_notifier/declaration/declaration.g.dart b/website/docs/migration/from_change_notifier/declaration/declaration.g.dart
new file mode 100644
index 000000000..317354e31
--- /dev/null
+++ b/website/docs/migration/from_change_notifier/declaration/declaration.g.dart
@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+// ignore_for_file: non_constant_identifier_names
+
+part of 'declaration.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+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>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, inference_failure_on_uninitialized_variable, inference_failure_on_function_return_type, inference_failure_on_untyped_parameter, deprecated_member_use_from_same_package
diff --git a/website/docs/migration/from_change_notifier/declaration/index.tsx b/website/docs/migration/from_change_notifier/declaration/index.tsx
new file mode 100644
index 000000000..1ad659c31
--- /dev/null
+++ b/website/docs/migration/from_change_notifier/declaration/index.tsx
@@ -0,0 +1,9 @@
+import raw from "!!raw-loader!./raw.dart";
+import codegen from "!!raw-loader!./declaration.dart";
+
+export default {
+ raw,
+ hooks: raw,
+ codegen,
+ hooksCodegen: codegen,
+};
diff --git a/website/docs/migration/from_change_notifier/declaration/raw.dart b/website/docs/migration/from_change_notifier/declaration/raw.dart
new file mode 100644
index 000000000..c7485bbba
--- /dev/null
+++ b/website/docs/migration/from_change_notifier/declaration/raw.dart
@@ -0,0 +1,33 @@
+// ignore_for_file: avoid_print, avoid_unused_constructor_parameters
+
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+class Todo {
+ const Todo(this.id);
+ Todo.fromJson(Object obj) : id = 0;
+
+ final int id;
+}
+
+class Http {
+ Future> get(String str) async => [str];
+ Future> post(String str) async => [str];
+}
+
+final http = Http();
+
+/* SNIPPET START */
+@riverpod
+class MyNotifier extends AutoDisposeAsyncNotifier> {
+ @override
+ FutureOr> build() {
+ // TODO ...
+ return [];
+ }
+
+ Future addTodo(Todo todo) async {
+ // TODO
+ }
+}
+
+final myNotifierProvider = AsyncNotifierProvider.autoDispose(MyNotifier.new);
diff --git a/website/docs/migration/from_change_notifier/initialization/index.tsx b/website/docs/migration/from_change_notifier/initialization/index.tsx
new file mode 100644
index 000000000..3b3fbd2cb
--- /dev/null
+++ b/website/docs/migration/from_change_notifier/initialization/index.tsx
@@ -0,0 +1,9 @@
+import raw from "!!raw-loader!./raw.dart";
+import codegen from "!!raw-loader!./initialization.dart";
+
+export default {
+ raw,
+ hooks: raw,
+ codegen,
+ hooksCodegen: codegen,
+};
diff --git a/website/docs/migration/from_change_notifier/initialization/initialization.dart b/website/docs/migration/from_change_notifier/initialization/initialization.dart
new file mode 100644
index 000000000..4da805e13
--- /dev/null
+++ b/website/docs/migration/from_change_notifier/initialization/initialization.dart
@@ -0,0 +1,29 @@
+// ignore_for_file: avoid_print, avoid_unused_constructor_parameters
+
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+part 'initialization.g.dart';
+
+class Todo {
+ const Todo(this.id);
+ Todo.fromJson(Object obj) : id = 0;
+
+ final int id;
+}
+
+class Http {
+ Future> get(String str) async => [str];
+ Future> post(String str) async => [str];
+}
+
+final http = Http();
+
+/* SNIPPET START */
+@riverpod
+class MyNotifier extends _$MyNotifier {
+ @override
+ FutureOr> build() async {
+ final json = await http.get('api/todos');
+ return [...json.map(Todo.fromJson)];
+ }
+}
diff --git a/website/docs/migration/from_change_notifier/initialization/initialization.g.dart b/website/docs/migration/from_change_notifier/initialization/initialization.g.dart
new file mode 100644
index 000000000..376ce998f
--- /dev/null
+++ b/website/docs/migration/from_change_notifier/initialization/initialization.g.dart
@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+// ignore_for_file: non_constant_identifier_names
+
+part of 'initialization.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+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>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, inference_failure_on_uninitialized_variable, inference_failure_on_function_return_type, inference_failure_on_untyped_parameter, 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
new file mode 100644
index 000000000..24ab265f7
--- /dev/null
+++ b/website/docs/migration/from_change_notifier/initialization/raw.dart
@@ -0,0 +1,29 @@
+// ignore_for_file: avoid_print, avoid_unused_constructor_parameters
+
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+class Todo {
+ const Todo(this.id);
+ Todo.fromJson(Object obj) : id = 0;
+
+ final int id;
+}
+
+class Http {
+ Future> get(String str) async => [str];
+ Future> post(String str) async => [str];
+}
+
+final http = Http();
+
+/* SNIPPET START */
+@riverpod
+class MyNotifier extends AutoDisposeAsyncNotifier> {
+ @override
+ FutureOr> build() async {
+ final json = await http.get('api/todos');
+ return [...json.map(Todo.fromJson)];
+ }
+}
+
+final myNotifierProvider = AsyncNotifierProvider.autoDispose(MyNotifier.new);
diff --git a/website/docs/migration/from_change_notifier/migrated/index.tsx b/website/docs/migration/from_change_notifier/migrated/index.tsx
new file mode 100644
index 000000000..075bfbdf5
--- /dev/null
+++ b/website/docs/migration/from_change_notifier/migrated/index.tsx
@@ -0,0 +1,9 @@
+import raw from "!!raw-loader!./raw.dart";
+import codegen from "!!raw-loader!./migrated.dart";
+
+export default {
+ raw,
+ hooks: raw,
+ codegen,
+ hooksCodegen: codegen,
+};
diff --git a/website/docs/migration/from_change_notifier/migrated/migrated.dart b/website/docs/migration/from_change_notifier/migrated/migrated.dart
new file mode 100644
index 000000000..b8f4ba829
--- /dev/null
+++ b/website/docs/migration/from_change_notifier/migrated/migrated.dart
@@ -0,0 +1,37 @@
+// ignore_for_file: avoid_print, avoid_unused_constructor_parameters
+
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+part 'migrated.g.dart';
+
+class Todo {
+ const Todo(this.id);
+ Todo.fromJson(Object obj) : id = 0;
+
+ final int id;
+}
+
+class Http {
+ Future> get(String str) async => [str];
+ Future> post(String str) async => [str];
+}
+
+final http = Http();
+
+/* SNIPPET START */
+@riverpod
+class MyNotifier extends _$MyNotifier {
+ @override
+ FutureOr> build() async {
+ final json = await http.get('api/todos');
+
+ return [...json.map(Todo.fromJson)];
+ }
+
+ Future addTodo(Todo todo) async {
+ // optional: state = const AsyncLoading();
+ final json = await http.post('api/todos');
+ final newTodos = [...json.map(Todo.fromJson)];
+ state = AsyncData(newTodos);
+ }
+}
diff --git a/website/docs/migration/from_change_notifier/migrated/migrated.g.dart b/website/docs/migration/from_change_notifier/migrated/migrated.g.dart
new file mode 100644
index 000000000..8eaddcfd9
--- /dev/null
+++ b/website/docs/migration/from_change_notifier/migrated/migrated.g.dart
@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+// ignore_for_file: non_constant_identifier_names
+
+part of 'migrated.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+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>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, inference_failure_on_uninitialized_variable, inference_failure_on_function_return_type, inference_failure_on_untyped_parameter, 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
new file mode 100644
index 000000000..8572db71d
--- /dev/null
+++ b/website/docs/migration/from_change_notifier/migrated/raw.dart
@@ -0,0 +1,37 @@
+// ignore_for_file: avoid_print, avoid_unused_constructor_parameters
+
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+class Todo {
+ const Todo(this.id);
+ Todo.fromJson(Object obj) : id = 0;
+
+ final int id;
+}
+
+class Http {
+ Future> get(String str) async => [str];
+ Future> post(String str) async => [str];
+}
+
+final http = Http();
+
+/* SNIPPET START */
+@riverpod
+class MyNotifier extends AutoDisposeAsyncNotifier> {
+ @override
+ FutureOr> build() async {
+ final json = await http.get('api/todos');
+
+ return [...json.map(Todo.fromJson)];
+ }
+
+ Future addTodo(Todo todo) async {
+ // optional: state = const AsyncLoading();
+ final json = await http.post('api/todos');
+ final newTodos = [...json.map(Todo.fromJson)];
+ state = AsyncData(newTodos);
+ }
+}
+
+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
new file mode 100644
index 000000000..4e65e823d
--- /dev/null
+++ b/website/docs/migration/from_change_notifier/old.dart
@@ -0,0 +1,60 @@
+// ignore_for_file: avoid_print, avoid_unused_constructor_parameters
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+class Todo {
+ const Todo(this.id);
+ Todo.fromJson(Object obj) : id = 0;
+
+ final int id;
+}
+
+class Http {
+ Future> get(String str) async => [str];
+ Future> post(String str) async => [str];
+}
+
+final http = Http();
+
+/* SNIPPET START */
+class MyChangeNotifier extends ChangeNotifier {
+ MyChangeNotifier() {
+ _init();
+ }
+ List todos = [];
+ bool isLoading = true;
+ bool hasError = false;
+
+ Future _init() async {
+ try {
+ final json = await http.get('api/todos');
+ todos = [...json.map(Todo.fromJson)];
+ } on Exception {
+ hasError = true;
+ } finally {
+ isLoading = false;
+ notifyListeners();
+ }
+ }
+
+ Future addTodo(int id) async {
+ isLoading = true;
+ notifyListeners();
+
+ try {
+ final json = await http.post('api/todos');
+ todos = [...json.map(Todo.fromJson)];
+ hasError = false;
+ } on Exception {
+ hasError = true;
+ } finally {
+ isLoading = false;
+ notifyListeners();
+ }
+ }
+}
+
+final myChangeProvider = ChangeNotifierProvider((ref) {
+ return MyChangeNotifier();
+});
diff --git a/website/docs/migration/from_state_notifier.mdx b/website/docs/migration/from_state_notifier.mdx
new file mode 100644
index 000000000..b2ba50348
--- /dev/null
+++ b/website/docs/migration/from_state_notifier.mdx
@@ -0,0 +1,264 @@
+---
+title: From `StateNotifier`
+---
+
+import buildInit from "./from_state_notifier/build_init";
+import buildInitOld from "!!raw-loader!./from_state_notifier/build_init_old.dart";
+import consumersDontChange from "!!raw-loader!./from_state_notifier/consumers_dont_change.dart";
+import familyAndDispose from "./from_state_notifier/family_and_dispose";
+import familyAndDisposeOld from "!!raw-loader!./from_state_notifier/family_and_dispose_old.dart";
+import asyncNotifier from "./from_state_notifier/async_notifier";
+import asyncNotifierOld from "!!raw-loader!./from_state_notifier/async_notifier_old.dart";
+import addListener from "./from_state_notifier/add_listener";
+import addListenerOld from "!!raw-loader!./from_state_notifier/add_listener_old.dart";
+import fromStateProvider from "./from_state_notifier/from_state_provider";
+import fromStateProviderOld from "!!raw-loader!./from_state_notifier/from_state_provider_old.dart";
+import oldLifecycles from "./from_state_notifier/old_lifecycles";
+import oldLifecyclesOld from "!!raw-loader!./from_state_notifier/old_lifecycles_old.dart";
+import oldLifecyclesFinal from "./from_state_notifier/old_lifecycles_final";
+import obtainNotifierOnTests from "!!raw-loader!./from_state_notifier/obtain_notifier_on_tests.dart";
+
+import { Link } from "../../src/components/Link";
+import { AutoSnippet } from "../../src/components/CodeSnippet";
+
+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.
+
+This page shows how to migrate from the deprecated `StateNotifier` to the new APIs.
+
+The main benefit introduced by `AsyncNotifier` is a better `async` support; indeed,
+`AsyncNotifier` can be thought as a `FutureProvider` which can expose ways to be modified from the UI..
+
+Furthermore, the new `(Async)Notifier`s:
+
+- Expose a `Ref` object inside its class
+- Offer similar syntax between codegen and non-codegen approaches
+- Offer similar syntax between their sync and async versions
+- Move away logic from Providers and centralize it into the Notifiers themselves
+
+Let's see how to define a `Notifier`, how it compares with `StateNotifier` and how to migrate
+the new `AsyncNotifier` for asynchronous state.
+
+## New syntax comparison
+
+Be sure to know how to define a `Notifier` before diving into this comparison.
+See .
+
+Let's write an example, using the old `StateNotifier` syntax:
+
+
+Here's the same example, built with the new `Notifier` APIs, which roughly translates to:
+
+
+Comparing `Notifier` with `StateNotifier`, one can observe these main differences:
+
+- `StateNotifier`'s reactive dependencies are declared in its provider, whereas `Notifier`
+ centralizes this logic in its `build` method
+- `StateNotifier`'s whole initialization process is split between its provider and its constructor,
+ whereas `Notifier` reserves a single place to place such logic
+- Notice how, as opposed to `StateNotifier`, no logic is ever written into a `Notifier`'s constructor
+
+Similar conclusions can be made with `AsyncNotifer`, `Notifier`'s asynchronous equivalent.
+
+## Migrating asynchronous `StateNotifier`s
+
+The main appeal of the new API syntax is an improved DX on asynchronous data.
+Take the following example:
+
+
+
+Here's the above example, rewritten with the new `AsyncNotifier` APIs:
+
+
+
+`AsyncNotifer`, just like `Notifier`, brings a simpler and more uniform API.
+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).
+This enables us to write much simpler logic when handling asynchronous mutations and side-effects.
+See also .
+:::tip
+Migrating from `StateNotifier>` to a `AsyncNotifer` boils down to:
+
+- Putting initialization logic into `build`
+- Removing any `catch`/`try` blocks in initialization or in side effects methods
+- Remove any `AsyncValue.guard` from `build`, as it converts `Future`s into `AsyncValue`s
+:::
+
+
+### Advantages
+
+After these few examples, let's now highligh the main advantages of `Notifier` and `AsyncNotifer`:
+- The new syntax should feel way simpler and more readable, especially for asynchronous state
+- New APIs are likely to have less boilerplate code in general
+- Syntax is now unified, no matter the type of provider you're writing, enabling code generation
+(see )
+
+Let's go further down and highlight more differences and similarities.
+
+## Explicit `.family` and `.autoDispose` modifications
+
+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`.
+As always, such modifications hese can be combined (aka `AutoDisposeFamilyNotifier`).
+`AsyncNotifer` has its asynchronous equivalent, too (e.g. `AutoDisposeFamilyAsyncNotifier`).
+
+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.
+This should bring better readability, more conciseness and overall less mistakes.
+
+Take the following example, in which a `StateNotifierProvider.family` is being defined.
+
+
+`BugsEncounteredNotifier` feels... heavy / hard to read.
+Let's take a look at its migrated `AsyncNotifier` counterpart:
+
+
+
+Its migrated counterpart should feel like a light read.
+
+:::info
+`(Async)Notifier`'s `.family` parameters are available via `this.arg` (or `this.paramName` when using codegen)
+:::
+
+## Lifecycles have a different behavior
+
+Lifecycles between `Notifier`/`AsyncNotifier` and `StateNotifier` differ substantially.
+
+This example showcases - again - how the old API have sparse logic:
+
+
+
+Here, if `durationProvider` updates, `MyNotifier` _disposes_: its instance is then re-instantiated
+and its internal state is then re-initialized.
+Furthermore, unlike every other provider, the `dispose` callback is to be defined
+in the class, separately.
+Finally, it is still possible to write `ref.onDispose` in its _provider_, showing once again how
+sparse the logic can be with this API; potentially, the developer might have to look into eight (8!)
+different places to understand this Notifier behavior!
+
+These ambiguities are solved with `Riverpod 2.0`.
+
+### Old `dispose` vs `ref.onDispose`
+`StateNotifier`'s `dispose` method refers to the dispose event of the notifier itself, aka it's a
+callback that gets called *before disposing of itself*.
+
+`(Async)Notifier`s don't have this property, since *they don't get disposed of on rebuild*; only
+their *internal state* is.
+In the new notifiers, dispose lifecycles are taken care of in only _one_ place, via `ref.onDispose`
+(and others), just like any other provider.
+This simplifies the API, and hopefully the DX, so that there is only _one_ place to look at to
+understand lifecycle side-effects: its `build` method.
+
+Shortly: to register a callback that fires before its *internal state* rebuilds, we can use
+`ref.onDispose` like every other provider.
+
+You can migrate the above snippet like so:
+
+
+
+In this last snippet there sure is some simplification, but there's still an open problem: we
+are now unable to understand whether or not our notifiers are still alive while performing `update`.
+This might arise an unwanted `StateError`s.
+
+### No more `mounted`
+This happens because `(Async)Notifier`s lacks a `mounted` property, which was available on
+`StateNotifier`.
+Considering their difference in lifecycle, this makes perfect sense; while possible, a `mounted`
+property would be misleading on the new notifiers: `mounted` would *almost always* be `true`.
+
+While it would be possible to craft a [custom workaround](https://github.com/rrousselGit/riverpod/issues/1879#issuecomment-1303189191),
+it's recomended to work around this by canceling the asynchronous operation.
+
+Canceling an operation can be done with a custom [Completer](https://api.flutter.dev/flutter/dart-async/Completer-class.html),
+or any custom derivative.
+
+For example, if you're using `Dio` to perform network requests, consider using a [cancel token](https://pub.dev/documentation/dio/latest/dio/CancelToken-class.html)
+(see also ).
+
+Therefore, the above example migrates to the following:
+
+
+
+## Mutations APIs are the same as before
+
+Up until now we've shown the differences between `StateNotifier` and the new APIs.
+Instad, one thing `Notifier`, `AsyncNotifer` and `StateNotifier` share is how their states
+can be consumed and mutated.
+
+Consumers can obtain data from these three providers with the same syntax, which is great in case
+you're migrating away from `StateNotifier`; this applies for notifiers methods, too.
+
+
+
+## Other migrations
+
+Let's explore the less-impactful differences between `StateNotifier` and `Notifier` (or `AsyncNotifier`)
+
+### From `.addListener` and `.stream`
+
+`StateNotifier`'s `.addListener` and `.stream` can be used to listen for state changes.
+These two APIs are now to be considered outdated.
+
+This is intentional due to the desire to reach full API uniformity with `Notifier`, `AsyncNotifier` and other providers.
+Indeed, using a `Notifier` or an `AsyncNotifier` shouldn't be any different from any other provider.
+
+Therefore this:
+
+
+Becomes this:
+
+
+In a nutshell: if you want to listen to a `Notifier`/`AsyncNotifer`, just use `ref.listen`.
+See .
+
+### From `.debugState` in tests
+
+`StateNotifier` exposes `.debugState`: this property is used for pkg:state_notifier users to enable
+state access from outside the class when in development mode, for testing purposes.
+
+If you're using `.debugState` to access state in tests, chances are that you need to drop this
+approach.
+
+`Notifier` / `AsyncNotifer` don't have a `.debugState`; instead, they directly expose `.state`,
+which is `@visibleForTesting`.
+
+:::danger
+AVOID accessing `.state` from tests; if you have to, do it _if and only if_ you had already have
+a `Notifier` / `AsyncNotifer` properly instantied;
+then, you could access `.state` inside tests freely.
+
+Indeed, `Notifier` / `AsyncNotifier` _should not_ be instantiated by hand; instead, they should be
+interacted with by using its provider: failing to do so will *break* the notifier,
+due to ref and family args not being initialized.
+:::
+
+Don't have a `Notifier` instance?
+No problem, you can obtain one with `ref.read`, just like you would read its exposed state:
+
+
+
+Learn more about testing in its dedicated guide. See .
+
+### From `StateProvider`
+
+`StateProvider` was exposed by Riverpod since its release, and it was made to save a few LoC for
+simplified versions of `StateNotifierProvider`.
+Since `StateNotifierProvider` is deprecated, `StateProvider` is to be avoided, too.
+Furthermore, as of now, there is no `StateProvider` equivalent for the new APIs.
+
+Nonetheless, migrating from `StateProvider` to `Notifier` is simple.
+
+This:
+
+
+Becomes:
+
+
+Even though it costs us a few more LoC, migrating away from `StateProvider` enables us to
+definetively archive `StateNotifier`.
diff --git a/website/docs/migration/from_state_notifier/add_listener/add_listener.dart b/website/docs/migration/from_state_notifier/add_listener/add_listener.dart
new file mode 100644
index 000000000..ead1c72d8
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/add_listener/add_listener.dart
@@ -0,0 +1,18 @@
+// ignore_for_file: avoid_print
+
+import 'package:flutter/material.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+part 'add_listener.g.dart';
+
+/* SNIPPET START */
+@riverpod
+class MyNotifier extends _$MyNotifier {
+ @override
+ int build() {
+ ref.listenSelf((_, next) => debugPrint('$next'));
+ return 0;
+ }
+
+ void add() => state++;
+}
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
new file mode 100644
index 000000000..161b3bba0
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/add_listener/add_listener.g.dart
@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+// ignore_for_file: non_constant_identifier_names
+
+part of 'add_listener.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$myNotifierHash() => r'9acd382ed579c545ace755687b155e28eba01d22';
+
+/// 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;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, inference_failure_on_uninitialized_variable, inference_failure_on_function_return_type, inference_failure_on_untyped_parameter, deprecated_member_use_from_same_package
diff --git a/website/docs/migration/from_state_notifier/add_listener/index.tsx b/website/docs/migration/from_state_notifier/add_listener/index.tsx
new file mode 100644
index 000000000..6d7ac6d37
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/add_listener/index.tsx
@@ -0,0 +1,9 @@
+import raw from "!!raw-loader!./raw.dart";
+import codegen from "!!raw-loader!./add_listener.dart";
+
+export default {
+ raw,
+ hooks: raw,
+ codegen,
+ hooksCodegen: codegen,
+};
diff --git a/website/docs/migration/from_state_notifier/add_listener/raw.dart b/website/docs/migration/from_state_notifier/add_listener/raw.dart
new file mode 100644
index 000000000..e25b4a181
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/add_listener/raw.dart
@@ -0,0 +1,17 @@
+// ignore_for_file: avoid_print
+
+import 'package:flutter/material.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+/* SNIPPET START */
+class MyNotifier extends Notifier {
+ @override
+ int build() {
+ ref.listenSelf((_, next) => debugPrint('$next'));
+ return 0;
+ }
+
+ void add() => state++;
+}
+
+final myNotifierProvider = NotifierProvider(MyNotifier.new);
diff --git a/website/docs/migration/from_state_notifier/add_listener_old.dart b/website/docs/migration/from_state_notifier/add_listener_old.dart
new file mode 100644
index 000000000..5934a0f88
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/add_listener_old.dart
@@ -0,0 +1,24 @@
+// ignore_for_file: avoid_print
+
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+/* SNIPPET START */
+class MyNotifier extends StateNotifier {
+ MyNotifier() : super(0);
+
+ void add() => state++;
+}
+
+final myNotifierProvider = StateNotifierProvider((ref) {
+ final notifier = MyNotifier();
+
+ final cleanup = notifier.addListener((state) => debugPrint('$state'));
+ ref.onDispose(cleanup);
+
+ // Or, equivalently:
+ // final listener = notifier.stream.listen((event) => debugPrint('$event'));
+ // ref.onDispose(listener.cancel);
+
+ return notifier;
+});
diff --git a/website/docs/migration/from_state_notifier/async_notifier/async_notifier.dart b/website/docs/migration/from_state_notifier/async_notifier/async_notifier.dart
new file mode 100644
index 000000000..5ead76378
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/async_notifier/async_notifier.dart
@@ -0,0 +1,28 @@
+// ignore_for_file: avoid_print, avoid_unused_constructor_parameters
+
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+part 'async_notifier.g.dart';
+
+class Todo {
+ Todo.fromJson(Object obj);
+}
+
+class Http {
+ Future> get(String str) async => [str];
+}
+
+final http = Http();
+
+/* SNIPPET START */
+@riverpod
+class AsyncTodosNotifier extends _$AsyncTodosNotifier {
+ @override
+ FutureOr> build() async {
+ final json = await http.get('api/todos');
+
+ return [...json.map(Todo.fromJson)];
+ }
+
+ // ...
+}
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
new file mode 100644
index 000000000..25bcda60d
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/async_notifier/async_notifier.g.dart
@@ -0,0 +1,29 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+// ignore_for_file: non_constant_identifier_names
+
+part of 'async_notifier.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+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>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, inference_failure_on_uninitialized_variable, inference_failure_on_function_return_type, inference_failure_on_untyped_parameter, deprecated_member_use_from_same_package
diff --git a/website/docs/migration/from_state_notifier/async_notifier/index.tsx b/website/docs/migration/from_state_notifier/async_notifier/index.tsx
new file mode 100644
index 000000000..a0ff513c3
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/async_notifier/index.tsx
@@ -0,0 +1,9 @@
+import raw from "!!raw-loader!./raw.dart";
+import codegen from "!!raw-loader!./async_notifier.dart";
+
+export default {
+ raw,
+ hooks: raw,
+ codegen,
+ hooksCodegen: codegen,
+};
diff --git a/website/docs/migration/from_state_notifier/async_notifier/raw.dart b/website/docs/migration/from_state_notifier/async_notifier/raw.dart
new file mode 100644
index 000000000..52da61d76
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/async_notifier/raw.dart
@@ -0,0 +1,29 @@
+// ignore_for_file: avoid_print, avoid_unused_constructor_parameters
+
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+class Todo {
+ Todo.fromJson(Object obj);
+}
+
+class Http {
+ Future> get(String str) async => [str];
+}
+
+final http = Http();
+
+/* SNIPPET START */
+class AsyncTodosNotifier extends AsyncNotifier> {
+ @override
+ FutureOr> build() async {
+ final json = await http.get('api/todos');
+
+ return [...json.map(Todo.fromJson)];
+ }
+
+ // ...
+}
+
+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
new file mode 100644
index 000000000..50d7f4aed
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/async_notifier_old.dart
@@ -0,0 +1,30 @@
+// ignore_for_file: avoid_print, avoid_unused_constructor_parameters
+
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+
+class Todo {
+ Todo.fromJson(Object obj);
+}
+
+class Http {
+ Future> get(String str) async => [str];
+}
+
+final http = Http();
+
+/* SNIPPET START */
+class AsyncTodosNotifier extends StateNotifier>> {
+ AsyncTodosNotifier() : super(const AsyncLoading()) {
+ _postInit();
+ }
+
+ Future _postInit() async {
+ state = await AsyncValue.guard(() async {
+ final json = await http.get('api/todos');
+
+ return [...json.map(Todo.fromJson)];
+ });
+ }
+
+ // ...
+}
diff --git a/website/docs/migration/from_state_notifier/build_init/build_init.dart b/website/docs/migration/from_state_notifier/build_init/build_init.dart
new file mode 100644
index 000000000..3e2303b7a
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/build_init/build_init.dart
@@ -0,0 +1,15 @@
+// ignore_for_file: avoid_print
+
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+part 'build_init.g.dart';
+
+/* SNIPPET START */
+@riverpod
+class CounterNotifier extends _$CounterNotifier {
+ @override
+ int build() => 0;
+
+ void increment() => state++;
+ void decrement() => state++;
+}
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
new file mode 100644
index 000000000..6b4621ecb
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/build_init/build_init.g.dart
@@ -0,0 +1,28 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+// ignore_for_file: non_constant_identifier_names
+
+part of 'build_init.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$counterNotifierHash() => r'8d4e4011da15a0ef79af9622336839a0c9e406ab';
+
+/// 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;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, inference_failure_on_uninitialized_variable, inference_failure_on_function_return_type, inference_failure_on_untyped_parameter, deprecated_member_use_from_same_package
diff --git a/website/docs/migration/from_state_notifier/build_init/index.tsx b/website/docs/migration/from_state_notifier/build_init/index.tsx
new file mode 100644
index 000000000..276a143ac
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/build_init/index.tsx
@@ -0,0 +1,9 @@
+import raw from "!!raw-loader!./raw.dart";
+import codegen from "!!raw-loader!./build_init.dart";
+
+export default {
+ raw,
+ hooks: raw,
+ codegen,
+ hooksCodegen: codegen,
+};
diff --git a/website/docs/migration/from_state_notifier/build_init/raw.dart b/website/docs/migration/from_state_notifier/build_init/raw.dart
new file mode 100644
index 000000000..0ba8eebed
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/build_init/raw.dart
@@ -0,0 +1,15 @@
+// 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 {
+ @override
+ int build() => 0;
+
+ void increment() => state++;
+ void decrement() => state++;
+}
+
+final counterNotifierProvider = NotifierProvider(CounterNotifier.new);
diff --git a/website/docs/migration/from_state_notifier/build_init_old.dart b/website/docs/migration/from_state_notifier/build_init_old.dart
new file mode 100644
index 000000000..82a8c54bd
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/build_init_old.dart
@@ -0,0 +1,13 @@
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+
+/* SNIPPET START */
+class CounterNotifier extends StateNotifier {
+ CounterNotifier() : super(0);
+
+ void increment() => state++;
+ void decrement() => state++;
+}
+
+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
new file mode 100644
index 000000000..6a964a5a6
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/consumers_dont_change.dart
@@ -0,0 +1,36 @@
+import 'package:flutter/material.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+
+class CounterNotifier extends StateNotifier {
+ CounterNotifier() : super(0);
+
+ void increment() => state++;
+ void decrement() => state++;
+}
+
+final counterNotifierProvider = StateNotifierProvider((ref) {
+ return CounterNotifier();
+});
+
+/* SNIPPET START */
+class SomeConsumer extends ConsumerWidget {
+ const SomeConsumer({super.key});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ /* highlight-start */
+ final counter = ref.watch(counterNotifierProvider);
+ /* highlight-end */
+ return Column(
+ children: [
+ Text("You've counted up until $counter, good job!"),
+ TextButton(
+ /* highlight-start */
+ 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
new file mode 100644
index 000000000..9a695614c
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/family_and_dispose/family_and_dispose.dart
@@ -0,0 +1,24 @@
+// ignore_for_file: unnecessary_this
+
+import 'dart:math';
+
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+import '../../utils.dart';
+
+part 'family_and_dispose.g.dart';
+
+/* SNIPPET START */
+@riverpod
+class BugsEncounteredNotifier extends _$BugsEncounteredNotifier {
+ @override
+ FutureOr build(String featureId) {
+ return 99;
+ }
+
+ Future fix(int amount) async {
+ final old = await future;
+ 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
new file mode 100644
index 000000000..62ae74a00
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/family_and_dispose/family_and_dispose.g.dart
@@ -0,0 +1,184 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+// ignore_for_file: non_constant_identifier_names
+
+part of 'family_and_dispose.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$bugsEncounteredNotifierHash() =>
+ r'c76e924f84db91c57d226896b062d9f4e8ab79e5';
+
+/// 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));
+ }
+}
+
+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();
+
+ /// See also [BugsEncounteredNotifier].
+ BugsEncounteredNotifierProvider call(
+ String featureId,
+ ) {
+ return BugsEncounteredNotifierProvider(
+ featureId,
+ );
+ }
+
+ @visibleForOverriding
+ @override
+ BugsEncounteredNotifierProvider getProviderOverride(
+ covariant BugsEncounteredNotifierProvider provider,
+ ) {
+ return call(
+ provider.featureId,
+ );
+ }
+
+ static const Iterable? _dependencies = null;
+
+ @override
+ Iterable? get dependencies => _dependencies;
+
+ static const Iterable? _allTransitiveDependencies = null;
+
+ @override
+ Iterable? get allTransitiveDependencies =>
+ _allTransitiveDependencies;
+
+ @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();
+
+ final String featureId;
+
+ @override
+ FutureOr runNotifierBuild(
+ covariant BugsEncounteredNotifier notifier,
+ ) {
+ return notifier.build(
+ featureId,
+ );
+ }
+
+ @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,
+ ),
+ );
+ }
+
+ @override
+ (String,) get argument {
+ return (featureId,);
+ }
+
+ @override
+ AutoDisposeAsyncNotifierProviderElement
+ createElement() {
+ return _BugsEncounteredNotifierProviderElement(this);
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return other is BugsEncounteredNotifierProvider &&
+ other.featureId == featureId;
+ }
+
+ @override
+ int get hashCode {
+ var hash = _SystemHash.combine(0, runtimeType.hashCode);
+ hash = _SystemHash.combine(hash, featureId.hashCode);
+
+ return _SystemHash.finish(hash);
+ }
+}
+
+mixin BugsEncounteredNotifierRef on AutoDisposeAsyncNotifierProviderRef {
+ /// The parameter `featureId` of this provider.
+ String get featureId;
+}
+
+class _BugsEncounteredNotifierProviderElement
+ extends AutoDisposeAsyncNotifierProviderElement with BugsEncounteredNotifierRef {
+ _BugsEncounteredNotifierProviderElement(super.provider);
+
+ @override
+ String get featureId => (origin as BugsEncounteredNotifierProvider).featureId;
+}
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, inference_failure_on_uninitialized_variable, inference_failure_on_function_return_type, inference_failure_on_untyped_parameter, deprecated_member_use_from_same_package
diff --git a/website/docs/migration/from_state_notifier/family_and_dispose/index.tsx b/website/docs/migration/from_state_notifier/family_and_dispose/index.tsx
new file mode 100644
index 000000000..0780f2135
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/family_and_dispose/index.tsx
@@ -0,0 +1,9 @@
+import raw from "!!raw-loader!./raw.dart";
+import codegen from "!!raw-loader!./family_and_dispose.dart";
+
+export default {
+ raw,
+ hooks: raw,
+ codegen,
+ hooksCodegen: codegen,
+};
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
new file mode 100644
index 000000000..8dfce9447
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/family_and_dispose/raw.dart
@@ -0,0 +1,27 @@
+// ignore_for_file: unnecessary_this
+
+import 'dart:math';
+
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+import '../../utils.dart';
+
+/* SNIPPET START */
+class BugsEncounteredNotifier extends AutoDisposeFamilyAsyncNotifier {
+ @override
+ FutureOr build(String featureId) {
+ return 99;
+ }
+
+ Future fix(int amount) async {
+ final old = await future;
+ final result = await ref.read(taskTrackerProvider).fix(id: this.arg, fixed: amount);
+ state = AsyncData(max(old - result, 0));
+ }
+}
+
+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
new file mode 100644
index 000000000..28f93a65c
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/family_and_dispose_old.dart
@@ -0,0 +1,30 @@
+// ignore_for_file: unnecessary_this
+
+import 'dart:math';
+
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+
+import '../utils.dart';
+
+/* SNIPPET START */
+class BugsEncounteredNotifier extends StateNotifier> {
+ BugsEncounteredNotifier({
+ required this.ref,
+ required this.featureId,
+ }) : super(const AsyncData(99));
+ final String featureId;
+ final Ref ref;
+
+ 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);
+ return max(old - result, 0);
+ });
+ }
+}
+
+final bugsEncounteredNotifierProvider =
+ StateNotifierProvider.family.autoDispose((ref, id) {
+ return BugsEncounteredNotifier(ref: ref, featureId: id);
+});
diff --git a/website/docs/migration/from_state_notifier/from_state_provider/from_state_provider.dart b/website/docs/migration/from_state_notifier/from_state_provider/from_state_provider.dart
new file mode 100644
index 000000000..2c71d6144
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/from_state_provider/from_state_provider.dart
@@ -0,0 +1,14 @@
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+part 'from_state_provider.g.dart';
+
+/* SNIPPET START */
+@riverpod
+class CounterNotifier extends _$CounterNotifier {
+ @override
+ int build() => 0;
+
+ @override
+ set state(int newState) => super.state = newState;
+ int update(int Function(int state) cb) => state = cb(state);
+}
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
new file mode 100644
index 000000000..9a3edeebc
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/from_state_provider/from_state_provider.g.dart
@@ -0,0 +1,28 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+// ignore_for_file: non_constant_identifier_names
+
+part of 'from_state_provider.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+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;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, inference_failure_on_uninitialized_variable, inference_failure_on_function_return_type, inference_failure_on_untyped_parameter, deprecated_member_use_from_same_package
diff --git a/website/docs/migration/from_state_notifier/from_state_provider/index.tsx b/website/docs/migration/from_state_notifier/from_state_provider/index.tsx
new file mode 100644
index 000000000..f59794999
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/from_state_provider/index.tsx
@@ -0,0 +1,9 @@
+import raw from "!!raw-loader!./raw.dart";
+import codegen from "!!raw-loader!./from_state_provider.dart";
+
+export default {
+ raw,
+ hooks: raw,
+ codegen,
+ hooksCodegen: codegen,
+};
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
new file mode 100644
index 000000000..97e4564f3
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/from_state_provider/raw.dart
@@ -0,0 +1,13 @@
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+/* SNIPPET START */
+class CounterNotifier extends Notifier {
+ @override
+ int build() => 0;
+
+ @override
+ set state(int newState) => super.state = newState;
+ int update(int Function(int state) cb) => state = cb(state);
+}
+
+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
new file mode 100644
index 000000000..246f44a0c
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/from_state_provider_old.dart
@@ -0,0 +1,6 @@
+import 'package:riverpod/riverpod.dart';
+
+/* SNIPPET START */
+final counterProvider = StateProvider((ref) {
+ return 0;
+});
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
new file mode 100644
index 000000000..df97a0fb2
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/obtain_notifier_on_tests.dart
@@ -0,0 +1,33 @@
+// ignore_for_file: unused_local_variable,omit_local_variable_types
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+
+class MyNotifier extends AutoDisposeNotifier {
+ @override
+ int build() {
+ return 0;
+ }
+}
+
+final myNotifierProvider = NotifierProvider.autoDispose(MyNotifier.new);
+
+/* SNIPPET START */
+void main(List args) {
+ test('my test', () {
+ final container = ProviderContainer();
+ addTearDown(container.dispose);
+
+ // Obtaining a notifier
+ /* highlight-start */
+ final AutoDisposeNotifier notifier = container.read(myNotifierProvider.notifier);
+ /* highlight-end */
+
+ // Obtaining its exposed state
+ /* highlight-start */
+ final int state = container.read(myNotifierProvider);
+ /* highlight-end */
+
+ // TODO write your tests
+ });
+}
diff --git a/website/docs/migration/from_state_notifier/old_lifecycles/index.tsx b/website/docs/migration/from_state_notifier/old_lifecycles/index.tsx
new file mode 100644
index 000000000..9b77f551a
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/old_lifecycles/index.tsx
@@ -0,0 +1,9 @@
+import raw from "!!raw-loader!./raw.dart";
+import codegen from "!!raw-loader!./old_lifecycles.dart";
+
+export default {
+ raw,
+ hooks: raw,
+ codegen,
+ hooksCodegen: codegen,
+};
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
new file mode 100644
index 000000000..a96f294c6
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/old_lifecycles/old_lifecycles.dart
@@ -0,0 +1,36 @@
+import 'dart:async';
+
+import 'package:dio/dio.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+import '../../utils.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 {
+ @override
+ int build() {
+ // Just read/write the code here, in one place
+ final period = ref.watch(durationProvider);
+ final timer = Timer.periodic(period, (t) => update());
+ ref.onDispose(timer.cancel);
+
+ return 0;
+ }
+
+ Future update() async {
+ await ref.read(repositoryProvider).update(state + 1);
+ // `mounted` is no more!
+ state++; // This might throw.
+ }
+}
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
new file mode 100644
index 000000000..d28e242a3
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/old_lifecycles/old_lifecycles.g.dart
@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+// ignore_for_file: non_constant_identifier_names
+
+part of 'old_lifecycles.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+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;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, inference_failure_on_uninitialized_variable, inference_failure_on_function_return_type, inference_failure_on_untyped_parameter, 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
new file mode 100644
index 000000000..691d71af9
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/old_lifecycles/raw.dart
@@ -0,0 +1,35 @@
+import 'dart:async';
+
+import 'package:dio/dio.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+import '../../utils.dart';
+
+final repositoryProvider = Provider<_MyRepo>((ref) {
+ return _MyRepo();
+});
+
+class _MyRepo {
+ Future update(int i, {CancelToken? token}) async {}
+}
+
+/* SNIPPET START */
+class MyNotifier extends Notifier {
+ @override
+ int build() {
+ // Just read/write the code here, in one place
+ final period = ref.watch(durationProvider);
+ final timer = Timer.periodic(period, (t) => update());
+ ref.onDispose(timer.cancel);
+
+ return 0;
+ }
+
+ Future update() async {
+ await ref.read(repositoryProvider).update(state + 1);
+ // `mounted` is no more!
+ state++; // This might throw.
+ }
+}
+
+final myNotifierProvider = NotifierProvider(MyNotifier.new);
diff --git a/website/docs/migration/from_state_notifier/old_lifecycles_final/index.tsx b/website/docs/migration/from_state_notifier/old_lifecycles_final/index.tsx
new file mode 100644
index 000000000..9823b1564
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/old_lifecycles_final/index.tsx
@@ -0,0 +1,9 @@
+import raw from "!!raw-loader!./raw.dart";
+import codegen from "!!raw-loader!./old_lifecycles_final.dart";
+
+export default {
+ raw,
+ hooks: raw,
+ codegen,
+ hooksCodegen: codegen,
+};
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
new file mode 100644
index 000000000..47bfe2516
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/old_lifecycles_final/old_lifecycles_final.dart
@@ -0,0 +1,38 @@
+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();
+});
+
+class _MyRepo {
+ Future update(int i, {CancelToken? token}) async {}
+}
+
+/* SNIPPET START */
+@riverpod
+class MyNotifier extends _$MyNotifier {
+ @override
+ int build() {
+ // Just read/write the code here, in one place
+ final period = ref.watch(durationProvider);
+ final timer = Timer.periodic(period, (t) => update());
+ ref.onDispose(timer.cancel);
+
+ return 0;
+ }
+
+ Future update() async {
+ final cancelToken = CancelToken();
+ ref.onDispose(cancelToken.cancel);
+ await ref.read(repositoryProvider).update(state + 1, token: cancelToken);
+ // When `cancelToken.cancel` is invoked, a custom Exception is thrown
+ state++;
+ }
+}
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
new file mode 100644
index 000000000..7c55609e1
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/old_lifecycles_final/old_lifecycles_final.g.dart
@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+// ignore_for_file: non_constant_identifier_names
+
+part of 'old_lifecycles_final.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$myNotifierHash() => r'8ea2586ea29d12306efd4b8b847142136dd20338';
+
+/// 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;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, inference_failure_on_uninitialized_variable, inference_failure_on_function_return_type, inference_failure_on_untyped_parameter, 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
new file mode 100644
index 000000000..94814a191
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/old_lifecycles_final/raw.dart
@@ -0,0 +1,36 @@
+import 'dart:async';
+
+import 'package:dio/dio.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+import '../../utils.dart';
+
+final repositoryProvider = Provider<_MyRepo>((ref) {
+ return _MyRepo();
+});
+
+class _MyRepo {
+ Future update(int i, {CancelToken? token}) async {}
+}
+
+/* SNIPPET START */
+class MyNotifier extends Notifier {
+ @override
+ int build() {
+ // Just read/write the code here, in one place
+ final period = ref.watch(durationProvider);
+ final timer = Timer.periodic(period, (t) => update());
+ ref.onDispose(timer.cancel);
+
+ return 0;
+ }
+
+ Future update() async {
+ final cancelToken = CancelToken();
+ ref.onDispose(cancelToken.cancel);
+ await ref.read(repositoryProvider).update(state + 1, token: cancelToken);
+ state++;
+ }
+}
+
+final myNotifierProvider = NotifierProvider(MyNotifier.new);
diff --git a/website/docs/migration/from_state_notifier/old_lifecycles_old.dart b/website/docs/migration/from_state_notifier/old_lifecycles_old.dart
new file mode 100644
index 000000000..8f6eaa485
--- /dev/null
+++ b/website/docs/migration/from_state_notifier/old_lifecycles_old.dart
@@ -0,0 +1,42 @@
+import 'dart:async';
+
+import 'package:dio/dio.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+import '../utils.dart';
+
+final repositoryProvider = Provider<_MyRepo>((ref) {
+ return _MyRepo();
+});
+
+class _MyRepo {
+ Future update(int i, {CancelToken? token}) async {}
+}
+
+/* SNIPPET START */
+class MyNotifier extends StateNotifier {
+ MyNotifier(this.ref, this.period) : super(0) {
+ // 1 init logic
+ _timer = Timer.periodic(period, (t) => update()); // 2 side effect on init
+ }
+ final Duration period;
+ final Ref ref;
+ late final Timer _timer;
+
+ Future update() async {
+ await ref.read(repositoryProvider).update(state + 1); // 3 mutation
+ if (mounted) state++; // 4 check for mounted props
+ }
+
+ @override
+ void dispose() {
+ _timer.cancel(); // 5 custom dispose logic
+ super.dispose();
+ }
+}
+
+final myNotifierProvider = StateNotifierProvider((ref) {
+ // 6 provider definition
+ final period = ref.watch(durationProvider); // 7 reactive dependency logic
+ return MyNotifier(ref, period); // 8 pipe down `ref`
+});
diff --git a/website/docs/migration/utils.dart b/website/docs/migration/utils.dart
new file mode 100644
index 000000000..e515b6f94
--- /dev/null
+++ b/website/docs/migration/utils.dart
@@ -0,0 +1,23 @@
+import 'dart:math' as math;
+
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+
+final randomProvider = Provider((ref) {
+ return math.Random().nextInt(6);
+});
+
+final taskTrackerProvider = Provider((ref) {
+ return TaskTrackerRepo();
+});
+
+class TaskTrackerRepo {
+ Future fix({required String id, required int fixed}) async => 0;
+}
+
+final durationProvider = Provider((ref) {
+ return Duration.zero;
+});
+
+final availableWaterProvider = Provider((ref) {
+ return 40;
+});
diff --git a/website/sidebars.js b/website/sidebars.js
index 8d31d85f5..1335c0298 100644
--- a/website/sidebars.js
+++ b/website/sidebars.js
@@ -164,12 +164,8 @@ module.exports = {
label: "Migration guides",
collapsible: false,
items: [
- // {
- // type: "link",
- // label: "Migrating StateNotifier/ChangeNotifier to Notifier (WIP)",
- // href: "https://github.com/rrousselGit/riverpod/tree/master/examples/marvel",
- // },
-
+ "migration/from_state_notifier",
+ "migration/from_change_notifier",
"migration/0.14.0_to_1.0.0",
"migration/0.13.0_to_0.14.0",
],
diff --git a/website/src/documents_meta.js b/website/src/documents_meta.js
index 958d51c78..83edcd4a7 100644
--- a/website/src/documents_meta.js
+++ b/website/src/documents_meta.js
@@ -7,6 +7,8 @@ export const documentTitles = {
"providers/notifier_provider": "(Async)NotifierProvider",
"providers/future_provider": "FutureProvider",
"providers/change_notifier_provider": "ChangeNotifierProvider",
+ "migration/from_state_notifier": "From `StateNotifier`",
+ "migration/from_change_notifier": "From `ChangeNotifier`",
"migration/0.14.0_to_1.0.0": "^0.14.0 to ^1.0.0",
"migration/0.13.0_to_0.14.0": "^0.13.0 to ^0.14.0",
"introduction/why_riverpod": "Why Riverpod?",