在Flutter开发中,状态管理始终是核心话题。从Provider到BLoC,开发者们不断追求更简洁、更高效的解决方案。而RiverPod 作为官方推荐的新一代状态管理库,以其类型安全、零依赖、响应式编程 的特性脱颖而出7。它不仅解决了传统Provider的嵌套问题,还通过代码生成器大幅提升开发效率。本文将带你从安装到实战,解锁RiverPod的高阶用法!
环境配置
本文采用 riverpod_generator + flutter_hooks的方式来使用
安装依赖包
riverpod_generator可自动生成模板代码,custom_lint提供实时语法检查
shell
flutter pub add hooks_riverpod
flutter pub add flutter_hooks
flutter pub add riverpod_annotation
flutter pub add dev:riverpod_generator
flutter pub add dev:build_runner
flutter pub add dev:custom_lint
flutter pub add dev:riverpod_lint
运行代码生成工具,当riverpod相关的代码修改会自动生成对应的代码
shell
dart run build_runner watch -d
启用代码规则: 在analysis_options.yaml文件中添加以下信息
shell
analyzer:
plugins:
- custom_lint
在命令行运行如下代码,检测代码规范
dart run custom_lint
插件安装:可通过快捷键快速生成Provider模板代码,效率提升
实战案例
1、基础使用
定义一个provider,获取provider中的数据展示
dart
// provider定义
import 'dart:convert';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_test/info/model/info_model_entity.dart';
part 'info_provider.g.dart';
// 从网络接口获取数据
@riverpod
Future<InfoModelEntity> info(Ref ref) async {
/**
* 返回的数据结构 ,转模型
* {
"resultcode": "101",
"reason": "错误的请求KEY",
"result": null,
"error_code": 10001
}
*/
// http://apis.juhe.cn/simpleWeather/query
String urlString = "http://apis.juhe.cn/simpleWeather/query";
final response = await http.get(Uri.parse(urlString));
final json = jsonDecode(response.body) as Map<String, dynamic>;
return InfoModelEntity.fromJson(json);
}
main.dart中的处理
dart
void main() {
// runApp(const MyApp());
runApp(
ProviderScope(
child: MyApp(),
),
);
}
Consumer基础使用
在需要使用的时候获取provider数据
dart
// StatelessWidget利用Consumer 获取数据
import 'info_provider.dart';
class InfoPage extends StatelessWidget {
const InfoPage({super.key});
@override
Widget build(BuildContext context) {
return Consumer(builder: (context, ref, child) {
// 获取数据
final AsyncValue<InfoModelEntity> model = ref.watch(infoProvider);
return Center(
child: switch (model) {
// 展示模型中的数据
// 获取数据成功
AsyncData(:final value) => Text('model: ${value.reason}'),
// 获取数据失败
AsyncError() => const Text('Oops, something unexpected happened'),
// 获取中...
_ => const CircularProgressIndicator(),
},
);
});
}
}
ConsumerWidget和ConsumerStatefulWidget案例
riverpod提供了ConsumerWidget/ConsumerStatefulWidget替换在StatelessWidget/StatefulWidget返回Consumer来获取数据的组件示例如下:
dart
// ConsumerWidget获取provider数据 相当于上面的StatelessWidget + Consumer
class InfoPage extends ConsumerWidget {
const InfoPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final AsyncValue<InfoModelEntity> model = ref.watch(infoProvider);
return Center(
child: switch (model) {
AsyncData(:final value) => Text('model: ${value.reason}'),
AsyncError() => const Text('Oops, something unexpected happened'),
_ => const CircularProgressIndicator(),
},
);
}
}
ConsumerStatefulWidget的用法
dart
class InfoPage extends ConsumerStatefulWidget {
const InfoPage({super.key});
@override
ConsumerState createState() => _InfoPageState();
}
class _InfoPageState extends ConsumerState<InfoPage> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
final AsyncValue<InfoModelEntity> model = ref.watch(infoProvider);
return Center(
child: switch (model) {
AsyncData(:final value) => Text('model: ${value.reason}'),
AsyncError() => const Text('Oops, something unexpected happened'),
_ => const CircularProgressIndicator(),
},
);
}
}
搭配flutter_hooks结合使用
riverpod结合flutter_hooks的用法
dart
class InfoPage extends HookConsumerWidget {
const InfoPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final AsyncValue<InfoModelEntity> model = ref.watch(infoProvider);
// infoProvider.future
final counter = useState(12);
return Column(children: [
switch (model) {
AsyncData(:final value) => Text('model: ${value.reason} + ${counter.value}'),
AsyncError() => const Text('Oops, something unexpected happened'),
_ => const CircularProgressIndicator(),
},
TextButton(onPressed: () {
// 改变counter的数据
counter.value = 33;
}, child: Text("点击")),
Text(' ${counter.value}')
],);
}
}
2、进阶技巧:数据可变的Provider
实现一个电影列表管理功能
案例是定义一个数据可变的Provider,能够对数据进行修改
dart
// 定义数据模型
import 'package:json_annotation/json_annotation.dart';
import 'dart:convert';
part 'movie_model.g.dart';
List<MovieModel> movieModelFromJson(String str) => List<MovieModel>.from(json.decode(str).map((x) => MovieModel.fromJson(x)));
String movieModelToJson(List<MovieModel> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
@JsonSerializable()
class MovieModel {
@JsonKey(name: "name")
String name;
@JsonKey(name: "date")
String date;
@JsonKey(name: "pic")
String pic;
MovieModel({
required this.name,
required this.date,
required this.pic,
});
factory MovieModel.fromJson(Map<String, dynamic> json) => _$MovieModelFromJson(json);
Map<String, dynamic> toJson() => _$MovieModelToJson(this);
}
// 定义Provider
import 'package:flutter/foundation.dart';
import 'model/movie_model.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'movie_provider.g.dart';
@riverpod
class MovieList extends _$MovieList {
@override
List<MovieModel> build() {
// 构造默认数据
final String jsonString = "[{\"name\":\"财狼的日子\",\"date\":\"22025-02-25\",\"pic\":\"https://www.sojson.com/pic1\"},{\"name\":\"财狼的日子1\",\"date\":\"22025-02-25\",\"pic\":\"https://www.sojson.com/pic1\"},{\"name\":\"财狼的日子2\",\"date\":\"22025-02-25\",\"pic\":\"https://www.sojson.com/pic1\"}]";
final models = movieModelFromJson(jsonString);
debugPrint("name = ${models[0].name}");
return models;
}
void deleteItem(int index) {
List<MovieModel> oldModel = state;
oldModel.removeAt(index);
state = [...oldModel];
}
void addItem(MovieModel model) {
List<MovieModel> oldModel = state;
oldModel.add(model);
state = [...oldModel];
}
}
// Provider中的数据获取以及修改
class InfoPage extends ConsumerWidget {
const InfoPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final models = ref.watch(movieListProvider);
return Scaffold(
appBar: AppBar(title: const Text('provider test'), actions: [
TextButton(onPressed: () {
final model = MovieModel(name: 'add test', date: '2025-07-25', pic: 'pic');
ref.read(movieListProvider.notifier).addItem(model);
}, child: Text("add"))
],),
body: ListView.builder(
itemCount: models.length,
itemBuilder: (context, index) {
final item = models[index];
return ListTile(
title: Text(item.name),
subtitle: Text("date: ${item.date}"),
trailing: TextButton(onPressed: () {
ref.read(movieListProvider.notifier).deleteItem(index);
},child: Text("delete")),
);
},
),
);
}
}
Provider依赖关系处理
Provider依赖另外一个Provider的用法
dart
// 演示一个 Provider依赖另一个Provider的用法
@riverpod
String getAllMovieName(ref) {
// 依赖上面定义的movieListProvider
final allMovies = ref.watch(movieListProvider);
String names = "";
for(MovieModel model in allMovies) {
names += "${model.name} ";
}
return names;
}
// 使用方法
final names = ref.watch(getAllMovieNameProvider);
debugPrint("names = $names");
3、企业级应用:复合网络请求案例
dart
// provider
import 'dart:convert';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'model/info_model.dart';
part 'info_data_provider.g.dart';
@riverpod
class InfoData extends _$InfoData {
@override
InfoModel build() {
// 可以在这里进行第一次网络数据获取
return InfoModel(resultcode: "", reason: "", result: "", errorCode: 0);
}
// 其他地方再次请求获取数据
Future<void> getWeather(String city) async {
final param = {"city": city, "key": 'key'};
final response = await http.post(
Uri.parse('http://apis.juhe.cn/simpleWeather/query'),
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: param.toString(),
);
final json = jsonDecode(response.body) as Map<String, dynamic>;
final model = InfoModel.fromJson(json);
state = model;
}
}
// 使用案例
class InfoPage extends ConsumerWidget {
const InfoPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final model = ref.watch(infoDataProvider);
return Column(
children: [
Text('model: ${model.reason}'),
TextButton(
onPressed: () {
// 网络获取数据
ref.read(infoDataProvider.notifier).getWeather("成都");
},
child: Text("获取数据"),
),
],
);
}
}