文章系列

Flutter Provider状态治理---介绍、类图剖析、根本应用

Flutter Provider状态治理---八种提供者应用剖析

视频系列

Flutter Provider状态治理---介绍、类图剖析、根本应用

Flutter Provider状态治理---八种提供者应用剖析

源码仓库地址

github仓库地址

前言

在咱们上一篇文章中对Provider进行了介绍以及类构造的阐明,最初还写了一个简略的示例,通过上一章节咱们对Provider有了一个根本的理解,这一章节咱们来说说Provider的8种提供者以及他们的应用区别。

Provider

Provider是最根本的Provider组件,能够应用它为组件树中的任何地位提供值,然而当该值更改的时候,它并不会更新UI,上面咱们给出一个示例

第一步:创立模型

class UserModel {  String name = "Jimi";  void changeName() {    name = "hello";  }}

第二步:应用程序入口设置

return Provider<UserModel>(  create: (_) => UserModel(),  child: MaterialApp(    debugShowCheckedModeBanner: false,    home: ProviderExample(),  ),);

第三步:应用共享数据

对于Consumer前面将消费者在提及,咱们这里只须要晓得有两个消费者,第一个用于展现模型的数据,第二个用于扭转模型的数据。

  • 第一个Comsumer是用于读取模型的数据name
  • 第二个Consumer用于扭转模型的数据name
import 'package:flutter/material.dart';import 'package:flutter_provider_example/provider_example/user_model.dart';import 'package:provider/provider.dart';class ProviderExample extends StatelessWidget {  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text("ProviderExample"),      ),      body: Center(        child: Column(          mainAxisAlignment: MainAxisAlignment.center,          children: [            Consumer<UserModel>(              builder: (_, userModel, child) {                return Text(userModel.name,                    style: TextStyle(                        color: Colors.red,                        fontSize: 30                    )                );              },            ),            Consumer<UserModel>(              builder: (_, userModel, child) {                return Padding(                  padding: EdgeInsets.all(20),                  child: ElevatedButton(                    onPressed: (){                      userModel.changeName();                    },                    child: Text("扭转值"),                  ),                );              },            ),          ],        ),      ),    );  }}

运行后果

咱们点击按钮的会导致模型数据扭转,然而模型数据扭转之后UI并没有变动也没有重建,那是因为Provider提供者组件不会监听它提供的值的变动。

ChangeNotifierProvider

它跟Provider组件不同,ChangeNotifierProvider会监听模型对象的变动,而且当数据扭转时,它也会重建Consumer(消费者),上面咱们给出一个示例

第一步:创立模型

仔细点咱们能够发现这里定义的模型有两处变动,如下:

  • 混入了ChangeNotifier
  • 调用了notifyListeners()

因为模型类应用了ChangeNotifier,那么咱们就能够拜访notifyListeners()并且在调用它的任何时候,ChangeNotifierProvider都会收到告诉并且消费者将重建UI。

import 'package:flutter/material.dart';class UserModel1 with ChangeNotifier {  String name = "Jimi";  void changeName() {    name = "hello";    notifyListeners();  }}

第二步:应用程序入口设置

return ChangeNotifierProvider<UserModel1>(  create: (_) => UserModel1(),  child: MaterialApp(    debugShowCheckedModeBanner: false,    home: ChangeNotifierProviderExample(),  ),);

第三步:应用共享数据

import 'package:flutter/material.dart';import 'package:flutter_provider_example/change_notifier_provider_example/user_model1.dart';import 'package:provider/provider.dart';class ChangeNotifierProviderExample extends StatelessWidget {  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text("ChangeNotifierProvider"),      ),      body: Center(        child: Column(          mainAxisAlignment: MainAxisAlignment.center,          children: [            Consumer<UserModel1>(              builder: (_, userModel, child) {                return Text(userModel.name,                    style: TextStyle(                        color: Colors.red,                        fontSize: 30                    )                );              },            ),            Consumer<UserModel1>(              builder: (_, userModel, child) {                return Padding(                  padding: EdgeInsets.all(20),                  child: ElevatedButton(                    onPressed: (){                      userModel.changeName();                    },                    child: Text("扭转值"),                  ),                );              },            ),          ],        ),      ),    );  }}

