Riverpode и Hooks в трепете, как реализовать StateNotifier для асинхронных вызовов?
Я новичок в трепетании, и недавно я решил изучить поставщиков, чтобы иметь возможность повторно использовать некоторые вызовы sqlite DB, которые я использую с помощью FutureBuilder, как предлагается в этом другом вопросе .
Прочитав об этом, я начал использовать riverpod_hooks, но застрял.
В настоящее время у меня есть провайдер для получения данных из sqlite:
final bloodPressureProvider = FutureProvider((_) => _findAllBloodPressures());
Future<List<BloodPressure>> _findAllBloodPressures() async {
var _bloodPressure = BloodPressure.forSearchOnly();
var result = await _bloodPressure.findAll();
return result;
}
Что я могу успешно использовать для визуализации диаграммы и отображения списка:
class BloodPressureList extends HookWidget {
@override
Widget build(BuildContext context) {
final bpList = useProvider(bloodPressureProvider);
return Scaffold(
drawer: NutriDrawer(context).drawer(),
appBar: AppBar(
title: Text('Blood Pressure History'),
),
body: Column(
children: [
Container(
padding: EdgeInsets.fromLTRB(0, 25, 15, 5),
child: bpList.when(
data: (bloodPressureList) => _buildLineChart(bloodPressureList),
loading: () => CircularProgressIndicator(),
error: (_, __) => Text('Ooooopsss error'),
),
),
Expanded(
child: bpList.when(
data: (bloodPressureList) => _getSlidableListView(bloodPressureList, context),
loading: () => CircularProgressIndicator(),
error: (_, __) => Text('Ooopsss error'),
),
),
],
), ...
Проблема, которая у меня сейчас есть, заключается в том, чтобы делать уведомление виджетам всякий раз, когда в БД добавляется новый элемент, который выполняется на другом экране / виджете, перед использованием поставщиков я бы использовал setState, но теперь я понимаю, что должен реализовать, возможно, StateNotifier, но я Мне не хватает правильного способа сделать это, интегрированного с асинхронным вызовом, возвращаемым из БД, используемой во фрагменте поставщика выше.
Приветствуются любые указатели в правильном направлении.
Обновление с решением: на основе принятого ниже ответа я использовал autodispose + context.refresh, и он сделал именно то, что я искал, изменения:
В провайдере добавлен модификатор autoDisposed:
final bloodPressureProvider = FutureProvider.autoDispose((_) => _findAllBloodPressures());
и на основе результата навигации, называемого context.refresh:
var result = Navigator.push(context, MaterialPageRoute(builder: (context) {
return BloodPressureDetail(title, bloodPressure);
}));
result.then((_) => context.refresh(bloodPressureProvider));
Таким образом, при возвращении с экрана редактирования / добавления контекст поставщика обновляется, а состояние диаграммы и списка обновляются автоматически.
1 ответ
По умолчанию FutureProvider кэширует результат будущего, поэтому он извлекается только при первом обращении. Если вы хотите получать новое значение при каждом переходе на экран, используйте FutureProvider.autoDispose. Если вы хотите обновить поставщика вручную, вы можете использовать context.refresh (bloodPressureProvider) с FutureProvider.
И если вы не хотите получать доступ к своей базе данных для получения значений, но все же хотите, чтобы экраны синхронизировались, вы можете реализовать stateNotifier, который будет представлять ваше состояние базы данных.
final dbState =
StateNotifierProvider<dbState>((ProviderReference ref) {
final dbState = DbState();
dbState.init();
return dbState;
});
class dbState extends StateNotifier<AsyncValue<List<String>>> {
/// when the constructor is called the initial state is loading
dbState() : super(AsyncLoading<List<String>>());
/// fetch the values from the database and set the state to AsyncData
///(could be good to add a try catch and set the sate to an error if one
/// occurs)
void init()async{
state = AsyncLoading<List<String>>();
final fetchedValues = await fetchValues();
state = AsyncData<List<String>>(fetchedValues);
}
}