pichillilorenzo / flutter_inappwebview

A Flutter plugin that allows you to add an inline webview, to use a headless webview, and to open an in-app browser window.
https://inappwebview.dev
Apache License 2.0
3.01k stars 1.33k forks source link

A AndroidPullToRefreshController was used after being disposed #2104

Open maxmitz opened 1 month ago

maxmitz commented 1 month ago

Lorenzo Pichilli Thanks for maintaining the InAppWebView!

Environment

Technology Version
Flutter version 3.19.3
Plugin version 6.0.0
Android version 33.0.1
iOS version 17.4.1
macOS version -
Xcode version 15.0
Google Chrome version -

Device information: reproducible on emulators and on real devices

Description

I am not 100% sure if this is a bug or intended. I think it is a bug.

When I reload the web view fast multiple times. The AndroidInAppWebViewWidget wants to dispose the AndroidPullToRefreshController but the controller was already disposed.

For the given example, there should be a timeout or something similar on the floatingActionButton but in general, I think that this should be the responsibility of the plugin to check if the pulltorefreshcontroller was already disposed, or do I oversee something?

https://github.com/pichillilorenzo/flutter_inappwebview/issues/1997#issuecomment-1921084524

In this comment Lorenzo Pichilli stated that the InAppWebViewController is null if it was disposed. Shouldn't the same apply to the RefreshController?

From in_app_webview.dart params.pullToRefreshController?.dispose(isKeepAlive: isKeepAlive);

Expected behavior:

The plugin should check if PulltoRefreshController was disposed.

Current behavior:

The implementing developer has to check if the PullToRefreshController has been disposed

Steps to reproduce

  1. Run code
  2. Click fast multiple times on the floatingActionButton.
  3. Error occurs.

import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';

void main() { runApp(const MyApp()); }

class MyApp extends StatelessWidget { const MyApp({super.key});

@override Widget build(BuildContext context) { return const MaterialApp(home: MyHomePage()); } }

class MyHomePage extends StatefulWidget { const MyHomePage({super.key});

@override State createState() => _MyHomePageState(); }

class _MyHomePageState extends State { UniqueKey key = UniqueKey();

@override Widget build(BuildContext context) { final pullToRefreshController = PullToRefreshController();

return Scaffold(
  body: Column(
    children: [
      Expanded(
        child: InAppWebView(
          key: key,
          pullToRefreshController: pullToRefreshController,
          onLoadStop: (_, __) {
            pullToRefreshController.endRefreshing();
          },
          initialUrlRequest:
              URLRequest(url: WebUri.uri(Uri.parse("https://flutter.dev"))),
        ),
      )
    ],
  ),
  floatingActionButton: FloatingActionButton(
    onPressed: () {
      setState(() {
        key = UniqueKey();
      });
    },
    child: const Icon(Icons.add),
  ),
);

} }

Images

Bildschirmfoto 2024-04-15 um 13 07 36

Stacktrace/Logcat

════════ Exception caught by widgets library ═══════════════════════════════════ The following assertion was thrown while finalizing the widget tree: A AndroidPullToRefreshController was used after being disposed. Once the AndroidPullToRefreshController has been disposed, it can no longer be used.

When the exception was thrown, this was the stack:

0 ChannelController.debugAssertNotDisposed. (package:flutter_inappwebview_platform_interface/src/util.dart:595:9)

util.dart:595

1 ChannelController.debugAssertNotDisposed (package:flutter_inappwebview_platform_interface/src/util.dart:602:6)

util.dart:602

2 InternalChannelController.channel (package:flutter_inappwebview_platform_interface/src/util.dart:611:30)

util.dart:611

3 InternalChannelController.disposeChannel (package:flutter_inappwebview_platform_interface/src/util.dart:641:7)

util.dart:641

4 AndroidPullToRefreshController.dispose (package:flutter_inappwebview_android/src/pull_to_refresh/pull_to_refresh_controller.dart:152:5)

pull_to_refresh_controller.dart:152

5 AndroidInAppWebViewWidget.dispose (package:flutter_inappwebview_android/src/in_app_webview/in_app_webview.dart:467:37)

in_app_webview.dart:467

6 _InAppWebViewState.dispose (package:flutter_inappwebview/src/in_app_webview/in_app_webview.dart:673:21)

in_app_webview.dart:673

7 StatefulElement.unmount (package:flutter/src/widgets/framework.dart:5689:11)

framework.dart:5689

8 _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2076:13)

framework.dart:2076

github-actions[bot] commented 1 month ago

👋 @maxmitz

NOTE: This comment is auto-generated.

Are you sure you have already searched for the same problem?

Some people open new issues but they didn't search for something similar or for the same issue. Please, search for it using the GitHub issue search box or on the official inappwebview.dev website, or, also, using Google, StackOverflow, etc. before posting a new one. You may already find an answer to your problem!

If this is really a new issue, then thank you for raising it. I will investigate it and get back to you as soon as possible. Please, make sure you have given me as much context as possible! Also, if you didn't already, post a code example that can replicate this issue.

In the meantime, you can already search for some possible solutions online! Because this plugin uses native WebView, you can search online for the same issue adding android WebView [MY ERROR HERE] or ios WKWebView [MY ERROR HERE] keywords.

Following these steps can save you, me, and other people a lot of time, thanks!

Julied5 commented 3 weeks ago

Is there any update on this ? I'm getting the same issue since 6.0.0 upgrade, does someone have a way to prevent this error ?

2x2xplz commented 5 days ago

Same issue. This appears to be a leftover debugging artifact that could be removed from the release version. If the developer manually disposes the webview and its controller, eventually the library internally calls its own dispose() method, which calls debugAssertNotDisposed(). This method throws an error below when the controller has already been disposed. Maybe the controller could be checked for disposal further up the stack rather than in this assert.

In the meantime, you can avoid this error by manually dispose()ing your webview, but NOT disposing the controller. But I think I'd rather dispose them both myself rather than just assume it will happen automatically.

  static bool debugAssertNotDisposed(ChannelController controller) {
    assert(() {
      if (controller.disposed) {
        throw FlutterError(
          'A ${controller.runtimeType} was used after being disposed.\n'
          'Once the ${controller.runtimeType} has been disposed, it '
          'can no longer be used.',
        );
      }
      return true;
    }());
    return true;
  }
E/flutter ( 4982): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: A AndroidInAppWebViewController was used after being disposed.
E/flutter ( 4982): Once the AndroidInAppWebViewController has been disposed, it can no longer be used.
E/flutter ( 4982): #0      ChannelController.debugAssertNotDisposed.<anonymous closure> (package:flutter_inappwebview_platform_interface/src/util.dart:595:9)
E/flutter ( 4982): #1      ChannelController.debugAssertNotDisposed (package:flutter_inappwebview_platform_interface/src/util.dart:602:6)
E/flutter ( 4982): #2      InternalChannelController.channel (package:flutter_inappwebview_platform_interface/src/util.dart:611:30)
E/flutter ( 4982): #3      InternalChannelController.disposeChannel (package:flutter_inappwebview_platform_interface/src/util.dart:641:7)
E/flutter ( 4982): #4      AndroidInAppWebViewController.dispose (package:flutter_inappwebview_android/src/in_app_webview/in_app_webview_controller.dart:2756:5)
E/flutter ( 4982): #5      AndroidHeadlessInAppWebView.dispose (package:flutter_inappwebview_android/src/in_app_webview/headless_in_app_webview.dart:427:25)