Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Looking up a deactivated widget's ancestor is unsafe. #189

Closed
lvyandev opened this issue May 20, 2024 · 22 comments
Closed

Looking up a deactivated widget's ancestor is unsafe. #189

lvyandev opened this issue May 20, 2024 · 22 comments
Assignees
Labels
bug Something isn't working

Comments

@lvyandev
Copy link

版本信息

  • Flutter版本:[v3.22.0]
  • flutter_smart_dialog版本:[v4.9.7]

描述bug/需求

暂未发现如何复现,且出现如下异常后无法显示toast

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.
Stack Trace:
#0      Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> (package:flutter/src/widgets/framework.dart:4743:9)
#1      Element._debugCheckStateIsActiveForAncestorLookup (package:flutter/src/widgets/framework.dart:4757:6)
#2      Element.visitAncestorElements (package:flutter/src/widgets/framework.dart:4869:12)
#3      LookupBoundary.findAncestorStateOfType (package:flutter/src/widgets/lookup_boundary.dart:166:13)
#4      Overlay.maybeOf (package:flutter/src/widgets/overlay.dart:580:26)
#5      Overlay.of (package:flutter/src/widgets/overlay.dart:530:34)
#6      overlay (package:flutter_smart_dialog/src/kit/view_utils.dart:19:55)
#7      CustomToast.showToast.showToast (package:flutter_smart_dialog/src/custom/toast/custom_toast.dart:46:7)
#8      CustomToast.normalToast (package:flutter_smart_dialog/src/custom/toast/custom_toast.dart:128:25)
#9      CustomToast.showToast (package:flutter_smart_dialog/src/custom/toast/custom_toast.dart:78:13)
#10     DialogProxy.showToast (package:flutter_smart_dialog/src/helper/dialog_proxy.dart:325:18)
<asynchronous suspension>

问题demo

@lvyandev
Copy link
Author

The specified entry is already present in the target Overlay.
The OverlayEntry was:
  SmartOverlayEntry#d7032(opaque: false; maintainState: false)
The Overlay the OverlayEntry was trying to insert to was:
  OverlayState#e45c5(lifecycle state: defunct, not mounted, entries: [OverlayEntry#a8309(opaque: false; maintainState: false)])
Consider calling remove on the OverlayEntry before inserting it to a different Overlay, or switching to the OverlayPortal API to avoid manual OverlayEntry management.
Stack Trace:
#0      OverlayState._debugCanInsertEntry (package:flutter/src/widgets/overlay.dart:631:7)
#1      OverlayState.insert (package:flutter/src/widgets/overlay.dart:669:12)
#2      CustomToast.showToast.showToast (package:flutter_smart_dialog/src/custom/toast/custom_toast.dart:46:41)
#3      CustomToast.normalToast (package:flutter_smart_dialog/src/custom/toast/custom_toast.dart:128:25)
#4      ToastTool.dispatchNext (package:flutter_smart_dialog/src/custom/toast/toast_tool.dart:80:25)
#5      CustomToast.normalToast.<anonymous closure> (package:flutter_smart_dialog/src/custom/toast/custom_toast.dart:131:26)
<asynchronous suspension>

@xdd666t xdd666t added the bug Something isn't working label May 21, 2024
@xdd666t xdd666t self-assigned this May 21, 2024
xdd666t added a commit that referenced this issue May 21, 2024
@xdd666t
Copy link
Member

xdd666t commented May 21, 2024

  • 试下新版本
dependencies:
  flutter_smart_dialog: ^4.9.7+1

@lvyandev
Copy link
Author

介系船新版本的报错

