Flutter跨类调用含setState方法触发构造器调用setState报错
问题描述
调用其他类中包含setState的方法时,在Page2页面触发如下报错:
当你对一个尚未插入组件树的Widget对应的State对象调用setState()时会触发该异常。无需在构造函数中调用setState(),因为组件初始创建时,状态本身就默认被标记为脏值、会自动触发刷新。
已查阅相关Stack Overflow解答,但内容和当前开发场景不匹配,无法直接套用。问题复现代码如下:
class Page1 extends StatefulWidget { @override _Page1State createState() => _Page1State(); } class _Page1State extends State<Page1> { @override void initState(){ Page2().method(); } @override Widget build(BuildContext context) { return Scaffold( body: Container( ), ); } } class Page2 extends StatefulWidget { method() => createState().methodInPage2(); @override _Page2State createState() => _Page2State(); } class _Page2State extends State<Page2> { Future<List<String>> methodInPage2() async{ //error here setState(){ //setState here, } } @override Widget build(BuildContext context) => Container(); }
报错根因
- 代码中通过
Page2().method()手动调用createState()生成了Page2对应的State实例,这个实例是脱离Flutter组件渲染管线的“孤儿”对象,从来没有被插入到组件树中,不满足setState的调用前置要求。 - Flutter框架规则明确:只有State对象完成挂载(
mounted == true)之后,才能合法调用setState触发界面刷新。手动创建的State对象永远不会被框架挂载,调用setState必然抛错。 - 原代码中Page2从来没有被作为组件嵌套在组件树里、也没有通过路由推入页面,本身就不存在对应的渲染实例,此时调用它的setState没有任何实际意义。
解决方案
根据实际业务场景二选一即可:
场景1:两个页面同时存在于组件树
如果Page2和Page1会同时挂载(比如Page2是Page1页面里的嵌套子组件),使用GlobalKey获取被框架自动创建的合法State实例,不要手动调用createState。
修正后代码示例:
// 放在两个页面可访问的作用域内,用于绑定组件状态 final page2GlobalKey = GlobalKey<_Page2State>(); class Page1 extends StatefulWidget { @override _Page1State createState() => _Page1State(); } class _Page1State extends State<Page1> { @override void initState(){ super.initState(); // 等首帧渲染完成、组件全部挂载后再调用方法 WidgetsBinding.instance.addPostFrameCallback((_) { // 判空避免组件未加载时调用 page2GlobalKey.currentState?.methodInPage2(); }); } @override Widget build(BuildContext context) { return Scaffold( body: Page2(key: page2GlobalKey), // 绑定key,将Page2真正插入组件树 ); } } class Page2 extends StatefulWidget { Page2({super.key}); @override _Page2State createState() => _Page2State(); } class _Page2State extends State<Page2> { Future<List<String>> methodInPage2() async{ // 调用setState前必须校验挂载状态,避免异步返回时组件已销毁 if(mounted) { setState(() { // 此处编写状态更新逻辑 }); } return <String>[]; } @override Widget build(BuildContext context) => Container(); }
场景2:Page2是后续跳转的页面
如果Page2是点击后才通过路由跳转的页面,不要在Page1初始化阶段就提前调用Page2的方法:
- 把需要传递给Page2的数据提前存到全局缓存/状态管理实例中
- 等Page2被路由推入、完成自身挂载后,在自己的
initState或者首帧回调里读取数据,再调用自己的setState更新界面即可
注意事项
- 永远不要手动调用StatefulWidget的
createState()方法生成State实例,这个方法是Flutter框架内部挂载组件时自动调用的,手动创建的实例不会被框架接管生命周期。 - 所有setState调用前都建议加上
mounted判断,避免异步请求、延时操作返回时组件已经被销毁导致的异常。
内容的提问来源于stack exchange,提问作者Soveyyy
相关产品推荐
相关产品推荐

