задний план
давно, в нашейQQ группаМой друг пытался вытащить меня[Provider](https://github.com/rrousselGit/provider)
учебники, но я никогда не был предан этому. Потому что я думаю, что если вы пишете туториал на уровне ворот, то уже есть официальные документы, и кто-то уже это написал, если вы хотите углубляться, я не буду. Но в последнее время все не так, из-за гидрологии.
государственное управление
Говоря оFlutter
, мы вряд ли можем избежать управления состоянием. дляReact
Управление состоянием знакомо разработчикам , но все еще несколько незнакомо таким разработчикам, как мы.Flutter
является декларативным, что означаетFlutter
Является отражением текущего состояния приложения путем обновления пользовательского интерфейса:
Flutter
, если мы хотим обновить наши элементы управления, самый простой способ должен бытьsetState()
.如果说我们一个页面里的组件不多,直接使用setState()
Это не проблема, но на практике наш макет страницы все еще достаточно сложен.
Один случай, когда мы находимся на странице:
Если мы возьмем всеWidget
Они все прописаны в класс, этот класс точно будет толстяком больше 200 фунтов, и в него легко попасть{{{{}}}}
вихрь. тогда будем думатьWidget
раскол, но в это время, если только опираясь наsetState()
, вы обнаружите, что это будет очень болезненно, потому чтоsetState()
ограничивается только какWidget
, то есть если вы только на днеWidget
вызыватьsetState
не обновляет верхний уровеньWidget
, что означает, что вы делаете это с помощью обратных вызовов, и в процессе вы обнаружите некоторыеWidget
Переменные в классе должны быть неизменяемыми, что вызовет другие неприятности, не говоря уже о них.
Вообще говоря, в реальной разработке очень возможно обмениваться данными между страницами:
На приведенном выше рисунке показана функция корзины, когда пользователь нажимаетAdd
, продукт будет добавлен в корзину, и когда мы нажмем на корзину, мы сразу увидим продукт. Подумайте, как мы должны реализовать это, не используя управление состоянием?
Сказав так много, я просто хочу сказать, что использование управления состоянием более важно. Проще говоря, как легко и быстроWidget
Делитесь данными между собой и отображайте данные на странице.
Flutter
Методы управления состоянием включают, но не ограничиваютсяProvider
,Bloc
,Redux
так же какFish-Redux
.
-
Bloc
Если быть точным, то это концепция, и это также первое управление состоянием, которое я использовал.Сейчас есть соответствующие библиотеки реализации, которые в основном основаны на реактивном программировании. -
Redux
дляReact
В конце концов, это знакомо разработчикам.Flutter
Это также заимствовано изReact
. -
Fish-Redux
Родился изRedux
, выпускаемый Али, в целом более сложен и подходит для средних и крупных проектов. Общество теперь рождаетсяFish-Redux
Инструменты для кода шаблона -
Provider
даGoogle
Рекомендуемое управление состоянием также является вторым используемым мной управлением состоянием, которое является относительно простым и беззаботным.
Далее я кратко представлюProvider
использование.
Первое знакомство с провайдером
Provider
На самом деле даInheritedWidgetупаковка. по сравнению с прямым использованиемInheritedWidget
,использоватьProvider
Есть много преимуществ, таких как упрощение выделения и удаления ресурсов, поддержка отложенной загрузки и т. д.
Provider
предоставляет нам различные видыProvider
. Для просмотра всех видовprovider
можно нажатьздесь
name | description |
---|---|
Provider | Самый простой провайдер. Он принимает значение и предоставляет это значение, независимо от того, что это за значение. |
ListenableProvider | дляListenable объект созданprovider .ListenableProvider будет прослушивать изменения объекта, покаListenableProvider слушатель называется,ListenableProvider Управления, которые зависят от провайдера, будут восстановлены. |
ChangeNotifierProvider |
ChangeNotifierProvider это особыйListenableProvider , который основан наChangeNotifier , и при необходимости он автоматически вызоветChangeNotifier.dispose . |
ValueListenableProvider | мониторValueListenable и выставит толькоValueListenable.value . |
StreamProvider | монитор одинStream И выставьте последнее отправленное значение внешнему миру. |
FutureProvider | нести одинFuture ,когдаFuture Когда это сделано, он обновляет элементы управления, которые зависят от него. |
Ввиду отсутствия у меня таланта и знаний в этой статье не будет описываться как использовать различныеProvider
, поэтому в этой статье выбран тот, который я использую чаще всегоChangeNotifierProvider
Объяснить, я надеюсь, что смогу бросить кирпич для привлечения нефрита.
Создать провайдера
Обычно создаютProvider
Есть два способа:
- конструктор по умолчанию
-
.value
Метод строительства
Когда мы хотим создать новый объект, мы хотим использовать конструктор по умолчанию вместо использования.value
конструктор, потому что если мы передаем.value
Создание объекта может вызвать утечку памяти или некоторые неожиданные проблемы.
Вот краткое объяснение, почему его нельзя использоватьvalue
Создайте объект, хороший английский видноОригинальный StackOverflow. потому чтоFlutter
серединаbuild
Метод должен быть чистым и без побочных эффектов, многие внешние факторы спровоцируютrebuild
, Например:
- Маршрутизация pop/push
- Изменение размера экрана, обычно из-за смены клавиатуры или изменения ориентации экрана.
- Родительский контроль перерисовывает дочерний контроль
- зависит от
InheritedWidget
Элемент управления (раздел Class.of(context)) изменился
Итак, используя.value
Проблема с созданием объекта заключается в том, что он делаетbuild
Стать чистым или иметь побочные эффекты, вызываемые извнеbuild
Звонок стал очень неприятным.
Этот вопрос до сих пор, друзья, которые любят учиться, могут исследовать себя.
-
хочуиспользовать
Provider
изcreate
объект создан в .
Provider(
create: (_) => MyModel(),
child: ...
)
-
не хочуиспользовать
Provider.value
Создавайте объекты.
ChangeNotifierProvider.value(
value: MyModel(),
child: ...
)
- не хочуСоздавайте объекты из переменных, которые могут меняться со временем. Потому что в этом случае созданный нами объект не будет обновляться, даже если ссылочная переменная изменится.
int count;
Provider(
create: (_) => MyModel(count),
child: ...
)
Если вы хотите передать в наш объект переменные, которые со временем изменяются, рассмотрите возможность использованияProxyProvider
:
int count;
ProxyProvider0(
update: (_, __) => MyModel(count),
child: ...
)
Примечания: При использовании
Provider
изcreate/update
При обратном звонке следует учитывать, что по умолчаниюcreate/update
Звонок вызывается лениво. Это означает, что только мыProvider
Данные запрашиваются хотя бы один раз,create/update
будет называться. Если мы хотим выполнить предварительную обработку, мы можем использоватьlazy
параметр для отключения этой функции:
MyProvider(
create: (_) => Something(),
lazy: false,
)
Чтение данных от провайдера
Самый простой способ прочитать данные — использоватьBuildContext
метод расширения:
- context.watch(), этот метод будет использовать соответствующий элемент управления для отслеживания изменений T.
- context.read(), этот метод возвращает T напрямую и не отслеживает изменения.
- context.select
(R cb(T value)), этот метод заставит соответствующий контроль контролировать только небольшую часть изменений T. Из названия мы знаем, что это фильтр.
Конечно, мы также можем использовать статические методы.Provider.of<T>(context)
, это иwatch/read
Поведение очень похожее, и таким же образом мы получаем данные перед методом расширения, описанным выше.
Эти методы ищут в управлении деревом и от прохожденияBuildContext
Связанные элементы управления для запуска, в конечном итоге возвращаются, чтобы найти и вернуть самую последнюю переменную типа T (если не найдено, выбросить).
Стоит отметить, что сложность этой операции составляет O(1). На самом деле, это не ходит по дереву управления.
Кстати говоря, это не более чем перевод документа, теперь приступим к работе~~~
Show me the code
История еще изFlutter
счетчиков, так как вновь созданныйFlutter
Шаблон проекта - это счетчик, теперь мы используемChangeNotifierProvider
Сделаем простую трансформацию этого проекта.
- Буду
MyHomePage
Зависит отStatefulWidget
изменить наStatelessWidget
. - использовать
ChangeNotifierProvider
обновить страницу
первая версия
Во-первых, мы собираемся создатьChangeNotifier
:
class MyChangeNotifier extends ChangeNotifier {
int _counter = 0;
int get counter => _counter;
incrementCounter() {
_counter++;
notifyListeners();//要更新UI记得调用这个方法
}
}
В настоящее время мы нажимаемFloatingActionButton
будет вызван, когдаMyChangeNotifier
изincrementCounter
метод, следует отметить, что когда мы закончим обработку бизнеса, если нам нужно обновить пользовательский интерфейс, нам нужно вызватьnotifyListeners
ставить в известностьProvider
Обновить пользовательский интерфейс.
Далее мы реализуем наш пользовательский интерфейс.
Во-первых, мы собираемся создатьMyHomePage
, Макет пользовательского интерфейса напрямую использует макет в примере, разница в том, что мы используемStatelessWidget
. Затем мы проходимBuildContext
выигратьMyChangeNotifier
пример. Обратите внимание, что когда мы нажимаемFloatingActionButton
Мы не звонилиsetState
(ерунда,StatelessWidget
тоже не могуsetState
), но наш пользовательский интерфейс все равно будет обновляться. код показывает, как показано ниже:
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
MyChangeNotifier notifier =
Provider.of(context); //通过Provider.of(context)获取MyChangeNotifier
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'${context.watch<MyChangeNotifier>().counter}',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: notifier.incrementCounter,//点击时我们期望输出点击次数
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Теперь мы будем использоватьChangeNotifierProvider
пакетMyHomePage
, что гарантирует, чтоMyHomePage
сквозьBuildContext
получатьMyChangeNotifier
пример.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: ChangeNotifierProvider(
create: (_) => MyChangeNotifier(),
child: MyHomePage(title: 'Flutter Demo Home Page')),
);
}
}
Пока что код написан, запускаем, эффект точно такой же, как в примере?
Конечно, мы можем напрямую
MyChangeNotifier
Определите поле непосредственно вoutputMessage
, то непосредственно вMyHomePage
давать напрямуюText
Назначение.
Text(
context.watch<MyChangeNotifier>().outputMessage,
style: Theme.of(context).textTheme.headline4,
),
вторая версия
Теперь кажется, что мы научилисьChangeNotifierProvider
Основное использование, теперь мы собираемся сделать простое преобразование приведенного выше кода.
- Буду
ChangeNotifierProvider
перейти кMyHomePage
.
Очень просто, код выглядит следующим образом:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
final MyChangeNotifier notifier = MyChangeNotifier();
MyHomePage({Key key, this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => notifier,
child: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'${context.watch<MyChangeNotifier>().counter}',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: notifier.incrementCounter,//点击时我们期望输出点击次数
tooltip: 'Increment',
child: Icon(Icons.add),
),
),
);
}
}
Когда мы успешно запустили приведенный выше код, мы столкнулись с некоторыми проблемами:
Когда я впервые столкнулся с этой ошибкой, я не мог не сказать что-то, что начинается с F и заканчивается на U. Но мы не можем не решить проблему. В это время нам может понадобитьсяConsumer
.
Consumer
использование
Consumer
В нем нет магии самой по себе и нет причудливой реализации. Просто используйте его в новом элементе управленияProvider.of
, затем поместите элементы управленияbuild
Метод делегирован параметрамbuilder
. этоbuilder
будет вызываться несколько раз. Это так просто.
Consumer
Дизайн имеет два первоначальных намерения
- когда наш
BuildContext
Указанный не существует вProvider
час,Consumer
позвольте намProvider
Получить данные в . - Оптимизация производительности достигается за счет более мелкозернистых перерисовок.
Сейчас мы находимся в первом случае, что касается второго случая, читатели могут исследовать его сами.
Итак, мы можем добавитьConsumer
решить вышеуказанноеProviderNotFoundException
вопрос:
class MyHomePage extends StatelessWidget {
final String title;
final MyChangeNotifier notifier = MyChangeNotifier();
MyHomePage({Key key, this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => notifier,
child: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Consumer<MyChangeNotifier>(
builder: (_, localNotifier, __) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'${localNotifier.counter}',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: notifier.incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
),
);
}
}
Запускаем еще раз, все идеально?
предварительное резюме
Время ограничено, хотелось закончить на одном дыхании, но в век интернета, как можно стыдиться сказать, что я был в интернете? . .
так какProvider
Первая статья о начале работы, эта статья еще очень проста, ведь я просто изменил пример Flutter. В следующей статье я расскажу большеProvider
Использование и проблемы, также включают более сложные демонстрации.
Продолжение следует. . . Ожидать или не ожидать, что последнее слово будет за вами.