Sådan bruges udbydermønsteret i Flutter

I dette indlæg ser vi på udbydermønsteret i Flutter. Nogle andre mønstre, såsom BLoC Architecture, bruger udbyderens mønster internt. Men udbyderens mønster er langt lettere at lære og har meget mindre kedelpladekode.

I dette indlæg tager vi standardtællerappen fra Flutter og refaktorer den for at bruge udbydermønsteret.

Hvis du vil vide, hvad Flutter-teamet hos Google siger om udbydermønsteret, skal du tjekke denne 2019-tale.

Hvis du vil lære mere om BLoC-arkitektur, skal du tjekke det her.

Kom godt i gang

Opret et nyt Flutter-projekt, og navngiv det, hvad du vil.

Først skal vi fjerne alle kommentarerne, så vi har en ren skifer at arbejde med:

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

Tilføj nu afhængigheden af ​​udbydermønsteret i pubspec.yamlfilen. I skrivende stund er den seneste version 4.1.2.

Sådan ser din pubspec.yamlfil ud nu:

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 

Standardappen er dybest set en stateful widget, der genopbygges hver gang du klikker på FloatingActionButton(som kalder setState()).

Men nu skal vi konvertere det til en statsløs widget.

Oprettelse af udbyderen

Lad os gå videre og oprette vores udbyder. Dette vil være den eneste kilde til sandhed for vores app. Det er her, vi gemmer vores tilstand, som i dette tilfælde er det aktuelle antal.

Opret en klasse med navnet Counterog tilføj countvariablen:

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

For at konvertere det til en udbyderklasse skal du udvide ChangeNotifierfra material.dartpakken. Dette giver os notifyListeners()metoden og giver alle lytterne besked, når vi ændrer en værdi.

Tilføj nu en metode til at øge tælleren:

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

I slutningen af ​​denne metode ringer vi notifyListeners(). Dette vil udløse en ændring overalt i appen, uanset hvilken widget der lytter til den.

Det er skønheden i udbydermønsteret i Flutter - du behøver ikke bekymre dig om manuelt at sende til streams.

Opret endelig en getter for at returnere tællerværdien. Vi bruger dette til at vise den seneste værdi:

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

Lytte til klik på klik

Nu hvor vi har konfigureret udbyderen, kan vi gå videre og bruge den i vores hovedwidget.

Lad os først konvertere MyHomePagetil en statsløs widget i stedet for en stateful. Vi bliver nødt til at fjerne setState()opkaldet, da det kun er tilgængeligt i 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), ), ); } } 

Når dette er gjort, kan vi nu bruge udbydermønsteret i Flutter til at indstille og få tællerværdien. For hvert knap skal vi øge tællerværdien med 1.

_incrementCountertilføj denne linje i metoden (som kaldes, når der trykkes på knappen) denne linje:

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

Hvad der sker her er, at du har bedt Flutter om at gå op i widgetræet og finde det første sted, hvor Counterder findes. (Jeg fortæller dig, hvordan du giver det i næste afsnit.) Dette er hvad der Provider.of()gør.

Generics (værdier indeni parenteser) fortæller Flutter, hvilken type udbyder han skal se efter. Derefter går Flutter op gennem widgetræet, indtil den finder den angivne værdi. Hvis værdien ikke gives nogen steder, kastes en undtagelse.

Endelig, når du har fået udbyderen, kan du ringe til en hvilken som helst metode på den. Her kalder vi vores incrementCountermetode.

Men vi har også brug for en kontekst, så vi accepterer konteksten som et argument og ændrer onPressedmetoden for også at passere konteksten:

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

Bemærk: Vi har indstillet lyt til falsk, fordi vi ikke behøver at lytte til nogen værdier her. Vi sender bare en handling, der skal udføres.

Levering af udbyderen

Udbydermønsteret i Flutter ser efter den seneste værdi, der er angivet. Diagrammet nedenfor hjælper dig med bedre at forstå.

I dette diagram vil det GRØNNE objekt A være tilgængeligt for resten af ​​elementerne under det, det vil sige B, C, D, E og F.

Antag nu, at vi vil tilføje nogle funktioner til appen, og vi opretter en anden udbyder, Z. Z kræves af E og F.

Så hvor er det bedste sted at tilføje det?

Vi kan tilføje den til roden over A . Dette ville fungere:

Men denne metode er ikke særlig effektiv.

Flutter går gennem alle widgets ovenfor og går endelig til roden. Hvis du har meget lange widgetræer - hvilket du helt sikkert vil have i en produktionsapp - er det ikke en god ide at sætte alt ved roden.

I stedet kan vi se på fællesnævneren for E og F. Det er C. Så hvis vi sætter Z lige over E og F, ville det fungere.

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!

Hvis du kunne lide denne artikel, er du velkommen til at dele den på Facebook eller LinkedIn. Du kan følge mig på LinkedIn, Twitter, Quora og Medium, hvor jeg besvarer spørgsmål relateret til mobiludvikling, Android og Flutter.