如何结合Riverpod处理TextEditingControllers及用户状态同步
实现方案:基于Riverpod的用户状态管理与跨页同步
需求概述
- 页面加载时通过Riverpod获取用户数据,初始化输入控制器状态
- 在其他页面复用已加载的用户状态,无需重复请求数据库
- 修改用户信息并保存到数据库后,返回原页面时自动同步Riverpod中的状态
现有代码分析
账户页面代码(MyAccount.dart)
class MyAccount extends ConsumerStatefulWidget { const MyAccount({super.key}); @override ConsumerState<MyAccount> createState() => _MyAccountState(); } double gap = 10; class _MyAccountState extends ConsumerState<MyAccount> { TextEditingController dobController = TextEditingController(); TextEditingController heightController = TextEditingController(); TextEditingController ageController = TextEditingController(); int? _genderSelectedIndex; int? _interestSelectedIndex; int? _lookingForIndex; List<String> _selectedHobbies = []; double width = 200; double gap = 10; Future<List<UserClass>> getUser() async { UserResponse user = await supabase.auth.getUser(); if (user.user?.id != null) { List<Map<String, dynamic>> response = await supabase.from('users').select().eq('id', user.user!.id); return response.map((data) => UserClass.fromMap(data)).toList(); } return []; } @override void initState() { super.initState(); ref.read(userClassProvider.notifier).fetchUser(); } @override void didChangeDependencies() { super.didChangeDependencies(); var result = ref.watch(userClassProvider); if (result != null) { _genderSelectedIndex = genders.indexOf(result.gender); _selectedHobbies += List<String>.from(result.hobbies); _interestSelectedIndex = genders.indexOf(result.interestIn); _lookingForIndex = lookingForListEnums.indexOf(result.lookingFor); dobController.text = DateFormat('dd-MM-yyyy') .format(DateTime.parse(result.dob)) .toString(); heightController.text = result.height.toString(); } } // 省略build方法及其他组件代码 }
用户模型与Riverpod Provider(UserModel.dart)
import 'package:glint/main.dart'; import 'package:riverpod/riverpod.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; class UserClass { final String id; final String gender; final List<String> hobbies; final String interestIn; final String lookingFor; final String dob; final int height; final int minAge; final int maxAge; UserClass({ required this.id, required this.minAge, required this.maxAge, required this.gender, required this.hobbies, required this.interestIn, required this.lookingFor, required this.dob, required this.height, }); factory UserClass.fromMap(Map<String, dynamic> data) { return UserClass( id: data['id'], gender: data['gender'] as String, hobbies: List<String>.from(data['hobbies']), interestIn: data['interest_in'] as String, lookingFor: data['looking_for'] as String, dob: data['dob'] as String, height: data['height'], minAge: data['min_age'], maxAge: data['max_age'], ); } factory UserClass.defaultUser() { return UserClass( id: '0', gender: 'Male', hobbies: [], interestIn: '', lookingFor: '', dob: '', height: 0, minAge: 0, maxAge: 0, ); } } class UserNotifier extends StateNotifier<UserClass?> { UserNotifier() : super(null); Future<void> fetchUser() async { UserResponse user = await supabase.auth.getUser(); if (user.user?.id != null) { List<Map<String, dynamic>> response = await supabase.from('users').select().eq('id', user.user!.id); state = response.isNotEmpty ? UserClass.fromMap(response[0]) : UserClass.defaultUser(); } } } final userClassProvider = StateNotifierProvider<UserNotifier, UserClass?>((ref) { return UserNotifier(); });
完整实现步骤
1. 扩展UserNotifier添加更新方法
在UserNotifier中新增更新用户状态的逻辑,确保数据库与Riverpod状态同步:
class UserNotifier extends StateNotifier<UserClass?> { UserNotifier() : super(null); // 原fetchUser方法不变... Future<void> updateUser(UserClass updatedUser) async { // 先更新Supabase数据库 await supabase.from('users').update({ 'gender': updatedUser.gender, 'hobbies': updatedUser.hobbies, 'interest_in': updatedUser.interestIn, 'looking_for': updatedUser.lookingFor, 'dob': updatedUser.dob, 'height': updatedUser.height, 'min_age': updatedUser.minAge, 'max_age': updatedUser.maxAge, }).eq('id', updatedUser.id); // 同步更新Riverpod状态 state = updatedUser; } }
2. 优化账户页面的状态监听逻辑
将状态初始化逻辑移到build方法中,避免重复赋值:
@override void initState() { super.initState(); // 仅在状态为空时触发获取,避免重复请求 final currentUser = ref.read(userClassProvider); if (currentUser == null) { ref.read(userClassProvider.notifier).fetchUser(); } } @override Widget build(BuildContext context) { final user = ref.watch(userClassProvider); return user == null ? const Center(child: CircularProgressIndicator()) : _buildProfileForm(user); } Widget _buildProfileForm(UserClass user) { // 首次构建时初始化控制器与选中状态 if (dobController.text.isEmpty) { dobController.text = DateFormat('dd-MM-yyyy').format(DateTime.parse(user.dob)); heightController.text = user.height.toString(); _genderSelectedIndex = genders.indexOf(user.gender); _selectedHobbies = List<String>.from(user.hobbies); _interestSelectedIndex = genders.indexOf(user.interestIn); _lookingForIndex = lookingForListEnums.indexOf(user.lookingFor); } // 构建表单UI... }
3. 实现编辑资料页面(复用状态+同步更新)
新建编辑页面,直接读取已有的用户状态,修改后调用更新方法:
class EditProfilePage extends ConsumerStatefulWidget { const EditProfilePage({super.key}); @override ConsumerState<EditProfilePage> createState() => _EditProfilePageState(); } class _EditProfilePageState extends ConsumerState<EditProfilePage> { late TextEditingController dobController; late TextEditingController heightController; int? _genderSelectedIndex; List<String> _selectedHobbies = []; int? _interestSelectedIndex; int? _lookingForIndex; @override void initState() { super.initState(); final user = ref.read(userClassProvider); if (user != null) { dobController = TextEditingController( text: DateFormat('dd-MM-yyyy').format(DateTime.parse(user.dob)) ); heightController = TextEditingController(text: user.height.toString()); _genderSelectedIndex = genders.indexOf(user.gender); _selectedHobbies = List<String>.from(user.hobbies); _interestSelectedIndex = genders.indexOf(user.interestIn); _lookingForIndex = lookingForListEnums.indexOf(user.lookingFor); } } Future<void> _saveChanges() async { final user = ref.read(userClassProvider); if (user == null) return; // 构建更新后的用户对象 final updatedUser = UserClass( id: user.id, gender: genders[_genderSelectedIndex ?? 0], hobbies: _selectedHobbies, interestIn: genders[_interestSelectedIndex ?? 0], lookingFor: lookingForListEnums[_lookingForIndex ?? 0], dob: DateFormat('yyyy-MM-dd').format(DateFormat('dd-MM-yyyy').parse(dobController.text)), height: int.parse(heightController.text), minAge: user.minAge, // 根据实际UI修改 maxAge: user.maxAge, // 根据实际UI修改 ); // 调用更新方法同步数据库与状态 await ref.read(userClassProvider.notifier).updateUser(updatedUser); // 返回原页面 if (mounted) Navigator.pop(context); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('编辑资料')), body: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ // 表单输入项(性别选择器、爱好多选框、日期选择器等) ElevatedButton( onPressed: _saveChanges, child: const Text('保存修改'), ) ], ), ), ); } }
4. 状态同步原理
- 编辑页面调用
updateUser时,先将数据写入数据库,再直接更新Riverpod的state - 原账户页面通过
ref.watch(userClassProvider)监听状态变化,会自动重建UI并同步最新数据
注意事项
- 确保
genders、lookingForListEnums等枚举列表在全局或页面中定义一致 - 日期格式转换需匹配后端存储格式(示例中前端显示
dd-MM-yyyy,后端存储yyyy-MM-dd) - 建议添加异常捕获,避免数据库操作失败导致状态不一致
内容的提问来源于stack exchange,提问作者EnCoder2000
相关产品推荐
相关产品推荐

