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.29k stars 1.62k forks source link

takeScreenshot not capturing canvas-elements (on iOS) #723

Closed reesz closed 1 month ago

reesz commented 3 years ago

Environment

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.0.1, on Mac OS X 10.15.7 19H2 darwin-x64, locale de-DE)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[✓] Android Studio (version 4.1)
[✓] VS Code (version 1.54.2)
[✓] Connected device (3 available)

• No issues found!

Device information: iPhone 11 Pro / iPhone X / iOS 14.4

Description

I'm trying to take a screenshot of the currently displayed webpage with the takeScreenshot() method. For regular websites this is working fine – but when trying to capture a webpage that displays all it's content on a canvas-element, the screenshot is only black (background color of the page).

I've found this old issue: https://github.com/pichillilorenzo/flutter_inappwebview/issues/409 that talks about having to disable hardwareAcceleration for Android, but for me it's affecting iOS.

The only weird thing that's throwing me off: When running the app on a simulator, taking the screenshot of canvas-pages works. On all of my real devices (iPhone 6/X/11 Pro) it doesn't.

Expected behavior: Expected the full page, including canvas elements, to show in the screenshot.

Current behavior: Canvas elements are not shown in the screenshot.

Steps to reproduce

Here's a minimal sample project to reproduce the issue. A fullscreen webview that takes a screenshot by tapping on it & displays it afterwards in the bottom left corner. On a real device this always displays a black screenshot. (Ignore the base64 encoding, this is just a left over from the project I copied it over from)

import 'dart:convert';
import 'dart:typed_data';

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

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.title}) : super(key: key);

  final String? title;

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

class _MyHomePageState extends State<MyHomePage> {
  InAppWebViewController? webViewController;
  final GlobalKey webViewKey = GlobalKey();
  String lastScreenshot = "";

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Listener(
          onPointerDown: (_) {
            createScreenshot();
          },
          child: InAppWebView(
            key: webViewKey,
            initialUrlRequest: URLRequest(
                url: Uri.parse(
                    "https://www.figma.com/proto/Gt05O5A6cvi31ZDX0oIiLG/Prototyping-in-Figma?node-id=0%3A725&viewport=-3636%2C252%2C0.5734453797340393&scaling=scale-down")),
            initialOptions: InAppWebViewGroupOptions(
              crossPlatform: InAppWebViewOptions(
                useShouldOverrideUrlLoading: true,
                useOnLoadResource: true,
                javaScriptEnabled: true,
                incognito: false,
                mediaPlaybackRequiresUserGesture: false,
                clearCache: true,
                javaScriptCanOpenWindowsAutomatically: false,
              ),
              android: AndroidInAppWebViewOptions(hardwareAcceleration: false),
              ios: IOSInAppWebViewOptions(allowsAirPlayForMediaPlayback: false, allowsInlineMediaPlayback: true),
            ),
            // pullToRefreshController: pullToRefreshController,
            onWebViewCreated: (controller) {
              webViewController = controller;
            },
            onLoadStop: (controller, url) async {
              webViewController = controller;
            },
            androidOnPermissionRequest: (controller, origin, resources) async {
              return PermissionRequestResponse(resources: resources, action: PermissionRequestResponseAction.GRANT);
            },
            onConsoleMessage: (controller, consoleMessage) {
              print(consoleMessage);
            },
          ),
        ),
        if (lastScreenshot.isNotEmpty)
          Positioned(
            bottom: 50,
            left: 20,
            child: Container(
              width: 150,
              height: 250,
              color: Colors.orange,
              child: Image.memory(base64Decode(lastScreenshot)),
            ),
          )
      ],
    );
  }

  void createScreenshot() async {
    /// Take Screenshot of Webview
    Uint8List? screenshotBytes = await webViewController?.takeScreenshot();
    if (screenshotBytes == null) return;

    /// Encode ImageData to base64
    String bs64 = base64Encode(screenshotBytes);

    /// Save base64 String to Prototype
    setState(() {
      lastScreenshot = bs64;
    });
  }
}
  1. Run the app.
  2. Wait for the url to fully load and display the sample "search screen"
  3. Tap on the screen
  4. See the black screenshot in the bottom left corner.

If there's anything else I can provide, please let me know.

Thanks for your time!

pichillilorenzo commented 3 years ago

I don't know what's happening. I can take screenshot of a canvas without any problems with this example:

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

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();

  if (Platform.isAndroid) {
    await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
  }

  runApp(MaterialApp(
      home: MyApp()
  ));
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {

  InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
    android: AndroidInAppWebViewOptions(
      useHybridComposition: true,
    ),);

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("InAppWebView example")),
      body: SafeArea(
          child: Column(children: <Widget>[
            Expanded(
              child: InAppWebView(
                initialOptions: options,
                initialData: InAppWebViewInitialData(
                  data: """
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<body>

<canvas id="myCanvas" width="200" height="100" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML canvas tag.</canvas>

<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.font = "30px Arial";
ctx.fillText("Hello World",10,50);
</script>

</body>
</html>

                  """
                ),
                onLoadStop: (controller, url) async {
                  await Future.delayed(Duration(seconds: 1));
                  var screenshot = await controller.takeScreenshot();
                  await showDialog(
                      context: context,
                      builder: (context) {
                    return AlertDialog(
                      content: Image.memory(screenshot!),
                    );
                  },
                  );
                },
              ),
            ),
          ])),
    );
  }
}

Or change your URL with https://www.w3schools.com/html/tryit.asp?filename=tryhtml5_canvas_tut_text

So, I think there are some problems with the website itself.

no-response[bot] commented 3 years ago

This issue has been automatically closed because it was marked as "answered", and there wasn't any recent activity on this issue.

panzikeji commented 3 years ago

I had the same problem when use url

reesz commented 3 years ago

@panzikeji could you provide an example URL of the type of content you're using?

panzikeji commented 3 years ago

flutter_inappwebview: ^4.0.0+4 takeScreenshot doesn't work on all of my real devices canvas example URL: https://app-pre.panzitech.com/preview?file=https://app-prod.obs.cn-east-2.myhuaweicloud.com:443/_4238_1610089676014_161008967145128820694-9834-4475-9257-c5feed21c299.ocf

chaoyang-zhou commented 2 years ago

Solve this problem?

chaoyang-zhou commented 2 years ago

I try to take a screenshot this link https://www.w3schools.com/html/tryit.asp?filename=tryhtml5_canvas_tut_text I got it, No 'Hello World' in Canvas. image

In flutter_inappwebview: ^5.4.4+3

If I disable hardwareAcceleration, my maps page doesn't finish loading.

github-actions[bot] commented 1 month ago

This issue is stale and has been automatically closed because it has been open for more than 365 days with no activity. Please reopen a new issue if you still have it.

github-actions[bot] commented 3 weeks ago

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug and a minimal reproduction of the issue.