Article series
Flutter Provider state management---introduction, class diagram analysis, basic use
Flutter Provider State Management---Analysis of the Use of Eight Providers
Flutter Provider State Management---Analysis of
Flutter Provider state management---MVVM architecture combat
Video series
Flutter Provider State Management---Introduction, class diagram analysis, basic use of
Flutter Provider State Management---Analysis of the Use of Eight Providers
Flutter Provider State Management---Analysis of
Flutter Provider State Management---MVVM Architecture Actual Combat
Source code warehouse address
Preface
In our last article, we Provider
and explained the class structure, and finally wrote a simple example. Through the previous chapter, we Provider
. In this chapter, let’s talk about it. The 8 types of providers of Provider
Provider
Provider
is the most basic Provider component. You can use it to provide a value for any position in the component tree, but when the value changes, it will not update the UI. Below we give an example
Step 1: Create a model
class UserModel {
String name = "Jimi";
void changeName() {
name = "hello";
}
}
Step 2: Application entry settings
return Provider<UserModel>(
create: (_) => UserModel(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ProviderExample(),
),
);
Step 3: Use shared data
Regarding Consumer
, the consumers will be mentioned later. We only need to know that there are two consumers. The first is used to display the data of the model, and the second is used to change the data of the model.
- The first
Comsumer
is the data used to read the modelname
- The second
Consumer
used to change the data of the modelname
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("改变值"),
),
);
},
),
],
),
),
);
}
}
operation result
We click the button to cause the model data to change, but the UI does not change or rebuild after the model data is changed. That's because the Provider
provider component does not monitor the changes in the values it provides.
ChangeNotifierProvider
It is different from the Provider
ChangeNotifierProvider
will monitor the changes of the model object, and when the data changes, it will also rebuild Consumer
(consumer), below we give an example
Step 1: Create a model
Careful we can find that there are two changes in the model defined here, as follows:
ChangeNotifier
with 061663dab24d50- Called
notifyListeners()
Because the model class uses ChangeNotifier
, then we can access notifyListeners()
and whenever it is called, ChangeNotifierProvider
will be notified and the consumer will rebuild the UI.
import 'package:flutter/material.dart';
class UserModel1 with ChangeNotifier {
String name = "Jimi";
void changeName() {
name = "hello";
notifyListeners();
}
}
Step 2: Application entry settings
return ChangeNotifierProvider<UserModel1>(
create: (_) => UserModel1(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ChangeNotifierProviderExample(),
),
);
Step 3: Use shared data
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("改变值"),
),
);
},
),
],
),
),
);
}
}
operation result
FutureProvider
In simple terms, FutureProvider
used to provide values that may not be ready when its value is ready to be used in the component tree. The main purpose is to ensure that null values are not passed to any subcomponents, and FutureProvider
has an initial value, which can be used by subcomponents. Future
value and tell the sub-component to use the new value to rebuild.
Note:
FutureProvider
will only be rebuilt once- Default display initial value
- Then display the
Future
value - Will not rebuild again in the end
Step 1: Create a model
The Provider
and 061663dab24eff is that the constructor is added, and changeName
becomes Future
. We simulate the network request to change its value after a two-second delay.
class UserModel2{
UserModel2({this.name});
String? name = "Jimi";
Future<void> changeName() async {
await Future.delayed(Duration(milliseconds: 2000));
name = "hello";
}
}
Step 2: Provide Future
We have a method, which is to obtain userModel2
asynchronously, the simulated network request is delayed for two seconds, and finally name
modified and returned to 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: "获取新的数据");
}
}
Step 3: Application entry settings
initialData
is the default value. For the create
parameter, we passed a Future<UserModel2>
because it received the model Create<Future<T>?>
return FutureProvider<UserModel2>(
initialData: UserModel2(name: "hello"),
create: (_) => UserFuture().asyncGetUserModel2(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: FutureProviderExample(),
),
);
Step 4: Use shared data
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("改变值"),
),
);
},
),
],
),
),
);
}
}
operation result
We can see the first show defaults hello
, finally get to show the results when obtain new data, we try to change its value, although the value of the change but did not refresh the UI.
StreamProvider
StreamProvider
provides a flow value around StreamBuilder
. The provided value will replace the new value when it is passed in. Like FutureProvider
, the main difference is that the value will rebuild the UI based on multiple triggers.
If you StreamBuilder
n’t know much about 061663dab2505a, then it is difficult for you to understand StreamProvider
, StreamProvider document address
Step 1: Create a model
class UserModel3{
UserModel3({this.name});
String? name = "Jimi";
void changeName() {
name = "hello";
}
}
Step 2: Provide Stream
The following code is similar to a timer, generating a number every second
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);
}
}
Step 3: Application entry settings
There are also initialData
initial value, and FutureProvider
similar, but create
property is to obtain a Stream
stream.
return StreamProvider<UserModel3>(
initialData: UserModel3(name: "hello"),
create: (_) => UserStream().getStreamUserModel(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: StreamProviderExample(),
),
);
Step 4: Use shared data
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("改变值"),
),
);
},
),
],
),
),
);
}
}
operation result
MultiProvider
In the above example, we only returned one provider. In the actual development process, there will definitely be multiple providers. Although we can use nested methods to solve this problem, this is undoubtedly messy and poor in readability. At this time, the powerful MultiProvder
is produced. Let's look at an example:
Step 1: Create two models
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();
}
}
Step 2: Application entry settings
Compared with the nested setting of mode one, mode two is particularly simple.
Method 1: Nested setting
return ChangeNotifierProvider<UserModel1>(
create: (_) => UserModel1(),
child: ChangeNotifierProvider<UserModel4>(
create: (_) => UserModel4(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: MultiProviderExample(),
),
),
);
Method 2: Use MultiProvider
return MultiProvider(
providers: [
ChangeNotifierProvider<UserModel1>(create: (_) => UserModel1()),
ChangeNotifierProvider<UserModel4>(create: (_) => UserModel4()),
/// 添加更多
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: MultiProviderExample(),
),
);
Step 3: Use shared data
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("改变值"),
),
);
},
),
],
),
),
);
}
}
operation result
ProxyProvider
When we have multiple models, there will be cases where the model depends on another model. In this case, we can use ProxyProvider
to get a value from another provider, and then inject it into another provider. Let's take a look at the code demo
Step 1: Create two models
Here we created two models UserModel5
and WalletModel
, while WalletModel
dependence and UserModel5
, when calling WalletModel
of changeName
when the method changes UserModel5
when the inside of the name, of course, we are not so simple in the actual development process, here is a demonstration model dependent If you use 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");
}
}
Step 2: Application entry settings
return MultiProvider(
providers: [
ChangeNotifierProvider<UserModel5>(create: (_) => UserModel5()),
ProxyProvider<UserModel5, WalletModel>(
update: (_, userModel5, walletModel) => WalletModel(userModel5: userModel5),
)
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ProxyProviderExample(),
),
);
Step 3: Use shared data
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("通过代理改变值"),
),
);
},
),
],
),
),
);
}
}
operation result
ChangeNotifierProxyProvider
The ProxyProvider
, the only difference is that it builds and synchronizes ChangeNotifier
of ChangeNotifierProvider
. When the provider data changes, the UI will be reconstructed.
Below we give an example:
- Get a list of books
- Get a list of favorite books
- Click on the book to add or uncollect
- Real-time refactoring of UI through proxy
Step 1: Create two models
1、BookModel
BookModel
Users store model data and convert books into models.
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
mainly used to manage books, collect books, cancel collections, etc.
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();
}
}
Step 2: Application entry settings
return MultiProvider(
providers: [
Provider(create: (_) => BookModel()),
ChangeNotifierProxyProvider<BookModel, BookManagerModel>(
create: (_) => BookManagerModel(BookModel()),
update: (_, bookModel, bookManagerModel) => BookManagerModel(bookModel),
)
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: ChangeNotifierProxyProviderExample(),
),
);
Step 3: Set up 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: "收藏"
)
],
),
);
}
}
The fourth step: UI construction of the book list
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),
),
);
}
}
Step 5: Build the favorite list 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),
),
);
}
}
Other auxiliary packaging
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),
);
}
}
operation result
ListenableProxyProvider
ListenableProxyProvider
is ListenableProvider
, but it is surprisingly consistent with ChangeNotifierProvider
ListenableProxyProvider
, please contact me to add.
Summarize
Provider
provides us with a lot of providers, there are a total of eight. But our more commonly used ones are ChangeNotifierProvider
, MultiProvider
, ChangeNotifierProxyProvider
, and other providers can be based on their actual application scenarios.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。