> 文章列表 > Flutter Provider状态管理---八种提供者使用分析

Flutter Provider状态管理---八种提供者使用分析

Flutter Provider状态管理---八种提供者使用分析

Provider

Provider是最基本的Provider组件,可以使用它为组件树中的任何位置提供值,但是当该值更改的时候,它并不会更新UI

class UserModel {String name = "kwok";void changeName() {name = "hello";}
}return Provider<UserModel>(create: (_) => UserModel(),child: MaterialApp(debugShowCheckedModeBanner: false,home: ProviderExample(),),
);import 'package:flutter/material.dart';
import 'package:flutter_provider_example/provider_example/user_model.dart';
import 'package:provider/provider.dart';class ProviderExample extends StatelessWidget {@overrideWidget 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(消费者),下面我们给出一个示例

import 'package:flutter/material.dart';class UserModel1 with ChangeNotifier {String name = "Kwok";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 {@overrideWidget 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并返回UserModel2import '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 {@overrideWidget 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("改变值"),),);},),],),),);}
    }
    

    StreamProvider

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

    第一步:创建模型

    class UserModel3{UserModel3({this.name});String? name = "kwok";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 {@overrideWidget 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 = "kwok";void changeName() {name = "hello";notifyListeners();}
    }class UserModel4 with ChangeNotifier {String name = "kwok";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 {@overrideWidget 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 = "keo";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 {@overrideWidget 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

  • 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 {// 依赖bookModelfinal BookModel _bookModel;// 获取数据所有的IDList<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()];@overrideWidget 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 {@overrideWidget 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 {@overrideWidget 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);@overrideWidget 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);@overrideWidget 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),);}
    }

    总结

    Provider为我们提供了非常多的提供者,总共有八种。但我们比较常用的是ChangeNotifierProviderMultiProviderChangeNotifierProxyProvider,关于其他的提供者可根据自己的实际应用场景来。