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

Android emulator using API 34 fails to draw on resume sometimes. #1981

Closed
dnfield opened this issue Jan 17, 2024 · 33 comments
Closed

Android emulator using API 34 fails to draw on resume sometimes. #1981

dnfield opened this issue Jan 17, 2024 · 33 comments
Labels
bug Something isn't working

Comments

@dnfield
Copy link

dnfield commented Jan 17, 2024

Description

Launch this simple in app webview app on an Android emulator using API 34. It will fail to render once resumed from the background if you run some other "heavy" app between.

import 'dart:io';

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});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late InAppWebViewController webViewController;

  InAppWebViewSettings settings = InAppWebViewSettings(
    clearCache: true,
    useHybridComposition: false,
  );

  @override
  void initState() {
    super.initState();
    if (Platform.isAndroid) {
      InAppWebViewController.setWebContentsDebuggingEnabled(false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.black,
      child: InAppWebView(
        initialSettings: settings,
        onWebViewCreated: (controller) => webViewController = controller,
        initialUrlRequest: URLRequest(url: WebUri.uri(Uri.parse('https://pub.dev'))),
      ),
    );
  }
}

Expected behavior:

Run the app, place it in the background, run youtube, re-launch the app from its icon. It should render as normal.

Current behavior:

The app renders a black screen. This does not appear to be due to a bug in the Flutter engine, and does not reproduce with e.g. webview_flutter.

@dnfield dnfield added the bug Something isn't working label Jan 17, 2024
Copy link

👋 @dnfield

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!

@dnfield
Copy link
Author

dnfield commented Jan 17, 2024

See some additional context in comments starting from flutter/flutter#139630 (comment).

I've hidden them in that issue because they're not relly relevant to it.

/cc @xOldeVx fyi

@pichillilorenzo
Copy link
Owner

pichillilorenzo commented Jan 17, 2024

Could this be caused by not using Hybrid Composition on Android?
@xOldeVx if you set useHybridComposition: true, does it work?

@johnmccutchan
Copy link

@pichillilorenzo When not using hybrid composition, this does not reproduce with other platforms views.

@pichillilorenzo
Copy link
Owner

pichillilorenzo commented Jan 17, 2024

useHybridComposition is only specific to Android, here is the API Reference about it: https://pub.dev/documentation/flutter_inappwebview/latest/flutter_inappwebview/InAppWebViewSettings/useHybridComposition.html

@pichillilorenzo
Copy link
Owner

pichillilorenzo commented Jan 17, 2024

Here is the usage of that setting:

