一、简介
在Flutter开发中,或多或少的都会设计到页面的多状态治理,如果大家对Flutter技术比拟相熟的话,那么应该晓得上面的一些状态治理框架,像Bloc,Getx我都用过,整体来说再状态治理不是很简单的状况下还是能够的。
接下来,咱们来看一下Flutter官网举荐的状态治理框架Provider是如何应用的。Flutter 针对不同类型对象提供了多种不同的 Provider;Provider 也是借助了 InheritWidget,将共享状态放到顶层 MaterialApp 之上;
- setState能刷新widget子树,刷新范畴太大,并且须要把数据对象传递到子类。
- InheritedWidget不必传递数据对象,通过
context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
获取父类的数据。然而刷新范畴大,只能由上而下传递。 - Provider能够实现部分刷新。只有数据对象扭转,UI能主动变动,实现响应式编程。屏蔽刷新逻辑,实现响应式数据与UI的绑定。无论是子类或父类扭转数据都能刷新绑定的UI。
二、根本应用
应用之前,须要先在pubspec.yaml增加provider
dependencies: flutter: sdk: flutter provider: ^6.0.2
2.1 Provider根本应用
Provider是一款基于数据流的观察者模式,应用的第一步就是新建一个继承自ChangeNotifier的数据管理类。上面,咱们来看一下官网的例子应用Provider形式如何实现。
import 'package:flutter/cupertino.dart';class CountProviderModel extends ChangeNotifier { int _count = 0; int get count => _count; void increment() { print("increment"); _count++; notifyListeners(); }}
在CountProviderModel类中,咱们定义了有一个数据减少的办法,最初还调用notifyListeners发送告诉。接着,咱们新建一个测试页面,该页面的根部应用ChangeNotifierProvider组件进行包裹,须要刷新的中央应用Consumer组件进行包裹,用于生产
import 'package:flutter/cupertino.dart';import 'package:flutter/material.dart';import 'package:provider/provider.dart';import 'count_provider.dart';class ProviderPage extends StatelessWidget { const ProviderPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return ChangeNotifierProvider<CountProviderModel>( create: (_) => CountProviderModel(), builder: (context, child) { return Scaffold( appBar: AppBar( title: const Text("ProviderPage"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text('You have pushed the button this many times:'), Consumer<CountProviderModel>( builder: (context, notifier, child) { return Text("${notifier.count}",style: const TextStyle( fontSize: 24, ), ); }, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () => context.read<CountProviderModel>().increment(), tooltip: 'Increment', child: const Icon(Icons.add), ), ); }, ); }}
最初,咱们批改一下Flutter我的项目的入口main文件。
// 改写 main.dartimport 'package:flutter/material.dart';import 'package:stateresearch/pages/ProviderPage.dart';void main() { runApp(MyApp());}class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter状态治理', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: ProviderPage(), ); }}
从新运行我的项目,点击右下角的按钮时数字就会主动加。
2.2 跨页面状态共享
作为一个全局的状态治理框架,跨页面的状态共享是必须的,为了不便阐明,咱们再新建两个页面ProviderPageTwo和ProviderPageThree,对应的代码如下:
ProviderPageTwo.dart
import 'package:flutter/material.dart';import 'package:flutter_app/provider_three_page.dart';import 'package:provider/provider.dart';import 'count_provider.dart';class ProviderPageTwo extends StatelessWidget { const ProviderPageTwo({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("ProviderPageTwo"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text('You have pushed the button this many times:'), Consumer<CountProviderModel>( builder: (context, notifier, child) { return Text("${notifier.count}",style: const TextStyle( fontSize: 24, )); }, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () { context.read<CountProviderModel>().increment(); // 2秒后跳转至新的页面 Future.delayed(const Duration(seconds: 2), () { Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) { return ProviderPageThree(); })); }); }, tooltip: 'Increment', child: const Icon(Icons.add), ), ); }}
ProviderPageThree.dart
import 'package:flutter/cupertino.dart';import 'package:flutter/material.dart';import 'package:provider/provider.dart';import 'count_provider.dart';class ProviderPageThree extends StatelessWidget { const ProviderPageThree({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("ProviderPageThree"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text('You have pushed the button this many times:'), Consumer<CountProviderModel>( builder: (context, notifier, child) { return Text("${notifier.count}",style: const TextStyle( fontSize: 24, )); }, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () => Provider.of<CountProviderModel>(context, listen: false).increment(), tooltip: 'Increment', child: const Icon(Icons.add), ), ); }}
接着,咱们在App顶层进行全局监听,因而,其余页面无需 ChangeNotifierProvider也能够获取 Model。
void main() { runApp(ChangeNotifierProvider( create: (_) => CountProviderModel(), child: MyApp(), ),);}class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: ProviderPageTwo(), ); }}
2.3 多Model全局共享
除了后面的两种应用场景,Provider还反对多Model的状态共享。首先,咱们再新建一个ProviderModel类。
import 'package:flutter/material.dart';class ListProviderModel extends ChangeNotifier { final List<String> _list = []; List<String> get list => _list; void push(String value) { _list.add(value); notifyListeners(); }}
而后,咱们批改ProviderPageTwo和ProviderPageThree两个页面,对应的代码如下:
ProviderPageTwo.dart
import 'dart:math';import 'package:flutter/material.dart';import 'package:flutter_app/provider_three_page.dart';import 'package:provider/provider.dart';import 'count_provider.dart';import 'list_provider.dart';class ProviderPageTwo extends StatelessWidget { const ProviderPageTwo({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("ProviderPageTwo"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text('You have pushed the button this many times:'), Consumer<CountProviderModel>( builder: (context, notifier, child) { return Text("${notifier.count}"); }, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () { context.read<CountProviderModel>().increment(); context.read<ListProviderModel>().push("List-${Random().nextInt(10)}"); Future.delayed(const Duration(seconds: 2), () { Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) { return const ProviderPageThree(); })); }); }, tooltip: 'Increment', child: Icon(Icons.add), ), ); }}
ProviderPageThree.dart
import 'package:flutter/cupertino.dart';import 'package:flutter/material.dart';import 'package:provider/provider.dart';import 'count_provider.dart';import 'list_provider.dart';class ProviderPageThree extends StatelessWidget { const ProviderPageThree({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("ProviderPageThree"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text('You have pushed the button this many times:'), Consumer<CountProviderModel>( builder: (context, notifier, child) { return Text("${notifier.count}"); }, ), Consumer<ListProviderModel>( builder: (context, notifier, child) { return Text("${notifier.list}"); }, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () => Provider.of<CountProviderModel>(context, listen: false).increment(), tooltip: 'Increment', child: const Icon(Icons.add), ), ); }}
最初,咱们批改main入口文件的代码,应用MultiProvider 包裹多个Model,如下。
void main() { runApp( MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => CountProviderModel()), ChangeNotifierProvider(create: (_) => ListProviderModel()), ], child: const MyApp(), ), );}class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: ProviderPageTwo(), ); }}
三、总结
通过后面的例子能够看到,应用Provider时咱们须要先新建一个Model对象,它继承自ChangeNotifier,是一个被察看的对象,当Model对象扭转时须要调用notifyListeners告诉观察者刷新。上面是ChangeNotifier的源码:
class ChangeNotifier implements Listenable { ObserverList<VoidCallback>? _listeners = ObserverList<VoidCallback>(); @protected bool get hasListeners { return _listeners!.isNotEmpty; } @override void addListener(VoidCallback listener) { _listeners!.add(listener); } @override void removeListener(VoidCallback listener) { _listeners!.remove(listener); } @mustCallSuper void dispose() { _listeners = null; } @protected @visibleForTesting void notifyListeners() { if (_listeners != null) { final List<VoidCallback> localListeners = List<VoidCallback>.from(_listeners!); for (final VoidCallback listener in localListeners) { try { if (_listeners!.contains(listener)) listener(); } catch (exception, stack) { ...... } } } }}
ChangeNotifier把办法增加到数组中,而后调用notifyListeners时会告诉观察者实现,整个工作流程示意图如下。