juicycleff / flutter-unity-view-widget

Embeddable unity game engine view for Flutter. Advance demo here https://github.com/juicycleff/flutter-unity-arkit-demo
BSD 3-Clause "New" or "Revised" License
2.12k stars 512 forks source link

[ANDROID] Screenshots no longer work on 4.0.0+ (Blank screen) #379

Open TudorAxinte opened 3 years ago

TudorAxinte commented 3 years ago

Screenshots no longer work on Android since the latest release. I've tried plenty of workarounds, yet ever since I updated to the latest release I always get a blank (white) screen. IOS still works fine.

String path = await NativeScreenshot.takeScreenshot();

if (path != null ) {
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => ScreenshotScreen(path: path,)));
}

Code gets executed properly. Yet, as the latest release changed the way platform views are handled the console throws:

_[ERROR:flutter/flow/layers/platform_viewlayer.cc(40)] Trying to embed a platform view but the PaintContext does not support embedding

Instead of saving a capture of the unity view as it used to for a very long time, it saves a capture of flutter's UI only (blank page). Reimplementing the project using flutter_unity_widget: ^2.0.0 solves the issue, but it's still a workaround.

[√] Flutter (Channel dev, 2.1.0-12.1.pre, on Microsoft Windows [Version 10.0.19042.867], locale en-US) [√] Android toolchain - develop for Android devices (Android SDK version 29.0.3)

Unity 2019.4.9f1

juicycleff commented 3 years ago

@TudorAxinte you mean screenshooting the app screen?

juicycleff commented 3 years ago

Or using flutter to capture the screen?

TudorAxinte commented 3 years ago

Flutter. In the latest version, it appears the order of the native layers changed, and any attempt to capture from flutter does not capture the Unity view as it's on top. Native screenshot works fine.

Eashiong commented 3 years ago

I have the same problem with IOS。 version: flutter_unity_widget: ^4.2.0

I use the screenshot to take a screenshot on the widget. oh my god,my picture is blank.

tomLeclercqDD commented 2 years ago

Hello, I face the same issue with flutter_unity_widget: ^4.2.4 on iOS The UnityWidget is ignored appears white on the screenshot - I'd like to share the scene as image. Is there any progress or workaround on this?

TudorAxinte commented 2 years ago

Hello, I face the same issue with flutter_unity_widget: ^4.2.4 on iOS The UnityWidget is ignored appears white on the screenshot - I'd like to share the scene as image. Is there any progress or workaround on this?

Hello Tom. Use the native screenshot package and the 4.0.0 plugin and you'll be fine.

tomLeclercqDD commented 2 years ago

Hello, I face the same issue with flutter_unity_widget: ^4.2.4 on iOS The UnityWidget is ignored appears white on the screenshot - I'd like to share the scene as image. Is there any progress or workaround on this?

Hello Tom. Use the native screenshot package and the 4.0.0 plugin and you'll be fine.

Many thanks for you reply, I was selecting few other widgets as overlay but not the entire application. I will go with the native solution for now but I hope we'll be able to use the RepaintBoundary in the future. Cheers,

TudorAxinte commented 2 years ago

Here's how I do it

import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart';
import 'package:native_screenshot/native_screenshot.dart';
import 'package:flutter_unity_widget/flutter_unity_widget.dart';

class AR extends StatefulWidget {
  @override
  _ARState createState() => _ARState();
}

