一、简介

在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时会告诉观察者实现,整个工作流程示意图如下。