运行后果

FutureProvider

简略来说,FutureProvider用于提供在组件树中筹备好应用其值时可能尚未筹备好的值,次要是确保空值不会传递给任何子组件,而且FutureProvider有一个初始值,子组件能够应用该Future值并通知子组件应用新的值来进行重建。

留神:

  • FutureProvider只会重建一次
  • 默认显示初始值
  • 而后显示Future
  • 最初不会再次重建

第一步:创立模型

这里和Provider不同的是减少了构造函数,以及changeName变成了Future,咱们模仿网络申请提早两秒后扭转其值。

class UserModel2{  UserModel2({this.name});  String? name = "Jimi";  Future<void> changeName() async {    await Future.delayed(Duration(milliseconds: 2000));    name = "hello";  }}

第二步:提供Future

咱们有一个办法,就是异步获取userModel2,模仿网络申请提早两秒执行,最初批改了name并返回UserModel2

import 'package:flutter_provider_example/future_provider_example/user_model2.dart';class UserFuture {  Future<UserModel2> asyncGetUserModel2() async {    await Future.delayed(Duration(milliseconds: 2000));    return UserModel2(name: "获取新的数据");  }}

第三步:应用程序入口设置

initialData是默认值,create参数咱们传了一个Future<UserModel2>,因为它接管的模型Create<Future<T>?>

return FutureProvider<UserModel2>(  initialData: UserModel2(name: "hello"),  create: (_) => UserFuture().asyncGetUserModel2(),  child: MaterialApp(    debugShowCheckedModeBanner: false,    home: FutureProviderExample(),  ),);

第四步:应用共享数据

import 'package:flutter/material.dart';import 'package:flutter_provider_example/future_provider_example/user_model2.dart';import 'package:provider/provider.dart';class FutureProviderExample extends StatelessWidget {  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text("FutureProviderExample"),      ),      body: Center(        child: Column(          mainAxisAlignment: MainAxisAlignment.center,          children: [            Consumer<UserModel2>(              builder: (_, userModel, child) {                return Text(userModel.name ?? "",                    style: TextStyle(                        color: Colors.red,                        fontSize: 30                    )                );              },            ),            Consumer<UserModel2>(              builder: (_, userModel, child) {                return Padding(                  padding: EdgeInsets.all(20),                  child: ElevatedButton(                    onPressed: (){                      userModel.changeName();                    },                    child: Text("扭转值"),                  ),                );              },            ),          ],        ),      ),    );  }}

运行后果

咱们能够看到先展现默认值hello,最初获取到后果的时候展现了获取新的数据,咱们尝试扭转其值,尽管值扭转然而并没有刷新UI。

StreamProvider

StreamProvider提供流值,是围绕StreamBuilder,所提供的值会在传入的时候替换掉新值。和FutureProvider一样,次要的区别在于值会依据屡次触发从新构建UI。

如果你对StreamBuilder不太理解的话,那么你就很难了解StreamProvider,StreamProvider文档地址

第一步:创立模型

class UserModel3{  UserModel3({this.name});  String? name = "Jimi";  void changeName() {    name = "hello";  }}

第二步:提供Stream

上面这段代码相似计时器,每隔一秒钟生成一个数字

import 'package:flutter_provider_example/stream_provider_example/user_model3.dart';class UserStream {  Stream<UserModel3> getStreamUserModel() {    return Stream<UserModel3>.periodic(Duration(milliseconds: 1000),        (value) => UserModel3(name: "$value")    ).take(10);  }}

第三步:应用程序入口设置

这里也有initialData初始值,和FutureProvider相似,只是create属性是获取一个Stream流。

return StreamProvider<UserModel3>(  initialData: UserModel3(name: "hello"),  create: (_) => UserStream().getStreamUserModel(),  child: MaterialApp(    debugShowCheckedModeBanner: false,    home: StreamProviderExample(),  ),);

第四步:应用共享数据

