Я начал изучать Flutter некоторое время назад и дошел до того, что мне нужно было передавать состояние между разными компонентами. Я думаю, я мог бы использовать параметры конструктора или что-то подобное, но я полагал, что есть способ получше.
Хотя, как правило, хорошо понимать, как все работает внутри, иногда вам просто нужно быстрое решение вашей проблемы. Поэтому в этой статье я кратко опишу найденный мною простейший способ, а вам дам готовое решение, особо не вдаваясь в подробности.
Задача: я хочу создать приложение, которое будет давать обзор баланса банковского счета, с возможностью вносить или снимать деньги с увеличением/уменьшением 100,00 евро, а также изменять отображать валюту между EUR, USD и CHF.
Прежде всего, давайте добавим зависимость к pubspec.yaml, что позволит нам использовать библиотеку Provider. На момент написания этой статьи последней версией была 4.3.2:
... dependencies: flutter: sdk: flutter provider: ^4.3.2 ...
Теперь мы можем создать класс, который будет содержать всю важную информацию о нашем банковском счете. Он расширит ChangeNotifier и, таким образом, сможет уведомить своих подписчиков, когда что-то изменится. Вот полный код этого класса:
class AccountBalance extends ChangeNotifier { String _currency = 'EUR'; double _exchangeRateUSD = 1.18; double _exchangeRateCHF = 1.08; double _currentBalanceEUR = 1000.00; String get getCurrency { return _currency; } double get getCurrentBalanceEUR { return _currentBalanceEUR; } double get getExchangeRateUSD { return _exchangeRateUSD; } double get getExchangeRateCHF { return _exchangeRateCHF; } set currency(String currency) { _currency = currency; notifyListeners(); } set currentBalanceEUR(double balance) { _currentBalanceEUR = balance; notifyListeners(); } void changeCurrentBalance(double delta) { _currentBalanceEUR += delta; notifyListeners(); } }
Обратите внимание, что все мутаторы вызывают notifyListeners(). Таким образом, мы можем обновлять отображаемые значения в другом месте всякий раз, когда изменяется баланс или отображаемая валюта. Наш AccountBalance теперь готов к использованию.
Мы обернем наше основное приложение MaterialApp виджетом MultiProvider из библиотеки provider. Прямо сейчас нам не нужно использовать этот конкретный провайдер, так как у нас будет только один источник данных, но давайте подготовим наше приложение к будущему и ко многим провайдерам, которые могут появиться. MultiProvider позволяет нам объединять множество поставщиков, сокращая при этом шаблонный код. Что нам нужно сделать, так это указать поле providers и добавить наш класс, который будет отправлять уведомления о произошедших изменениях:
... class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MultiProvider( providers: [ ChangeNotifierProvider.value( value: AccountBalance(), ), ], child: MaterialApp( ... home: MyHomePage(), ... ), ); // MultiProvider } } ...
В виджете MyHomePage теперь мы можем подписываться на разные поля и вызывать разные методы из AccountBalance, используя синтаксис Provider.of‹AccountBalance›(context). . Если мы хотим вызвать метод, который увеличит баланс на 100 евро (например, внутри метода onPressed кнопки), мы сделаем следующее:
Provider.of<AccountBalance>(context, listen: false).changeCurrentBalance(100.00);
Мы устанавливаем для параметра listen значение false, поскольку мы только вызываем метод и нам не нужно ничего прослушивать.
Если мы хотим получить значение нашего текущего баланса в евро, мы должны сделать следующий вызов:
double _currentBalanceEUR = Provider.of<AccountBalance>(context).getCurrentBalanceEUR;
В этом месте мы опустили listen: false, что означает, что по умолчанию оно будет иметь значение true. Это необходимо, так как мы хотим прослушивать любое обновление значения нашей переменной. Теперь мы можем отображать _currentBalanceEUR в любом месте нашего виджета, и его значение будет автоматически обновляться при каждом вызове одного из мутаторов currentBalance в AccountBalance. >
Аналогичным образом мы реализуем кнопки, которые изменят отображаемую валюту, и мы добавим поле для отображаемого значения, которое будет рассчитываться на основе выбранной валюты, текущей суммы в евро и соответствующего обменного курса.
Вот полный код:
import 'package:flutter/material.dart'; import 'package:flutter_provider_demo/account_balance.dart'; import 'package:provider/provider.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MultiProvider( providers: [ ChangeNotifierProvider.value( value: AccountBalance(), ), ], child: MaterialApp( title: 'Flutter Provider Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(), ), ); } } class MyHomePage extends StatelessWidget { MyHomePage({Key key}) : super(key: key); @override Widget build(BuildContext context) { double _currentBalanceEUR = Provider.of<AccountBalance>(context).getCurrentBalanceEUR; double _exchangeRateUSD = Provider.of<AccountBalance>(context).getExchangeRateUSD; double _exchangeRateCHF = Provider.of<AccountBalance>(context).getExchangeRateCHF; String _currentCurrency = Provider.of<AccountBalance>(context).getCurrency; double _currentBalanceToDisplay = (_currentCurrency == 'EUR' ? _currentBalanceEUR : (_currentCurrency == 'USD' ? _currentBalanceEUR * _exchangeRateUSD : _currentBalanceEUR * _exchangeRateCHF)); return Scaffold( appBar: AppBar( title: Text('Flutter Provider Demo'), ), body: Center( child: Padding( padding: const EdgeInsets.all(10.0), child: Column( children: [ Text('Your current balance:'), Text( _currentBalanceToDisplay.toStringAsFixed(2) + ' ' + _currentCurrency, style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: _currentBalanceToDisplay >= 0 ? Colors.green : Colors.red, ), ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ FlatButton( child: Text('Deposit 100 EUR'), color: Color.fromRGBO(100, 250, 255, 1), splashColor: Colors.blue, onPressed: () { Provider.of<AccountBalance>(context, listen: false) .changeCurrentBalance(100.00); }, ), FlatButton( child: Text('Withdraw 100 EUR'), color: Color.fromRGBO(100, 250, 255, 1), splashColor: Colors.blue, onPressed: () { Provider.of<AccountBalance>(context, listen: false) .changeCurrentBalance(-100.00); }, ), ], ), Text('Select the display currency:'), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ FlatButton( child: Text('EUR'), color: Color.fromRGBO(100, 250, 255, 1), splashColor: Colors.blue, onPressed: () { Provider.of<AccountBalance>(context, listen: false) .currency = 'EUR'; }, ), FlatButton( child: Text('USD'), color: Color.fromRGBO(100, 250, 255, 1), splashColor: Colors.blue, onPressed: () { Provider.of<AccountBalance>(context, listen: false) .currency = 'USD'; }, ), FlatButton( child: Text('CHF'), color: Color.fromRGBO(100, 250, 255, 1), splashColor: Colors.blue, onPressed: () { Provider.of<AccountBalance>(context, listen: false) .currency = 'CHF'; }, ), ], ), ], ), ), ), ); } }
Как я уже сказал, это быстрый и простой способ реализовать функции провайдера в простом приложении. Не стесняйтесь оставлять любые комментарии об альтернативных способах сделать это.
Увидимся позже :)