Виджеты не обновляются при изменении поставщика Riverpod вне пользовательского интерфейса

Я пытаюсь обновить значение внутри своего провайдера извне пользовательского интерфейса, как описано в документации:

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

Внутри моего метода setMode я вызываю notifyListeners (). Теперь проблема в том, что мои виджеты не перестраиваются, хотя значение в моем провайдере успешно изменилось и уведомило его слушателей. Виджеты слушают вот так:

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

При обновлении поставщика из внутри пользовательского интерфейса виджеты перестраиваются, как и ожидалось.

Что мне нужно сделать, чтобы мой пользовательский интерфейс правильно перестроился и в этом случае?


person Chrispy11    schedule 30.01.2021    source источник


Ответы (1)


В этом случае документы несколько вводят в заблуждение. Это правда, что вы можете получить доступ к Provider без контекста таким образом, но вы также создаете экземпляр нового ProviderContainer, в котором хранится состояние всех ваших провайдеров. Поступая таким образом, вы создаете, а затем изменяете новый Notifier; это означает, что Notifier, который слушают ваши виджеты, остается нетронутым.

Один из способов использования провайдера вне дерева виджетов - это объявить ваш ProviderContainer вне ProviderScope. Будьте осторожны, так как это может привести к непредвиденным последствиям.

Замените свой main.dart код следующим:

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'),
            )
          ],
        ),
      ),
    );
  }
}
person mskolnick    schedule 31.01.2021
comment
Я понимаю, насколько я понял, основным преимуществом Riverpod была возможность доступа к поставщикам из любого места, но я думаю, что это не совсем так и полезно только для тестирования. Делать так, как вы показали, работает! Но, наверное, тогда, я думаю, мы не должны этого делать. Тогда можно и с Провайдером. - person Chrispy11; 31.01.2021
comment
@ Chrispy11 Riverpod никогда не утверждал, что вы можете получить доступ к провайдерам где угодно. Фактически, это функция, которой вы не можете. В противном случае это была бы просто глобальная переменная, что плохо для ремонтопригодности. Приведенный здесь код правильный (хотя я бы не советовал использовать ProviderContainer в качестве глобальной переменной). - person Rémi Rousselet; 31.01.2021
comment
@ RémiRousselet, есть ли у вас какие-нибудь советы, как вызвать провайдера вне ui, если не использовать глобальную переменную ProviderContainer? На этот раз мне нужно вызвать его из-за пределов пользовательского интерфейса для FirebaseMessaging фонового сообщения. - person Vayth; 23.02.2021
comment
Я тоже хотел бы знать это - вы получили какой-нибудь ответ? - person ajonno; 19.06.2021