Flutter集成Firebase注册出现_AssertionError问题求助
你碰到的这个_AssertionError ('package:firebase_auth/src/firebase_auth.dart': Failed assertion: line 95 pos 12: 'email != null': is not true.)崩溃,问题根源完全和你推测的一致——TextFormField的onSaved逻辑没正确更新类里的成员变量,导致传给Firebase的email和password都是null,触发了断言检查。
为什么原来的代码会失效?
你在buildTextField方法里把_saved作为参数传递,但Dart里的String是值类型,在onSaved: (input) => _saved = input这行里,你只是修改了方法内部的局部变量副本,根本没碰到底层类的_email、_password这些成员变量。所以调用form.save()后,这些变量依然是null,传给Firebase接口时自然就炸了。
修复方案
我们需要让onSaved直接操作类成员变量,最灵活的方式是给buildTextField传入一个赋值回调函数,具体步骤如下:
1. 修改buildTextField方法定义
移除没用的_saved参数,新增一个Function(String)类型的onSaved回调参数:
Widget buildTextField( double width, String text, IconData icon, bool obscureText, String isEmpty, bool autoValidate, Function(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, ), ), ), ), ); }
2. 调用buildTextField时传入对应回调
每个TextField传入直接赋值给类成员的回调:
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. 增强注册逻辑的健壮性
在_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; } try{ setState(() => _loading = true); AuthResult result = await FirebaseAuth.instance.createUserWithEmailAndPassword(email: _email, password: _password); FirebaseUser user = result.user; // 可选:更新用户的displayName到Firebase Auth await user.updateProfile(displayName: _displayName); // 注册成功后跳转到主页(替换成你的目标页面) Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => MyApp())); }catch(e){ setState(() => _loading = false); scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Erro ao registrar: ${e.toString()}"))); } } else { setState(() => _autoValidate = true); } }
完整修复后的代码
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>(); @override void initState() { super.initState(); } Widget buildTextField( double width, String text, IconData icon, bool obscureText, String isEmpty, bool autoValidate, Function(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: _validateSubmitRegister, label: Text( 'Registrar', style: TextStyle(fontSize: 15, color: Colors.white), ), color: Colors.red, icon: 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; } try{ setState(() => _loading = true); AuthResult result = await FirebaseAuth.instance.createUserWithEmailAndPassword(email: _email, password: _password); FirebaseUser user = result.user; await user.updateProfile(displayName: _displayName); Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => MyApp())); }catch(e){ setState(() => _loading = false); scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Erro ao registrar: ${e.toString()}"))); } } else { setState(() => _autoValidate = true); } } }
这样修改后,form.save()就能正确把输入值赋值给类成员变量,Firebase接口也能拿到有效的email和password,崩溃问题就解决了。额外增加的密码校验和异常处理也能让注册流程更顺畅。
内容的提问来源于stack exchange,提问作者Rafael Canuto

