A simple way to access state while robust and testable.

Last update: Jun 29, 2022

Build Status codecov Star on Github License: MIT Discord Buy Me A Coffee

Riverpod


A state-management library that:

  • catches programming errors at compile time rather than at runtime
  • removes nesting for listening/combining objects
  • ensures that the code is testable
riverpod pub package
flutter_riverpod pub package
hooks_riverpod pub package

Welcome to Riverpod!

This project can be considered as a rewrite of provider to make improvements that would be otherwise impossible.

For learning how to use Riverpod, see its documentation: https://riverpod.dev

Long story short:

  • Declare your providers as global variables:

    final counterProvider = StateNotifierProvider((ref) {
      return Counter();
    });
    
    class Counter extends StateNotifier<int> {
      Counter(): super(0);
    
      void increment() => state++;
    }
  • Use them inside your widgets in a compile time safe way. No runtime exceptions!

    class Example extends ConsumerWidget {
      @override
      Widget build(BuildContext context, ScopedReader watch) {
        final count = watch(counterProvider);
        return Text(count.toString());
      }
    }

See the FAQ if you have questions about what this means for provider.

Index

Motivation

If provider is a simplification of InheritedWidgets, then Riverpod is a reimplementation of InheritedWidgets from scratch.

It is very similar to provider in principle, but also has major differences as an attempt to fix the common problems that provider face.

Riverpod has multiple goals. First, it inherits the goals of provider:

  • Being able to safely create, observe and dispose states without having to worry about losing the state on widget rebuild.
  • Making our objects visible in Flutter's devtool by default.
  • Testable and composable
  • Improve the readability of InheritedWidgets when we have multiple of them (which would naturally lead to a deeply nested widget tree).
  • Make apps more scalable with a unidirectional data-flow.

From there, Riverpod goes a few steps beyond:

  • Reading objects is now compile-safe. No more runtime exception.
  • It makes the provider pattern more flexible, which allows supporting commonly requested features like:
    • Being able to have multiple providers of the same type.
    • Disposing the state of a provider when it is no longer used.
    • Have computed states
    • Making a provider private.
  • Simplifies complex object graphs. It is easier to depend on asynchronous state.
  • Makes the pattern independent from Flutter

These are achieved by no longer using InheritedWidgets. Instead, Riverpod implements its own mechanism that works in a similar fashion.

For learning how to use Riverpod, see its documentation: https://riverpod.dev

Contributing

Contributions are welcomed!

Here is a curated list of how you can help:

  • Report bugs and scenarios that are difficult to implement
  • Report parts of the documentation that are unclear
  • Update the documentation / add examples
  • Implement new features by making a pull-request

FAQ

Why another project when provider already exists?

While provider is largely used and well accepted by the community, it is not perfect either.

People regularly file issues or ask questions about some problems they face, such as:

  • Why do I have a ProviderNotFoundException?
  • How can I make that my state automatically disposed of when not used anymore?
  • How to make a provider that depends on other (potentially complex) providers?

These are legitimate problems, and I believe that something can be improved to fix those.

The issue is, these problems are deeply rooted in how provider works, and fixing those problems is likely impossible without drastic changes to the mechanism of provider.

In a way, if provider is a candle then Riverpod is a lightbulb. They have very similar usages, but we cannot create a lightbulb by improving our candle.

Is it safe to use in production?

Yes, but with caution.

Riverpod recently left its experimental status, but it isn't fully stable either. The API may change slightly when more features are added, and some use-cases may not be as simple as they could be.

But overall, you should be able to use Riverpod without trouble.

Will this get merged with provider at some point?

No. At least not until it is proven that the community likes Riverpod and that it doesn't cause more problems than it solves.

While provider and this project have a lot in common, they do have some major differences. Differences big enough that it would be a large breaking change for users of provider to migrate Riverpod.

Considering that, separating both projects initially sounds like a better compromise.

Will provider be deprecated/stop being supported?

Not in the short term, no.

This project is still experimental and unpopular. While it is, in a way, a provider 2.0, its worth has yet to be proven.

Until it is certain that Riverpod is a better way of doing things and that the community likes it, provider will still be maintained.

GitHub