I/flutter (25749): Bad state: Future already completed
I/flutter (25749): #0      _AsyncCompleter.complete (dart:async/future_impl.dart:43:31)
I/flutter (25749): #1      DialogProxy.beforeShow (package:flutter_smart_dialog/src/helper/dialog_proxy.dart:371:20)
I/flutter (25749): <asynchronous suspension>
I/flutter (25749): #2      DialogProxy.showLoading (package:flutter_smart_dialog/src/helper/dialog_proxy.dart:296:5)
I/flutter (25749): <asynchronous suspension>
I/flutter (25749): 'package:flutter/src/widgets/overlay.dart': Failed assertion: line 207 pos 12: '_overlay != null': is not true.
I/flutter (25749): #0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
I/flutter (25749): #1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
I/flutter (25749): #2      OverlayEntry.remove (package:flutter/src/widgets/overlay.dart:207:12)
I/flutter (25749): #3      SmartOverlayEntry.remove (package:flutter_smart_dialog/src/widget/helper/smart_overlay_entry.dart:21:11)
I/flutter (25749): #4      _SmartOverlayState.initState.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:flutter_smart_dialog/src/widget/helper/smart_overlay.dart:52:46)
I/flutter (25749): #5      State.setState (package:flutter/src/widgets/framework.dart:1203:30)
I/flutter (25749): #6      _SmartOverlayState.initState.<anonymous closure>.<anonymous closure> (package:flutter_smart_dialog/src/widget/helper/smart_overlay.dart:42:9)
I/flutter (25749): #7      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1392:15)
I/flutter (25749): #8      SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1326:11)
I/flutter (25749): #9      SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:117
I/flutter (25749): 'package:flutter/src/widgets/overlay.dart': Failed assertion: line 207 pos 12: '_overlay != null': is not true.
I/flutter (25749): #0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
I/flutter (25749): #1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
I/flutter (25749): #2      OverlayEntry.remove (package:flutter/src/widgets/overlay.dart:207:12)
I/flutter (25749): #3      SmartOverlayEntry.remove (package:flutter_smart_dialog/src/widget/helper/smart_overlay_entry.dart:21:11)
I/flutter (25749): #4      _SmartOverlayState.initState.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:flutter_smart_dialog/src/widget/helper/smart_overlay.dart:52:46)
I/flutter (25749): #5      State.setState (package:flutter/src/widgets/framework.dart:1203:30)
I/flutter (25749): #6      _SmartOverlayState.initState.<anonymous closure>.<anonymous closure> (package:flutter_smart_dialog/src/widget/helper/smart_overlay.dart:42:9)
I/flutter (25749): #7      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1392:15)
I/flutter (25749): #8      SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1326:11)
I/flutter (25749): #9      SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:117
I/flutter (25749): Bad state: Future already completed
I/flutter (25749): #0      _AsyncCompleter.complete (dart:async/future_impl.dart:43:31)
I/flutter (25749): #1      DialogProxy.beforeShow (package:flutter_smart_dialog/src/helper/dialog_proxy.dart:371:20)
I/flutter (25749): <asynchronous suspension>
I/flutter (25749): #2      DialogProxy.show (package:flutter_smart_dialog/src/helper/dialog_proxy.dart:130:5)
I/flutter (25749): <asynchronous suspension>
I/flutter (25749): 'package:flutter/src/widgets/overlay.dart': Failed assertion: line 207 pos 12: '_overlay != null': is not true.
I/flutter (25749): #0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
I/flutter (25749): #1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
I/flutter (25749): #2      OverlayEntry.remove (package:flutter/src/widgets/overlay.dart:207:12)
I/flutter (25749): #3      SmartOverlayEntry.remove (package:flutter_smart_dialog/src/widget/helper/smart_overlay_entry.dart:21:11)
I/flutter (25749): #4      _SmartOverlayState.initState.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:flutter_smart_dialog/src/widget/helper/smart_overlay.dart:52:46)
I/flutter (25749): #5      State.setState (package:flutter/src/widgets/framework.dart:1203:30)
I/flutter (25749): #6      _SmartOverlayState.initState.<anonymous closure>.<anonymous closure> (package:flutter_smart_dialog/src/widget/helper/smart_overlay.dart:42:9)
I/flutter (25749): #7      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1392:15)
I/flutter (25749): #8      SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1326:11)
I/flutter (25749): #9      SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:117