if (hybridComposition) {
return PlatformViewsService.initExpensiveAndroidView(
id: id,
viewType: viewType,
layoutDirection: layoutDirection,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
return PlatformViewsService.initSurfaceAndroidView(
id: id,
viewType: viewType,
layoutDirection: layoutDirection,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);

@johnmccutchan
Copy link

We do not recommend using hybrid composition on Android.

@pichillilorenzo
Copy link
Owner

@johnmccutchan as stated here https://github.com/flutter/flutter/wiki/Hybrid-Composition#android:

Starting from Flutter 1.20.0, hybrid composition can be used on Android. This new feature fixes most of the issues with the preview platform view approach (Virtual Display); in particular, accessibility and keyboard related issues. See also Android Platform Views for an overview of modes.

I think that, for WebViews, having "accessibility and keyboard related issues" fixed is kind of a MUST to have it working properly for end-users.

Have you any advice? Something better? Thanks.

@johnmccutchan
Copy link

@johnmccutchan as stated here https://github.com/flutter/flutter/wiki/Hybrid-Composition#android:

Starting from Flutter 1.20.0, hybrid composition can be used on Android. This new feature fixes most of the issues with the preview platform view approach (Virtual Display); in particular, accessibility and keyboard related issues. See also Android Platform Views for an overview of modes.

Flutter has changed a lot since Flutter 1.2 (we are now on 3.16). Today, we recommend that you use Texture Layer Hybrid Composition (TLHC) mode and not Hybrid Composition or Virtual Display modes.

I think that, for WebViews, having "accessibility and keyboard related issues" fixed is kind of a MUST to have it working properly for end-users.

If you encounter issues with TLHC mode please file specific bugs with reproduction steps.

@pichillilorenzo
Copy link
Owner

pichillilorenzo commented Jan 17, 2024

Ok, I should play a little bit using initAndroidView to see how it behaves.
In this case, I should add a new option to select which Android platform view mode a developer wants to use, based on: https://github.com/flutter/flutter/wiki/Android-Platform-Views#selecting-a-mode

Also, one important thing I remember was related to the native view performances.
What I mean is that, at that time, when Hybrid Composition was introduced, it guaranteed greater performance, especially when the user scrolled, the animations were much more fluid.
Hope it will be the same using TLHC.

By the way, the official webview_flutter plugin is using the same logic currently.
It differs by the fact that hybrid composition is disabled by default:
https://github.com/flutter/packages/blob/807c2fcf433e26913b4336a44233cfd639836e3c/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart#L1057-L1072

Here is also some useful links about HC in that file:
https://github.com/flutter/packages/blob/807c2fcf433e26913b4336a44233cfd639836e3c/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart#L860-L871
The issues mentioned in there are not closed yet.
I don't know if using TLHC will fix them or not.

@dnfield
Copy link
Author

dnfield commented Jan 17, 2024

I had done some debugging on this yesterday and the issue seems to be that when the Flutter engine asks the subview to draw itself, it's just not drawing anything. But I've only reproduced this issue on an API 34 emulator, and only with this particular webview plugin. I started trying to understand the way the Android views are setup in this plugin, but I think one of the authors here more familiar with the Android code would be better suited.

My suspicion is that something in this plugin is getting the view hierarchy confused in this resume case. If you can boil things down to a smaller reproduction that points to a bug in the engine, I'm happy to take another look at it.

@pichillilorenzo
Copy link
Owner

pichillilorenzo commented Jan 17, 2024

@dnfield I haven't taken a look at the issue itself yet, unfortunately.

By the way:

This is the Android PlatformViewFactory Java class implementation: https://github.com/pichillilorenzo/flutter_inappwebview/blob/master/flutter_inappwebview_android/android/src/main/java/com/pichillilorenzo/flutter_inappwebview_android/webview/FlutterWebViewFactory.java

The Android PlatformView Java class is implemented by: https://github.com/pichillilorenzo/flutter_inappwebview/blob/master/flutter_inappwebview_android/android/src/main/java/com/pichillilorenzo/flutter_inappwebview_android/webview/in_app_webview/FlutterWebView.java
that wraps up my current Android native WebView implementation.

in this resume case

"Resume" is something related to the Android Activity, am I right?
This is the main class of the plugin that extends also io.flutter.embedding.engine.plugins.activity.ActivityAware: https://github.com/pichillilorenzo/flutter_inappwebview/blob/master/flutter_inappwebview_android/android/src/main/java/com/pichillilorenzo/flutter_inappwebview_android/InAppWebViewFlutterPlugin.java
but there isn't any code specific to resume/pause lifecycles for WebViews.

A developer can handle that on the Flutter side by calling InAppWebViewController.resume and InAppWebViewController.pause respectively.
In this case, on the Flutter side, the developer should use WidgetsBindingObserver to listen to the lifecycle events and call the methods manually.

@johnmccutchan
Copy link

What we are saying:

  1. Don't use hybrid composition. Use TLHC mode (this is now the default for flutter_google_maps and flutter_webview).
  2. The official flutter webview has no issues after resume.
  3. Other platform view plugins (e.g. google maps) also have no issues after resume.
  4. flutter_inappwebview stops drawing after a resume. Specifically, we call the PlatformView's draw method and it doesn't draw anything after a resume.

We don't know why (3) is happening but it is unlikely to be an issue in Flutter itself given all the other platform view plugins work fine. So it is very likely an issue in flutter_inappwebview that you will need to debug and fix. We can't provide guidance on where your bug is or how you should fix it as we don't know your code.

@pichillilorenzo
Copy link
Owner

We can't provide guidance on where your bug is or how you should fix it as we don't know your code.

Indeed, I'm not asking that! As I said, I will take a look at the issue as soon as possible by myself (any help would be appreciated anyway).
My questions are about TLHC and possible issues that should I be aware of!
For example performance issues or accessibility and keyboard issues, etc. I talked about in my previous comments.

Anyway, I will let you know when I have news, thanks for the moment.

@johnmccutchan
Copy link

I am not aware of any major bugs or gotchas in TLHC mode. If you encounter specific issues, please file bugs.

@pichillilorenzo
Copy link
Owner

@dnfield
I found the root cause of the "resume" issue when Hybrid Composition is disabled and it is related to enabling Hardware Acceleration for the Android WebView by default by the plugin (using the current Java API setLayerType(View.LAYER_TYPE_HARDWARE, null);).

The option is InAppWebViewSettings.hardwareAcceleration.

Currently, setting that option to false will impact performance when not using HC because it will use setLayerType(View.LAYER_TYPE_SOFTWARE, null);.

This is the current implementation:

if (customSettings.hardwareAcceleration)
setLayerType(View.LAYER_TYPE_HARDWARE, null);
else
setLayerType(View.LAYER_TYPE_SOFTWARE, null);

@johnmccutchan
Honestly, I don't know why enabling Hardware Acceleration on Android native Views will cause this "resume" issue if you don't use Hybrid Composition.

So, could it be an issue with the current Flutter Platform View Android implementation?

A workaround that I can implement would be to use it only when Hybrid Composition is enabled.

I am not aware of any major bugs or gotchas in TLHC mode.

Indeed, I tested again now just to check if something changed about rendering performance, but using HC for rendering Android native views still guarantees better rendering performance.

I tried with the official webview_flutter plugin and mine.

Flutter version used for the test:

Flutter (Channel stable, 3.16.1, on macOS 14.0 23A344 darwin-x64, locale it-IT)

Plugins version:

- webview_flutter: ^4.4.4
- flutter_inappwebview: ^6.0.0

Screen record:

test.mp4

This is the code that you can use to test it by yourself:

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

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late WebViewController? controller;
  late InAppWebViewController? webViewController;

  bool displayWithHybridComposition = false;
  bool useOfficialWebViewFlutter = true;

  @override
  void initState() {
    super.initState();
    if (useOfficialWebViewFlutter) {
      controller = WebViewController()
        ..setJavaScriptMode(JavaScriptMode.unrestricted)
        ..loadRequest(Uri.parse('https://pub.dev'));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Using: ' + (useOfficialWebViewFlutter
                ? 'webview_flutter'
                : 'flutter_inappwebview')),

        actions: [
          PopupMenuButton(itemBuilder: (context) {
            return [
              PopupMenuItem<int>(
                value: 0,
                child: Text("webview_flutter"),
              ),
              PopupMenuItem<int>(
                value: 1,
                child: Text("flutter_inappwebview"),
              ),
            ];
          }, onSelected: (value) {
            setState(() {
              useOfficialWebViewFlutter = value == 0;
            });
          }),
        ],
      ),
      floatingActionButton: ElevatedButton(
        child: Text('HC enabled: $displayWithHybridComposition'),
        onPressed: () {
          setState(() {
            displayWithHybridComposition = !displayWithHybridComposition;
          });
        },
      ),
      body: Container(
        color: Colors.black,
        child: useOfficialWebViewFlutter
            ? WebViewWidget.fromPlatformCreationParams(
                params: AndroidWebViewWidgetCreationParams(
                    key: GlobalKey(),
                    controller: controller!.platform,
                    displayWithHybridComposition: displayWithHybridComposition))
            : InAppWebView(
                key: GlobalKey(),
                initialSettings: InAppWebViewSettings(
                  useHybridComposition: displayWithHybridComposition,
                ),
                onWebViewCreated: (controller) =>
                    webViewController = controller,
                initialUrlRequest:
                    URLRequest(url: WebUri.uri(Uri.parse('https://pub.dev'))),
              ),
      ),
    );
  }
}

