În acest post vom arunca o privire asupra modelului furnizorului în Flutter. Unele alte tipare, cum ar fi BLoC Architecture, utilizează tiparul furnizorului intern. Dar modelul furnizorului este mult mai ușor de învățat și are mult mai puțin cod de cazan.

În această postare, vom lua aplicația implicită Counter furnizată de Flutter și o vom refactura pentru a utiliza modelul furnizorului.

Dacă doriți să știți ce are de spus echipa Flutter de la Google despre modelul furnizorului, verificați acest discurs din 2019.

Dacă doriți să aflați mai multe despre BLoC Architecture, verificați-o aici.

Noțiuni de bază

Creați un nou proiect Flutter și denumiți-l oricum doriți.

ad-banner

Mai întâi trebuie să eliminăm toate comentariile, astfel încât să avem o listă curată cu care să lucrăm:

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<MyHomePage> {
  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: <Widget>[
            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),
      ),
    );
  }
}

Acum adăugați dependența pentru modelul furnizorului în pubspec.yaml fişier. La momentul scrierii, ultima versiune este 4.1.2.

Iată cum pubspec.yaml fișierul va arăta acum:

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

Aplicația implicită este practic un widget de stare care se reconstruiește de fiecare dată când faceți clic pe FloatingActionButton (care apelează setState() ).

Dar acum îl vom converti într-un widget fără stare.

Crearea furnizorului

Să mergem mai departe și să creăm furnizorul nostru. Aceasta va fi singura sursă de adevăr pentru aplicația noastră. Aici vom stoca starea noastră, care în acest caz este numărul curent.

Creați o clasă numită Counter și adăugați count variabil:

import 'package:flutter/material.dart';

class Counter {
  var _count = 0;
}

Pentru a-l converti într-o clasă de furnizor, extindeți ChangeNotifier de la material.dart pachet. Acest lucru ne oferă notifyListeners() și vom informa toți ascultătorii ori de câte ori schimbăm o valoare.

Acum adăugați o metodă pentru a crește contorul:

import 'package:flutter/material.dart';

class Counter extends ChangeNotifier {
  var _count = 0;
  void incrementCounter() {
    _count += 1;
  }
}

La sfârșitul acestei metode vom apela notifyListeners(). Acest lucru va declanșa o schimbare în toată aplicația în funcție de orice widget îl ascultă.

Aceasta este frumusețea tiparului furnizorului în Flutter – nu trebuie să vă preocupe de expediere manuală la fluxuri.

În final, creați un getter pentru a returna valoarea contorului. Vom folosi acest lucru pentru a afișa cea mai recentă valoare:

import 'package:flutter/material.dart';

class Counter extends ChangeNotifier {
  var _count = 0;
  int get getCounter {
    return _count;
  }

  void incrementCounter() {
    _count += 1;
    notifyListeners();
  }
}

Ascultarea clicurilor pe butoane

Acum, că avem furnizorul configurat, îl putem folosi în widget-ul nostru principal.

În primul rând, să ne convertim MyHomePage către un widget fără stare în loc de unul cu stare. Va trebui să eliminăm setState() apel, deoarece acesta este disponibil numai într-un 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: <Widget>[
            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),
      ),
    );
  }
}

După ce am făcut acest lucru, putem folosi acum modelul furnizorului în Flutter pentru a seta și a obține valoarea contorului. La fiecare clic pe buton trebuie să creștem valoarea contorului cu 1.

Deci, în _incrementCounter metoda (care se numește atunci când butonul este apăsat) adăugați această linie:

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

Ce se întâmplă aici este că i-ai cerut lui Flutter să urce în copac widget și găsiți primul loc unde Counter este furnizat. (Vă voi spune cum să îl furnizați în secțiunea următoare.) Aceasta este ceea ce Provider.of() face.

Genericele (valorile din interior <> paranteze) spune Flutter ce tip de furnizor trebuie să caute. Apoi Flutter urcă prin arborele widgetului până când găsește valoarea furnizată. Dacă valoarea nu este furnizată nicăieri, atunci se lansează o excepție.

În cele din urmă, după ce ați obținut furnizorul, puteți apela orice metodă pe acesta. Aici îl numim pe al nostru incrementCounter metodă.

