Flutter/Dart报错:setState() called after dispose() 问题求助
解决Flutter中
setState() called after dispose()错误 问题成因
点击Login按钮调用_submitFormOnLogin函数时触发错误,核心原因是:登录成功后执行Navigator.pop(context)销毁当前页面,但函数后续仍会执行setState操作,此时组件已被销毁,导致触发更新已销毁组件的错误。
问题代码分析
问题集中在_submitFormOnLogin函数:
void _submitFormOnLogin() async { final isValid = _loginFormKey.currentState!.validate(); if (isValid) { setState(() { _isLoading = true; }); try { await _auth.signInWithEmailAndPassword( email: _emailTextController.text.trim().toLowerCase(), password: _passTextController.text.trim()); Navigator.canPop(context) ? Navigator.pop(context) : null; } catch (error) { setState(() { _isLoading = false; }); GlobalMethod.showErrorDialog(error: error.toString(), ctx: context); print('error occurred! $error'); } } setState(() { _isLoading = false; }); }
登录成功执行Navigator.pop后,函数末尾的setState(() => _isLoading = false)仍会运行,此时页面已被弹出,组件处于销毁状态,调用setState必然触发错误。
解决方案
方案1:终止函数执行(最简单直接)
在登录成功弹出页面后添加return,直接终止函数,避免后续代码执行:
void _submitFormOnLogin() async { final isValid = _loginFormKey.currentState!.validate(); if (isValid) { setState(() { _isLoading = true; }); try { await _auth.signInWithEmailAndPassword( email: _emailTextController.text.trim().toLowerCase(), password: _passTextController.text.trim()); if (Navigator.canPop(context)) { Navigator.pop(context); return; // 终止函数,不再执行后续setState } } catch (error) { setState(() { _isLoading = false; }); GlobalMethod.showErrorDialog(error: error.toString(), ctx: context); print('error occurred! $error'); } } setState(() { _isLoading = false; }); }
方案2:添加组件挂载状态判断
在所有setState调用前检查mounted属性,仅当组件处于挂载状态时才执行状态更新:
void _submitFormOnLogin() async { final isValid = _loginFormKey.currentState!.validate(); if (isValid) { if (mounted) { setState(() { _isLoading = true; }); } try { await _auth.signInWithEmailAndPassword( email: _emailTextController.text.trim().toLowerCase(), password: _passTextController.text.trim()); if (Navigator.canPop(context)) { Navigator.pop(context); } } catch (error) { if (mounted) { setState(() { _isLoading = false; }); } GlobalMethod.showErrorDialog(error: error.toString(), ctx: context); print('error occurred! $error'); } } if (mounted) { setState(() { _isLoading = false; }); } }
方案3:优化代码结构(更严谨)
将状态更新逻辑移到finally块中,结合mounted判断,确保无论成功或失败都能安全更新状态:
void _submitFormOnLogin() async { final isValid = _loginFormKey.currentState!.validate(); if (isValid) { if (mounted) { setState(() { _isLoading = true; }); } try { await _auth.signInWithEmailAndPassword( email: _emailTextController.text.trim().toLowerCase(), password: _passTextController.text.trim()); if (Navigator.canPop(context)) { Navigator.pop(context); } } catch (error) { GlobalMethod.showErrorDialog(error: error.toString(), ctx: context); print('error occurred! $error'); } finally { if (mounted) { setState(() { _isLoading = false; }); } } } }
完整修正后的页面代码
import 'package:cached_network_image/cached_network_image.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:ijob_clone_app/ForgetPassword/forget_password_screen.dart'; import 'package:ijob_clone_app/SingUpPage/signup_screen.dart'; import '../Services/global_methods.dart'; import '../Services/global_variables.dart'; class Login extends StatefulWidget { @override State<Login> createState() => _LoginState(); } class _LoginState extends State<Login> with TickerProviderStateMixin { late Animation<double> _animation; late AnimationController _animationController; final TextEditingController _emailTextController = TextEditingController(text: ''); final TextEditingController _passTextController = TextEditingController(text: ''); final FocusNode _passFocusNode = FocusNode(); bool _isLoading = false; bool _obscureText = true; final FirebaseAuth _auth = FirebaseAuth.instance; final _loginFormKey = GlobalKey<FormState>(); @override void dispose() { _animationController.dispose(); _emailTextController.dispose(); _passTextController.dispose(); _passFocusNode.dispose(); super.dispose(); } @override void initState() { _animationController = AnimationController(vsync: this, duration: const Duration(seconds: 20)); _animation = CurvedAnimation(parent: _animationController, curve: Curves.linear) ..addListener(() { if (mounted) { setState(() {}); } }) ..addStatusListener((animationStatus) { if (animationStatus == AnimationStatus.completed) { _animationController.reset(); _animationController.forward(); } }); _animationController.forward(); super.initState(); } void _submitFormOnLogin() async { final isValid = _loginFormKey.currentState!.validate(); if (isValid) { if (mounted) { setState(() { _isLoading = true; }); } try { await _auth.signInWithEmailAndPassword( email: _emailTextController.text.trim().toLowerCase(), password: _passTextController.text.trim()); if (Navigator.canPop(context)) { Navigator.pop(context); } } catch (error) { GlobalMethod.showErrorDialog(error: error.toString(), ctx: context); print('error occurred! $error'); } finally { if (mounted) { setState(() { _isLoading = false; }); } } } } @override Widget build(BuildContext context) { return Scaffold( body: Stack(children: [ CachedNetworkImage( imageUrl: loginUrlImage, placeholder: (context, url) => Image.asset( 'assets/images/wallpaper.jpg', fit: BoxFit.fill, ), errorWidget: (context, url, error) => const Icon(Icons.error), width: double.infinity, height: double.infinity, fit: BoxFit.cover, alignment: FractionalOffset(_animation.value, 0), ), Container( color: Colors.black54, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 80), child: ListView( children: [ Padding( padding: const EdgeInsets.only(left: 80, right: 80), child: Image.asset('assets/images/login.png')), const SizedBox( height: 15, ), Form( key: _loginFormKey, child: Column(children: [ TextFormField( textInputAction: TextInputAction.next, onEditingComplete: () => FocusScope.of(context) .requestFocus(_passFocusNode), keyboardType: TextInputType.emailAddress, controller: _emailTextController, validator: (value) { if (value!.isEmpty || !value.contains('@')) { return 'Please enter a valid Email address'; } else { return null; } }, style: const TextStyle(color: Colors.white), decoration: const InputDecoration( hintText: 'Email', hintStyle: TextStyle(color: Colors.white), enabledBorder: UnderlineInputBorder( borderSide: BorderSide(color: Colors.white)), focusedBorder: UnderlineInputBorder( borderSide: BorderSide(color: Colors.white), ), errorBorder: UnderlineInputBorder( borderSide: BorderSide(color: Colors.red), ))), const SizedBox( height: 5, ), TextFormField( textInputAction: TextInputAction.next, focusNode: _passFocusNode, keyboardType: TextInputType.visiblePassword, controller: _passTextController, obscureText: !_obscureText, validator: (value) { if (value!.isEmpty || value.length < 7) { return 'please enter a valid password'; } else { return null; } }, style: const TextStyle(color: Colors.white), decoration: InputDecoration( suffixIcon: GestureDetector( onTap: () { if (mounted) { setState(() { _obscureText = !_obscureText; }); } }, child: Icon( _obscureText ? Icons.visibility : Icons.visibility_off, color: Colors.white, )), hintText: 'Password', hintStyle: const TextStyle(color: Colors.white), enabledBorder: const UnderlineInputBorder( borderSide: BorderSide(color: Colors.white)), focusedBorder: const UnderlineInputBorder( borderSide: BorderSide(color: Colors.white), ), errorBorder: const UnderlineInputBorder( borderSide: BorderSide(color: Colors.red), ), ), ), ])), const SizedBox(height: 15), Align( alignment: Alignment.bottomRight, child: TextButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ForgetPassword())); }, child: const Text('Forget password?', style: TextStyle( color: Colors.white, fontSize: 17, fontStyle: FontStyle.italic, )))), const SizedBox( height: 10, ), MaterialButton( onPressed: _submitFormOnLogin, color: Colors.cyan, elevation: 8, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(13)), child: Padding( padding: EdgeInsets.symmetric(vertical: 14), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: const [ Text('Login', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16, )) ], ), ), ), const SizedBox(height:40), Center( child:RichText( text:TextSpan( children:[ const TextSpan( text:'Do not have an account?', style:TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16, ), ), const TextSpan(text: ' '), TextSpan( recognizer: TapGestureRecognizer() ..onTap = () => Navigator.push(context, MaterialPageRoute(builder: (context) => SignUp() )), text: 'SignUp', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 20, ) ) ] ) ) ) ], ))) ])); } }
内容的提问来源于stack exchange,提问作者user17281101
相关产品推荐
相关产品推荐

