Flutter中如何将TextField输入值同步显示到其他页面单选按钮?
问题描述
在Flutter项目中,使用Riverpod连接Firestore实现了AddMealPage页面,其中包含一个用于输入餐品名称的TextField。需求是:每当修改该TextField的输入值时,需实时同步显示到另一个包含RadioListTile的RadioOption页面的单选按钮标题中。
相关代码片段
AddMealPage代码
class AddMealPage extends ConsumerWidget { const AddMealPage({Key? key}) : super(key: key); static const String route = "/addMeal"; @override Widget build(BuildContext context, WidgetRef ref) { final model = ref.read(addMealProvider); return LoadingLayer( child: Scaffold( appBar: AppBar( elevation: 0, iconTheme: const IconThemeData( color: Colors.black, ), title: const Text( "Create Meal", style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold), ), backgroundColor: Colors.white, ), bottomNavigationBar: Padding( padding: const EdgeInsets.fromLTRB(24, 0, 24, 24), child: MaterialButton( padding: const EdgeInsets.all(16), color: Colors.black, onPressed: model.enabled ? () async { try { await model.writeMeal(); Navigator.pop(context); } catch (e) { AppSnackbar(context).error(e); } } : null, child: const Text( "Add meal", style: TextStyle(color: Color.fromARGB(255, 247, 245, 245)), ), ), ), body: SingleChildScrollView( child: Container( padding: const EdgeInsets.symmetric(vertical: 60.0, horizontal: 10.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ GestureDetector( onTap: () async { final picked = await ImagePicker() .pickImage(source: ImageSource.gallery); if (picked != null) { model.file = File(picked.path); } }, child: Container( margin: const EdgeInsets.only(), width: MediaQuery.of(context).size.width, height: 210.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), image: (model.mealUrl != null || model.file != null) ? DecorationImage( image: model.file != null ? FileImage(model.file!) : NetworkImage(model.mealUrl!) as ImageProvider, fit: BoxFit.cover, ) : null), child: Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (model.mealUrl == null && model.file == null) const Expanded( child: Center( child: Icon( Icons.photo, ), ), ), const Material( color: Colors.transparent, child: Padding( padding: EdgeInsets.all(8.0), ), ), ], ), ), ), const SizedBox( height: 10, ), const Padding( padding: EdgeInsets.only(left: 15.0), child: Text( "Meal Name", style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500), ), ), Container( height: 50, padding: const EdgeInsets.only(top: 10), width: MediaQuery.of(context).size.width, margin: const EdgeInsets.symmetric(horizontal: 20), child: TextFormField( textInputAction: TextInputAction.done, initialValue: model.mealName, maxLines: 1, decoration: InputDecoration( enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide(color: Colors.grey), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: const BorderSide(color: Colors.grey), ), ), onChanged: (v) => model.mealName = v, ), ),
RadioOption页面代码
enum Variant { mealName } class RadioOption extends StatefulWidget { const RadioOption({Key? key}) : super(key: key); @override State<RadioOption> createState() => _RadioOptionState(); } class _RadioOptionState extends State<RadioOption> { Variant? _character = Variant.mealName; @override Widget build(BuildContext context) { return Column( children: <Widget>[ RadioListTile<Variant>( title: const Text('$mealName'), value: Variant.MealName, groupValue: _character, onChanged: (Variant? value) { setState(() { _character = value; }); }, ),
实现方案
要完成餐品名称的实时同步,核心是利用Riverpod的状态监听机制,确保输入值的变化能被RadioOption页面感知并更新。具体步骤如下:
1. 完善AddMealProvider的状态通知机制
首先确保你的addMealProvider对应的模型类能在mealName变化时通知监听者,否则RadioOption无法感知到值的更新。根据你使用的Riverpod状态类型,做对应修改:
如果你用的是ChangeNotifier:
class AddMealModel extends ChangeNotifier { String _mealName = ''; String get mealName => _mealName; set mealName(String value) { _mealName = value; notifyListeners(); // 必须调用此方法触发状态更新通知 } // 其他已有方法(如writeMeal、enabled等)保持不变 } // 对应provider定义 final addMealProvider = ChangeNotifierProvider((ref) => AddMealModel());
如果你用的是StateNotifier:
// 先定义状态类 class AddMealState { final String mealName; // 其他状态字段(如file、mealUrl、enabled等) AddMealState({ this.mealName = '', // 初始化其他字段 }); AddMealState copyWith({ String? mealName, // 其他字段的copy参数 }) { return AddMealState( mealName: mealName ?? this.mealName, // 其他字段赋值 ); } } class AddMealModel extends StateNotifier<AddMealState> { AddMealModel() : super(AddMealState()); set mealName(String value) { state = state.copyWith(mealName: value); // 通过copyWith更新状态 } // 其他已有方法保持不变 } // 对应provider定义 final addMealProvider = StateNotifierProvider<AddMealModel, AddMealState>((ref) => AddMealModel());
2. 修正AddMealPage的provider读取方式
在AddMealPage中,将ref.read改为ref.watch(确保provider的状态更新机制能正常工作):
@override Widget build(BuildContext context, WidgetRef ref) { final model = ref.watch(addMealProvider); // 替换ref.read为ref.watch // 后续代码保持不变 }
3. 修改RadioOption页面以监听状态变化
将RadioOption改为ConsumerWidget(或ConsumerStatefulWidget,如果需要保留本地选中状态),读取addMealProvider中的mealName来动态更新单选按钮标题:
简化版(无本地选中状态):
enum Variant { mealName } class RadioOption extends ConsumerWidget { const RadioOption({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { // 实时监听mealName的变化 final mealName = ref.watch(addMealProvider).mealName; Variant? selectedVariant = Variant.mealName; return Column( children: <Widget>[ RadioListTile<Variant>( // 动态显示餐品名称,为空时显示默认文本 title: Text(mealName.isNotEmpty ? mealName : '未输入餐品名称'), value: Variant.mealName, // 修正枚举大小写错误 groupValue: selectedVariant, onChanged: (Variant? value) { // 若需要将选中状态存入provider,可在此调用model方法 }, ), ], ); } }
保留本地选中状态版:
class RadioOption extends ConsumerStatefulWidget { const RadioOption({Key? key}) : super(key: key); @override ConsumerState<RadioOption> createState() => _RadioOptionState(); } class _RadioOptionState extends ConsumerState<RadioOption> { Variant? _selectedVariant = Variant.mealName; @override Widget build(BuildContext context) { final mealName = ref.watch(addMealProvider).mealName; return Column( children: <Widget>[ RadioListTile<Variant>( title: Text(mealName.isNotEmpty ? mealName : '未输入餐品名称'), value: Variant.mealName, groupValue: _selectedVariant, onChanged: (Variant? value) { setState(() { _selectedVariant = value; }); }, ), ], ); } }
4. 修复原代码中的错误
- 枚举值大小写:原RadioOption代码中
Variant.MealName要改为Variant.mealName,和枚举定义一致 - 动态文本不能用const:原代码中
const Text('$mealName')要去掉const,因为mealName是动态变量,无法编译通过
修改完成后,当你在AddMealPage的TextField中输入餐品名称时,RadioOption页面的RadioListTile标题会实时同步显示输入内容。
内容的提问来源于stack exchange,提问作者Jabu
相关产品推荐
相关产品推荐

