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

Flutter:如何从自定义Stateful Widget获取控制器值存入本地存储

解决方案

要实现获取CustomTextFormField的输入值并保存到本地存储,最合理的方式是让父组件(注册页)管理输入控制器,避免直接访问子组件内部状态,以下是具体步骤:

步骤1:修改CustomTextFormField,支持外部传入控制器

将组件内部的_controller改为由父组件传入,这样父组件可以直接持有并获取输入值:

class CustomTextFormField extends StatefulWidget {
  // 新增外部控制器参数
  final TextEditingController controller;
  
  const CustomTextFormField(
      {super.key,
      required this.hint,
      required this.inputType,
      required this.regExp,
      required this.controller});

  final String hint;
  final TextInputType inputType;
  final RegExp regExp;
  
  @override
  State<CustomTextFormField> createState() => _CustomTextFormFieldState();
}

class _CustomTextFormFieldState extends State<CustomTextFormField> {
  // 不再自行创建控制器,直接使用父组件传入的实例
  @override
  void dispose() {
    // 注意:控制器由父组件创建,因此这里不需要销毁,交给父组件处理
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: TextFormField(
          decoration: InputDecoration(
            hintText: widget.hint,
            fillColor: Colors.white,
            border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(8),
            ),
          ),
          controller: widget.controller,
          keyboardType: widget.inputType,
          validator: (value) {
            if (value == null || value.isEmpty) {
              return "Field can not be empty";
            }
            if (widget.regExp.hasMatch(value)) {
              return "Field has invalid value";
            }
            return null;
          }),
    );
  }
}

步骤2:在注册页管理控制器并实现本地存储逻辑

在RegisterPage的状态类中创建对应字段的控制器,传递给子组件,并完成用户信息的本地存储:

import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';

class _RegisterPageState extends State<RegisterPage> {
  final _formKey = GlobalKey<FormState>();
  // 创建各输入字段的控制器
  final _userNameController = TextEditingController();
  final _phoneNumController = TextEditingController();
  final _passwordController = TextEditingController();

  @override
  void dispose() {
    // 销毁所有控制器,避免内存泄漏
    _userNameController.dispose();
    _phoneNumController.dispose();
    _passwordController.dispose();
    super.dispose();
  }

  // 保存用户信息到本地存储
  Future<void> _saveUserToLocal() async {
    final prefs = await SharedPreferences.getInstance();
    final user = User(
      phoneNum: _phoneNumController.text.trim(),
      email: "", // 若需要邮箱字段,可添加对应输入框及控制器
      userName: _userNameController.text.trim(),
      passWord: _passwordController.text.trim(),
    );
    // 将User实例转为JSON字符串存入本地
    await prefs.setString('user', jsonEncode(user.toJson()));
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        FocusScopeNode currentFocus = FocusScope.of(context);
        if (!currentFocus.hasPrimaryFocus) {
          currentFocus.unfocus();
        }
      },
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.orange[50],
        ),
        body: SingleChildScrollView(
          child: Form(
            key: _formKey,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                CustomTextFormField(
                    hint: 'Username',
                    inputType: TextInputType.name,
                    regExp: RegExpControl.userNameRegExp,
                    controller: _userNameController),
                CustomTextFormField(
                    hint: 'Phone number',
                    inputType: TextInputType.phone,
                    regExp: RegExpControl.phoneNumRegExp,
                    controller: _phoneNumController),
                // 修改PasswordChecking组件,让它接收外部控制器(逻辑同CustomTextFormField)
                PasswordChecking(controller: _passwordController),
                MaterialButton(
                  color: Colors.amber,
                  onPressed: () async {
                    if (_formKey.currentState!.validate()) {
                      await _saveUserToLocal();
                      Navigator.pushNamedAndRemoveUntil(
                          context, NamedRoute.homePageName, (route) => false);
                    } else {
                      Dialogs.alert(context, "Err", 'ERRR');
                    }
                  },
                  child: const Text(
                    "Register",
                    style: TextStyle(color: Colors.black87),
                  ),
                ),
                TextButton(
                    onPressed: () {
                      Navigator.pushNamedAndRemoveUntil(
                          context, NamedRoute.loginPageName, (route) => false);
                    },
                    child: const Text("Have an account ? Login"))
              ],
            ),
          ),
        ),
      ),
    );
  }
}

步骤3:完善User类的JSON转换方法

补充toJson方法,实现User实例与JSON的双向转换(同时修正原代码中formJson的拼写错误):

class User {
  String phoneNum;
  String email;
  String userName;
  String passWord;

  User({
    required this.phoneNum,
    required this.email,
    required this.userName,
    required this.passWord,
  });

  // 修正拼写错误:formJson → fromJson
  User.fromJson(Map<String, dynamic> json)
      : phoneNum = json['phoneNum'],
        email = json['email'],
        userName = json['userName'],
        passWord = json['passWord'];

  // 新增转JSON方法
  Map<String, dynamic> toJson() {
    return {
      'phoneNum': phoneNum,
      'email': email,
      'userName': userName,
      'passWord': passWord,
    };
  }
}

可选:修改PasswordChecking组件

参照CustomTextFormField的修改方式,让PasswordChecking支持外部传入控制器,确保能获取密码输入值。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 03:37:36