Как да използвам модела на доставчика в Flutter

В тази публикация ще разгледаме модела на доставчика във Flutter. Някои други модели, като BLoC Architecture, използват модела на доставчика вътрешно. Но моделът на доставчика е много по-лесен за научаване и има много по-малко шаблонния код.

В тази публикация ще вземем приложението Counter по подразбиране, предоставено от Flutter, и ще го рефакторираме, за да използваме шаблона на доставчика.

Ако искате да знаете какво казва екипът на Flutter в Google за модела на доставчика, вижте тази беседа за 2019 г.

Ако искате да научите повече за BLoC Architecture, вижте тук.

Приготвяме се да започнем

Създайте нов проект на Flutter и го кръстете, както искате.

Първо трябва да премахнем всички коментари, за да можем да работим с:

import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } 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 StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); } } 

Сега добавете зависимостта за модела на доставчика във pubspec.yamlфайла. По време на писането последната версия е 4.1.2.

Ето как pubspec.yamlще изглежда вашият файл сега:

name: provider_pattern_explained description: A new Flutter project. publish_to: 'none' version: 1.0.0+1 environment: sdk: ">=2.7.0 <3.0.0" dependencies: flutter: sdk: flutter provider: ^4.1.2 cupertino_icons: ^0.1.3 dev_dependencies: flutter_test: sdk: flutter flutter: uses-material-design: true 

Приложението по подразбиране е основно приспособление с състояние, което се възстановява всеки път, когато щракнете върху FloatingActionButton(което извиква setState()).

Но сега ще го преобразуваме в джаджа без гражданство.

Създаване на доставчика

Нека да продължим и да създадем нашия доставчик. Това ще бъде единственият източник на истина за нашето приложение. Тук ще съхраняваме нашето състояние, което в този случай е текущото броене.

Създайте клас с име Counterи добавете countпроменливата:

import 'package:flutter/material.dart'; class Counter { var _count = 0; } 

За да го конвертирате в клас на доставчик, разширете ChangeNotifierот material.dartпакета. Това ни предоставя notifyListeners()метода и ще уведомява всички слушатели, когато променим стойност.

Сега добавете метод за увеличаване на брояча:

import 'package:flutter/material.dart'; class Counter extends ChangeNotifier { var _count = 0; void incrementCounter() { _count += 1; } } 

В края на този метод ще извикаме notifyListeners(). Това ще задейства промяна в цялото приложение, която и джаджа да го слуша.

Това е красотата на модела на доставчика във Flutter - не е нужно да се грижите за ръчно изпращане в потоци.

И накрая, създайте гетер за връщане на стойността на брояча. Ще използваме това, за да покажем най-новата стойност:

import 'package:flutter/material.dart'; class Counter extends ChangeNotifier { var _count = 0; int get getCounter { return _count; } void incrementCounter() { _count += 1; notifyListeners(); } } 

Слушане на кликвания върху бутони

След като вече сме настроили доставчика, можем да го използваме в нашата основна джаджа.

Първо, нека да преобразуваме MyHomePageв приспособление без състояние, вместо такова. Ще трябва да премахнем setState()обаждането, тъй като това е налично само в StatefulWidget:

import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } 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 { int _counter = 0; final String title; MyHomePage({this.title}); void _incrementCounter() {} @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); } } 

С това вече можем да използваме шаблона на доставчика във Flutter, за да зададем и получим стойността на брояча. При всяко щракване на бутона трябва да увеличим стойността на брояча с 1.

И така, в _incrementCounterметода (който се извиква при натискане на бутона) добавете този ред:

Provider.of(context, listen: false).incrementCounter();

Това, което се случва тук, е, че сте помолили Flutter да се качи в дървото на приспособленията и да намери първото място, където Counterе предоставено. (Ще ви кажа как да го предоставите в следващия раздел.) Ето какво Provider.of()прави.