https://github.com/rrousselGit/river_pod
Comments
  • 1. [RFC] Unifying syntax for listening to providers (v2)

    This RFC is a follow-up to https://github.com/rrousselGit/river_pod/issues/246 with a slightly different proposal.

    The problems are the same:

    • Riverpod needs a way to add new ways of "listening" to a provider (such as an equivalent to ProviderListener that doesn't involve nesting, but not exclusively)
    • Adding parameters to "build"/"builder" of ConsumerWidget/Consumer would be a large breaking change
    • StatefulWidgets may want to listen to providers without having to resort to Consumer
    • having a different syntax between reading a provider from another provider and reading a provider from a widget causes confusion

    See https://github.com/rrousselGit/river_pod/issues/246 for a bit more explanation

    Proposal

    Instead of passing directly "watch" as parameter to widgets, Riverpod could do like with its providers and pass a "ref" object"

    As such, instead of:

    class StatelessExample extends ConsumerWidget {
      @override
      Widget build(BuildContext context, ScopedReader watch) {
        A value = watch(a);
      }
    }
    

    we'd have:

    class StatelessExample extends ConsumerWidget {
      @override
      Widget build(BuildContext context, WidgetReference ref) {
        A value = ref.watch(a);
      }
    }
    

    Similarly, Consumer would become:

    Consumer(
      builder: (context, ref, child) {
        A value = ref.watch(a);
      },
    );
    

    The behaviour would be strictly identical. But this then allows Riverpod to add extra methods on WidgetsReference, which could allow:

    class StatelessExample extends ConsumerWidget {
      @override
      Widget build(BuildContext context, WidgetReference ref) {
        ref.listen<A>(a, (value) {
          print('provider a changed $value');
        });
      }
    }
    

    This would be equivalent to ProviderListener but without involving nesting.

    Hooks consideration

    While hooks_riverpod doesn't suffer from the problem listed at the start of the issue, the logic wants that hooks_riverpod should also use the same syntax too (both to reduce confusion and simplify maintenance).

    As such, useProvider would be deprecated and a ConsumerHookWidget would be introduced. Which means that instead of:

    class HooksExample extends HookWidget {
      @override
      Widget build(BuildContext context) {
        A value = useProvider(a);
      }
    }
    

    we'd have:

    class HooksExample extends ConsumerHookWidget {
      @override
      Widget build(BuildContext context, WidgetReference ref) {
        A value = ref.watch(a);
      }
    }
    

    This would also clarify that the only purpose of hooks_riverpod is to use both hooks and Riverpod simultaneously.

    context.read/context.refresh considerations

    context.read(myProvider) and context.refresh(provider) would be deprecated.

    Instead, ref should now be used. So the previous:

    class StatelessExample extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return ElevatedButton(
          onPressed: () => context.read(counterProvider).state++;
        );
      }
    }
    

    would become:

    class StatelessExample extends ConsumerWidget {
      @override
      Widget build(BuildContext context, WidgetReference ref) {
        return ElevatedButton(
          onPressed: () => ref.read(counterProvider).state++;
        );
      }
    }
    

    (and same thing with refresh)

    This has two side-effects:

    • StatelessWidget can no-longer be used to just "read" providers
    • there is no-longer a name collision between package:provider and Riverpod for context.read, which would simplify migration.

    StatefulWidget consideration

    An optional goal of this change is to support StatefulWidget.

    This could be done by shipping a State mixin that adds a ref property, which would allow us to write:

    class StatefulExample extends StatefulWidget {
       @override
      _StatefulExampleState createState() => _StatefulExampleState();
    }
    
    class _StatefulExampleState extends State<StatefulExample> with ConsumerStateMixin {
      @override
      Widget build(BuildContext context) {
        A value = ref.watch(a);
      }
    }
    

    Note that this is entirely optional, as Consumer can already be used.

    ProviderReference vs WidgetReference

    While the syntax for listening a provider in widgets and providers would now look similar with both being ref.watch(myProvider), it is important to note that ProviderReference and WidgetReference are distinct objects.

    They are not interchangeable, and you could not assign WidgetReference to ProviderReference for example.

    Their main difference is, ProviderReference does not allow interacting with ScopedProviders. On the other hand, WidgetReference do.

    Similarly, it is entirely possible that in the future, some functionalities as added to one without being added to the other (such as https://github.com/rrousselGit/river_pod/pull/302 which allows ProviderReference to manipulate the state of its provider)

    Conclusion

    That's it, thanks for reading!

    As opposed to https://github.com/rrousselGit/river_pod/issues/246, one major difference is that this proposal is "compile safe" without having to rely on a custom linter.
    The downside is that the syntax for reading providers becomes a tiny bit more verbose.

    What do you think of the proposed change?

    Feel free to leave a comment. You can also use :+1: and :-1: to express your opinion. All feedbacks are welcomed!

    Reviewed by rrousselGit at 2021-02-19 11:14
  • 2. [RFC] Change on the syntax for listening providers

    Hello!
    This issue is an open discussion for a potential change on how to consume/combine providers.

    The goal of these changes are:

    • offer a uniformized way to listen to providers (no-longer have ref.watch vs useProvider vs watch)
    • add an equivalent to ProviderListener that works inside providers too
    • revamp ProviderListener not to involve nesting
    • support a ConsumerStatefulWidget
    • support useProvider(myProvider.select((user) => user.name)) in all the places that may want to listen to a provider

    The problem

    At the moment, there are many similar but slightly different syntax for listening to a provider:

    • providers wanting to listen to another provider may use ref.watch(myProvider):
      final provider = Provider((ref) {
        final value = ref.watch(anotherProvider);
      });
      
    • HookWidget may use useProvider(myProvider):
      class Example extends HookWidget {
        @override
        Widget build(BuildContext context) {
          final value = useProvider(anotherProvider);
      
        }
      }
      
    • ConsumerWidget & Consumer may use watch(myProvider):
      class Example extends ConsumerWidget {
        @override
        Widget build(BuildContext context, ScopedReader watch) {
          final value = watch(anotherProvider);
        }
      }
      ...
      Consumer(
        builder: (context, watch, child) {
          final value = watch(anotherProvider);
        }
      )
      
    • all widgets can use ProviderListener but not providers
      class Example extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return ProviderListener<Something>(
          provider: anotherProvider,
          onChange: (context, value) {}
        );
      }
      }
      

    This works fine but is slightly confusing. While in practice, there's only one solution on how to listen to a provider depending on where you are, the fact that the syntax isn't the same everywhere confuses newcomers.

    On top of that, due to how ConsumerWidget/Consumer works, it is inconvenient to add new ways to listen to providers (such as a simplification of ProviderListener that does not involve nesting) because that would require adding parameters to the build method.

    This also means that it is impossible to listen to providers inside StatefulWidget, as it is impossible to add extra parameters to State.build. This requires developers to use Consumer, which decreases readability.

    Proposal

    This proposal is about fusing all of these methods for listening providers into two static functions:

    • watch, the equivalent of ref.watch/useProvider/...
    • listen, the equivalent of ProviderListener

    These two functions would work in all providers and inside ConsumerWidget, Consumer, and two new additions:

    • ConsumerHookWidget
    • ConsumerStatefulWidget

    Long story short, here is how both functions would look like in practice:

    final a = Provider<A>(...);
    
    final b = Provider((ref) {
      A value = watch(a);
      listen<A>(a, (value) {
      });
    });
    
    Consumer(
      builder: (context) {
        A value = watch(a);
        listen<A>(a, (value) {
        });
      },
    );
    
    class StatelessExample extends ConsumerWidget {
      @override
      Widget build(BuildContext context) {
        A value = watch(a);
        listen<A>(a, (value) {
        });
    
      }
    }
    
    class HooksExample extends ConsumerHookWidget {
      @override
      Widget build(BuildContext context) {
        A value = watch(a);
        listen<A>(a, (value) {
        });
    
      }
    }
    
    class StatefulExample extends ConsumerStatefulWidget {
       @override
      _StatefulExampleState createState() => _StatefulExampleState();
    }
    
    class _StatefulExampleState extends State<StatefulExample> {
      @override
      Widget build(BuildContext context) {
        A value = watch(a);
        listen<A>(a, (value) {
        });
    
      }
    }
    

    This change would involve:

    • deprecating useProvider in favor of ConsumerHookWidget + watch
    • deprecating ProviderListener in favor of listen
    • deprecating ref.watch in favor of watch
    • removing the extra parameter of ConsumerWidget.build

    This change would have no impact on <ref/context>.read

    The downside

    The main issue with this change is, we lose some of the "compilation safety".

    More specifically, there are two cases that currently purposefully do not compile but would now compile (and throw) after this change:

    • reading a ScopedProvider inside a non-scoped provider:

      final scoped = ScopedProvider(...)
      
      final provider = Provider((ref) {
        watch(scoped);
      });
      
    • reading an .autoDispose provider inside a provider that is not marked with .autoDispose:

      final autoDispose = Provider.autoDispose(...)
      
      final provider = Provider((ref) {
        watch(autoDispose);
      });
      

    Since these would now throw exceptions instead of compilation errors, this would make refactoring more difficult.

    The bright side is, there is an alternate solution for these: custom lint rules. Instead of relying on the Dart compiler to have a compilation error, we could extend the Dart analyzer to add support for these scenarios.

    This is a separate project on its own but is feasible.

    Closing word

    That's it~

    What do you think of the proposed change, and what do you think about the downsides?

    Feel free to comment or vote on this issue to express your opinion:

    • vote 🚀 if you want this change even without the analyzer plugin (the analyzer plugin would still come but after the release of such change)
    • vote 🎉 if you want this change, but only if we have the analyzer plugin (this would lock the release of such change until the analyzer plugin is ready)
    • for 👎 if you don't want this change at all
    • leave a comment for any other opinion
    Reviewed by rrousselGit at 2020-12-13 07:50
  • 3. added useProviderListener hook

    @rrousselGit I added this to close #163. I based the useProviderListener hook on both ProviderListener code and userProvider hook code. I'll be adding some tests, I just wanted to review this with you first.

    Reviewed by yiss at 2020-10-02 11:35
  • 4. Explicit Dependency / Factories

    Is your feature request related to a problem? Please describe. Yes. The problem is that I want to be able to debug a mobile multiplayer game application by having two copies of it side by side in a flutter desktop application.

    Describe the solution you'd like I want to be able to override one provider, and any provider that depends on it is also overridden. Essentially I want to give each instance of the application a unique id, and then use the same set of providers that depend on that unique ID but have the reference to them result in a different set of providers per unique ID.

    Describe alternatives you've considered Currently I override the unique ID provider for each subtree and also have to override all providers that depend on the unique ID. The issue with this is that it creates a lot of code bloat in the widget tree (at the place I'm overriding). It also makes it not compile-safe, since I can't know for sure if I've overridden all of the providers that I need to.

    The ideal solution would be to just need to override the unique ID provider, and all providers that depend on it are automatically resolved to a different instance of the provider. (I understand why this is not the case, since some providers are meant to be singletons, rather than factories).

    I was using this library as soon as I watched your youtube video introducing this. I'm so glad to see the progress and continued work on this! Back then there was no Computed or Families, which both seem to sort of address this problem.

    However, using families mean everywhere I obtain a provider, I also have to obtain the unique ID provider to reference the provider I actually want. I could solve this by using a Computed that computes the provider I want based on the unique ID provider. However, this means that for every provider I want duplicated I have to create a computed to go along with it. As well as in the documentation of computed it mentions that it might get called multiple times, even if the inputs haven't changed.

    Additional context Here is my provider dependency diagram. Multiplayer ID is the unique ID that I've been talking about. gamestate

    Proposed Solution I think I would propose an argument to the providers. uniqueForDependencies or keys, that would make them resolve to different providers if the set of uniqueForDependencies / keys are different. Similar to the keys argument of flutter_hooks.

    i.e.

    // Providers
    final idProvider = Provider((_) => 1)
    final storageProvider = Provider((ref) => StorageProvider(ref), uniqueForDependencies: [idProvider])
    
    class StorageProvider {
      ProviderReference ref;
      Box box;
      StorageProvider(this.ref){
       init();
      }
      Future<void> init() async {
        box = Hive.box('${ref.read(idProvider).value}');
      }
      String get clientId => box.get('clientId');
    }
    
    // Widget tree
    void main() => runApp(
    ProviderScope(
      child: Row(
        children: [
          // App id == 1
          MyApp(), 
          ProviderScope(
            child: MyApp(),
            overrides: [
               // App id == 2
               // Overriding id Provider should also override storage provider for this subtree
               idProvider.overrideForSubtree(Provider((_) => 2))
             ],
          ),
        ],
      ),
    ));
    
    class MyApp extends HookWidget {
      // Inside my app
      Widget build(BuildContext context){
        // This resolves to a different instance of storage provider per MyApp instance.
        final storage = useProvider(storageProvider);
        print(storage.clientId);
        // gets client id from one storage box within one MyApp instance
        // and a different client id from a different storage box within the other MyApp instance.
      } 
    }
    
    Reviewed by TimWhiting at 2020-06-23 15:01
  • 5. Why not call read in the body of the provider?

    I'm confused about this line the docs: image

    Could you elaborate on the rationale?

    Why is this good:

    final repositoryProvider = Provider((ref) => Repository(ref.read));
    class Repository {
       Repository(this.read);
       final Reader read;
       String get token => read(userTokenProvider).state;
    }
    

    But this is bad?

    final repositoryProvider = Provider((ref) => Repository(ref.read(userTokenProvider));
    class Repository {
      Repository(this.token);
       final UserToken token;
       String get token => userToken.state;
    }
    

    It seems from the Repository standpoint it's cleaner to say the thing it wants, rather than concern itself with how that thing is injected. Especially as read opens the Repository full access to all other providers, while passing UserToken tells it only what it needs to know.

    I actually like it even dumber, where it only takes a string, which removes another dependency from the Repository that it need not know about (ie UserToken):

    final repositoryProvider = Provider((ref) => Repository(() => ref.read(userTokenProvider).state);
    class Repository {
      Repository(this.tokenDelegate);
       final String Function() tokenDelegate;
       String get token => tokenDelegate();
    }
    

    This ends up with the most portable code, as this repository is now unconcerned with the DataInjection layer and just needs a simple string builder.

    Reviewed by esDotDev at 2021-10-28 14:14
  • 6. Using "actors" or event dispatcher

    Hi, the more time I'm spending with Riverpod, the more I'm removing StateNotifier and StateNotifierProvider for just FutureProvider and StreamProvider. It works from me a little like react-query with hooks in one in react word.

    Can't find a good answer for my question: how can we deal with events? I mean something like:

    1. We have a list of Posts.
    2. Using FutureProvider to get all Posts IDs from remote database
    3. Using StreamProvider to watch every particular Post (likes, comments etc.)
    4. Want to send an event to data base to like one of the post, so my StreamProvider from point above, will notify about Post change and will update the data of this particular Post.

    So what is best practice to send this event of like/unlike the Post? Normally I would have a Repository, StateNotifier which takes this Repository and have a method likeOrDisllike. Then I can use something like final controller = useProvider(thisStateNotifierProvider) and on like click do controller.likeOrDislike(). But as I said, with good of Future/Stream Provider I see, there might be 95% of my CRUD app logic with just this two.

    I was thinking about Providing the method from Repository like:

    final actor =
      Provider.family.autoDispose<Future<void>, String>((ref, postId) async {
      final repo = ref.watch(firebaseSpotRepositoryProvider);
      final user = ref.watch(userProvider);
      await repo.likeOrDisslikePost(userId: user.id.getOrCrash(), postId: postId);
    });
    

    The on "UI" :

    Button(
        onTap: () async {
                  context.read(
                    actor(
                      post.id.getOrCrash(),
                    ),
              );
          },
    )
    

    Does it make sense? Or is there a better approach or it's super stupid and whole thinking about resign from any class controllers like StateNotfier is wrong.

    Reviewed by Chojecki at 2021-01-25 13:25
  • 7. FutureProvider disposed unexpectedly in 1.0.0-dev3

    Describe the bug

    In the following code, future returns error with Bad state: The provider AutoDisposeFutureProvider<String>#25550(example) was disposed before a value was emitted..

    v0.14.0+3 works fine.

    To Reproduce

    final controllerProvider =
        StateNotifierProvider<Controller, List<Future<String>>>(
            (ref) => Controller(ref.read));
    
    final futureProvider =
        FutureProvider.autoDispose.family<String, String>((ref, str) async {
      final ret = await Future.delayed(Duration(seconds: 1), () => str);
      ref.maintainState = true;
      return ret;
    });
    
    class Controller extends StateNotifier<List<Future<String>>> {
      Controller(this._read) : super([]);
    
      Reader _read;
    
      void add(String str) {
        final f = _read(futureProvider(str).future);
        state = [f, ...state];
      }
    }
    
    class MyHomePage extends ConsumerWidget {
      const MyHomePage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final items = ref.watch(controllerProvider);
    
        return Scaffold(
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              ref.read(controllerProvider.notifier).add("example");
            },
            child: const Icon(Icons.add),
          ),
          body: ListView.builder(
            itemCount: items.length,
            itemBuilder: (context, index) {
              return FutureBuilder(
                future: items[index],
                builder: (context, snapshot) {
                  if (snapshot.hasError) {
                    return ListTile(
                      title: Text("${snapshot.error}"),
                    );
                  }
                  if (!snapshot.hasData) {
                    return const ListTile(
                      title: Center(child: CircularProgressIndicator()),
                    );
                  }
                  return ListTile(
                    title: Text("${snapshot.data}"),
                  );
                },
              );
            },
          ),
        );
      }
    }
    

    Expected behavior

    Show 'example' in ListTile.

    Reviewed by limenote135 at 2021-07-11 07:21
  • 8. Why watching future goes to the loading state first?

    Describe the bug I just switched my app from flutter_bloc to river_pod and I'm pretty happy. Thank you for the wonderful work.

    However, I did notice one issue. I'm chaining a lot of stream providers and find that using await ref.watch(stream_provider.future) goes to the loading state first, even though the data is already available. See the below simple repro.

    To Reproduce

    Code
    import 'package:flutter/material.dart';
    import 'package:flutter_riverpod/flutter_riverpod.dart';
    
    class Logger extends ProviderObserver {
      @override
      void didUpdateProvider(
        ProviderBase provider,
        Object? previousValue,
        Object? newValue,
        ProviderContainer container,
      ) {
        debugPrint('${provider.name}, $newValue');
      }
    }
    
    Stream<String> text() async* {
      yield 'hello';
      await Future.delayed(const Duration(seconds: 1));
      yield 'hello again';
    }
    
    final textProvider = StreamProvider((ref) => text(), name: 'text');
    
    final firstProvider = StreamProvider((ref) async* {
      final text = await ref.watch(textProvider.future);
      yield 'first $text';
    }, name: 'first');
    
    final secondProvider = StreamProvider((ref) async* {
      final text = await ref.watch(firstProvider.future);
      yield 'second $text';
    }, name: 'second');
    
    void main() {
      runApp(ProviderScope(observers: [Logger()], child: const MyApp()));
    }
    
    class MyApp extends ConsumerWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final text = ref.watch(secondProvider);
        debugPrint('MyApp: ${text.toString()}');
        return Container();
      }
    }
    
    Log output
    flutter: MyApp: AsyncLoading<String>()
    flutter: text, AsyncData<String>(value: hello)
    flutter: first, AsyncData<String>(value: first hello)
    flutter: second, AsyncData<String>(value: second first hello)
    flutter: MyApp: AsyncData<String>(value: second first hello)
    flutter: text.future, Instance of 'Future<String>'
    flutter: text, AsyncData<String>(value: hello again)
    flutter: first.future, Instance of 'Future<String>'
    flutter: first, AsyncData<String>(isLoading: true, value: first hello)
    flutter: second, AsyncData<String>(isLoading: true, value: second first hello)
    flutter: MyApp: AsyncData<String>(isLoading: true, value: second first hello)
    flutter: first, AsyncData<String>(value: first hello again)
    flutter: second, AsyncData<String>(value: second first hello again)
    flutter: MyApp: AsyncData<String>(value: second first hello again)
    

    As you can see from the log. Even though we got hello again, firstProvider and secondProvider went to loading state first, which in my mind is totally unnecessary. They should have the data already.

    This is problematic that the app might show an unnecessary progress indicator if it responds to isLoading field.

    Related question, what's the difference between isLoading in AsyncData vs AsyncLoading?

    Reviewed by terryl1900 at 2022-03-20 22:31
  • 9. Protesting Removal of useProvider and HookWidget

    In hooks_riverpod 1.0.0-dev.4 .. You are removing useProvider . Most of us moved from provider to riverpod to reduce the boilerplate code. Can you please bring it back ? It's removal will break more than 10 apps my team and I have put into production already.

    You were selling riverpod as provider but different. I see no difference in deprecating some core functionalities. Bring back HookWidget

    PLEASE @rrousselGit

    Reviewed by wazini-john at 2021-07-06 13:02
  • 10. Error: The argument type 'ScopedProvider' can't be assigned to the parameter type 'AlwaysAliveProviderBase'

    Describe the bug Currently unable to get data from a StreamProvider that is created from a stream that comes from a ScopedProvider.

    Note: I may be using this wrong. If so, apologies. In any case I hope this can help to improve the documentation.

    To Reproduce

    Here's some minimal code from my app:

    class FirestoreDatabase {
      FirestoreDatabase({@required this.uid});
      final String uid;
    
      Stream<List<Job>> jobsStream() => /* reads and parses some stream from Firestore */
    }
    
    // created somewhere
    final databaseProvider =
        ScopedProvider<FirestoreDatabase>((ref) => throw UnimplementedError());
    
    // then, somewhere else:
    ProviderScope(
            overrides: [
              databaseProvider
                  .overrideAs((watch) => FirestoreDatabase(uid: user.uid)),
            ],
     child: JobsPage());
    
    // inside JobsPage:
    class JobsPage extends ConsumerWidget {
      Widget build(BuildContext context, ScopedReader watch) {
        final jobsStreamProvider = StreamProvider<List<Job>>(
          (ref) => ref.watch<FirestoreDatabase>(databaseProvider).jobsStream(),
        );
        final jobsStream = watch(jobsStreamProvider);
        return jobsStream.when(
          data: (data) => SomeWidget(data),
          loading: () => CircularProgressIndicator(),
          error: (_, __) => Text('error')
        );
       }
    }
    

    I'm currently experiencing two problems with the code above.

    If I create the jobsStreamProvider like this (by using the watch argument from the build method):

        final jobsStreamProvider = StreamProvider<List<Job>>(
          (ref) => watch<FirestoreDatabase>(databaseProvider).jobsStream(),
        );
    

    then the code compiles and runs, but the UI is stuck in the loading state (that is, I never get data inside jobsStream.when).

    If instead I try to use ref to get the databaseProvider:

        final jobsStreamProvider = StreamProvider<List<Job>>(
          (ref) => ref.watch<FirestoreDatabase>(databaseProvider).jobsStream(),
        );
    

    then I get the following compile error:

    lib/app/home/jobs/jobs_page.dart:49:45: Error: The argument type 'ScopedProvider<FirestoreDatabase>' can't be assigned to the parameter type 'AlwaysAliveProviderBase<Object, FirestoreDatabase>'.
     - 'ScopedProvider' is from 'package:riverpod/src/framework.dart' ('../../../../flutter/flutter_beta/.pub-cache/hosted/pub.dartlang.org/riverpod-0.10.0/lib/src/framework.dart').
     - 'FirestoreDatabase' is from 'package:starter_architecture_flutter_firebase/services/firestore_database.dart' ('lib/services/firestore_database.dart').
     - 'AlwaysAliveProviderBase' is from 'package:riverpod/src/framework.dart' ('../../../../flutter/flutter_beta/.pub-cache/hosted/pub.dartlang.org/riverpod-0.10.0/lib/src/framework.dart').
     - 'Object' is from 'dart:core'.
          (ref) => ref.watch<FirestoreDatabase>(databaseProvider).jobsStream(),
                                                ^
    
    
    FAILURE: Build failed with an exception.
    

    Additional questions:

    • If I want to create a StreamProvider that depends on a ScopedProvider that is only usable after it is overridden, does it make sense to create it locally inside a build method, rather than on the global scope?
    • When should one use ref.watch as opposed to just watch from a Consumer or ConsumerWidget?

    Expected behavior No compile error, StreamProvider produces data rather than being stuck on loading state.

    Reviewed by bizz84 at 2020-09-19 21:32
  • 11. Watching value only from context

    I was exploring the possibility of adding riverpod to some parts of my existing project which uses provider at the moment. One of my current providers from the provider package is the theme of the app with custom colors and such.

    From any of my widgets I can do Provider.of<MyTheme>(context). I was wondering if there is a way of watching a specific riverpod provider by its name, without relying on Consumer or ConsumerWidget.

    Something along the lines of:

    static MyTheme of(BuildContext context){
      return  ProviderScope.containerOf(context).watch(_myThemeProvider);
    }
    

    I find migrating my current MyTheme dependency to riverpod very hard or nearly impossible if using Consumer/ConsumerWidget/useProvider is the only solution because many of my widgets depend on the signature from above.

    Migrating to using Consumers can be doable but not for moderately sized projects.

    Reviewed by davidmartos96 at 2020-09-17 10:10
  • 12. ref.listen listener is not called whenever the provider used is changed

    Describe the bug I have a widget which controlled using a controller, however I need to get the value from a FutureProviderFamily, so I use ref.listen to set the value into controller imperatively. But it seems the value from ref.listen is not consistent with the value from ref.watch. I'm guessing the listener is not called whenever the provider used is changed. But I'm not really sure whether it's correct behaviour or not from ref.listen.

    I'm using flutter_riverpod 1.0.4

    To Reproduce

    import 'package:flutter/material.dart';
    import 'package:flutter_riverpod/flutter_riverpod.dart';
    
    Future<void> main() async {
      WidgetsFlutterBinding.ensureInitialized();
    
      runApp(const ProviderScope(child: App()));
    }
    
    class App extends StatelessWidget {
      const App({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          home: Home(),
        );
      }
    }
    
    class Home extends StatefulWidget {
      const Home({Key? key}) : super(key: key);
    
      @override
      State<Home> createState() => _HomeState();
    }
    
    class _HomeState extends State<Home> {
      int _index = 0;
      final _controller = TextEditingController();
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Consumer(
            builder: (context, ref, child) {
              ref.listen<AsyncValue<Result>>(
                myProvider(_index),
                (previous, next) {
                  final value = next.when<String>(
                    data: (data) => '${data.value} (${data.dateTime})',
                    error: (error, _) => 'Error: $error',
                    loading: () => '...',
                  );
    
                  _controller.text = value;
                },
              );
              final value = ref.watch(myProvider(_index));
    
              final content = value.when(
                loading: () => const Center(
                  child: CircularProgressIndicator.adaptive(),
                ),
                error: (error, _) => Center(
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Text(error.toString()),
                      IconButton(
                        onPressed: () {
                          ref.refresh(myProvider(_index));
                        },
                        icon: const Icon(Icons.refresh),
                      ),
                    ],
                  ),
                ),
                data: (data) => Center(
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Text('Value: ${data.value}'),
                      Text('DateTime: ${data.dateTime}'),
                      IconButton(
                        onPressed: () {
                          ref.refresh(myProvider(_index));
                        },
                        icon: const Icon(Icons.refresh),
                      ),
                    ],
                  ),
                ),
              );
    
              return Column(
                children: [
                  Expanded(
                    child: Padding(
                      padding: const EdgeInsets.all(24.0),
                      child: Center(
                        child: TextField(
                          enabled: false,
                          controller: _controller,
                        ),
                      ),
                    ),
                  ),
                  Expanded(child: content),
                ],
              );
            },
          ),
          bottomNavigationBar: BottomNavigationBar(
            currentIndex: _index,
            type: BottomNavigationBarType.fixed,
            onTap: (value) {
              setState(() {
                _index = value;
              });
            },
            items: const [
              BottomNavigationBarItem(
                icon: Icon(Icons.home),
                label: '0',
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.home),
                label: '1',
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.home),
                label: '2',
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.home),
                label: '3',
              ),
            ],
          ),
        );
      }
    }
    
    final myProvider = FutureProvider.family<Result, int>((ref, args) async {
      await Future.delayed(const Duration(seconds: 1));
    
      return Result(
        value: args,
        dateTime: DateTime.now(),
      );
    });
    
    class Result {
      Result({
        required this.value,
        required this.dateTime,
      });
    
      final int value;
      final DateTime dateTime;
    }
    
    1. Tap every index on navigation bottom bar
    2. Go back to index 0
    3. See the value from listen and watch is different.
    4. Try refresh the provider using the Icon refresh. The value from listen will match the value from watch.
    5. Go to other index, same problem with no. 3

    https://user-images.githubusercontent.com/1574722/176606277-904bf626-04df-4cde-bbed-ed4ccfdc4e26.mov

    Expected behavior Listener should be called again whenever provider used is changed

    Reviewed by SuperPenguin at 2022-06-30 06:19
  • 13. Add ContainerNode and RiverpodNode to devtool

    This PR adds support for Flutter DevTools by improving the already present file devtool.dart.

    With the changes present here we can more easily get the data needed to present the provider state on Flutter DevTools.

    For more information check the discussion here.

    The UI that these changes enable is the following:

    https://user-images.githubusercontent.com/11666470/174911002-a28792d1-ebca-4c7c-b56e-b88002afdfea.mp4

    Soon I will open a PR on devtools with the addition of RiverpodScreen.

    Reviewed by adsonpleal at 2022-06-22 20:30
  • 14. StateProvider name return null

    State provider appears to be null in the didUpdateProvider call. didDisposeProvider and didUpdateProvider also work fine.

    didAddProvider: -myprovidername.notifier: Provider started with value: Instance of 'StateController<int>' didUpdateProvider: - null: Provider started with value: Instance of 'StateController<int>' didUpdateProvider: - null: previousValue: Instance of 'StateController<int>'

    final myProvider = StateProvider.autoDispose<int>((ref) {
      return 0;
    }, name: "myprovidername");
    

    version: 2.0.0-dev.9

    Reviewed by murataksuu at 2022-06-20 19:17
  • 15. Create `ref.onRefresh` and `ref.onInvalidate` so that a provider can ensure it refreshes properly

    Is your feature request related to a problem? Please describe.

    I have run into a problem recently where if a FutureProvider depends on data from other FutureProviders, refreshing the top provider will not result in us getting any new data, as the lower level providers have already emitted a value.

    Example:

    final dataAProvider = FutureProvider<DataA>((ref) => http.get('/a'));
    final dataBProvider = FutureProvider<DataB>((ref) => http.get('/b'));
    
    final pageDataProvider = FutureProvider<PageModel>((ref) async {
       final dataA = await ref.watch(dataAProvider.future));
       final dataB = await ref.watch(dataBProvider.future));
    
       return PageModel(dataA, dataB);
    });
    

    I would like to be able to refresh the data required by my page by simply calling ref.refresh(pageDataProvider).

    Describe the solution you'd like

    If, similar to ref.onDispose, we had a ref.onRefresh, we could within a provider say what happens when the provider is refreshed, so that it can ensure it refreshes all of the data it needs.

    In my example from above it would look something like this:

    final pageDataProvider = FutureProvider<PageModel>((ref) async {
       ref.onRefresh(() {
          ref.refresh(dataAProvider);
          ref.refresh(dataBProvider);
       });
    
       final dataA = await ref.watch(dataAProvider.future));
       final dataB = await ref.watch(dataBProvider.future));
    
       return PageModel(dataA, dataB);
    });
    

    Describe alternatives you've considered

    To get around this for now, I have created separate refreshPageDataProvider that I call instead of being able to just refresh the provider.

    Update: Only just noticed this open PR

    • Any idea when this will be completed?
    • Any plans on also adding an onInvalidate?
    Reviewed by dominic-cot at 2022-05-12 10:47

