You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何结合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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 14:05:57