Flutter – 使用 Riverpod 进行状态管理简介
Riverpod 是一个响应式状态管理和依赖注入框架,它使用不同的提供程序让我们访问和监听应用程序中的状态变化,它由 Remi Rousselet 构建。如果您不知道什么是状态,那么我建议您先阅读这篇文章,因为在不了解“状态”本身的情况下,您很难理解 Riverpod。
Riverpod实际上是做什么的?
Riverpod 是一个状态管理助手。它基本上使我们的状态(明确地说,我们的变量的值)在应用程序的所有部分都可以访问,它将我们的状态放在小部件树的顶部,让我们监听这些状态变化并相应地更新我们的用户界面。
Riverpod 提供多种类型的提供商,我们将一一介绍。
什么是提供者?
提供者是 Riverpod 应用程序中最重要的部分, “提供者是一个对象,它封装了一段状态并允许监听该状态。”
供应商类型:
- 状态提供者
- 未来提供者
- 流提供者
- 提供者
让我们开始建造吧!
1. 将 Riverpod 添加到我们的应用程序中:
Dart
dependencies:
flutter_riverpod: ^0.14.0+3
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(ProviderScope(child: MyApp()));
}
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
// Instead of String you can use other data types as well,
// we can also use custom data types.
final userNameProvider=StateProvider((ref) {
// we can also return an empty String here, for the sake of simplicity,
// let's return a sample name
return "SomeName";
});
Dart
import 'Providers/providers.dart' as providers.dart; //for easy access
import 'package:flutter_riverpod/flutter_riverpod.dart';
class Home extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
// Listens to the value exposed by userNameProvider
// ".state" method lets us get the state easily & directly
String name = watch(providers.userNameProvider).state;
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Center(child: const Text('GFG 🙂 '))),
body: Center(
// displaying the value
child: Text('$name'),
),
),
);
}
}
Dart
// for easy access
import 'Providers/providers.dart' as providers.dart;
class Home extends ConsumerWidget {
int n = 0;
@override
Widget build(BuildContext context, ScopedReader watch) {
// Listens to the value exposed by userNameProvider
StateController nameController = watch(providers.userNameProvider);
// ".state" method lets us get the state easily & directly
return MaterialApp(
home: Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
n++;
nameController.state =
// now we can set the state using the state setter.
"New Name $n";
},
),
appBar: AppBar(title: Center(child: const Text('GFG 🙂 '))),
body: Center(
child: Text(
// displaying the value
'${nameController.state}',
),
),
),
);
}
}
Dart
class FutureClass {
// in production, the below method could be any network call
Future getData(String para) async {
await Future.delayed(Duration(seconds: 5));
return 25;
}
}
Dart
final response = FutureProvider((ref) async {
final client = ref.read(futureClass);
return client.getData('some text as a parameter');
});
Dart
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("FutureProvider Demo"),),
body: Center(
child: Column(
children: [
Consumer(
builder: (context, watch, child) {
final futureData = watch(response);
},
),
],
),
),
);
}
}
Dart
Consumer(
builder: (context, watch, child) {
final futureData = watch(response);
return futureData.map(
,// have data
data: (data) => Text('${data.value}',)
,// in progress
loading: (_) => CircularProgressIndicator()
// has an error
error: (message) => Text(message.error),
);
},
),
Dart
final response=
FutureProvider.autoDispose.family((ref, i_am_a_param) async {
final client = ref.read(futureClass);
return client.getData(i_am_a_param);
});
// the family modifier is used to pass a String value,
// which is used to call the get() method.
// The modifier "autoDispose" destroys the state of a provider
// when it is no longer used, even when the widget state is not yet dispose
Dart
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("FutureProvider Demo"),),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer(
builder: (context, watch, child) {
final futureData = watch(providers.response);
return futureData.map(
data: (data) => Text(
'${data.value}',
), // have data
loading: (_) => Column(
children: [
CircularProgressIndicator(),
Text(
'Fetching data',
),
],
), // in progress
error: (message) =>
Text(message.error.toString()),
// has an error
);
},
),
],
),
),
),
),
);
}
}
Dart
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("StreamProvider Demo"),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer(
builder: (context, watch, child) {
final streamValue = watch(providers.streamProvider);
return streamValue.when(
data: (data) => Text(
'${data}',
), // have data
loading: () => Column(
children: [
CircularProgressIndicator(),
Text(
'Fetching data',
),
],
), // in progress
error: (message, e) =>
Text(message.toString()),
// has an error
);
},
),
],
),
),
),
),
);
}
}
Dart
final stream = Stream.fromIterable([105, 50]);
StreamBuilder(
stream: stream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData) {
// data
return SomeWidget(snapshot.data);
} else if (snapshot.hasError) {
// error state
return SomeErrorWidget(snapshot.error);
} else {
// no data
return Text('No data');
}
} else {
// loading state
return CircularProgressIndicator();
}
}
)
2. 包装我们的应用程序:
为了使提供者工作,我们必须在Flutter应用程序的根目录中添加 ProviderScope:
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(ProviderScope(child: MyApp()));
}
3. 创建提供者。dart文件:
我们将在此文件中定义所有全局提供程序,以便更轻松地维护项目。让我们定义一个 StateProvider。
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
// Instead of String you can use other data types as well,
// we can also use custom data types.
final userNameProvider=StateProvider((ref) {
// we can also return an empty String here, for the sake of simplicity,
// let's return a sample name
return "SomeName";
});
4. 阅读提供者:
现在我们已经声明了一个 StateProvider,让我们学习如何读取提供者。要阅读任何提供程序,我们有多个小部件,在本文中,我们将介绍 ConsumerWidget() 和 Consumer()。
使用 ConsumerWidget():
消费者小部件是我们可以用来代替有状态/无状态小部件的小部件。它使我们能够阅读/更改提供者的状态并听取他们的意见。
Dart
import 'Providers/providers.dart' as providers.dart; //for easy access
import 'package:flutter_riverpod/flutter_riverpod.dart';
class Home extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
// Listens to the value exposed by userNameProvider
// ".state" method lets us get the state easily & directly
String name = watch(providers.userNameProvider).state;
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Center(child: const Text('GFG 🙂 '))),
body: Center(
// displaying the value
child: Text('$name'),
),
),
);
}
}
输出:
很好。所以现在,只要userNameProvider的值发生变化,Text() 就会相应地更新。但是我们如何更新 userNameProvider 的值呢?
5. 更新/更改 StateProvider 的值:
要更改 StateProvider 的值,我们需要一个 StateController 。所以让我们创建它。
首先,让我们删除变量“name”。 ̶
S̶t̶r̶i̶n̶g̶ ̶n̶a̶m̶e̶ ̶=̶ ̶w̶a̶t̶c̶h̶(̶p̶r̶o̶v̶i̶d̶e̶r̶s̶.̶u̶s̶e̶r̶N̶a̶m̶e̶P̶r̶o̶v̶i̶d̶e̶r̶)̶.̶s̶t̶a̶t̶e̶;̶
//removed as with it we can only listen/get the state not change/mutate the state.
//let's use this StateController instead.
StateController nameController = watch(providers.userNameProvider);
// now we can get / set the state using name.state.
为简单起见,让我们使用 FloatingActionButton,并尝试使用它来改变(改变)userNameProvider 的状态。
Dart
// for easy access
import 'Providers/providers.dart' as providers.dart;
class Home extends ConsumerWidget {
int n = 0;
@override
Widget build(BuildContext context, ScopedReader watch) {
// Listens to the value exposed by userNameProvider
StateController nameController = watch(providers.userNameProvider);
// ".state" method lets us get the state easily & directly
return MaterialApp(
home: Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
n++;
nameController.state =
// now we can set the state using the state setter.
"New Name $n";
},
),
appBar: AppBar(title: Center(child: const Text('GFG 🙂 '))),
body: Center(
child: Text(
// displaying the value
'${nameController.state}',
),
),
),
);
}
}
输出:
现在,当我们按下 FloatingActionButton() 时,我们会看到名称每次都会更改为不同的数字。就是这样了。使用这些信息,我们可以轻松地使用 StateProviders,改变它们的值并听取它。
现在,让我们回顾一下异步提供程序。
1. FutureProvider:
当我们处理异步代码时,我们经常使用一些基于 Future 的 API,让我们看看如何使用 Riverpod 处理 Future 事件。为了演示,我们将创建一个返回 Future 的方法。
Dart
class FutureClass {
// in production, the below method could be any network call
Future getData(String para) async {
await Future.delayed(Duration(seconds: 5));
return 25;
}
}
为 FutureClass 创建一个提供者:
final futureClass = Provider((ref) => FutureClass());
现在,让我们创建我们的 FutureProvider,
Dart
final response = FutureProvider((ref) async {
final client = ref.read(futureClass);
return client.getData('some text as a parameter');
});
现在,我们可以使用 ConsumerWidget 来监听状态变化并相应地更新我们的 UI。
使用 FutureProvider:
除了 ConsumerWidget(),我们还有一个 Consumer() 小部件。不同之处在于,当状态改变时 ConsumerWidget 重建整个屏幕,而 Consumer() 只重建它的子屏幕,确保我们不会遇到性能问题。
让我们使用 Consumer() 小部件来观察(监听)状态变化。
Dart
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("FutureProvider Demo"),),
body: Center(
child: Column(
children: [
Consumer(
builder: (context, watch, child) {
final futureData = watch(response);
},
),
],
),
),
);
}
}
由于 Future 可以有 3 个状态,即完成、进行中和错误,我们有能力使用 .map函数分别处理这些状态。
Dart
Consumer(
builder: (context, watch, child) {
final futureData = watch(response);
return futureData.map(
,// have data
data: (data) => Text('${data.value}',)
,// in progress
loading: (_) => CircularProgressIndicator()
// has an error
error: (message) => Text(message.error),
);
},
),
如果我们想将一个变量/对象作为参数传递给提供者,那么我们可以这样做——
Dart
final response=
FutureProvider.autoDispose.family((ref, i_am_a_param) async {
final client = ref.read(futureClass);
return client.getData(i_am_a_param);
});
// the family modifier is used to pass a String value,
// which is used to call the get() method.
// The modifier "autoDispose" destroys the state of a provider
// when it is no longer used, even when the widget state is not yet dispose
完整代码:
Dart
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("FutureProvider Demo"),),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer(
builder: (context, watch, child) {
final futureData = watch(providers.response);
return futureData.map(
data: (data) => Text(
'${data.value}',
), // have data
loading: (_) => Column(
children: [
CircularProgressIndicator(),
Text(
'Fetching data',
),
],
), // in progress
error: (message) =>
Text(message.error.toString()),
// has an error
);
},
),
],
),
),
),
),
);
}
}
输出:
2. 流提供者:
我们经常在flutter应用程序中使用流,无论是从 Firestore 获取数据还是从文件中读取数据。让我们学习如何使用 Riverpod 处理这些流。
让我们先声明一个流,
final streamProvider = StreamProvider((ref) {
return Stream.fromIterable([105, 50]);
//in production this could be a stream of documents from Firestore
});
使用 StreamProvider:
这次我们用ConsumerWidget()来消费StreamProvider的数据,和FutureProvider的差不多。
Dart
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("StreamProvider Demo"),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer(
builder: (context, watch, child) {
final streamValue = watch(providers.streamProvider);
return streamValue.when(
data: (data) => Text(
'${data}',
), // have data
loading: () => Column(
children: [
CircularProgressIndicator(),
Text(
'Fetching data',
),
],
), // in progress
error: (message, e) =>
Text(message.toString()),
// has an error
);
},
),
],
),
),
),
),
);
}
}
现在,每次流有新数据时,我们的 UI 都会相应地更新。
请注意,我们不必自己管理不同的状态,如果没有 Riverpod,上面的代码将如下所示:
Dart
final stream = Stream.fromIterable([105, 50]);
StreamBuilder(
stream: stream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData) {
// data
return SomeWidget(snapshot.data);
} else if (snapshot.hasError) {
// error state
return SomeErrorWidget(snapshot.error);
} else {
// no data
return Text('No data');
}
} else {
// loading state
return CircularProgressIndicator();
}
}
)
就是这样。这应该涵盖了我们开始使用 Riverpod 所需的几乎所有内容。
可以在此处找到上述文章的完整代码。