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

Flutter BottomModalSheet TextField校验问题及页面路由需求

解决方案

下面给你两个问题的具体实现方案,都是Flutter项目中常用的可靠写法:

一、TextField 非空及格式校验(避免空值提交)

用Flutter内置的Form组件结合GlobalKey做校验是最规范的方式,能直接拦截空值和格式错误的输入:

实现步骤:

  1. 在页面的State类中定义Form的全局Key和存储输入值的变量
  2. 用Form包裹BottomModalSheet里的三个TextField,每个配置validator规则
  3. 提交按钮先触发校验,通过后再执行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实现页面跳转,同时传递联系人数据到详情页:

实现步骤:

  1. 定义联系人模型类(如果还没有)
  2. 创建详情页组件,接收联系人对象作为参数
  3. 在联系人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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 07:50:25