Flutter BottomModalSheet TextField校验问题及页面路由需求
解决方案
下面给你两个问题的具体实现方案,都是Flutter项目中常用的可靠写法:
一、TextField 非空及格式校验(避免空值提交)
用Flutter内置的Form组件结合GlobalKey做校验是最规范的方式,能直接拦截空值和格式错误的输入:
实现步骤:
- 在页面的State类中定义Form的全局Key和存储输入值的变量
- 用
Form包裹BottomModalSheet里的三个TextField,每个配置validator规则 - 提交按钮先触发校验,通过后再执行CRUD逻辑
代码示例:
// 页面State类内的变量定义 final _formKey = GlobalKey<FormState>(); String? _contactName, _contactPhone, _contactEmail; // 弹出BottomModalSheet的代码 showModalBottomSheet( context: context, isScrollControlled: true, // 避免键盘遮挡输入框 builder: (context) => Padding( padding: EdgeInsets.only( left: 16, right: 16, top: 16, bottom: MediaQuery.of(context).viewInsets.bottom + 20, ), child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // 姓名输入框 TextFormField( decoration: const InputDecoration(labelText: '姓名'), validator: (value) { if (value == null || value.trim().isEmpty) { return '请输入姓名'; } return null; // 返回null表示校验通过 }, onSaved: (value) => _contactName = value?.trim(), ), const SizedBox(height: 12), // 手机号输入框(带格式校验) TextFormField( decoration: const InputDecoration(labelText: '手机号'), keyboardType: TextInputType.phone, validator: (value) { if (value == null || value.trim().isEmpty) { return '请输入手机号'; } // 国内手机号正则校验,可根据需求调整 if (!RegExp(r'^1[3-9]\d{9}$').hasMatch(value.trim())) { return '请输入正确的手机号格式'; } return null; }, onSaved: (value) => _contactPhone = value?.trim(), ), const SizedBox(height: 12), // 邮箱输入框(带格式校验) TextFormField( decoration: const InputDecoration(labelText: '邮箱'), keyboardType: TextInputType.emailAddress, validator: (value) { if (value == null || value.trim().isEmpty) { return '请输入邮箱'; } // 通用邮箱正则校验 if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value.trim())) { return '请输入正确的邮箱格式'; } return null; }, onSaved: (value) => _contactEmail = value?.trim(), ), const SizedBox(height: 20), // 提交按钮 ElevatedButton( onPressed: () { // 触发所有输入框的校验 if (_formKey.currentState!.validate()) { // 校验通过后保存输入值 _formKey.currentState!.save(); // 执行你的CRUD逻辑(比如添加联系人到列表) addContact( name: _contactName!, phone: _contactPhone!, email: _contactEmail!, ); // 关闭BottomModalSheet Navigator.pop(context); } }, child: const Text('保存联系人'), ), ], ), ), ), );
说明:validate()会自动调用每个输入框的validator,如果返回非空字符串就会显示错误提示;onSaved会把校验通过的值存到变量中,避免直接使用TextField的controller(减少状态管理复杂度)。
二、联系人Card跳转详情页
用Navigator.push实现页面跳转,同时传递联系人数据到详情页:
实现步骤:
- 定义联系人模型类(如果还没有)
- 创建详情页组件,接收联系人对象作为参数
- 在联系人Card的
onTap回调中触发路由跳转
代码示例:
1. 联系人模型类
class Contact { final String name; final String phone; final String email; const Contact({ required this.name, required this.phone, required this.email, }); }
2. 详情页组件
class ContactDetailPage extends StatelessWidget { final Contact contact; const ContactDetailPage({super.key, required this.contact}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(contact.name), centerTitle: true, ), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildDetailItem('姓名', contact.name), _buildDetailItem('手机号', contact.phone), _buildDetailItem('邮箱', contact.email), ], ), ), ); } // 封装详情项组件,减少重复代码 Widget _buildDetailItem(String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: const TextStyle( fontSize: 14, color: Colors.grey, ), ), const SizedBox(height: 4), Text( value, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w500, ), ), ], ), ); } }
3. 联系人Card添加跳转逻辑
// 假设你在HomePage中遍历联系人列表生成Card Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: ListTile( title: Text(contact.name), subtitle: Text(contact.phone), trailing: const Icon(Icons.arrow_forward_ios), // 点击跳转详情页 onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ContactDetailPage(contact: contact), ), ); }, ), )
说明:如果你的项目使用了命名路由,可以把MaterialPageRoute换成NamedRoute,但上面的写法更直接,适合快速实现。
内容的提问来源于stack exchange,提问作者Learning Curious
相关产品推荐
相关产品推荐

