Flutter中如何从有状态组件PhoneFormField向无状态登录页传值
如何在LoginScreen中获取PhoneFormField内的手机号值
场景说明
我有一个无状态的登录页面LoginScreen,其中包含自定义有状态组件PhoneFormField。LoginScreen中的_buildPhoneNumberSubmittedBloc函数需要传入手机号参数,但该参数仅存在于PhoneFormField组件内部。
LoginScreen核心代码
Scaffold( resizeToAvoidBottomInset: true, backgroundColor: Colors.white, body: Form( key: _phoneFormKey, child: Container( margin: EdgeInsets.only( left: width * 0.08, right: width * 0.08, top: height * 0.1), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildIntroTexts(context), SizedBox(height: height * 0.125), PhoneFormField(), SizedBox(height: height * 0.07), _buildnextButton(context), //_buildPhoneNumberSubmittedBloc(phoneNumber!), ], ), ), ), );
PhoneFormField核心代码
Row( children: [ Expanded( flex: 1, child: Container( padding: EdgeInsets.symmetric(vertical: height * 0.015), decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), border: Border.all(color: MyColors.lightGrey), ), child: GestureDetector( onTap: () async { final code = await countryPicker.showPicker(context: context); setState(() { countryCode = code; }); }, child: Row( children: [ Container( margin: const EdgeInsets.only(left: 8.0), child: countryCode != null ? Text( generateCountryFlag(countryCode!.code), style: TextStyle(fontSize: 18.sp), ) : Text( generateCountryFlag('eg'), style: TextStyle(fontSize: 18.sp), ), ), Container( padding: const EdgeInsets.symmetric(vertical: 4.0), margin: const EdgeInsets.symmetric(horizontal: 8.0), child: countryCode != null ? Text(countryCode!.dialCode, style: TextStyle(color: Colors.black, fontSize: 16.sp)) : Text('+20', style: TextStyle(color: Colors.black, fontSize: 16.sp)), ), ], ), ), ), ), const SizedBox(width: 16), Expanded( flex: 2, child: Container( width: double.infinity, padding: EdgeInsets.symmetric(horizontal: width * 0.05), decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), border: Border.all(color: MyColors.blue), ), child: TextFormField( autofocus: true, keyboardType: TextInputType.phone, textInputAction: TextInputAction.done, style: TextStyle( fontSize: 16.sp, color: Colors.black, letterSpacing: 1.5, ), decoration: InputDecoration( hintText: 'Phone Number', hintStyle: TextStyle( color: Colors.grey, fontSize: 16.sp, letterSpacing: 1, ), border: InputBorder.none, ), cursorColor: Colors.black, validator: (value) { if (value!.isEmpty) { return 'Please enter your phone number'; } else if (value.length < 11) { return 'Please enter a valid phone number'; } return null; }, onSaved: (newValue) { phoneNumber = newValue; }, onChanged: (value) { phoneNumber = value; }, )), ) ], );
解决方案
方法1:通过回调函数实时传递值
这是父子组件通信最直接的方式,给PhoneFormField添加回调参数,在手机号变化时主动把值传给LoginScreen。
- 修改
PhoneFormField的StatefulWidget定义,添加回调参数:
class PhoneFormField extends StatefulWidget { final void Function(String?) onPhoneChanged; const PhoneFormField({super.key, required this.onPhoneChanged}); @override State<PhoneFormField> createState() => _PhoneFormFieldState(); }
- 在
PhoneFormField的State中,触发回调更新父组件的值:
// 修改TextFormField的onChanged和onSaved onChanged: (value) { phoneNumber = value; widget.onPhoneChanged(value); // 传递最新值给父组件 }, onSaved: (newValue) { phoneNumber = newValue; widget.onPhoneChanged(newValue); // 保存时同步传递 },
- 在
LoginScreen中接收并维护手机号变量:
// LoginScreen中定义变量 String? _phoneNumber; // 使用PhoneFormField时传入回调 PhoneFormField( onPhoneChanged: (value) { setState(() { _phoneNumber = value; }); }, ), // 调用函数时传入值 _buildPhoneNumberSubmittedBloc(_phoneNumber!),
方法2:利用Form的GlobalKey统一获取表单值
因为LoginScreen已经使用了Form组件,可以通过Form的save方法统一触发子表单字段的保存逻辑,再获取值。
- 给
PhoneFormField添加保存回调:
class PhoneFormField extends StatefulWidget { final void Function(String?) onPhoneSaved; const PhoneFormField({super.key, required this.onPhoneSaved}); @override State<PhoneFormField> createState() => _PhoneFormFieldState(); }
- 在
PhoneFormField的TextFormField的onSaved中触发回调:
onSaved: (newValue) { phoneNumber = newValue; widget.onPhoneSaved(newValue); },
- 在
LoginScreen的下一步按钮点击事件中,先验证并保存表单:
void _handleNext() { if (_phoneFormKey.currentState!.validate()) { _phoneFormKey.currentState!.save(); // 此时_phoneNumber已更新,可调用目标函数 _buildPhoneNumberSubmittedBloc(_phoneNumber!); } } // 使用PhoneFormField PhoneFormField( onPhoneSaved: (value) { _phoneNumber = value; }, ), // _buildnextButton绑定点击事件 Widget _buildnextButton(BuildContext context) { return ElevatedButton( onPressed: _handleNext, child: const Text('Next'), ); }
方法3:状态管理工具(复杂场景可选)
如果项目中已经使用Provider、Riverpod等状态管理库,可以把手机号状态放到全局状态容器中,PhoneFormField负责更新状态,LoginScreen直接读取状态值即可。这种方式适合跨多层组件的状态共享。
内容的提问来源于stack exchange,提问作者Mustafa Adel
相关产品推荐
相关产品推荐

