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

Flutter中传递Dart Setter及LateInitializationError问题求助

Flutter多页面注册流程问题

我正在开发一个多页面注册流程,目前包含姓名页和年龄页。我编写了如下UserModel,期望更新任一字段时触发注册页面重建,从而切换至注册的下一步。我有两个问题:

  1. 是否可以像代码中这样传递UserModel的setter?或者有更优的实现方式?
  2. 点击“Next_na”按钮添加姓名时,出现如下错误:

LateInitializationError: Field 'firstName' has not been initialized.

UserModel代码

class UserModel extends ChangeNotifier {
  late String firstName;
  late int userAge;
  int indexer = 0;
  set addFName(String firstName) {
    firstName = this.firstName;
    indexer = indexer + 1;
    notifyListeners();
  }

  String get fName {
    return firstName;
  }

  set addUAge(int userAge) {
    userAge = this.userAge;
    indexer = indexer + 1;
    notifyListeners();
  }

  int get uAge {
    return userAge;
  }
}

注册页面代码

@override
  Widget build(BuildContext context) {
    return Scaffold(
        body: ChangeNotifierProvider(
      create: (context) => UserModel(),
      child: Center(
          child: Padding(
        padding: const EdgeInsets.all(18.0),
        child: Consumer<UserModel>(builder: (context, user, child) {
          return Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text(
                "Sign Up",
                style: TextStyle(fontSize: 24),
                textAlign: TextAlign.center,
              ),
              const Divider(
                height: 30,
                endIndent: 20,
                indent: 20,
                thickness: 1.5,
                color: Colors.grey,
              ),
              const SizedBox(
                height: 30,
              ),
              if (user.indexer == 0)
                FirstNameWidget(
                  user: user,
                )
              else
                FirstNameWidget(
                  user: user,
                )
            ],
          );
        }),
      )),
    );
  }

FirstNameWidget代码

class FirstNameWidget extends StatelessWidget {
  FirstNameWidget({Key? key, required this.user}) : super(key: key);
  
  final UserModel user;

  final firstNameController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Form(
      //key: _firstNameFormKey,
      child: Column(
        children: [
          TextFormField(
            keyboardType: TextInputType.text,
            controller: firstNameController,
            decoration: const InputDecoration(
              border: OutlineInputBorder(),
              hintText: 'Enter First Name',
            ),

            //autovalidateMode: AutovalidateMode.onUserInteraction,
          ),
          const SizedBox(
            height: 20,
          ),
          SizedBox(
            width: MediaQuery.of(context).size.width * 0.8,
            child: ElevatedButton(
              onPressed: () {
                print(firstNameController.text);
                commitFirstName(fName: firstNameController.text, user: user);

                //user.addFName = firstNameController.text;
              },
              child: const Text('Next_na'),
            ),
          ),
        ],
      ),
    );
  }
}

void commitFirstName({required String fName, required UserModel user}) {
  user.addFName = fName;
}

问题解答

问题2:LateInitializationError错误修复

这个错误的核心是UserModel的setter赋值逻辑完全写反,且late修饰的firstName从未被初始化。

原setter代码:

set addFName(String firstName) {
  firstName = this.firstName; // 错误:把未初始化的成员变量赋值给参数,完全搞反方向
  indexer = indexer + 1;
  notifyListeners();
}

修正后:

set addFName(String firstName) {
  this.firstName = firstName; // 将参数值赋给类成员变量
  indexer += 1;
  notifyListeners();
}

同理,addUAge setter也要修正:

set addUAge(int userAge) {
  this.userAge = userAge;
  indexer += 1;
  notifyListeners();
}

额外建议:避免用late修饰可能未提前初始化的变量,改用可空类型或默认值更安全:

// 方案1:可空类型
String? firstName;
int? userAge;

// 方案2:默认值
String firstName = '';
int userAge = 0;

问题1:UserModel传递及优化方案

你当前传递UserModel实例的方式可行,但有更简洁的优化方向:

1. 直接在Widget中通过Provider获取实例

不需要通过构造函数传递user,在FirstNameWidget里直接获取,代码更解耦:

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

  final firstNameController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    // 不需要监听变化,listen设为false
    final user = Provider.of<UserModel>(context, listen: false);
    return Form(
      child: Column(
        children: [
          // 省略TextFormField代码
          ElevatedButton(
            onPressed: () {
              user.addFName = firstNameController.text;
            },
            child: const Text('Next_na'),
          ),
        ],
      ),
    );
  }
}

此时注册页面调用FirstNameWidget无需传参,且else分支要替换成年龄页Widget:

if (user.indexer == 0)
  const FirstNameWidget()
else
  const AgeWidget() // 替换为你的年龄页Widget

2. 用普通方法替代setter

对于包含业务逻辑(更新indexer+通知监听)的操作,普通方法可读性更强:

class UserModel extends ChangeNotifier {
  String firstName = '';
  int userAge = 0;
  int indexer = 0;

  void setFirstName(String name) {
    firstName = name;
    indexer++;
    notifyListeners();
  }

  void setUserAge(int age) {
    userAge = age;
    indexer++;
    notifyListeners();
  }

  String get fName => firstName;
  int get uAge => userAge;
}

调用时直接用user.setFirstName(firstNameController.text),比setter更直观。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 20:35:23