xdd666t added a commit that referenced this issue May 21, 2024
@xdd666t
Copy link
Member

xdd666t commented May 21, 2024

  • 试下这个
dependencies:
  flutter_smart_dialog: ^4.9.7+2

@lvyandev
Copy link
Author

现在future的问题没有了

I/flutter (29519): 'package:flutter/src/widgets/overlay.dart': Failed assertion: line 207 pos 12: '_overlay != null': is not true.
I/flutter (29519): #0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
I/flutter (29519): #1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
I/flutter (29519): #2      OverlayEntry.remove (package:flutter/src/widgets/overlay.dart:207:12)
I/flutter (29519): #3      SmartOverlayEntry.remove (package:flutter_smart_dialog/src/widget/helper/smart_overlay_entry.dart:21:11)
I/flutter (29519): #4      _SmartOverlayState.initState.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:flutter_smart_dialog/src/widget/helper/smart_overlay.dart:52:46)
I/flutter (29519): #5      State.setState (package:flutter/src/widgets/framework.dart:1203:30)
I/flutter (29519): #6      _SmartOverlayState.initState.<anonymous closure>.<anonymous closure> (package:flutter_smart_dialog/src/widget/helper/smart_overlay.dart:42:9)
I/flutter (29519): #7      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1392:15)
I/flutter (29519): #8      SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1326:11)
I/flutter (29519): #9      SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:117
I/flutter (29519): 'package:flutter/src/widgets/overlay.dart': Failed assertion: line 207 pos 12: '_overlay != null': is not true.
I/flutter (29519): #0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
I/flutter (29519): #1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
I/flutter (29519): #2      OverlayEntry.remove (package:flutter/src/widgets/overlay.dart:207:12)
I/flutter (29519): #3      SmartOverlayEntry.remove (package:flutter_smart_dialog/src/widget/helper/smart_overlay_entry.dart:21:11)
I/flutter (29519): #4      _SmartOverlayState.initState.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:flutter_smart_dialog/src/widget/helper/smart_overlay.dart:52:46)
I/flutter (29519): #5      State.setState (package:flutter/src/widgets/framework.dart:1203:30)
I/flutter (29519): #6      _SmartOverlayState.initState.<anonymous closure>.<anonymous closure> (package:flutter_smart_dialog/src/widget/helper/smart_overlay.dart:42:9)
I/flutter (29519): #7      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1392:15)
I/flutter (29519): #8      SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1326:11)
I/flutter (29519): #9      SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:117

@xdd666t
Copy link
Member

xdd666t commented May 21, 2024

怎么复现的? 给个demo我试试

@lvyandev
Copy link
Author

在项目里面测出来的,一时半会感觉剥不出来,要不然我用master先测,测好了再发?

@lvyandev
Copy link
Author

CleanShot 2024-05-21 at 17 51 17@2x
为啥会出现entry有值,overlay为null的情况呀

@lvyandev
Copy link
Author

把版本降回4.9.6后没有问题

@xdd666t
Copy link
Member

xdd666t commented May 22, 2024

4.9.7初始化做了重构, 解决以前难以解决的问题, 你那边在4.9.7发生的问题, 可以新建个项目, 然后给下最简可运行可复现问题的代码, 我这边复现后才好尝试解决

4.9.7的初始化重构是有一定意义的, 在调试场景和相关性能都有相关提升

@lvyandev
Copy link
Author

现在已经定位到问题,是用的loading dialog出了问题,我把我自定义的loading dialog代码如下

/// 在接口请求前后加上loading
class LoadingInterceptor extends InterceptorsWrapper {
  /// 请求的队列,用于保证当前只有一个loading弹窗在界面上展示
  final List<String> _loadingRequests = <String>[];

  /// 展示弹窗
  Future<void> _showLoading(String tag) async {
    return SmartDialog.showLoading(
      clickMaskDismiss: false,
      backDismiss: false,
      maskColor: Colors.black12,
      builder: (_) => const LoadingDialog(),
    );
  }