class _ARState extends State<AR> {
  late UnityWidgetController _unityWidgetController;
  final ValueNotifier<bool> _takingScreenshot = ValueNotifier(false);

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.black,
        title: Image.asset(
          'assets/logo_white.png',
          height: 24,
        ),
        iconTheme: IconThemeData(
          color: Colors.white, //change your color here
        ),
        leading: ValueListenableBuilder<bool>(
          valueListenable: _takingScreenshot,
          builder: (_, takingScreenshot, __) => Visibility(
            visible: !takingScreenshot,
            child: IconButton(
              onPressed: () {
                dispose();
                Navigator.of(context).pop();
              },
              icon: Icon(Icons.arrow_back),
            ),
          ),
        ),
        automaticallyImplyLeading: false,
      ),
      body: UnityWidget(
        onUnityCreated: onUnityCreated,
        borderRadius: BorderRadius.all(
          Radius.circular(0),
        ),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      floatingActionButton: ValueListenableBuilder<bool>(
        valueListenable: _takingScreenshot,
        builder: (_, takingScreenshot, __) => Visibility(
          visible: !takingScreenshot,
          child: FloatingActionButton(
            onPressed: () async {
              try {
                _takingScreenshot.value = true;
                await Future.delayed(Duration(seconds: 1));
                var path = await NativeScreenshot.takeScreenshot();
                _takingScreenshot.value = false;
                if (path != null) {
                  await _unityWidgetController.unload();
                  Navigator.of(context).pushReplacement(
                    MaterialPageRoute(
                      builder: (_) => ScreenshotScreen(
                        path,
                      ),
                    ),
                  );
                }
              } catch (e) {
                BotToast.showText(text: e.toString(), duration: Duration(seconds: 5));
              }
            },
            child: Icon(Icons.camera_alt),
          ), // This trailing comma makes auto-formatting nicer for build methods.
        ),
      ),
    );
  }

  void onUnityCreated(controller) {
    this._unityWidgetController = controller;
  }
}
tomLeclercqDD commented 2 years ago

Here's how I do it

import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart';
import 'package:native_screenshot/native_screenshot.dart';
import 'package:flutter_unity_widget/flutter_unity_widget.dart';

class AR extends StatefulWidget {
  @override
  _ARState createState() => _ARState();
}

class _ARState extends State<AR> {
  late UnityWidgetController _unityWidgetController;
  final ValueNotifier<bool> _takingScreenshot = ValueNotifier(false);

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.black,
        title: Image.asset(
          'assets/logo_white.png',
          height: 24,
        ),
        iconTheme: IconThemeData(
          color: Colors.white, //change your color here
        ),
        leading: ValueListenableBuilder<bool>(
          valueListenable: _takingScreenshot,
          builder: (_, takingScreenshot, __) => Visibility(
            visible: !takingScreenshot,
            child: IconButton(
              onPressed: () {
                dispose();
                Navigator.of(context).pop();
              },
              icon: Icon(Icons.arrow_back),
            ),
          ),
        ),
        automaticallyImplyLeading: false,
      ),
      body: UnityWidget(
        onUnityCreated: onUnityCreated,
        borderRadius: BorderRadius.all(
          Radius.circular(0),
        ),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      floatingActionButton: ValueListenableBuilder<bool>(
        valueListenable: _takingScreenshot,
        builder: (_, takingScreenshot, __) => Visibility(
          visible: !takingScreenshot,
          child: FloatingActionButton(
            onPressed: () async {
              try {
                _takingScreenshot.value = true;
                await Future.delayed(Duration(seconds: 1));
                var path = await NativeScreenshot.takeScreenshot();
                _takingScreenshot.value = false;
                if (path != null) {
                  await _unityWidgetController.unload();
                  Navigator.of(context).pushReplacement(
                    MaterialPageRoute(
                      builder: (_) => ScreenshotScreen(
                        path,
                      ),
                    ),
                  );
                }
              } catch (e) {
                BotToast.showText(text: e.toString(), duration: Duration(seconds: 5));
              }
            },
            child: Icon(Icons.camera_alt),
          ), // This trailing comma makes auto-formatting nicer for build methods.
        ),
      ),
    );
  }

  void onUnityCreated(controller) {
    this._unityWidgetController = controller;
  }
}

Yes Indeed, I figured out rather the same kind of solution, with maintainSize option so that the UI stays put while the screenshot is being captured. Pretty neat 👌

Thanks a lot for this amazing package!