Related

Simple global state management for Flutter

Slices Slices is a minimalist state manegement, focused specifically for applications that needs a global state where different "pieces" of the applic

Jun 15, 2021
MobX for the Dart language. Hassle-free, reactive state-management for your Dart and Flutter apps.
MobX for the Dart language. Hassle-free, reactive state-management for your Dart and Flutter apps.

Language: English | Português | Chinese mobx.dart MobX for the Dart language. Supercharge the state-management in your Dart apps with Transparent Func

Jun 22, 2022
A predictable state management library that helps implement the BLoC design pattern
A predictable state management library that helps implement the BLoC design pattern

A predictable state management library that helps implement the BLoC design pattern. Package Pub bloc bloc_test flutter_bloc angular_bloc hydrated_blo

Jun 27, 2022
Another state management solution

VxState VxState is a state management library built for Flutter apps with focus on simplicity. It is inspired by StoreKeeper & libraries like Redux, V

Jun 20, 2022
A flutter boilerplate project with GetX state management.
A flutter boilerplate project with GetX state management.

flutter_getx_boilerplate Languages: English (this file), Chinese. Introduction During my study of Flutter, I have been using the flutter_bloc state ma

Jun 30, 2022
Manage the state of your widgets using imperative programming concepts.

Imperative Flutter Manage the state of your widgets using imperative programming concepts. Setup Intall imperative_flutter package in pubspec.yaml dep