  /// 取消弹窗
  Future<void> _dismissLoading([String? tag]) async {
    if (tag != null) {
      // 先移除队列中当前的接口
      _loadingRequests.remove(tag);
    }
    return SmartDialog.dismiss<void>(status: SmartStatus.loading, tag: tag);
  }

  /// 展示loading的处理
  void _handleShow(
    RequestOptions requestOptions,
    Map<String, dynamic> extra,
  ) {
    final bool? shouldLoading = extra[JunnyConstants.requestLoading] as bool?;

    if (shouldLoading ?? true) {
      final String uri = requestOptions.uri.normalizePath().toString();

      _loadingRequests.add(uri);

      // 如果队列中还有正在请求的接口而且当前loading弹窗正在展示
      // 那么在队列中等待展示
      if (SmartDialog.config.checkExist(
        dialogTypes: const <SmartAllDialogType>{SmartAllDialogType.loading},
      )) {
        return;
      }

      _showLoading(uri);
    }
  }

  /// 取消loading的处理
  Future<void> _handleDismiss(
    RequestOptions requestOptions,
    Map<String, dynamic> extra,
  ) async {
    final bool? shouldLoading = extra[JunnyConstants.requestLoading] as bool?;

    final String uri = requestOptions.uri.normalizePath().toString();

    // 取消当前接口弹窗ui展示
    await _dismissLoading(uri);

    // 如果请求队列中还有数据
    if (_loadingRequests.isNotEmpty) {
      if (shouldLoading ?? true) {
        // 继续展示最后一个加入的接口
        _showLoading(_loadingRequests.last);
      }
    } else {
      // 此行是为了解决已经dismiss掉所有loading
      // 且界面上没有loading dialog显示的情况下isExist仍然为true的情况
      await _dismissLoading();
    }
  }

  @override
  Future<void> onRequest(
    RequestOptions options,
    RequestInterceptorHandler handler,
  ) async {
    final Map<String, dynamic> extra = options.extra;

    _handleShow(options, extra);

    handler.next(options);
  }

  @override
  Future<void> onResponse(
    Response<dynamic> response,
    ResponseInterceptorHandler handler,
  ) async {
    handler.next(response);

    final RequestOptions requestOptions = response.requestOptions;
    final Map<String, dynamic> extra = requestOptions.extra;

    _handleDismiss(requestOptions, extra);
  }

  @override
  Future<void> onError(
    DioException err,
    ErrorInterceptorHandler handler,
  ) async {
    handler.next(err);

    final RequestOptions requestOptions = err.requestOptions;
    final Map<String, dynamic> extra = requestOptions.extra;

    _handleDismiss(requestOptions, extra);
  }
}

@xdd666t
Copy link
Member

xdd666t commented May 22, 2024

没太看懂你的例子, 你的意思是dismiss完loading后, 然后你又dismiss了loading, 就出了问题?

@lvyandev
Copy link
Author

lvyandev commented May 22, 2024

这个interceptor的作用是

请求开始时:
如果没有其他请求在进行,显示加载提示。

请求完成时:
隐藏加载提示。
检查是否有未完成的请求,如果有,重新显示加载提示。

并发请求管理:
正确管理未完成请求的计数,避免加载提示过早或不关闭的情况。

现在我不清楚这个逻辑哪里出的问题,因为我把这个拦截器取消之后报错就没有了,或者我把SmartDialog.showLoading换成SmartDialog.show也不会出现报错,但是SmartDialog.show在多并发请求的时候会出现最后一个loading未关闭的情况。都会

有尝试抽取一个demo,但是并不能复现

xdd666t added a commit that referenced this issue May 22, 2024
@xdd666t
Copy link
Member

xdd666t commented May 22, 2024

  • ..........., 试下新版本
dependencies:
  flutter_smart_dialog: ^4.9.7+3

