如何在Riverpod的Provider中使用对象列表?
Riverpod 实现多页面共享Form状态方案
核心概念快速梳理
- StateNotifierProvider:封装带更新逻辑的共享状态,把Form列表和修改方法集中管理,所有页面可访问并触发更新。
- Consumer:局部监听状态变化,仅重建需要更新的UI部分,避免全页面刷新。
- Selector:精准监听状态中特定字段/对象的变化,适合列表项的局部更新场景。
步骤1:定义Form模型(支持JSON序列化)
先实现Form实体类,支持与JSON互转,满足SharedPreferences存储需求:
import 'dart:convert'; class FormItem { final int id; String title; String content; FormItem({ required this.id, required this.title, required this.content, }); // 转为JSON格式 Map<String, dynamic> toJson() { return { 'id': id, 'title': title, 'content': content, }; } // 从JSON解析为对象 factory FormItem.fromJson(Map<String, dynamic> json) { return FormItem( id: json['id'], title: json['title'], content: json['content'], ); } }
步骤2:创建StateNotifier管理Form状态
封装本地数据读写、Form更新逻辑,把状态和操作绑定在一起:
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; class FormListNotifier extends StateNotifier<List<FormItem>> { FormListNotifier() : super([]) { // 初始化时自动加载本地数据 _loadFromPrefs(); } // 从SharedPreferences读取数据 Future<void> _loadFromPrefs() async { final prefs = await SharedPreferences.getInstance(); final jsonString = prefs.getString('form_list'); if (jsonString != null) { final List<dynamic> jsonList = jsonDecode(jsonString); state = jsonList.map((e) => FormItem.fromJson(e)).toList(); } else { // 无本地数据时使用固定长度的默认列表 state = [ FormItem(id: 0, title: '默认表单1', content: '默认内容1'), FormItem(id: 1, title: '默认表单2', content: '默认内容2'), FormItem(id: 2, title: '默认表单3', content: '默认内容3'), ]; // 把默认值存入本地 _saveToPrefs(); } } // 保存数据到SharedPreferences Future<void> _saveToPrefs() async { final prefs = await SharedPreferences.getInstance(); final jsonList = state.map((form) => form.toJson()).toList(); prefs.setString('form_list', jsonEncode(jsonList)); } // 更新单个Form的方法 void updateForm(int id, {String? title, String? content}) { state = state.map((form) { if (form.id == id) { return FormItem( id: form.id, title: title ?? form.title, content: content ?? form.content, ); } return form; }).toList(); // 更新后自动保存到本地 _saveToPrefs(); } } // 创建全局Provider,供所有页面调用 final formListProvider = StateNotifierProvider<FormListNotifier, List<FormItem>>( (ref) => FormListNotifier(), );
步骤3:页面1(列表页面,实现局部更新)
用Selector监听单个Form的变化,仅刷新修改的列表项,避免全列表重建:
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'page2.dart'; class Page1 extends ConsumerWidget { const Page1({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final formList = ref.watch(formListProvider); return Scaffold( appBar: AppBar(title: const Text('表单列表')), body: ListView.builder( itemCount: formList.length, itemBuilder: (context, index) { final targetForm = formList[index]; // 仅监听当前id的Form变化,变化时只重建该列表项 return Selector<FormListNotifier, FormItem>( selector: (ref, notifier) => notifier.state.firstWhere((f) => f.id == targetForm.id), builder: (context, currentForm, child) { return ListTile( title: Text(currentForm.title), subtitle: Text(currentForm.content), onTap: () { // 跳转到页面2编辑当前表单 Navigator.push( context, MaterialPageRoute( builder: (context) => Page2(formId: currentForm.id), ), ); }, ); }, ); }, ), ); } }
步骤4:页面2(编辑页面,实现状态同步)
获取指定Form并修改,调用更新方法后,页面1和预览区域会自动同步状态:
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class Page2 extends ConsumerStatefulWidget { final int formId; const Page2({super.key, required this.formId}); @override ConsumerState<Page2> createState() => _Page2State(); } class _Page2State extends ConsumerState<Page2> { late TextEditingController _titleController; late TextEditingController _contentController; @override void initState() { super.initState(); // 用当前Form数据初始化输入控制器 final targetForm = ref.read(formListProvider).firstWhere((f) => f.id == widget.formId); _titleController = TextEditingController(text: targetForm.title); _contentController = TextEditingController(text: targetForm.content); } @override void dispose() { _titleController.dispose(); _contentController.dispose(); super.dispose(); } @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( appBar: AppBar(title: const Text('编辑表单')), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( controller: _titleController, decoration: const InputDecoration(labelText: '标题'), ), TextField( controller: _contentController, decoration: const InputDecoration(labelText: '内容'), maxLines: 3, ), const SizedBox(height: 20), ElevatedButton( onPressed: () { // 调用Provider的更新方法修改表单 ref.read(formListProvider.notifier).updateForm( widget.formId, title: _titleController.text, content: _contentController.text, ); // 返回页面1,此时对应列表项已同步更新 Navigator.pop(context); }, child: const Text('保存'), ), const SizedBox(height: 10), // 预览按钮,点击后展示最新输入内容 ElevatedButton( onPressed: () { showDialog( context: context, builder: (context) { return AlertDialog( title: Text(_titleController.text), content: Text(_contentController.text), actions: [TextButton(onPressed: () => Navigator.pop(context), child: const Text('关闭'))], ); }, ); }, child: const Text('预览'), ), ], ), ), ); } }
关键逻辑说明
- 本地数据持久化:
FormListNotifier初始化时自动读取SharedPreferences数据,更新后自动保存,确保数据不丢失。 - 局部更新优化:页面1通过
Selector精准监听单个Form的变化,仅重建修改的列表项,不会刷新整个ListView。 - 多入口更新支持:任何页面只要获取
formListProvider.notifier,调用updateForm即可修改指定Form,所有监听该状态的UI都会同步更新。 - 状态实时同步:页面2修改表单后,页面1的对应列表项、预览弹窗会自动更新,无需手动刷新页面。
内容的提问来源于stack exchange,提问作者Jerm
相关产品推荐
相关产品推荐