Jan 9, 2022
Flutter MVU architecture/state management package

mvu_flutter No mutability. No builders. No connectors. No reducers. No StreamControllers and subscription management. A truly declarative state manage

Jul 29, 2021
The modular state management solution for flutter.
The modular state management solution for flutter.

The modular state management solution for flutter. Easy debugging : each event is predictable and goes into a single pipeline Centralized state : soli

Jan 5, 2022
Example of use bloc + freezed with a state that contains a list

blocfreezedlistexample A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you s

Mar 21, 2022
London App Brewery State Management Project

todey_flutter A new Flutter application. Getting Started This project is a starting point for a Flutter application. A few resources to get you starte

Nov 1, 2021
Timer based on provider state manager

timer_provider Timer based on provider state manager Getting Started This project is a starting point for a Flutter application. A few resources to ge

Nov 6, 2021
Flutter State Management with provider :rocket:
Flutter State Management with provider :rocket:

Flutter - Gerenciamento de Estados com Provider Objetivos ao completar os estudos Aprenda a gerenciar o estado da sua aplicação com Single Source of T

Dec 6, 2021
InheritedWidgets, but simple
InheritedWidgets, but simple

English | Português | 简体中文 | Español A wrapper around InheritedWidget to make them easier to use and more reusable. By using provider instead of manua