@lvyandev
Copy link
Author

还是不行,可能是这个拦截器里面用法有问题,我再试试吧

@lakatosdavid
Copy link

WidgetsBinding.instance.addPostFrameCallback((_) async { SmartDialog.showLoading(); });

this works for me. there is no error after i did this in my code

@xdd666t
Copy link
Member

xdd666t commented May 27, 2024

dependencies:
  flutter_smart_dialog: ^4.9.7+7

@S2V
Copy link

S2V commented Jun 12, 2024

Still reproducible using ^4.9.7+7

@lvyandev
Copy link
Author

lvyandev commented Jun 13, 2024

@xdd666t master分支fba973b似乎修复了,啥时候发布?

@ArturoRen
Copy link

ArturoRen commented Aug 8, 2024

我在ios上遇到上面问题,安卓上没有遇到这个问题。

  1. 版本4.9.7+8
    2.第一个toast弹出时出现问题:
    LateInitializationError: Field 'contextCustom' has not been initialized.
    3.后续所有弹窗无法弹出,报问题:
    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.

复现:我不知道具体位置,在app启动的第一个Dialog就报了上面问题
安卓和iOS的唯一区别就是:ios有两个模块(即两套:MaterialApp)切换的逻辑,安卓只有一个顶层MaterialApp。

flutter: 弹窗执行 flutter: LateInitializationError: Field 'contextCustom' has not been initialized. flutter: #0 DialogProxy.contextCustom (package:flutter_smart_dialog/src/helper/dialog_proxy.dart) dialog_proxy.dart:1 flutter: #1 DialogProxy.timelyContextCustom dialog_proxy.dart:65 flutter: #2 CustomDialog._pushDialog.<anonymous closure> custom_dialog.dart:266 flutter: #3 ViewUtils.addSafeUse view_utils.dart:10 flutter: #4 CustomDialog._pushDialog custom_dialog.dart:264 flutter: #5 CustomDialog._handleMustOperate custom_dialog.dart:246 flutter: #6 CustomDialog.show custom_dialog.dart:53 flutter: #7 DialogProxy.show dialog_proxy.dart:138 flutter: <asynchronous suspension>
一点帮助:
**
image
**

@ArturoRen
Copy link

我在ios上遇到上面问题,安卓上没有遇到这个问题。

  1. 版本4.9.7+8
    2.第一个toast弹出时出现问题:
    LateInitializationError: Field 'contextCustom' has not been initialized.
    3.后续所有弹窗无法弹出,报问题:
    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.

复现:我不知道具体位置,在app启动的第一个Dialog就报了上面问题 安卓和iOS的唯一区别就是:ios有两个模块(即两套:MaterialApp)切换的逻辑,安卓只有一个顶层MaterialApp。

flutter: 弹窗执行 flutter: LateInitializationError: Field 'contextCustom' has not been initialized. flutter: #0 DialogProxy.contextCustom (package:flutter_smart_dialog/src/helper/dialog_proxy.dart) dialog_proxy.dart:1 flutter: #1 DialogProxy.timelyContextCustom dialog_proxy.dart:65 flutter: #2 CustomDialog._pushDialog.<anonymous closure> custom_dialog.dart:266 flutter: #3 ViewUtils.addSafeUse view_utils.dart:10 flutter: #4 CustomDialog._pushDialog custom_dialog.dart:264 flutter: #5 CustomDialog._handleMustOperate custom_dialog.dart:246 flutter: #6 CustomDialog.show custom_dialog.dart:53 flutter: #7 DialogProxy.show dialog_proxy.dart:138 flutter: <asynchronous suspension> 一点帮助: ** image **

已经解决,无法再多个MaterialApp下重复初始化,内部应该是类似单例维护了context吗

@xlfdyzcs
Copy link

我遇到这个问题的原因是:注册了多次(FlutterSmartDialog.init()),只初始化一次解决了这个问题,希望它对排查有一定帮助

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

6 participants