什么是Provider
Provider
是 Flutter 中最流行、最成熟的状态管理方法之一。
- Provider 的内部
DelegateWidget
是一个StatefulWidget
,所以可以更新且具有生命周期。 - Provider 是InheritedWidget的包装器。
Provider
原理
创建Provider
最常用到的类有:
- ChangeNotifierProvider
- Provider
- MultiProvider
- ProxyProvider
- ChangeNotifierProxyProvider
通常,Provider
与ChangeNotifer
一起使用。设置到 ChangeNotifierProvider
的 ChangeNotifer
会被执行 addListener
添加监听 listener
。
listener
内会调用 StateDelegate
的 StateSetter
方法,从而调用到 StatefulWidget
的 setState
。
当我们执行 ChangeNotifer
的 notifyListeners
时,就会最终触发 setState
更新。
访问Provider
的state
常见方法:
- 使用
Provider.of()
方法 - 使用
Consumer()
方法
Provider
常见类ChangeNotifierProvider
的使用
- 安装依赖
dependencies:
provider: ^6.0.4
- 定义需要共享的数据
我们这里创建了一个类
CountNotifier
继承了ChangeNotifier
,我们这里的案例是以计数器为案例,所有我们定义一个变量count
,以及一个改变数值的increment
方法,当我们调用increment
后会对count
进行+1,最后调用notifyListeners()
来更新数据,代码如下:
class CountNotifier extends ChangeNotifier{
int count = 0;
void increment(){
count++;
notifyListeners();
}
}
- 在应用程序入口初始化,在MaterialApp之前对定义的共享数据进行了初始化
import 'package:flutter/material.dart';
import 'package:flutter_base/ProviderPage.dart';
import 'package:provider/provider.dart';
void main() => runApp( MyApp());
class MyApp extends StatelessWidget{
const MyApp({super.key});
@override
Widget build(BuildContext context){
return ChangeNotifierProvider(
create: (_)=>CountNotifier(),
child: const MaterialApp(
home: ProviderPage(),
),
);
}
}
- 使用共享数据
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ProviderPage extends StatefulWidget {
const ProviderPage({Key? key}) : super(key: key);
@override
State<ProviderPage> createState() => _ProviderPageState();
}
class _ProviderPageState extends State<ProviderPage> {
@override
Widget build(BuildContext context) {
final counter = Provider.of<CountNotifier>(context);
return Scaffold(
appBar: AppBar(title: const Text('Provider'),),
floatingActionButton: FloatingActionButton(
onPressed: (){
counter.increment();
},
child: const Icon(Icons.add),
),
body: Center(
child: Text(counter.count.toString()),
),
);
}
}
class CountNotifier extends ChangeNotifier{
int count = 0;
void increment(){
count++;
notifyListeners();
}
}
Provider
常见类Provider
的使用
Provider
是最基本的Provider
组件,可以使用它为组件树中的任何位置提供值,但是当该值更改的时候,它并不会更新UI,下面我们给出一个示例:
- 第一步:创建模型
class UserModel{
String name = "张三";
void changeName(){
name = "李四";
}
}
- 第二步:应用程序入口设置
import 'package:flutter/material.dart';
import 'package:flutter_base/ProviderPage2.dart';
import 'package:provider/provider.dart';
void main() => runApp( MyApp());
class MyApp extends StatelessWidget{
const MyApp({super.key});
@override
Widget build(BuildContext context){
return Provider(
create: (_) => UserModel(),
child: const MaterialApp(
home: ProviderPage2(),
),
);
}
}
- 第三步:使用共享数据
这里只需要知道有两个消费者,第一个用于展示模型的数据,第二个用于改变模型的数据,
第一个
Comsumer
是用于读取模型的数据name,第二个Consumer
用于改变模型的数据name。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class UserModel{
String name = "张三";
void changeName(){
print('22222');
name = "李四";
}
}
class ProviderPage2 extends StatefulWidget {
const ProviderPage2({Key? key}) : super(key: key);
@override
State<ProviderPage2> createState() => _ProviderPage2State();
}
class _ProviderPage2State extends State<ProviderPage2> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Provider'),),
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: const EdgeInsets.all(20),
child: ElevatedButton(
//调用方法
onPressed: (){userModel.changeName();},
child: const Text('改变值'),
),
);
}
)
],
),
),
);
}
}
我们点击按钮的会导致模型数据改变,但是模型数据改变之后UI并没有变化也没有重建,那是因为Provider
提供者组件不会监听它提供的值的变化。
ChangeNotifierProvider
跟Provider
组件不同,ChangeNotifierProvider
会监听模型对象的变化,而且当数据改变时,它也会重建Consumer
(消费者),我们可以在创建模型时混入ChangeNotifier
,并且调用notifyListeners()
方法:
class UserModel with ChangeNotifier{
String name = "张三";
void changeName(){
print('22222');
name = "李四";
notifyListeners();
}
}
第二步修改成
import 'package:flutter/material.dart';
import 'package:flutter_base/ProviderPage2.dart';
import 'package:provider/provider.dart';
void main() => runApp( MyApp());
class MyApp extends StatelessWidget{
const MyApp({super.key});
@override
Widget build(BuildContext context){
return ChangeNotifierProvider<UserModel>(
create: (_) => UserModel(),
child: const MaterialApp(
home: ProviderPage2(),
),
);
}
}
修改之后运行的结果为:
Provider
常见类MultiProvider
的使用
Provider
放置的位置一般是在相应的widget
的外层,也就是数据状态的共享都是在该层widget
内部进行。需要使用多个Provider
的话,可以选择MultiProvider
来完成放置。
- 第一步:创建两个模型
class UserModelOne with ChangeNotifier{
String name = '张三';
void changeName (){
name = '李四';
notifyListeners();
}
}
class UserModelTwo with ChangeNotifier{
String name = '张三';
int age = 18;
void changeName(){
name = '李四';
age = 20;
notifyListeners();
}
}
- 程序入口设置
方式一:嵌套设置
import 'package:flutter/material.dart';
import 'package:flutter_base/ProviderPage3.dart';
import 'package:provider/provider.dart';
void main() => runApp( MyApp());
class MyApp extends StatelessWidget{
const MyApp({super.key});
@override
Widget build(BuildContext context){
return ChangeNotifierProvider<UserModelOne>(
create: (_) => UserModelOne(),
child: ChangeNotifierProvider<UserModelTwo>(
create: (_) => UserModelTwo(),
child: const MaterialApp(
home: ProviderPage3(),
),
)
);
}
}
方式二:MultiProvider
import 'package:flutter/material.dart';
import 'package:flutter_base/ProviderPage3.dart';
import 'package:provider/provider.dart';
void main() => runApp( MyApp());
class MyApp extends StatelessWidget{
const MyApp({super.key});
@override
Widget build(BuildContext context){
return MultiProvider(
providers: [
ChangeNotifierProvider<UserModelOne>(create: (_) => UserModelOne()),
ChangeNotifierProvider<UserModelTwo>(create: (_) => UserModelTwo())
],
child: const MaterialApp(
home: ProviderPage3(),
),
);
}
}
Provider
常见类ProxyProvider
的使用
当我们有多个模型的时候,会有模型依赖另一个模型的情况,在这种情况下,我们可以使用ProxyProvider
从另一个提供者获取值,然后将其注入到另一个提供者中。
- 第一步:创建两个模型
创建两个模型UserModelThree
和WalletModel
,而WalletModel
依赖与UserModelThree
,调用WalletModel
的changeName
方法时会改变UserModelThree
里面的name
。
class UserModelThree with ChangeNotifier{
String name = '张三';
void changeName({required String newName}){
name = newName;
notifyListeners();
}
}
class WalletModel{
UserModelThree? userModelThree;
WalletModel({this.userModelThree});
void changeName(){
userModelThree?.changeName(newName: '李四');
}
}
- 第二步:程序入口设置
class MyApp extends StatelessWidget{
const MyApp({super.key});
@override
Widget build(BuildContext context){
return MultiProvider(
providers: [
ChangeNotifierProvider<UserModelThree>(create: (_) => UserModelThree()),
ProxyProvider<UserModelThree,WalletModel>(
update: (_,userModelThree,walletModel) => WalletModel(userModelThree: userModelThree)
)
],
child: const MaterialApp(
home: ProxyProviderPage(),
),
);
}
}
- 第三步:使用共享数据
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class UserModelThree with ChangeNotifier{
String name = '张三';
void changeName({required String newName}){
name = newName;
notifyListeners();
}
}
class WalletModel{
UserModelThree? userModelThree;
WalletModel({this.userModelThree});
void changeName(){
userModelThree?.changeName(newName: '王五');
}
}
class ProxyProviderPage extends StatefulWidget {
const ProxyProviderPage({Key? key}) : super(key: key);
@override
State<ProxyProviderPage> createState() => _ProxyProviderPageState();
}
class _ProxyProviderPageState extends State<ProxyProviderPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title:const Text('ProxyProvider'),),
body: Center(
child: Column(
children: [
Consumer<UserModelThree>(
builder: (_,userModel,child){
return Text(userModel.name,style: const TextStyle(
color: Colors.red,
fontSize: 30
),);
}
),
Consumer<UserModelThree>(
builder: (_,userModel,child){
return Padding(
padding: const EdgeInsets.all(20),
child: ElevatedButton(
onPressed: (){
userModel.changeName(newName: '李四');
},
child: const Text('改变值'),
),
);
}
),
Consumer<WalletModel>(
builder: (_,walletModel,child){
return Padding(
padding: const EdgeInsets.all(20),
child: ElevatedButton(
onPressed: (){
walletModel.changeName();
},
child: const Text('通过代理改变值'),
),
);
}
),
],
),
),
);
}
}
Provider
常见类ChangeNotifierProxyProvider
的使用
和ProxyProvider
原理一样,唯一的区别在于它构建和同步ChangeNotifier
的ChangeNotifierProvider
,当提供者数据变化时,将会重构UI。
下面我们给出一个例子:
- 获取书籍列表
- 获取收藏书籍列表
- 点击书籍可加入或者取消收藏
- 通过代理实时重构UI
- 第一步:创建两个模型
1、BookModel BookModel用户存储模型数据,将书籍转换成模型。
class BookModel{
static final _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/cupertino.dart';
import 'package:flutter_base/ChangeNotifierProxyProviderPage.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 addBook(Book book){
_bookIds?.add(book.bookId);
notifyListeners();
}
//删除书籍
void removeBook(Book book){
_bookIds!.remove(book.bookId);
notifyListeners();
}
}
- 第二步:应用程序入口设置
class MyApp extends StatelessWidget{
const MyApp({super.key});
@override
Widget build(BuildContext context){
return MultiProvider(
providers: [
Provider(create: (_) => BookModel()),
ChangeNotifierProxyProvider<BookModel,BookManagerModel>(
create: (_) => BookManagerModel(BookModel()),
update: (_,bookModel,bookManagerModel)=> BookManagerModel(bookModel)
)
],
child: const MaterialApp(
home: ChangeNotifierProxyProviderPage(),
),
);
}
}
- 第三步:设置BottomNavigationBar
import 'package:flutter/material.dart';
import 'package:flutter_base/pages/PageA.dart';
import 'package:flutter_base/pages/PageB.dart';
class ChangeNotifierProxyProviderPage extends StatefulWidget {
const ChangeNotifierProxyProviderPage({Key? key}) : super(key: key);
@override
State<ChangeNotifierProxyProviderPage> createState() => _ChangeNotifierProxyProviderPageState();
}
class _ChangeNotifierProxyProviderPageState extends State<ChangeNotifierProxyProviderPage> {
var _selectedIndex = 0;
var _pages = [PageA(),PageB()];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('ChangeNotifierProxyProvider'),),
body: _pages[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: (index){
setState(() {
_selectedIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.book),
label: '书籍列表'
),
BottomNavigationBarItem(
icon: Icon(Icons.favorite),
label: '收藏'
),
],
),
);
}
}
- PageA书籍列表
import 'package:flutter/material.dart';
import 'package:flutter_base/model/BookManagerModel.dart';
import 'package:flutter_base/model/BookModel.dart';
import 'package:provider/provider.dart';
class PageA extends StatelessWidget {
const PageA({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
//获取书籍列表数据
var bookLists = Provider.of<BookModel>(context);
return Scaffold(
appBar: AppBar(title: const Text('书籍列表'),),
body: ListView.builder(
itemCount: bookLists.length,
itemBuilder: (context,index) => BookItem(id:index+1)
),
);
}
}
class BookItem extends StatelessWidget {
final int id;
const BookItem({Key? key, required this.id}) : super(key: key);
@override
Widget build(BuildContext context) {
var bookLists = Provider.of<BookModel>(context);
var book = bookLists.getById(id);
return ListTile(
leading: CircleAvatar(
child: Text("${book.bookId}"),
),
title: Text(book.bookName,style: const TextStyle(
color: Colors.black87
),),
trailing: BookButton(book:book),
);
}
}
class BookButton extends StatelessWidget {
final Book book;
const BookButton({Key? key, required this.book}) : super(key: key);
@override
Widget build(BuildContext context) {
final bookManagerModel = Provider.of<BookManagerModel>(context);
return GestureDetector(
onTap: (bookManagerModel.books!).contains(book)
? ()=>bookManagerModel.removeBook(book)
: ()=>bookManagerModel.addBook(book),
child: SizedBox(
width: 100,
height: 60,
child: (bookManagerModel.books!).contains(book)
? const Icon(Icons.star,color: Colors.red,)
: const Icon(Icons.star_border),
),
);
}
}
- PageB 收藏列表
import 'package:flutter/material.dart';
import 'package:flutter_base/pages/PageA.dart';
import 'package:provider/provider.dart';
import '../model/BookManagerModel.dart';
class PageB extends StatelessWidget {
const PageB({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
var bookManagerModel = Provider.of<BookManagerModel>(context);
var bookCount = bookManagerModel.length;
return Scaffold(
appBar: AppBar(title: const Text('收藏列表'),),
body: ListView.builder(
itemCount: bookCount,
itemBuilder: (context,index) => BookItem(id: bookManagerModel.getByPosition(index).bookId),
),
);
}
}
参考原文:https://www.liujunmin.com/flutter/provider/eight_provider.html
评论(0)