Flutter Riverpod — использование метода чтения () внутри метода сборки

#flutter #flutter-provider

#flutter #flutter-provider

Вопрос:

Предположим, я хочу инициализировать текстовое поле, используя initialValue: свойство для a TextFormField , и мне нужно, чтобы мое начальное значение поступало от поставщика. Я читал в документах, что вызов read() изнутри метода сборки считается плохой практикой, но вызов из обработчиков — это нормально (например onPressed ). Итак, мне интересно, нормально ли вызывать read из initialValue свойства, как показано ниже?

введите описание изображения здесь

Ответ №1:

Нет, вы должны использовать useProvider , если вы используете хуки, или ConsumerWidget / Consumer , если вы этого не делаете.

Разница в том, initialValue что поле является частью метода сборки и, как вы сказали, onPressed является обработчиком вне метода сборки.

Основным аспектом поставщиков является оптимизация перестроек по мере изменения предоставленных значений. Использование context.read в методе сборки сводит на нет это преимущество, поскольку вы не слушаете предоставленное значение.

context.read Настоятельно рекомендуется использовать в анонимных функциях ( onChanged , onPressed , onTap , и т. Д.), Поскольку эти функции извлекают указанное значение во время выполнения функции.Это означает, что функция всегда будет выполняться с текущим значением этого поставщика, без необходимости прослушивать поставщика. Другие методы для поставщиков чтения используют прослушиватель, который является более дорогостоящим и ненужным в случае анонимных функций.

В вашем примере вы хотели установить initialValue a TextFormField . Ниже показано, как вы могли бы использовать hooks_riverpod и flutter_hooks для достижения этой цели.

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

  @override
  Widget build(BuildContext context) {
    return TextFormField(
      initialValue: useProvider(loginStateProv).email,
    );
  }
}
  

И для читателей, которые предпочитают не использовать хуки:

 class ConsumerWidgetExample extends ConsumerWidget {
  const ConsumerWidgetExample({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context, ScopedReader watch) {
    return TextFormField(
      initialValue: watch(loginStateProv).email,
    );
  }
}
  

Или:

 class ConsumerExample extends StatelessWidget {
  const ConsumerExample({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Consumer(
      builder: (context, watch, child) {
        return TextFormField(
          initialValue: watch(loginStateProv).email,
        );
      },
    );
  }
}
  

Основное отличие заключается в том, что Consumer он будет перестраивать только свои дочерние элементы, потому что только они полагаются на предоставленные данные.

Комментарии:

1. Спасибо, вроде как подозревал это. Я ценю информацию, поэтому, если я правильно понял, я должен сделать что-то вроде initialValue: useProvider(someProvider.select((value) => value.someField)), ?

2. предполагая, что я хочу отобразить someField . Кроме того, onChanged относится к той же категории onPressed , что и использование read() safe в onChanged на a TextFormField ?

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

4. Спасибо @alex-hartford, рад, что я нашел это — мне очень помогло!

5. @OmarEssam read , как и его имя, считывает значение во время вызова read . watch считывает значение во время вызова watch , но продолжает следить за обновлениями предоставленного значения и обновлять / перестраивать при появлении этих обновлений.