Jun 26, 2022
Functional programming in Dart and Flutter. All the main functional programming types and patterns fully documented, tested, and with examples.

Fpdart Functional programming in Dart and Flutter. All the main functional programming types and patterns fully documented, tested, and with examples.

Jun 24, 2022
Megaflixz is a movie app for the movie lover in you. Includes all new and upcoming movies and TV series. Also has an option to save movies to watch later list
Megaflixz is a movie app for the movie lover in you. Includes all new and upcoming movies and TV series. Also has an option to save movies to watch later list

MegaflixZ MegaflixZ is a movie app for the movie lover in you. It is an app that displays new and upcoming movies and tv series. Team members Deepak M

Aug 23, 2021
Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get.
Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get.

Languages: English (this file), Indonesian, Urdu, Chinese, Brazilian Portuguese, Spanish, Russian, Polish, Korean, French. About Get Installing Counte

Jul 2, 2022
App to Watch Animes And Read Manga.
App to Watch Animes And Read Manga.

App To Watch Anime And Read Manga NOTE: Manga Section Contains 18+ content. Sometimes App may take some time to load. Constants Folder is removed from

Sep 12, 2021
A simple and robust way to interact with Anilibria API.

anilibria.dart A simple and robust way to interact with Anilibria API. Example import 'package:anilibria/anilibria.dart'; void main() async { final

Jun 13, 2022
This is not an app. I made this architecture to build robust and easy-to-maintain products but in a faster way.

simple_architecture_flutter This is not an app. I made this architecture to build robust and easy-to-maintain products but in a faster way. Info I use

May 22, 2022
A simple app to demonstrate a testable, maintainable, and scalable architecture for flutter. flutter_bloc, hive, and REST API are some of the tech stacks used in this project.
A simple app to demonstrate a testable, maintainable, and scalable architecture for flutter. flutter_bloc, hive, and REST API are some of the tech stacks used in this project.

last_fm A simple app to demonstrate a testable, maintainable, and scalable architecture for flutter. flutter_bloc, hive, and REST API are some of the

May 22, 2022