Рубрики
Flutter

Flutter Provider — объяснение работы

В библиотеке Flutter Provider представляет собой смесь Инъекции Зависимостей (Dependency Injection или DI) и управления состоянием, построенная с помощью виджетов и для виджетов.

Он использует Флаттер-виджеты для управления состоянием вместо Дарт классов, таких как Steam. Причина в том, что виджеты очень просты, но надежны и масштабируемы.

Содержание статьи:

Используя виджеты для управления состоянием, провайдер может гарантировать:

  • удобное управление кодом, благодаря принудительному однонаправленному потоку данных
  • тестируемость и компонуемость, так как всегда можно переопределять значение
  • надежность, так как тяжело забыть обработать сценарий обновления модели или виджета

Страница пакета pub.dev/packages/provider

Установка значения Flutter Provider #

Для установки значения переменной, используя провайдер, оберните один из виджетов в виджет Provider (провайдер) и передайте ему свою переменную. После чего все потомки этого виджета смогу получить доступ к ней.

В качестве примера — можно обернуть все приложение в виджет Provider и передать ему нашу переменную:

Provider<String>.value(
  value: 'Hello World',
  child: MaterialApp(
    home: Home(),
  )
)

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

Следующий код создает и предоставляет класс MyComplexClass. И в случае, когда провайдер удаляется из дерева виджетов, экземпляр MyComplexClass будет удален.

Provider<MyComplexClass>(
  create: (context) => MyComplexClass(),
  dispose: (context, value) => value.dispose()
  child: SomeWidget(),
)

Чтение значения установленной переменной Flutter Provider #

Простой способ чтения значения — это использование статического метода Provider.of<T>(BuildContext context).

Этот метод будет искать в дереве виджетов, начиная с виджета, связанного с переданным BuildContext(родительский виджет), и он вернет ближайшую найденную переменную типа T или вернет Throw, если ничего не найдено.

В сочетании с первым примером, где мы установили значение, этот виджет будет читать наше значение (value) типа String и отображать «Hello World»:

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text(
      /// Не забывайте передавать тип объекта, который вы хотите получить
      Provider.of<String>(context)
    );
  }
}

В качестве альтерантивы Provider.of, мы можем использовать Consumer and Selector.

Они могут быть полезны для оптимизации производительности, или же при трудностях получения BuildContext провайдера.

MultiProvider

Используйте MultiProvider, когда хотите передать несколько значений ниже по дереву.

Dart Flutter

Когда вы передаете несколько значений, Flutter Provider может стать слишком нечитабельным, например:

Provider<Foo>(
  create: (_) => Foo(),
  child: Provider<Bar>(
    create: (_) => Bar(),
    child: Provider<Baz>(
      create: (_) => Baz(),
      child: someWidget,
    )
  )
)

В подобной ситуации, лучше использовать MultiProvider, для повышения читаемости:

MultiProvider(
  providers: [
    Provider<Foo>(create: (_) => Foo()),
    Provider<Bar>(create: (_) => Bar()),
    Provider<Baz>(create: (_) => Baz()),
  ],
  child: someWidget,
)

Поведение этих двух блоков кода совершенно одинаково, отличается только внешний вид кода.

ProxyProvider

Начиная с версии 3.0.0, появился новый вид провайдера: ProxyProvider.

ProxyProvider — это провайдер, который объединяет несколько значений из других провайдеров в новый объект и отправляет результат провайдеру.

Этот новый объект будет обновляться всякий раз, когда один из провайдеров зависящий от обновлений (например ChangeNotifierProvider).

В следующем примере Прокси-провайдер используется для построения переводов на основе счетчика, поступающего от другого провайдера:

Widget build(BuildContext context) {
  return MultiProvider(
    providers: [
      ChangeNotifierProvider(create: (_) => Counter()),
      ProxyProvider<Counter, Translations>(
        create: (_, counter, __) => Translations(counter.value),
      ),
    ],
    child: Foo(),
  );
}

class Translations {
  const Translations(this._value);

  final int _value;

  String get title => 'You clicked $_value times';
}

Consumer #

Иногда необходимо, чтобы только некоторые виджеты обновлялись, а не все вложенные. Для этого вместо Provider.of используем Consumer или Selector.

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

Foo(
  child: Consumer<A>(
    builder: (_, a, child) {
      return Bar(a: a, child: child);
    },
    child: Baz(),
  ),
)

В этом примере только Bar будет перестроен, когда обночиться A. Но Foo и Baz не будут при этом перестраиваться.

Selector #

Чтобы продвинуться еще глубже, можно использовать Selector для игнорирования изменений, если они не влияют на дерево виджетов:

Selector<List, int>(
  selector: (_, list) => list.length,
  builder: (_, length, __) {
    return Text('$length');
  }
);

Этот фрагмент будет перестроен только если длина списка изменится. Но не будет обновляться при обновлении элементов списка.

5 ответов к “Flutter Provider — объяснение работы”

Добавить комментарий