A szolgáltatói minta használata a csapkodásban

Ebben a bejegyzésben megnézzük a szolgáltató mintáját a Flutterben. Néhány más minta, például a BLoC Architecture, a szolgáltatói mintát használja belsőleg. De a szolgáltatói mintát sokkal könnyebb megtanulni, és sokkal kevesebb kazán kódja van.

Ebben a bejegyzésben átvesszük a Flutter által biztosított alapértelmezett Counter alkalmazást, és átdolgozzuk a szolgáltatói minta használatához.

Ha tudni szeretné, mit mond a Google Flutter csapata a szolgáltatói mintáról, nézze meg ezt a 2019-es beszélgetést.

Ha többet szeretne megtudni a BLoC Architecture-ről, nézze meg itt.

Elkezdeni

Hozzon létre egy új Flutter projektet, és nevezze el, amit csak akar.

Először el kell távolítanunk az összes megjegyzést, hogy tiszta lappal rendelkezzünk:

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), ), ); } } 

Most adja hozzá a szolgáltatói minta függőségét a pubspec.yamlfájlba. A cikk írásakor a legújabb verzió a 4.1.2.

Így pubspec.yamlnéz ki most a fájl:

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 

Az alapértelmezett alkalmazás alapvetően egy állapotfigyelő widget, amely minden egyes alkalommal, amikor rákattint a FloatingActionButton(amely hív setState()), újjáépül .

De most hontalan modullá alakítjuk át.

A Szolgáltató létrehozása

Menjünk előre, és hozzuk létre a szolgáltatónkat. Ez lesz az egyetlen igazságforrás az alkalmazásunkhoz. Itt tároljuk az állapotunkat, amely ebben az esetben az aktuális szám.

Hozz létre egy nevű osztályt, Counterés add hozzá a countváltozót:

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

Szolgáltató osztályra konvertálásához bővítse ChangeNotifierki a material.dartcsomagból. Ez biztosítja számunkra a notifyListeners()módszert, és értesíti az összes hallgatót, ha megváltoztatunk egy értéket.

Most adjon hozzá egy módszert a számláló növelésére:

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

A módszer végén felhívjuk notifyListeners(). Ez kiváltja az alkalmazást az egész alkalmazásban, amelyik modult hallgatja.

Ez a szépség a szolgáltatói mintának a Flutterben - nem kell törődnie azzal, hogy manuálisan küldje el a patakoknak.

Végül hozzon létre egy gettert a számlálóérték visszaadásához. Ezt használjuk a legfrissebb érték megjelenítésére:

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

Gombkattintások hallgatása

Most, hogy beállítottuk a szolgáltatót, folytathatjuk és használhatjuk a fő widgetünkben.

Először térjünk át MyHomePagehontalan widget-re államis helyett. El kell távolítanunk a setState()hívást, mivel ez csak a következőkben érhető el 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), ), ); } } 

Ezzel elkészülhetünk a Flutter szolgáltatói mintájával a számláló értékének beállításához és megszerzéséhez. Minden gombnyomásra 1-gyel kell növelnünk a számláló értékét.

Tehát a _incrementCountermetódusban (amelyet a gomb megnyomásakor hívnak meg) adja hozzá ezt a sort:

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

Az történik itt, hogy megkérted Fluttert, hogy menjen fel a widget-fába, és keresse meg az első helyet, ahol Counterrendelkezésre áll. (A következő szakaszban elmondom, hogyan kell szolgáltatni.) Ez az, ami Provider.of().

A generikumok (értékek belül zárójelben) mondja meg Flutternek, hogy milyen típusú szolgáltatót kell keresnie. Ezután a Flutter felmegy a widgetfán, amíg meg nem találja a megadott értéket. Ha az érték sehol nincs megadva, akkor kivételt vetünk.

Végül, miután megkapta a szolgáltatót, bármelyik módszert felhívhatja rajta. Itt nevezzük a incrementCountermódszerünket.

De szükségünk van egy kontextusra is, ezért elfogadjuk a kontextust érvként, és megváltoztatjuk a onPressedmetódust a kontextus átadásához is:

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

Megjegyzés: A hamis beállítást beállítottuk, mert itt nem kell semmilyen értéket hallgatnunk. Csak végrehajtandó műveletet küldünk.

Szolgáltató biztosítása

A Flutter szolgáltatói mintája a legújabb értéket keresi. Az alábbi ábra segít jobban megérteni.

Ezen a diagramon a zöld objektumot Egy elérhető lesz, hogy a többi elem alatta, azaz a B, C, D, E, és F

Tegyük fel, hogy hozzá akarunk adni bizonyos funkciókat az alkalmazáshoz, és létrehozunk egy másik szolgáltatót, Z. Z-t E és F szükséges.

Tehát hol lehet a legjobban hozzáadni?

Hozzáadhatjuk az A feletti gyökhöz . Ez működne:

De ez a módszer nem túl hatékony.

A Flutter végigmegy az összes fenti eszközön, majd végül a gyökérhez megy. Ha nagyon hosszú kütyüfáid vannak - amelyeket mindenképp meg fogsz valósítani egy produkciós alkalmazásban -, akkor nem célszerű mindent a gyökérbe helyezni.

Ehelyett megnézhetjük E és F. közös nevezőjét. Ez C. Az tehát, ha Z-t éppen E és F fölé tesszük, akkor működne.

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.