Widgets not updating when modifying a Riverpod Provider from outside the UI

2,660

The docs are somewhat misleading in this case. It is true that you can access a Provider without the context in that way, but you are also instantiating a new ProviderContainer which is where the state of all of your providers is stored. By doing it this way, you are creating then modifying a new Notifier; which means the Notifier your widgets are listening to is left untouched.

One way you could use a provider outside the widget tree is to declare your ProviderContainer outside the ProviderScope. Be careful with this as it could lead to unintended consequences.

Replace your main.dart code with this:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

//Provider container which holds the state of all my providers
//This would normally be inaccessible inside of the ProviderScope
final providerContainer = ProviderContainer();

//A function that accesses and uses myNotifierProvider ***Without needing a context***
void incrementCountWithoutContext() {
  var provider = providerContainer.read(myNotifierProvider);
  provider.incrementCount();
}

final myNotifierProvider = ChangeNotifierProvider((_) {
  return MyNotifier();
});

class MyNotifier extends ChangeNotifier {
  int count = 0;

  void incrementCount() {
    count++;
    notifyListeners();
  }
}

void main() {
  runApp(
    //Here is where we pass in the providerContainer declared above
    UncontrolledProviderScope(
      container: providerContainer,
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, ScopedReader watch) {
    final _provider = watch(myNotifierProvider);

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Text(
              '${_provider.count}',
              style: Theme.of(context).textTheme.headline4,
            ),
            ElevatedButton(
              //Increment count by accessing the provider the usual way
              onPressed: _provider.incrementCount,
              child: Text('Increment count the usual way'),
            ),
            ElevatedButton(
              //Increment the count using our global function
              //Notice no context is passed to this method
              onPressed: incrementCountWithoutContext,
              child: Text('Increment count without context'),
            )
          ],
        ),
      ),
    );
  }
}
Share:
2,660
Chrispy11
Author by

Chrispy11

Updated on November 28, 2022

Comments

  • Chrispy11
    Chrispy11 over 1 year

    I'm trying to update a value inside my Provider from outside the UI, as described in the docs:

    final container = riverpod.ProviderContainer();
    AppProvider _appProvider = container.read(appProvider);
    _appProvider.setMode(true);
    

    Inside my setMode method I call notifyListeners(). Now the problem is that my Widgets don't rebuild, even though the value in my provider successfully changed and notified its listeners. The widgets are listening like this:

    riverpod.Consumer(builder: (context, watch, child) {
        AppProvider _appProvider = watch(appProvider);
    ...
    

    When updating the provider from inside the UI, the widgets are rebuild as expected.

    What do I have to do to make my UI rebuild correctly in this case aswell?

  • Chrispy11
    Chrispy11 about 3 years
    I see, as I understood the main selling point of Riverpod was the possibility to access providers from anywhere, but I guess this is not entirely true and only useful for testing. Doing it the way you showed works! But probably not something we are supposed to do then I guess. Might as well stick with Provider then.
  • Rémi Rousselet
    Rémi Rousselet about 3 years
    @Chrispy11 Riverpod never claimed that you can access providers "everywhere". In fact, it is a feature that you can't. Otherwise that'd just be a global variable, which is bad for maintainability. The code given here is correct (although I would advise against having the ProviderContainer as a global variable).
  • Vayth
    Vayth about 3 years
    @RémiRousselet, Do you have any advice on how calling the the provider outside the ui, if not for using global ProviderContainer variable? For once, I need to call it from outside the ui for FirebaseMessaging background message.
  • ajonno
    ajonno almost 3 years
    I'd like to know this too - did you get any reply ?
  • SalahAdDin
    SalahAdDin almost 2 years
    @Vayth did you solve it?