Dar avem nevoie și de un context, așa că acceptăm contextul ca argument și modificăm onPressed metoda de a trece și contextul:

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

Notă: Am setat ascultarea la fals deoarece nu este nevoie să ascultăm nici o valoare aici. Trimitem doar o acțiune care trebuie efectuată.

Furnizarea furnizorului

Modelul furnizorului din Flutter va căuta cea mai recentă valoare furnizată. Diagrama de mai jos vă va ajuta să înțelegeți mai bine.

Cum se foloseste modelul furnizorului in Flutter

În această diagramă VERDE obiect A va fi disponibil pentru restul elementelor de sub ea, adică B, C, D, E, și F.

Acum, să presupunem că dorim să adăugăm unele funcționalități aplicației și să creăm un alt furnizor, Z. Z este cerut de E și F.

Deci, unde este cel mai bun loc pentru a adăuga asta?

Îl putem adăuga la rădăcina de mai sus A. Acest lucru ar funcționa:

1612014006 443 Cum se foloseste modelul furnizorului in Flutter

Dar această metodă nu este foarte eficientă.

Flutter va trece prin toate widgeturile de mai sus și apoi va merge în cele din urmă la rădăcină. Dacă aveți copaci widget foarte lungi – pe care cu siguranță îi veți avea într-o aplicație de producție – atunci nu este o idee bună să puneți totul la rădăcină.

În schimb, ne putem uita la numitorul comun al lui E și F. Adică C. Deci, dacă punem Z chiar deasupra lui E și F, ar funcționa.

1612014006 602 Cum se foloseste modelul furnizorului in Flutter

Dar dacă vrem să adăugăm un alt obiect X asta e cerute de E și F? Vom face același lucru. Dar observați cum copacul continuă să crească.

1612014006 717 Cum se foloseste modelul furnizorului in Flutter

Există o modalitate mai bună de a gestiona asta. Ce se întâmplă dacă oferim toate obiectele la un nivel?

1612014006 864 Cum se foloseste modelul furnizorului in Flutter

Acest lucru este perfect și așa vom implementa în cele din urmă modelul furnizorului nostru în Flutter. Vom folosi ceva numit MultiProvider ceea ce ne permite să declarăm mai mulți furnizori la un nivel.

Vom lua MultiProvider a înfășura 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"),
      ),
    );
  }
}

Cu aceasta, am furnizat furnizorului arborelui nostru widget și îl putem folosi oriunde sub acest nivel în arbore.

Mai rămâne doar un lucru: trebuie să actualizăm valoarea afișată.

Actualizarea textului

Pentru a actualiza textul, introduceți furnizorul în funcția de compilare a dvs. MyHomePage widget. Vom folosi getter-ul pe care l-am creat pentru a obține cea mai recentă valoare.

Apoi, adăugați această valoare la widgetul de text de mai jos.

Și am terminat! Așa e finalul tău main.dart fișierul ar trebui să arate:

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<Counter>(context, listen: false).incrementCounter();
  }

  @override
  Widget build(BuildContext context) {
    var counter = Provider.of<Counter>(context).getCounter;
    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(
              '$counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _incrementCounter(context),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

Notă: nu am stabilit listen:false în acest caz, pentru că dorim să ascultăm orice actualizări ale valorii de numărare.

Iată codul sursă de pe GitHub dacă doriți să aruncați o privire: https://github.com/Ayusch/Flutter-Provider-Pattern.

Spuneți-mi dacă aveți probleme.

Bine ați venit pe AndroidVille 🙂

AndroidVille este o comunitate de dezvoltatori de telefonie mobilă, unde împărtășim cunoștințe legate de dezvoltarea Android, dezvoltarea Flutter, tutoriale React Native, Java, Kotlin și multe altele.

Faceți clic pe acest link pentru a vă alătura spațiului de lucru AndroidVille SLACK. Este absolut gratuit!

Dacă ți-a plăcut acest articol, nu ezita să-l distribui pe Facebook sau LinkedIn. Poți să mă urmărești mai departe LinkedIn, Stare de nervozitate, Quora, și Mediu unde răspund la întrebări legate de dezvoltarea mobilă, Android și Flutter.