Both plugins call PlatformViewsService.initExpensiveAndroidView when HC is enabled, otherwise PlatformViewsService.initSurfaceAndroidView.

Furthermore, for my plugin, I tried to use explicitly PlatformViewsService.initAndroidView changing manually my dart code to see if it could behave better, but I didn't get any rendering performance boost over PlatformViewsService.initSurfaceAndroidView.
It seems to have the same rendering performance.

Without HC enabled, the scroll is more sluggish and there are more frame drops.
Also, native view resizing is sluggish (see when the keyboard opens and disappears).

So, I'm not saying that HC should be used or not, at least for WebViews.
I'm just reporting facts.

The APIs are there, the choice ultimately falls to the developer who will use them.

Am I missing something? Is there something else that could boost rendering performance without using HC? Any advice or it is just as it is for now?

In the meantime, thanks for the support to all of you!

@pichillilorenzo
Copy link
Owner

Here is more info about the issue with the native view Hardware acceleration enabled: if you rotate the device or open the keyboard or somehow you manage to get the Flutter widget that contains the native view to be resized, it draws it back correctly.

By the way, the native view itself stills there and gestures still work correctly as the view was visible, it just doesn’t draw the “texture” or whatever it is for Android Flutter Platform View.

@xOldeVx
Copy link