import 'package:flutter/material.dart';import 'package:flutter_provider_example/stream_provider_example/user_model3.dart';import 'package:provider/provider.dart';class StreamProviderExample extends StatelessWidget {  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text("StreamProviderExample"),      ),      body: Center(        child: Column(          mainAxisAlignment: MainAxisAlignment.center,          children: [            Consumer<UserModel3>(              builder: (_, userModel, child) {                return Text(userModel.name ?? "",                    style: TextStyle(                        color: Colors.red,                        fontSize: 30                    )                );              },            ),            Consumer<UserModel3>(              builder: (_, userModel, child) {                return Padding(                  padding: EdgeInsets.all(20),                  child: ElevatedButton(                    onPressed: (){                      userModel.changeName();                    },                    child: Text("扭转值"),                  ),                );              },            ),          ],        ),      ),    );  }}

运行后果

MultiProvider

在下面的例子中咱们都只是返回了一个提供者,在理论开发过程中必定会有多个提供者,咱们尽管能够采纳嵌套的形式来解决,然而这样无疑是凌乱的,可读性级差。这个时候弱小的MultiProvder就产生了,咱们来看下示例:

第一步:创立两个模型

import 'package:flutter/material.dart';class UserModel1 with ChangeNotifier {  String name = "Jimi";  void changeName() {    name = "hello";    notifyListeners();  }}class UserModel4 with ChangeNotifier {  String name = "Jimi";  int age = 18;  void changeName() {    name = "hello";    age = 20;    notifyListeners();  }}

第二步:应用程序入口设置

绝对于形式一这种嵌套形式设置,形式二就显得尤为简略。

形式一:嵌套设置

return ChangeNotifierProvider<UserModel1>(  create: (_) => UserModel1(),  child: ChangeNotifierProvider<UserModel4>(    create: (_) => UserModel4(),    child: MaterialApp(      debugShowCheckedModeBanner: false,      home: MultiProviderExample(),    ),  ),);

形式二:应用MultiProvider

return MultiProvider(  providers: [    ChangeNotifierProvider<UserModel1>(create: (_) => UserModel1()),    ChangeNotifierProvider<UserModel4>(create: (_) => UserModel4()),    /// 增加更多  ],  child: MaterialApp(    debugShowCheckedModeBanner: false,    home: MultiProviderExample(),  ),);

第三步:应用共享数据

import 'package:flutter/material.dart';import 'package:flutter_provider_example/change_notifier_provider_example/user_model1.dart';import 'package:flutter_provider_example/multi_provider_example/user_model4.dart';import 'package:provider/provider.dart';class MultiProviderExample extends StatelessWidget {  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text("MultiProviderExample"),      ),      body: Center(        child: Column(          mainAxisAlignment: MainAxisAlignment.center,          children: [            Consumer<UserModel1>(              builder: (_, userModel, child) {                return Text(userModel.name,                    style: TextStyle(                        color: Colors.red,                        fontSize: 30                    )                );              },            ),            Consumer<UserModel4>(              builder: (_, userModel, child) {                return Text(userModel.age.toString(),                    style: TextStyle(                        color: Colors.green,                        fontSize: 30                    )                );              },            ),            Consumer2<UserModel1, UserModel4>(              builder: (_, userModel1, userModel4, child) {                return Padding(                  padding: EdgeInsets.all(20),                  child: ElevatedButton(                    onPressed: (){                      userModel1.changeName();                      userModel4.changeName();                    },                    child: Text("扭转值"),                  ),                );              },            ),          ],        ),      ),    );  }}

运行后果

ProxyProvider

当咱们有多个模型的时候,会有模型依赖另一个模型的状况,在这种状况下,咱们能够应用ProxyProvider从另一个提供者获取值,而后将其注入到另一个提供者中。咱们来看下代码演示

第一步:创立两个模型

上面咱们创立了两个模型UserModel5WalletModel,而WalletModel依赖与UserModel5,当调用WalletModelchangeName办法时会扭转UserModel5外面的name,当然咱们在理论开发的过程中并不是这么简略,这里只是演示模型依赖时如果应用ProxyProvider

import 'package:flutter/material.dart';class UserModel5 with ChangeNotifier {  String name = "Jimi";  void changeName({required String newName}) {    name = newName;    notifyListeners();  }}class WalletModel {  UserModel5? userModel5;  WalletModel({this.userModel5});  void changeName() {    userModel5?.changeName(newName: "JIMI");  }}

