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

Flutter+Firebase注册功能报错:setState调用于空对象

问题分析与解决方案

先帮你拆解下遇到的两个核心问题,以及对应的修复方案:

1. setState调用在null对象上的错误

错误日志里的NoSuchMethodError: The method 'setState' was called on null,根源是你在_validateSubmitRegister里调用了_sheetController.setState(() { _loading = true; });,但_sheetController这个变量从来没有被初始化过,一直处于null状态。而且这里完全不需要用_sheetController的setState,直接用当前State类本身的setState方法就能更新状态。

2. TextFormField无法正确获取输入值

你的buildTextField方法里,onSaved: (input) => _saved = input这行代码逻辑错误:这里的_saved是方法的局部参数,赋值给它并不会更新类的成员变量_displayName、_email等。所以当你调用form.save()时,这些成员变量依然是null,后续的Firebase注册请求自然无法正常执行。


具体修复步骤

步骤1:修正setState调用错误

把_sheetController.setState(...)替换成当前State的setState,并且删除没用的_sheetController变量:

setState(() {
  _loading = true;
});

步骤2:修复TextFormField的onSaved逻辑

修改buildTextField方法,去掉_saved参数,改成接收一个ValueChanged<String>类型的回调,让调用方直接传入成员变量的赋值逻辑:

Widget buildTextField(
    double width,
    String text,
    IconData icon,
    bool obscureText,
    String isEmpty,
    bool autoValidate,
    ValueChanged<String> onSaved, // 新增回调参数
){
  return Container(
    margin: EdgeInsets.only(bottom: 15),
    width: width * 0.9,
    child: TextFormField(
      validator: (input) => input.isEmpty ? isEmpty : null,
      onSaved: onSaved, // 直接使用传入的回调
      autovalidate: autoValidate,
      obscureText: obscureText,
      decoration: InputDecoration(
        suffixIcon: Padding(
          padding: EdgeInsetsDirectional.only(end: 12.0),
          child: Icon(icon, color: Colors.red),
        ),
        hintText: text,
        hintStyle: TextStyle(color: Colors.redAccent, fontSize: 15.0),
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(30.0),
          borderSide: BorderSide(
            width: 5.0,
            color: Colors.redAccent,
          ),
        ),
      ),
    ),
  );
}

然后在调用buildTextField时,传入对应的赋值逻辑:

buildTextField(width, "Digite seu nome", Icons.person, false, "Por favor, digite seu nome!", _autoValidate, (input) => _displayName = input),
buildTextField(width, "Digite seu e-mail", Icons.email, false, "Por favor, digite seu e-mail!", _autoValidate, (input) => _email = input),
buildTextField(width, "Digite sua senha", Icons.lock, true, "Por favor, digite sua senha!", _autoValidate, (input) => _password = input),
buildTextField(width, "Confirme sua senha", Icons.lock, true, "Por favor, confirme sua senha!", _autoValidate, (input) => _passwordConfirm = input),

步骤3:添加密码校验和错误捕获

目前代码没有校验两次输入的密码是否一致,也没有处理Firebase注册的异常,这会导致按钮点击后无反馈。可以在_validateSubmitRegister里补充这些逻辑:

void _validateSubmitRegister() async{
  final form = formKey.currentState;
  if (form.validate()) {
    form.save();
    // 校验两次密码是否一致
    if (_password != _passwordConfirm) {
      scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Senhas não coincidem!")));
      return;
    }
    
    setState(() {
      _loading = true;
    });
    
    try {
      AuthResult result = await FirebaseAuth.instance.createUserWithEmailAndPassword(email: _email, password: _password);
      FirebaseUser user = result.user;
      
      // 可选:更新用户的显示名称
      UserUpdateInfo updateInfo = UserUpdateInfo();
      updateInfo.displayName = _displayName;
      await user.updateProfile(updateInfo);
      
      setState(() {
        _loading = false;
      });
      
      // 注册成功后跳转到首页
      Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => Home()));
    } catch (e) {
      setState(() {
        _loading = false;
      });
      // 显示错误提示
      scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Erro ao registrar: ${e.toString()}")));
    }
  } else {
    setState(() {
      _autoValidate = true; // 开启自动校验,提示用户输入错误
    });
  }
}

步骤4:添加加载状态(可选)

可以给按钮添加加载状态提示,避免用户重复点击:

RaisedButton.icon(
  onPressed: _loading ? null : _validateSubmitRegister, // 加载时禁用按钮
  label: _loading 
      ? CircularProgressIndicator(color: Colors.white) 
      : Text(
          'Registrar',
          style: TextStyle(fontSize: 15, color: Colors.white),
        ),
  color: Colors.red,
  icon: _loading 
      ? SizedBox() 
      : Icon(Icons.account_circle, color: Colors.white),
  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30.0)),
),

