Flutter RiverPod: Как добавить в FutureProvider постпроцесс, который запускается каждый раз?

В своем приложении я использую riverpod и flutter_tts, чтобы прочитать вслух HTTP-ответ. Во-первых, я добавил его после получения данных в FutureProvider следующим образом:

final futureProvider = FutureProvider.family<String, String>((ref, itemId) async {
  // fetch data from server
  final result = await Future.delayed(Duration(seconds: 1)).then((value) => "$itemId");
  await SpeakResult(result); // speak here
  return result;
});

но он был прочитан вслух ТОЛЬКО в первый раз, даже если FutureProvider вызывался несколько раз с одним и тем же itemId.

Во-вторых, я добавил его в метод build, но он ВСЕГДА читался вслух при пересборке виджета.

useProvider(futureProvider("item id")).when(
  loading: () => CircularProgressIndicator(),
  error: (error, stackTrace) => Text("$error"),
  data: (value) async {
    await SpeakResult(value);
    return Text("$value");
  },
);

Есть идея решить это?

Полный код здесь:


final futureProvider = FutureProvider.autoDispose.family<String, String>((ref, id) async {
  ref.onDispose(() => print("disposed"));
  // fetch data from server
  final result = Future.delayed(Duration(seconds: 1)).then((value) => "$id");
  // await SpeakResult(result);
  return result;
});

final asyncListProvider = StateNotifierProvider((_) => AsyncList());

class AsyncList extends StateNotifier<List<AutoDisposeFutureProvider<String>>> {
  AsyncList() : super([]);

  void add(String id) {
    /*
      //I want to do something like this:
      final asyncValue = futureProvider(id).whenData((value) async {
        await SpeakResult(result);
        return value;
      });
    */
    final asyncValue = futureProvider(id);
    state = [asyncValue, ...state];
  }
}

class MyHomePage extends HookWidget {
  const MyHomePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final list = useProvider(asyncListProvider.state);
    return ListView(
      children: [
        RaisedButton(
          child: Text("Add data"),
          onPressed: () {
            context.read(asyncListProvider).add("item id");
          },
        ),
        for (final item in list)
          useProvider(item).when(
            loading: () => ListTile(
              title: Center(child: CircularProgressIndicator()),
            ),
            error: (error, stackTrace) => Text("$error"),
            data: (value) {
              // await SpeakResult(value);
              return Text("$value");
            },
          ),
      ],
    );
  }
}

person limenote    schedule 19.09.2020    source источник


Ответы (1)


Одна из замечательных особенностей использования поставщиков заключается в том, что они эффективны, потому что они вычисляют значение только один раз, если только что-то не изменится, что может изменить это значение.

Причина, по которой вы видите поведение в своем первом примере:

но он был прочитан вслух ТОЛЬКО в первый раз, даже если FutureProvider вызывался несколько раз с одним и тем же itemId.

происходит из-за собственности провайдеров, о которых я упоминал выше. Если вы добавите декоратор autoDispose в свой futureProvider, я считаю, что это решит вашу проблему.

Кроме того, причина, по которой это всегда читается вслух во втором примере, заключается в том, что все в методе сборки всегда перестраивается, когда что-либо изменяется, даже если это не является визуально очевидным. Это восходит к моему первому пункту об эффективности поставщиков, потому что вам не нужно повторно оценивать предоставленное значение, если вы явно не хотите такого поведения.

person Alex Hartford    schedule 22.09.2020
comment
В моем полном коде все провайдеры постоянно отслеживаются, чтобы показать статус или результат. Так что я думаю, что все они никогда не утилизируются. - person limenote; 24.09.2020
comment
В этом проблема, вы должны добавить декоратор autoDispose в свой futureProvider. - person Alex Hartford; 24.09.2020
comment
Отредактировал код этого выпуска. Я добавил декоратор autoDispose, но он не был удален. И я не хочу избавляться от этого провайдера. Возвращать кешированный HTTP-ответ, но каждый раз выполнять постобработку. - person limenote; 25.09.2020