第二步:应用程序入口设置

return MultiProvider(  providers: [    ChangeNotifierProvider<UserModel5>(create: (_) => UserModel5()),    ProxyProvider<UserModel5, WalletModel>(      update: (_, userModel5, walletModel) => WalletModel(userModel5: userModel5),    )  ],  child: MaterialApp(    debugShowCheckedModeBanner: false,    home: ProxyProviderExample(),  ),);

第三步:应用共享数据

import 'package:flutter/material.dart';import 'package:flutter_provider_example/proxy_provider_example/user_model5.dart';import 'package:flutter_provider_example/proxy_provider_example/wallet_model.dart';import 'package:provider/provider.dart';class ProxyProviderExample extends StatelessWidget {  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text("ProxyProviderExample"),      ),      body: Center(        child: Column(          mainAxisAlignment: MainAxisAlignment.center,          children: [            Consumer<UserModel5>(              builder: (_, userModel, child) {                return Text(userModel.name,                    style: TextStyle(                        color: Colors.red,                        fontSize: 30                    )                );              },            ),            Consumer<UserModel5>(              builder: (_, userModel, child) {                return Padding(                  padding: EdgeInsets.all(20),                  child: ElevatedButton(                    onPressed: (){                      userModel.changeName(newName: "hello");                    },                    child: Text("扭转值"),                  ),                );              },            ),            Consumer<WalletModel>(              builder: (_, walletModel, child) {                return Padding(                  padding: EdgeInsets.all(20),                  child: ElevatedButton(                    onPressed: (){                      walletModel.changeName();                    },                    child: Text("通过代理扭转值"),                  ),                );              },            ),          ],        ),      ),    );  }}

运行后果

ChangeNotifierProxyProvider

ProxyProvider原理一样,惟一的区别在于它构建和同步ChangeNotifierChangeNotifierProvider,当提供者数据变动时,将会重构UI。

上面咱们给出一个例子:

  • 获取书籍列表
  • 获取珍藏书籍列表
  • 点击书籍可退出或者勾销珍藏
  • 通过代理实时重构UI

第一步:创立两个模型

1、BookModel

BookModel用户存储模型数据,将书籍转换成模型。

