目录
前言
在 Flutter 开发中,状态管理是一个常见的需求。Provider 是 Flutter 官方推荐的一种简单而强大的状态管理解决方案。本文将介绍 Provider 的基本用法和一些常见场景下的应用。
1.什么是 Provider
Provider 是一个 Flutter 的插件包,旨在简化状态管理和依赖注入。它使用 InheritedWidget 作为底层实现,通过提供一种订阅与更新的机制,能够让应用在状态变化时自动刷新对应的 UI,极大地提升了开发体验。
2.如何安装 Provider
在项目的 pubspec.yaml 文件中添加 Provider:
dependencies:
provider: ^6.1.2
然后执行以下命令安装依赖:
flutter pub get
3.Provider的常见类型
在使用 Provider 时,以下几种类型是最常见的:
ChangeNotifierProvider:适用于可变状态。
Provider:用于提供静态数据。
FutureProvider:用于处理异步任务。
StreamProvider:用于处理流式数据。
4.常见类型的用法
1.ChangeNotifierProvider
ChangeNotifierProvider 是 Flutter 中 Provider 的一种,专为管理需要状态变化通知的情况设计。它使用 ChangeNotifier 类来处理状态管理并通知依赖其状态的组件更新。
1.ChangeNotifier
ChangeNotifier
是 Flutter SDK 中的一个简单的类。它用于向监听器发送通知。换言之,如果被定义为 ChangeNotifier
,你可以订阅它的状态变化。(这和大家所熟悉的观察者模式相类似)。
在 provider
中,ChangeNotifier
是一种能够封装应用程序状态的方法。对于特别简单的程序,你可以通过一个 ChangeNotifier
来满足全部需求。在相对复杂的应用中,由于会有多个模型,所以可能会有多个 ChangeNotifier
。
我们以下面的计时器为例,点击++或者--按钮计时器分别执行加一和减一操作。
图1.使用Provider实现的计时器的例子
在我们的应用程序中,我们使用新建一个CounterModel类,用来存储count属性,在方法中调用notifyListeners方法,这样它的值发生变化的时候,这个类的监听者都会收到通知。
代码如下:
class CounterModel with ChangeNotifier{
int _count = 0;
int get count=>_count;
void increment(){
_count++;
notifyListeners();
}
void decrement(){
_count--;
notifyListeners();
}
}
然后我们通过ChangeNotifierProvider wiget把我们的app包裹起来之后,子结点就可以访问到我们这个类。
void main() {
runApp(ChangeNotifierProvider(
create: (_) => CounterModel(),
child: const MyApp(),
));
}
2. 使用 Consumer 或 Provider.of 读取状态
1.Provoder.of读取状态
完成上述的配置之后,我们可以通过实例化一个Provider.of来获取和调用我们定义的类。
import 'package:flutter/material.dart';
import 'package:provider_demos/counter_model.dart';
import 'package:provider/provider.dart';
void main() {
runApp(ChangeNotifierProvider(
create: (_) => CounterModel(),
child: const MyApp(),
));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Provider用法'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
final counter = Provider.of<CounterModel>(context);
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'计数器:${counter.count}',
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 20,),
ElevatedButton(
onPressed: () {
counter.increment();
},
child: const Text("counter++")),
ElevatedButton(
onPressed: () {
counter.decrement();
},
child: const Text("counter--")),
],
),
),
);
}
}
2.使用Provider.of 手动读取状态
Provider.of 可以用于获取 Provider 提供的对象。若不需要 UI 自动更新,可以使用 listen: false 参数,以避免不必要的重建。
floatingActionButton: FloatingActionButton(
onPressed: () {
Provider.of<Counter>(context, listen: false).increment();
},
child: Icon(Icons.add),
)
3.Consumer
我们还可以使用Consumer来获取我们要监听的类。
在使用Consumer类的回时候,我们需要制定访问的模型类型。例如在计时器的例子中,我们就要写上Consumer<CounterModel>。
Consumer在被调用的时候会用到三个参数。第一个是context,在每个build方法中都能找到这个参数。第二个参数就是ChangeNotifier的实例。他就是我们最开始得到的实例。第三个参数是child,是使用我们定义的数据模型的widget。
这里需要注意的是,我们尽量把Consumer放在 widget 树尽量低的位置上。
import 'package:flutter/material.dart';
import 'package:provider_demos/counter_model.dart';
import 'package:provider/provider.dart';
void main() {
runApp(ChangeNotifierProvider(
create: (_) => CounterModel(),
child: const MyApp(),
));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Provider用法'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Consumer<CounterModel>(builder: (context,counter,child){
return Text(
'计数器:${counter.count}',
style: Theme.of(context).textTheme.headlineMedium,
);
}),
const SizedBox(height: 20,),
Consumer<CounterModel>(builder: (context,counter,child){
return ElevatedButton(
onPressed: () {
counter.increment();
},
child: const Text("counter++"));
}),
Consumer<CounterModel>(builder: (context,counter,child){
return ElevatedButton(
onPressed: () {
counter.decrement();
},
child: const Text("counter--"));
}),
],
),
),
);
}
}
2.使用Provider
Provider 是最基础的 Provider 类型,通常用于提供静态数据或者不可变对象。以下是一个简单的例子,提供一个用户模型
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// 数据模型
class User {
final String name;
final int age;
User(this.name, this.age);
}
void main() {
runApp(
Provider<User>(
create: (context) => User('John Doe', 25),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: UserScreen(),
);
}
}
class UserScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
return Scaffold(
appBar: AppBar(
title: Text('Provider 示例'),
),
body: Center(
child: Text(
'用户名: ${user.name}, 年龄: ${user.age}',
style: TextStyle(fontSize: 24),
),
),
);
}
}
3.使用FutureProvider
FutureProvider 用于处理异步任务,例如从网络获取数据或从本地数据库加载信息。以下是一个模拟从网络获取用户数据的示例。
示例代码:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// 模拟网络服务
class UserService {
Future<String> fetchUserName() async {
await Future.delayed(Duration(seconds: 2)); // 模拟网络延迟
return "John Doe";
}
}
void main() {
runApp(
FutureProvider<String>(
create: (context) => UserService().fetchUserName(),
initialData: '加载中...',
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: UserScreen(),
);
}
}
class UserScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final userName = Provider.of<String>(context);
return Scaffold(
appBar: AppBar(
title: Text('FutureProvider 示例'),
),
body: Center(
child: Text(
'用户名: $userName',
style: TextStyle(fontSize: 24),
),
),
);
}
}
4.使用StreamProvider
StreamProvider 用于处理流数据,例如监听实时更新数据流。以下是一个模拟数字计时器的例子。
示例代码:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// 模拟一个计数器流
class CounterService {
Stream<int> counterStream() async* {
for (int i = 0; i <= 10; i++) {
await Future.delayed(Duration(seconds: 1)); // 模拟延迟
yield i; // 每次发送一个新值
}
}
}
void main() {
runApp(
StreamProvider<int>(
create: (context) => CounterService().counterStream(),
initialData: 0,
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: CounterScreen(),
);
}
}
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = Provider.of<int>(context);
return Scaffold(
appBar: AppBar(
title: Text('StreamProvider 示例'),
),
body: Center(
child: Text(
'当前计数值: $counter',
style: TextStyle(fontSize: 24),
),
),
);
}
}