Flutter:查找停用的小部件的祖先是不安全的 [英] Flutter: Looking up a deactivated widget's ancestor is unsafe

查看:91
本文介绍了Flutter:查找停用的小部件的祖先是不安全的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


我知道有关此问题已经有两篇文章,但是我无法解决这些问题.可能是因为我的问题不同.


I know that there are already two posts regarding this question, but I could not solve my issue looking at them. Probably because in my case the issue is different.

代码如下.我想在显示加载页面时从数据库加载一些数据.加载数据后,我使用加载的数据初始化提供程序,然后进入另一个页面.这段代码不需要放在StatefulWidget中,但是我尝试将其放在StatefulWidget中以解决该问题,但是没有成功.

The code is the following. I want to load some data from the database while showing a loading page. After the data is loaded, I initialize the provider with the loaded data and then I move into a different page. This code does not need to be in a StatefulWidget, but I try to put it in a StatefulWidget to solve the issue, but without success.

class _InitDBDataState extends State<_InitDBData> {
  @override
  Widget build(BuildContext context) {
    _fetchData(context);
    return const Center(child: const CircularProgressIndicator());
  }

  Future<void> _fetchData(BuildContext context) async {
    print('fetching data...');
    print('context: $context');
    final initData = await DBService.service.getInitialData();
    print('Data fetched');
    print('context: $context');
    Provider.of<DataProvider>(context, listen: false).init(initData);
    Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
  }
}

如果应用程序从头开始运行,我没有任何错误,但是当我执行热重装"时,我经常会收到以下错误,这很烦人,因为我需要为每次小的更改重新启动应用程序代码.

I do not have any error if the application runs from scratch, but when I am doing a "Hot Reload" I get the following error pretty often, and it is annoying since I need to restart the application for each small change in the code.

I/flutter ( 9596): fetching data...
I/flutter ( 9596): context: _InitDBData(dirty, state: _InitDBDataState#46860)
I/flutter ( 9596): fetching data...
I/flutter ( 9596): context: _InitDBData(dirty, state: _InitDBDataState#55124)
I/flutter ( 9596): Data fetched
I/flutter ( 9596): context: _InitDBData
E/flutter ( 9596): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
E/flutter ( 9596): At this point the state of the widget's element tree is no longer stable.
E/flutter ( 9596): To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
E/flutter ( 9596): #0      Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> 
package:flutter/…/widgets/framework.dart:3508
E/flutter ( 9596): #1      Element._debugCheckStateIsActiveForAncestorLookup 
package:flutter/…/widgets/framework.dart:3522
E/flutter ( 9596): #2      Element.getElementForInheritedWidgetOfExactType 
package:flutter/…/widgets/framework.dart:3588
E/flutter ( 9596): #3      Provider.of 
package:provider/src/provider.dart:221
E/flutter ( 9596): #4      _InitDBDataState._fetchData 
package:productive_diary/initScreen.dart:46
E/flutter ( 9596): <asynchronous suspension>
E/flutter ( 9596): #5      _InitDBDataState.build 

我不知道为什么两次打印获取数据...",而且我不知道如何解决该问题.

I don't know why "fetching data..." is printed twice, and I have no clue on how to solve the issue.

我认为该问题已通过 Saman Salehi 的解决方案得以解决,但是在调试模式下,我使用了_fetchData函数中的相同异常,现在在函数 initState()

I thought the issue was solved with the solution of Saman Salehi, but working in debug mode I had the same exception in the _fetchData function, that now is called in the function initState()

Exception has occurred.
FlutterError (Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.)


应用了 Stewie Griffin建议的修改后,我又遇到了另一个错误.

错误出现在 Provider.of< DataProvider>(上下文,侦听:false).init(initData);

我在热装过程中得到了它.它似乎比其他错误少见,因此Stewie Griffin的回答肯定会提高我 Stewie-Griffin的稳定性

I got it during a hot reload. It seems less common than the other error, so the answer of Stewie Griffin surely improved the stability of my Stewie Griffin

E/flutter (23815): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The getter 'owner' was called on null.
E/flutter (23815): Receiver: null
E/flutter (23815): Tried calling: owner
E/flutter (23815): #0      Object.noSuchMethod  (dart:core-patch/object_patch.dart:53:5)
E/flutter (23815): #1      Provider.of 
package:provider/src/provider.dart:193
E/flutter (23815): #2      _InitDBDataState._fetchData 
package:productive_diary/initScreen.dart:49
E/flutter (23815): <asynchronous suspension>
E/flutter (23815): #3      _InitDBDataState.initState 

能请你帮我吗?

推荐答案

首先,切勿如上所述在build内部调用异步方法.生成方法会不断重建,这将导致您的获取方法像无限循环一样重复进行.解决此问题后,由于这一部分,您仍然会遇到相同的错误:

First of all, never call async methods inside of build as mentioned. Build method is constantly rebuilt and it will cause your fetch method to be repeated like an infinite loop. After you fix that, you still get the same error because of this part:

Navigator.of(context).pushReplacementNamed(MainScreen.routeName);

在构建期间不应调用Navigator.这是您需要做的:

You shouldn't call Navigator during build. Here is what you need to do:

将此行添加到文件顶部以使用 SchedulerBinding :

Add this line to the top of your file to use SchedulerBinding:

import 'package:flutter/scheduler.dart';

使用SchedulerBinding包装导航器,以等待完成状态,然后导航到另一个屏幕.然后,在 initState 内部调用您的异步方法.

Wrap Navigator with SchedulerBinding to wait for completing the state before navigating to another screen. Then, call your async method inside of initState.

class _InitDBDataState extends State<_InitDBData> {
@override
  void initState() {
    // Call your async method here
    _fetchData();
    super.initState();
  }

  Future<void> _fetchData() async {
    print('fetching data...');
    print('context: $context');
    final initData = await DBService.service.getInitialData();
    print('Data fetched');
    print('context: $context');
    Provider.of<DataProvider>(context, listen: false).init(initData);

    // Wrap Navigator with SchedulerBinding to wait for rendering state before navigating
    SchedulerBinding.instance.addPostFrameCallback((_) {
      Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
    });
  }
  @override
  Widget build(BuildContext context) {
    return Center(child: CircularProgressIndicator());
  }
}

提示:您无需在有状态小部件中传递上下文,因为您可以从任何地方访问它.

Tip: You don't need to pass the context in a Stateful Widget because you can access it from everywhere.

这篇关于Flutter:查找停用的小部件的祖先是不安全的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