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

如何在Flutter中获取Otp输入框的验证码值并提交表单

如何获取自定义OTP组件的输入并提交表单(Flutter)

要实现收集4位OTP并提交表单,核心是管理多个输入框的状态,下面提供两种常用方案:

方案一:使用Form组件+onSaved回调(推荐,符合表单规范)

修改步骤:

  1. 将MyHomePage改为StatefulWidget,维护表单状态和OTP值列表
  2. 添加GlobalKey<FormState>管理表单,创建列表存储每个OTP输入框的值
  3. 修改Otp组件,接收onSaved回调以传递输入值
  4. 添加提交按钮,点击时收集并拼接OTP

完整代码:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _formKey = GlobalKey<FormState>();
  List<String> _otpValues = List.filled(4, '');

  void _submitForm() {
    if (_formKey.currentState!.validate()) {
      _formKey.currentState!.save();
      String fullOtp = _otpValues.join();
      // 替换为你的业务提交逻辑,比如发送到服务器
      print('提交的验证码:$fullOtp');
      
      // 示例:弹窗展示结果
      showDialog(
        context: context,
        builder: (ctx) => AlertDialog(
          title: const Text('提交成功'),
          content: Text('你输入的验证码是:$fullOtp'),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(ctx),
              child: const Text('确定'),
            ),
          ],
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.transparent,
        elevation: 0,
        leading: IconButton(
            onPressed: () {},
            icon: const Icon(Icons.arrow_back_ios_new),
            color: Colors.black),
        actions: [
          IconButton(
              onPressed: () {},
              icon: const Icon(Icons.info),
              color: Colors.black),
        ],
      ),
      body: Column(
        children: [
          const SizedBox(height: 40),
          const Icon(Icons.dialpad_rounded, size: 50),
          const SizedBox(height: 40),
          const Text(
            "Enter Mary's PIN",
            style: TextStyle(fontSize: 40),
          ),
          const SizedBox(height: 20),
          // 用Form包裹OTP输入框
          Form(
            key: _formKey,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                Otp(onSaved: (value) => _otpValues[0] = value ?? ''),
                Otp(onSaved: (value) => _otpValues[1] = value ?? ''),
                Otp(onSaved: (value) => _otpValues[2] = value ?? ''),
                Otp(onSaved: (value) => _otpValues[3] = value ?? ''),
              ],
            ),
          ),
          const SizedBox(height: 40),
          // 添加提交按钮
          ElevatedButton(
            style: ElevatedButton.styleFrom(
              minimumSize: const Size(200, 50),
            ),
            onPressed: _submitForm,
            child: const Text('提交验证码', style: TextStyle(fontSize: 20)),
          ),
          const SizedBox(height: 20),
          const Text(
            "Rider can't find a pin",
            style: TextStyle(fontSize: 20),
          ),
        ],
      ),
    );
  }
}

class Otp extends StatelessWidget {
  final void Function(String?)? onSaved;

  const Otp({
    Key? key,
    required this.onSaved,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 50,
      height: 50,
      child: TextFormField(
        keyboardType: TextInputType.number,
        style: Theme.of(context).textTheme.headline6,
        textAlign: TextAlign.center,
        inputFormatters: [
          LengthLimitingTextInputFormatter(1),
          FilteringTextInputFormatter.digitsOnly
        ],
        onChanged: (value) {
          if (value.length == 1) {
            FocusScope.of(context).nextFocus();
          }
          if (value.isEmpty) {
            FocusScope.of(context).previousFocus();
          }
        },
        decoration: const InputDecoration(
          hintText: ('0'),
        ),
        // 验证输入是否为空
        validator: (value) {
          if (value == null || value.isEmpty) {
            return '';
          }
          return null;
        },
        onSaved: onSaved,
      ),
    );
  }
}

方案二:使用TextEditingController(适合实时获取输入)

如果需要实时监听OTP输入状态,可以用多个TextEditingController绑定每个输入框:

核心修改:

在_MyHomePageState中创建控制器列表:

final List<TextEditingController> _otpControllers = List.generate(4, (_) => TextEditingController());

修改Otp组件接收控制器:

class Otp extends StatelessWidget {
  final TextEditingController controller;

  const Otp({
    Key? key,
    required this.controller,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 50,
      height: 50,
      child: TextFormField(
        controller: controller,
        // 其他属性保持不变
      ),
    );
  }
}

提交时拼接值:

void _submitForm() {
  String fullOtp = _otpControllers.map((ctrl) => ctrl.text).join();
  // 处理提交逻辑
}

关键说明:

  • 方案一利用Flutter表单系统,自动处理验证和保存逻辑,代码更规范
  • 方案二可以实时获取每个输入框的内容,适合需要输入完成自动提交的场景
  • 两种方案都能完美收集4位OTP,根据你的业务需求选择即可

内容的提问来源于stack exchange,提问作者humans9198

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 04:34:50