xOldeVx commented Jan 18, 2024

My s22 Ultra got new firmware update and I can't reproduce this bug anymore, by the way if setting hardwareAcceleration to false (In InAppWebViewSettings) the webview is black like the current bug.

How can I select mode? I mean initAndroidView or initSurfaceAndroidView

@pichillilorenzo
Copy link
Owner

@xOldeVx you can’t now. That logic is not implemented, it was just a test of mine locally.
Currently, because of this issue, changing hardwareAcceleration or not, will not resolve the issue.
This is something that needs a new plugin version to be released when not using HC on Android.

@xOldeVx
Copy link

xOldeVx commented Jan 19, 2024

I'm updating again again, the problem is still here, for some reason it's worked without any problem for few days, and now it's occurs almost every time.
By the way, the Hybrid Composition option is very bad, it's make the widgets AND the WebView very lag, especially if it's video in the WebView, I'm using s22 Ultra, I tried this option on Samsung SM-A245F too, and playing video is really really lag.
Any solution/workaround to this problem?

@pichillilorenzo
Copy link
Owner

@xOldeVx as I said, this is not fixed yet! It requires a new version to be published when it is fixed.
Currently, I'm focused on other tasks.

Unfortunately, Hybrid Composition comes with trade-offs on Flutter side animations, check: https://github.com/flutter/flutter/wiki/Hybrid-Composition#performance

You should enable HC or not based on your App use cases.
If you don't need it, then don't use HC.

But, currently, as I said, the "resume" issue is not fixed yet.

@xOldeVx
Copy link

xOldeVx commented Jan 28, 2024

@pichillilorenzo Does it have an estimated date or a temporary solution, even not hermetic but only for Samsung Android 14 owners, because this problem is gaining momentum (more and more users update to Android 14) and I will probably have to move to another library in the meantime

@pichillilorenzo
Copy link
Owner

@xOldeVx sorry but I was focused on other things.
I’m planning to release a new version with the fix, other fixes and also Windows plugin initial support next weak, probably in 2/3 days.
The current workaround, in the meantime, is to just use Hybrid Composition.

@pichillilorenzo
Copy link
Owner

pichillilorenzo commented Jan 31, 2024

@xOldeVx I have released the new flutter_inappwebview_android implementation version 1.0.13 with the fix.
You can just run flutter pub upgrade to upgrade the plugin's Android-specific platform implementation.

@xOldeVx
Copy link

xOldeVx commented Jan 31, 2024

You mean flutter pub upgrade
I checked it, it's working, thank you!!

@xOldeVx
Copy link

xOldeVx commented Jan 31, 2024

Sorry, I checked it again, it's still happening.
Upgraded as you can see:
Screenshot 2024-01-31 at 18 23 18