class BookModel {    static var _books = [    Book(1, "夜的命名数"),    Book(2, "大奉打更人"),    Book(3, "星门"),    Book(4, "大魏读书人"),    Book(5, "我师兄切实太持重了"),    Book(6, "深空此岸"),  ];  // 获取书籍长度  int get length => _books.length;  // 依据ID获取书籍  Book getById(int id) => _books[id -1];  // 依据索引获取数据  Book getByPosition(int position) => _books[position];  // 更多....}class Book {  final int bookId;  final String bookName;    Book(this.bookId, this.bookName);}

2、BookManagerModel

BookManagerModel次要用于治理书籍、珍藏书籍、勾销珍藏等操作

import 'package:flutter/material.dart';import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart';class BookManagerModel with ChangeNotifier {  // 依赖bookModel  final BookModel _bookModel;  // 获取数据所有的ID  List<int>? _bookIds;  // 构造函数  BookManagerModel(this._bookModel, {BookManagerModel? bookManagerModel})    : _bookIds = bookManagerModel?._bookIds ?? [];  // 获取所有的书  List<Book> get books => _bookIds!.map((id) => _bookModel.getById(id)).toList();  // 依据索引获取数据  Book getByPosition(int position) => books[position];  // 获取书籍的长度  int get length => _bookIds?.length ?? 0;  // 增加书籍  void addFaves(Book book) {    _bookIds!.add(book.bookId);    notifyListeners();  }  // 删除书籍  void removeFaves(Book book) {    _bookIds!.remove(book.bookId);    notifyListeners();  }}

第二步:应用程序入口设置

return MultiProvider(  providers: [    Provider(create: (_) => BookModel()),    ChangeNotifierProxyProvider<BookModel, BookManagerModel>(      create: (_) => BookManagerModel(BookModel()),      update: (_, bookModel, bookManagerModel) => BookManagerModel(bookModel),    )  ],  child: MaterialApp(    debugShowCheckedModeBanner: false,    home: ChangeNotifierProxyProviderExample(),  ),);

第三步:设置BottomNavigationBar

import 'package:flutter/material.dart';import 'package:flutter_provider_example/change_notifier_proxy_provider_example/pages/page_a.dart';import 'package:flutter_provider_example/change_notifier_proxy_provider_example/pages/page_b.dart';class ChangeNotifierProxyProviderExample extends StatefulWidget {  @override  _ChangeNotifierProxyProviderExampleState createState() => _ChangeNotifierProxyProviderExampleState();}class _ChangeNotifierProxyProviderExampleState extends State<ChangeNotifierProxyProviderExample> {  var _selectedIndex = 0;  var _pages = [PageA(), PageB()];  @override  Widget build(BuildContext context) {    return Scaffold(      body: _pages[_selectedIndex],      bottomNavigationBar: BottomNavigationBar(        currentIndex: _selectedIndex,        onTap: (index) {          setState(() {            _selectedIndex = index;          });        },        items: [          BottomNavigationBarItem(            icon: Icon(Icons.book),            label: "书籍列表"          ),          BottomNavigationBarItem(            icon: Icon(Icons.favorite),            label: "珍藏"          )        ],      ),    );  }}

第四步:书籍列表UI构建

import 'package:flutter/material.dart';import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart';import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart';import 'package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_item.dart';import 'package:provider/provider.dart';class PageA extends StatelessWidget {  @override  Widget build(BuildContext context) {        var bookModel = Provider.of<BookModel>(context);        return Scaffold(      appBar: AppBar(        title: Text("书籍列表"),      ),      body: ListView.builder(        itemCount: bookModel.length,        itemBuilder: (_, index) => BookItem(id: index + 1),      ),    );  }}

第五步:珍藏列表UI构建

import 'package:flutter/material.dart';import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart';import 'package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_item.dart';import 'package:provider/provider.dart';class PageB extends StatelessWidget {  @override  Widget build(BuildContext context) {    var bookManagerModel = Provider.of<BookManagerModel>(context);    var bookCount = bookManagerModel.length;    return Scaffold(      appBar: AppBar(        title: Text("珍藏列表"),      ),      body: ListView.builder(        itemCount: bookCount,        itemBuilder: (_, index) => BookItem(id: bookManagerModel.getByPosition(index).bookId),      ),    );  }}

其余辅助封装类

import 'package:flutter/material.dart';import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart';import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart';import 'package:provider/provider.dart';class BookButton extends StatelessWidget {    final Book book;    BookButton({    Key? key,    required this.book  }) : super(key: key);    @override  Widget build(BuildContext context) {        var bookManagerModel = Provider.of<BookManagerModel>(context);        return GestureDetector(      onTap: bookManagerModel.books.contains(this.book)          ?  () => bookManagerModel.removeFaves(this.book)          :  () => bookManagerModel.addFaves(this.book),      child: SizedBox(        width: 100,        height: 60,        child: bookManagerModel.books.contains(this.book)            ?  Icon(Icons.star, color: Colors.red,)            :  Icon(Icons.star_border),      ),    );  }}
import 'package:flutter/material.dart';import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart';import 'package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_button.dart';import 'package:provider/provider.dart';class BookItem extends StatelessWidget {  final int id;  BookItem({    Key? key,    required this.id  }) : super(key: key);  @override  Widget build(BuildContext context) {    var bookModel = Provider.of<BookModel>(context);    var book = bookModel.getById(id);    return ListTile(      leading: CircleAvatar(        child: Text("${book.bookId}"),      ),      title: Text("${book.bookName}",        style: TextStyle(            color: Colors.black87        ),      ),      trailing: BookButton(book: book),    );  }}

运行后果

ListenableProxyProvider

ListenableProxyProviderListenableProvider的一个变体,然而在应用上和ChangeNotifierProvider成果惊人的统一,如果大家对ListenableProxyProvider有更深的了解,请分割我补充。

总结

Provider为咱们提供了十分多的提供者,总共有八种。但咱们比拟罕用的是ChangeNotifierProviderMultiProviderChangeNotifierProxyProvider,对于其余的提供者可依据本人的理论利用场景来。