Дженериците (стойности вътре скоби) кажете на Flutter какъв тип доставчик да търси. След това Flutter се изкачва през дървото на приспособленията, докато намери предоставената стойност. Ако никъде не е предоставена стойността, тогава се извежда изключение.

И накрая, след като се сдобиете с доставчика, можете да извикате всеки метод за него. Тук наричаме нашия incrementCounterметод.

Но ние също се нуждаем от контекст, така че приемаме контекста като аргумент и променяме onPressedметода, за да предадем и контекста:

void _incrementCounter(BuildContext context) { Provider.of(context, listen: false).incrementCounter(); } 

Забележка: Задали сме слушане на false, защото тук не е необходимо да слушаме никакви стойности. Ние просто изпращаме действие, което трябва да бъде извършено.

Предоставяне на Доставчика

Шаблонът на доставчика във Flutter ще търси най-новата предоставена стойност. Диаграмата по-долу ще ви помогне да разберете по-добре.

В тази диаграма ЗЕЛЕНИЯТ обект A ще бъде достъпен за останалите елементи под него, т.е. B, C, D, E и F.

Сега да предположим, че искаме да добавим някаква функционалност към приложението и създадем друг доставчик, Z. Z се изисква от E и F.

И така, къде е най-доброто място да добавите това?

Ние можем да го добавите към корена горе A . Това ще работи:

Но този метод не е много ефективен.

Flutter ще премине през всички приспособления по-горе и след това най-накрая ще премине към корена. Ако имате много дълги дървета на джаджи - което определено ще имате в производствено приложение - тогава не е добра идея да поставяте всичко в основата.

Вместо това можем да разгледаме общия знаменател на E и F. Това е C. Така че, ако поставим Z точно над E и F, ще работи.

But what if we want to add another object X that'srequired by E and F? We'll do the same thing. But notice how the tree keeps growing.

There’s a better way to manage that. What if we provide all the objects at one level?

This is perfect, and is how we’ll eventually implement our provider pattern in Flutter. We’ll make use of something called MultiProviderwhich lets us declare multiple providers at one level.

We'll get MultiProvider to wrap the MaterialApp widget:

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MultiProvider( providers: [ ChangeNotifierProvider.value( value: Counter(), ), ], child: MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(title: "AndroidVille Provider Pattern"), ), ); } } 

With this, we’ve provided the provider to our widget tree and can use it anywhere below this level in the tree.

There’s just one more thing left: we need to update the value that's displayed.

Updating the text

To update the text, get the provider in the build function of your MyHomePage widget. We’ll use the getter we created to get the latest value.

Then just add this value to the text widget below.

And we’re done! This is how your final main.dart file should look:

import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:provider_pattern_explained/counter.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MultiProvider( providers: [ ChangeNotifierProvider.value( value: Counter(), ), ], child: MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(title: "AndroidVille Provider Pattern"), ), ); } } class MyHomePage extends StatelessWidget { final String title; MyHomePage({this.title}); void _incrementCounter(BuildContext context) { Provider.of(context, listen: false).incrementCounter(); } @override Widget build(BuildContext context) { var counter = Provider.of(context).getCounter; return Scaffold( appBar: AppBar( title: Text(title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'You have pushed the button this many times:', ), Text( '$counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () => _incrementCounter(context), tooltip: 'Increment', child: Icon(Icons.add), ), ); } } 

Note: We haven’t set listen:false in this case because we want to listen to any updates in the count value.

Here's the source code on GitHub if you want to have a look: //github.com/Ayusch/Flutter-Provider-Pattern.

Let me know if you have any issues.

Welcome to AndroidVille :)

AndroidVille is a community of Mobile Developers where we share knowledge related to Android Development, Flutter Development, React Native Tutorials, Java, Kotlin and much more.

Click on this link to join the AndroidVille SLACK workspace. It’s absolutely free!

If you liked this article, feel free to share it on Facebook or LinkedIn. You can follow me on LinkedIn, Twitter, Quora, and Medium where I answer questions related to mobile development, Android, and Flutter.