Flutter状态管理新宠:RiverPod全面解析与实战指南

在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模板代码,效率提升

Flutter Riverpod Snippets

实战案例

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("获取数据"),
        ),
      ],
    );
  }
}
相关推荐
「、皓子~11 小时前
海狸IM 2.0 正式发布:六端齐发,开源 IM 迈入新阶段
flutter·electron·开源软件·ai编程·交友·im
JIngles12316 小时前
flutter避免对widget图片作重复刷新(含实际图片发生变化或不发生变化)
flutter
ltlovezh17 小时前
ROI 编码学习指南:Android 与 FFmpeg 的真实实现边界
android·ffmpeg·音视频开发
心前阳光19 小时前
Unity之2021.3.45f2c1发布安卓程序遇到的问题
android·unity·游戏引擎
雾沉川20 小时前
Flutter 入门开发环境完整搭建教程
学习·flutter
utf8mb4安全女神20 小时前
MySQL5.7升级到MySQL8.0并进行数据迁移
android
黄林晴20 小时前
Android XR DP4 重磅发布:手机 App 直投眼镜,Compose 原生玩转 3D 内容
android·google io
MemoriKu20 小时前
Flutter 本地 AI 相册工程收口:从屏幕常亮、标签体系到照片属性后台队列
大数据·人工智能·python·flutter·elasticsearch·搜索引擎·数据库架构
炼川淬海DB21 小时前
数据库开发规范
android·adb·数据库开发