完整修正后的代码

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:cruke_app/main.dart';
import 'package:cruke_app/ui/login.dart';

class CrukeRegister extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Register(),
    );
  }
}

class Register extends StatefulWidget {
  @override
  _RegisterState createState() => _RegisterState();
}

class _RegisterState extends State<Register> {
  String _email, _password, _passwordConfirm, _displayName;
  final formKey = new GlobalKey<FormState>();
  bool _autoValidate = false;
  final FirebaseAuth auth = FirebaseAuth.instance;
  bool _loading = false;
  final scaffoldKey = new GlobalKey<ScaffoldState>();

  Widget buildTextField(
      double width,
      String text,
      IconData icon,
      bool obscureText,
      String isEmpty,
      bool autoValidate,
      ValueChanged<String> onSaved,
  ){
    return Container(
      margin: EdgeInsets.only(bottom: 15),
      width: width * 0.9,
      child: TextFormField(
        validator: (input) => input.isEmpty ? isEmpty : null,
        onSaved: onSaved,
        autovalidate: autoValidate,
        obscureText: obscureText,
        decoration: InputDecoration(
          suffixIcon: Padding(
            padding: EdgeInsetsDirectional.only(end: 12.0),
            child: Icon(icon, color: Colors.red),
          ),
          hintText: text,
          hintStyle: TextStyle(color: Colors.redAccent, fontSize: 15.0),
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(30.0),
            borderSide: BorderSide(
              width: 5.0,
              color: Colors.redAccent,
            ),
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    double width = MediaQuery.of(context).size.width;
    double height = MediaQuery.of(context).size.height;
    return Scaffold(
      key: scaffoldKey,
      backgroundColor: Colors.white,
      body: SingleChildScrollView(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Center(
              child: Container(
                margin: EdgeInsets.only(top: height * 0.025),
                child: Image.asset("images/logo_vertical.png", width: width * 0.5),
              ),
            ),
            Form(
              key: formKey,
              autovalidate: _autoValidate,
              child: Column(
                children: <Widget>[
                  buildTextField(width, "Digite seu nome", Icons.person, false, "Por favor, digite seu nome!", _autoValidate, (input) => _displayName = input),
                  buildTextField(width, "Digite seu e-mail", Icons.email, false, "Por favor, digite seu e-mail!", _autoValidate, (input) => _email = input),
                  buildTextField(width, "Digite sua senha", Icons.lock, true, "Por favor, digite sua senha!", _autoValidate, (input) => _password = input),
                  buildTextField(width, "Confirme sua senha", Icons.lock, true, "Por favor, confirme sua senha!", _autoValidate, (input) => _passwordConfirm = input),
                  Container(
                    height: height * 0.09,
                    width: width * 0.9,
                    padding: EdgeInsets.only(top: 10.0),
                    child: RaisedButton.icon(
                      onPressed: _loading ? null : _validateSubmitRegister,
                      label: _loading
                          ? CircularProgressIndicator(color: Colors.white)
                          : Text(
                              'Registrar',
                              style: TextStyle(fontSize: 15, color: Colors.white),
                            ),
                      color: Colors.red,
                      icon: _loading
                          ? SizedBox()
                          : Icon(Icons.account_circle, color: Colors.white),
                      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30.0)),
                    ),
                  ),
                ],
              ),
            ),
            Container(
              margin: EdgeInsets.only(top: 15),
              child: Material(
                child: InkWell(
                  onTap: (){
                    Navigator.push(context, MaterialPageRoute(builder: (context) => CrukeLogin()));
                  },
                  child: Text("Já tem uma conta? Faça Login!", style: TextStyle(fontSize: 15, color: Colors.red),),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  void _validateSubmitRegister() async{
    final form = formKey.currentState;
    if (form.validate()) {
      form.save();
      
      if (_password != _passwordConfirm) {
        scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Senhas não coincidem!")));
        return;
      }
      
      setState(() {
        _loading = true;
      });
      
      try {
        AuthResult result = await FirebaseAuth.instance.createUserWithEmailAndPassword(email: _email, password: _password);
        FirebaseUser user = result.user;
        
        UserUpdateInfo updateInfo = UserUpdateInfo();
        updateInfo.displayName = _displayName;
        await user.updateProfile(updateInfo);
        
        setState(() {
          _loading = false;
        });
        
        Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => Home()));
      } catch (e) {
        setState(() {
          _loading = false;
        });
        scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Erro ao registrar: ${e.toString()}")));
      }
    } else {
      setState(() {
        _autoValidate = true;
      });
    }
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:14:53