@pichillilorenzo
Copy link
Owner

I’m not able to reproduce it anymore using the emulator.
Could you share minimum code to reproduce the issue?

Also, it could be useful a screen record of what is happening.
Are you using an emulator or a real device? Are you experiencing the same issue also on emulator?

@xOldeVx
Copy link

xOldeVx commented Jan 31, 2024

I checked it in the emulator (Pixel) and it's not reproduce anymore, right, but in real device Ultra S22 it's reproduce easy.

Code:

import 'dart:io';

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});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late InAppWebViewController webViewController;

  InAppWebViewSettings settings = InAppWebViewSettings(
    clearCache: true,
    useHybridComposition: false,
  );

  @override
  void initState() {
    super.initState();
    if (Platform.isAndroid) {
      InAppWebViewController.setWebContentsDebuggingEnabled(false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.black,
      child: InAppWebView(
        initialSettings: settings,
        onWebViewCreated: (controller) => webViewController = controller,
        initialUrlRequest: URLRequest(url: WebUri.uri(Uri.parse('https://pub.dev'))),
      ),
    );
  }
}

A screenshot exactly like here:
Screen_recording_20240103_094558.webm

You need to switch to another app like in the video, and then back to your app by clicking on the icon (I recheck it, not only by clicking on the icon, also if you switch again to your app you see a black screen)

@dnfield
Copy link
Author

dnfield commented Jan 31, 2024

The reproduction on the Samsung phone is probably a different bug. We're trying to work with Samsung to get it fixed.

@pichillilorenzo
Copy link
Owner

@dnfield do you mean that it's a bug of Flutter PlatformView?

@pichillilorenzo
Copy link
Owner

@xOldeVx When the black screen appears, if you change the phone orientation, does the WewbView become visible again?

@johnmccutchan
Copy link

@pichillilorenzo Samsung broke something in their Android 14 release. They've told us it's fixed in their upcoming update 🤞

@xOldeVx
Copy link

xOldeVx commented Feb 7, 2024

Is there a way to set useHybridComposition through the InAppWebViewController? because there are quite a few customers with this problem that is only increasing, I would be happy to solve this even temporarily

ps9310 pushed a commit to mayank4741/flutter_inappwebview that referenced this issue Feb 24, 2024
Klim-Karma added a commit to Shoptagr/flutter_inappwebview that referenced this issue Mar 19, 2024
* pichillilorenzo-master: (162 commits)
  added APPLINK_BLOCK to NavigationActionPolicy
  windows: removed not wanted debug log in in_app_webview.cpp
  initial windows implementation pichillilorenzo#460
  added InAppWebViewControllerKeepAliveProps constructor parameters default value
  fix pichillilorenzo#1981
  windows: added openDevTools, callDevToolsProtocolMethod, addDevToolsProtocolEventListener and removeDevToolsProtocolEventListener methods, added some more inappwebview and inappbrowser basic settings
  windows: completed CookieManager implementation, added WebViewEnvironment getAvailableVersion and compareBrowserVersions methods
  windows: updated webview environment docs, updated cookie manager to support webview environment
  windows: added WebViewEnvironment support for headlessinappwebview and inappbrowser
  windows: fixed some c++ include
  windows: fixed dealloc webviews logic, implemented takeScreenshot, added WebViewEnvironment and WebViewEnvironmentSettings classes
  windows: added headless inappwebview initial implementation
  windows: fixed javascript evaluation, added callAsyncJavaScript implementation, call DestroyWindow(parentWindow) on InAppWebView dealloc
  windows: fixed custom platform view context menu and window position
  windows: added content world support for user scripts and javascript evaluation
  windows: updated webview and browser creation params, added support for openWithSystemBrowser
  windows: completed initial javascript handler communication and user scripts support, added support for loadFile and loadData
  windows: implemented some other methods and events, initial implementation work for UserScripts and JavaScript handlers
  windows: added c++ nlohmann.json dependency, added getCopyBackForwardList implementation
  windows: added inappwebview widget support, added some other methods
  ...
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

4 participants