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
RenderRepaintBoundary screenshot sometimes fails to render images #75316
Comments
Hi @BaroneX Ouput
Try the code sample below code sampleimport 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:path_provider/path_provider.dart';
void main() => runApp(MyApp());
class UiImagePainter extends CustomPainter {
final ui.Image image;
UiImagePainter(this.image);
@override
void paint(ui.Canvas canvas, ui.Size size) {
// simple aspect fit for the image
var hr = size.height / image.height;
var wr = size.width / image.width;
double ratio;
double translateX;
double translateY;
if (hr < wr) {
ratio = hr;
translateX = (size.width - (ratio * image.width)) / 2;
translateY = 0.0;
} else {
ratio = wr;
translateX = 0.0;
translateY = (size.height - (ratio * image.height)) / 2;
}
canvas.translate(translateX, translateY);
canvas.scale(ratio, ratio);
canvas.drawImage(image, new Offset(0.0, 0.0), new Paint());
}
@override
bool shouldRepaint(UiImagePainter other) {
return other.image != image;
}
}
class UiImageDrawer extends StatelessWidget {
final ui.Image image;
const UiImageDrawer({Key key, this.image}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomPaint(
size: Size.infinite,
painter: UiImagePainter(image),
);
}
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
GlobalKey<OverRepaintBoundaryState> globalKey = GlobalKey();
ui.Image image;
Future<void> writeToFile(ByteData data, String path) {
final buffer = data.buffer;
return new File(path).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: image == null
? Capturer(
overRepaintKey: globalKey,
)
: UiImageDrawer(image: image),
floatingActionButton: image == null
? FloatingActionButton(
child: Icon(Icons.camera),
onPressed: () async {
var renderObject =
globalKey.currentContext.findRenderObject();
RenderRepaintBoundary boundary = renderObject;
ui.Image captureImage = await boundary.toImage();
String dir = (await getApplicationDocumentsDirectory()).path;
print(dir);
ByteData _byteData = await captureImage.toByteData(
format: ui.ImageByteFormat.png);
writeToFile(_byteData, "$dir/exported_image.png");
setState(() => image = captureImage);
},
)
: FloatingActionButton(
onPressed: () => setState(() => image = null),
child: Icon(Icons.remove),
),
),
);
}
}
class Capturer extends StatelessWidget {
static final Random random = Random();
final GlobalKey<OverRepaintBoundaryState> overRepaintKey;
const Capturer({Key key, this.overRepaintKey}) : super(key: key);
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: OverRepaintBoundary(
key: overRepaintKey,
child: RepaintBoundary(
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
child: Container(
margin: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"This is a RenderRepaintBoundary test",
style: Theme.of(context)
.textTheme
.headline6
.copyWith(color: Colors.blue),
),
ClipRRect(
borderRadius: BorderRadius.circular(25),
child: Container(
color: Colors.grey[100],
child: FlutterLogo(
size: 300,
),
),
),
],
),
),
),
),
),
);
}
}
class OverRepaintBoundary extends StatefulWidget {
final Widget child;
const OverRepaintBoundary({Key key, this.child}) : super(key: key);
@override
OverRepaintBoundaryState createState() => OverRepaintBoundaryState();
}
class OverRepaintBoundaryState extends State<OverRepaintBoundary> {
@override
Widget build(BuildContext context) {
return widget.child;
}
}
flutter doctor -v[✓] Flutter (Channel stable, 1.22.6, on macOS 11.1 20C69 darwin-x64, locale en-GB)
• Flutter version 1.22.6 at /Users/tahatesser/Code/flutter_stable
• Framework revision 9b2d32b605 (12 days ago), 2021-01-22 14:36:39 -0800
• Engine revision 2f0af37152
• Dart version 2.10.5
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
• Android SDK at /Volumes/Extreme/SDK
• Platform android-30, build-tools 30.0.3
• ANDROID_HOME = /Volumes/Extreme/SDK
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 12.4)
• Xcode at /Volumes/Extreme/Xcode.app/Contents/Developer
• Xcode 12.4, Build version 12D4e
• CocoaPods version 1.10.1
[!] Android Studio (version 4.1)
• Android Studio at /Applications/Android Studio.app/Contents
✗ Flutter plugin not installed; this adds Flutter specific functionality.
✗ Dart plugin not installed; this adds Dart specific functionality.
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
[✓] VS Code (version 1.52.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.19.0
[!] Connected device
! No devices available
! Doctor found issues in 2 categories. [✓] Flutter (Channel master, 1.26.0-18.0.pre.156, on macOS 11.1 20C69 darwin-x64, locale en-GB)
• Flutter version 1.26.0-18.0.pre.156 at /Users/tahatesser/Code/flutter_master
• Framework revision 45508985b1 (8 hours ago), 2021-02-02 22:46:04 -0500
• Engine revision 2c144c3eeb
• Dart version 2.12.0 (build 2.12.0-282.0.dev)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
• Android SDK at /Volumes/Extreme/SDK
• Platform android-30, build-tools 30.0.3
• ANDROID_HOME = /Volumes/Extreme/SDK
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS
• Xcode at /Volumes/Extreme/Xcode.app/Contents/Developer
• Xcode 12.4, Build version 12D4e
• CocoaPods version 1.10.1
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 4.1)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
[✓] VS Code (version 1.52.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.19.0
[✓] Connected device (2 available)
• macOS (desktop) • macos • darwin-x64 • macOS 11.1 20C69 darwin-x64
• Chrome (web) • chrome • web-javascript • Google Chrome 88.0.4324.96
• No issues found! Can you please provide more details and a minimal complete reproducible code sample? |
@TahaTesser Thank you for your attention to this issue code sample
|
Hi @BaroneX code sampleimport 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:qr_flutter/qr_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: QrCodeWidget(),
),
);
}
}
class QrCodeWidget extends StatefulWidget {
QrCodeWidget({Key key}) : super(key: key);
@override
_QrCodeWidgetState createState() => _QrCodeWidgetState();
}
class _QrCodeWidgetState extends State<QrCodeWidget> {
final ScrollController controller =
ScrollController(initialScrollOffset: -1, keepScrollOffset: true);
ui.Image image;
GlobalKey repaintWidgetKey = GlobalKey(); // 绘图key值
String _barCodeString = "123456";
_saveImage() async {
ByteData sourceByteData = await _capturePngToByteData();
final result = await ImageGallerySaver.saveImage(
sourceByteData.buffer.asUint8List(),
quality: 60,
name: "hello");
if (result["isSuccess"]) {
print("success");
} else {
print("fail");
}
}
Future<ByteData> _capturePngToByteData() async {
try {
RenderRepaintBoundary boundary =
repaintWidgetKey.currentContext.findRenderObject();
double dpr = ui.window.devicePixelRatio;
ui.Image image = await boundary.toImage(pixelRatio: dpr);
ByteData _byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
return _byteData;
} catch (e) {
print(e);
}
return null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Stack(
children: <Widget>[
Container(
child: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: Container(
child: GestureDetector(
child: Container(
width: 110,
alignment: Alignment.center,
margin: EdgeInsets.only(top: 20),
height: 35,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(19)),
color: Colors.transparent,
border:
Border.all(color: Colors.white, width: 0.5)),
child: Text(
"save",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
onTap: () {
_saveImage();
},
),
),
),
],
controller: controller,
),
),
_renderQrCaptureView(),
],
),
);
}
_renderQrCaptureView() {
double topImageWidth = 75.0;
double qrCodeImageWitdh = 135;
return Positioned(
top: MediaQuery.of(context).size.height,
child: RepaintBoundary(
key: repaintWidgetKey,
child: Container(
width: MediaQuery.of(context).size.width,
height: 667,
decoration: BoxDecoration(
color: Colors.black,
image: DecorationImage(
image: AssetImage("res/images/img_share_image_bg.png"),
fit: BoxFit.fill),
),
child: Stack(
alignment: Alignment.topCenter,
children: [
Positioned(
top: 164,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Container(
color: Colors.white,
width: 335,
height: 365,
child: Column(
children: [
Expanded(
flex: 135,
child: Container(
constraints: BoxConstraints.expand(),
color: Colors.grey,
child: Column(
children: [
Container(
margin: EdgeInsets.only(top: 50),
child: Text(
"Name",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
)),
Container(
margin: EdgeInsets.only(
top: 20, left: 5, right: 5),
child: Text(
"desc",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
fontSize: 20),
overflow: TextOverflow.ellipsis,
maxLines: 1,
)),
],
),
)),
Expanded(
flex: 231,
child: Container(
color: Colors.white,
child: Column(
children: [
Container(
margin: EdgeInsets.only(top: 18),
child: QrImage(
data: _barCodeString,
version: QrVersions.auto,
padding: EdgeInsets.all(5),
size: qrCodeImageWitdh,
backgroundColor: Colors.white,
errorCorrectionLevel:
QrErrorCorrectLevel.H,
embeddedImage: AssetImage(
'res/images/icon_default_portrait.png'),
embeddedImageStyle: QrEmbeddedImageStyle(
size: Size(qrCodeImageWitdh / 5.0,
qrCodeImageWitdh / 5.0),
),
),
),
Container(
margin: EdgeInsets.only(top: 15),
child: Text(
"time",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
fontSize: 14),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
Container(
margin: EdgeInsets.only(top: 5),
child: Text(
"text",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
fontSize: 13),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
],
),
)),
],
),
),
),
),
Positioned(
top: 127,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular((topImageWidth + 4) / 2.0)),
border: Border.all(color: Colors.white, width: 2),
),
child: Container(
height: topImageWidth,
width: topImageWidth,
child: CircleAvatar(
radius: topImageWidth / 2.0,
child: Image.asset(
"res/images/icon_default_portrait.png",
)),
),
),
),
Positioned(
bottom: 31,
child: Image.asset(
"res/images/icon_default_portrait.png",
height: 21,
fit: BoxFit.contain,
),
),
],
),
),
),
);
}
}
flutter doctor -v[✓] Flutter (Channel stable, 1.22.6, on macOS 11.1 20C69 darwin-x64, locale en-GB)
• Flutter version 1.22.6 at /Users/tahatesser/Code/flutter_stable
• Framework revision 9b2d32b605 (12 days ago), 2021-01-22 14:36:39 -0800
• Engine revision 2f0af37152
• Dart version 2.10.5
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
• Android SDK at /Volumes/Extreme/SDK
• Platform android-30, build-tools 30.0.3
• ANDROID_HOME = /Volumes/Extreme/SDK
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 12.4)
• Xcode at /Volumes/Extreme/Xcode.app/Contents/Developer
• Xcode 12.4, Build version 12D4e
• CocoaPods version 1.10.1
[!] Android Studio (version 4.1)
• Android Studio at /Applications/Android Studio.app/Contents
✗ Flutter plugin not installed; this adds Flutter specific functionality.
✗ Dart plugin not installed; this adds Dart specific functionality.
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
[✓] VS Code (version 1.52.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.19.0
[✓] Connected device (1 available)
• Taha’s iPhone (mobile) • 00008020-001059882212002E • ios • iOS 14.4
! Doctor found issues in 1 category. [✓] Flutter (Channel master, 1.26.0-18.0.pre.186, on macOS 11.1 20C69 darwin-x64, locale en-GB)
• Flutter version 1.26.0-18.0.pre.186 at /Users/tahatesser/Code/flutter_master
• Framework revision 39114251af (2 hours ago), 2021-02-04 01:16:04 -0500
• Engine revision 1c2c78398c
• Dart version 2.12.0 (build 2.12.0-284.0.dev)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
• Android SDK at /Volumes/Extreme/SDK
• Platform android-30, build-tools 30.0.3
• ANDROID_HOME = /Volumes/Extreme/SDK
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS
• Xcode at /Volumes/Extreme/Xcode.app/Contents/Developer
• Xcode 12.4, Build version 12D4e
• CocoaPods version 1.10.1
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 4.1)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
[✓] VS Code (version 1.52.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.19.0
[✓] Connected device (3 available)
• Taha’s iPhone (mobile) • 00008020-001059882212002E • ios • iOS 14.4
• macOS (desktop) • macos • darwin-x64 • macOS 11.1 20C69 darwin-x64
• Chrome (web) • chrome • web-javascript • Google Chrome 88.0.4324.96
• No issues found! Thank you |
@TahaTesser
code sample
|
Hi @BaroneX Previewcode sampleimport 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:qr_flutter/qr_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: QrCodeWidget(),
),
);
}
}
class QrCodeWidget extends StatefulWidget {
QrCodeWidget({Key key}) : super(key: key);
@override
_QrCodeWidgetState createState() => _QrCodeWidgetState();
}
class _QrCodeWidgetState extends State<QrCodeWidget> {
final ScrollController controller =
ScrollController(initialScrollOffset: -1, keepScrollOffset: true);
ui.Image image;
GlobalKey repaintWidgetKey = GlobalKey();
String _barCodeString = "123456";
_saveImage() async {
PermissionStatus permission = await Permission.photos.request();
bool isPhotoGranted = (permission.isUndetermined ||
permission.isRestricted ||
permission.isGranted);
if (!isPhotoGranted) {
return;
}
ByteData sourceByteData = await _capturePngToByteData();
final result = await ImageGallerySaver.saveImage(
sourceByteData.buffer.asUint8List(),
quality: 60,
name: "hello");
if (result["isSuccess"]) {
print("success");
} else {
print("fail");
}
}
Future<ByteData> _capturePngToByteData() async {
try {
RenderRepaintBoundary boundary =
repaintWidgetKey.currentContext.findRenderObject();
double dpr = ui.window.devicePixelRatio;
ui.Image image = await boundary.toImage(pixelRatio: dpr);
ByteData _byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
return _byteData;
} catch (e) {
print(e);
}
return null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.green,
body: Stack(
children: <Widget>[
Container(
child: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: Container(
child: GestureDetector(
child: Container(
width: 110,
alignment: Alignment.center,
margin: EdgeInsets.only(top: 20),
height: 35,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(19)),
color: Colors.transparent,
border:
Border.all(color: Colors.white, width: 0.5)),
child: Text(
"save",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
onTap: () {
_saveImage();
},
),
),
),
],
controller: controller,
),
),
_renderQrCaptureView(),
],
),
);
}
_renderQrCaptureView() {
double topImageWidth = 75.0;
double qrCodeImageWitdh = 135;
return Positioned(
top: MediaQuery.of(context).size.height,
child: RepaintBoundary(
key: repaintWidgetKey,
child: Container(
width: MediaQuery.of(context).size.width,
height: 667,
decoration: BoxDecoration(
color: Colors.black,
image: DecorationImage(
image: AssetImage("assets/dash.png"), fit: BoxFit.fill),
),
child: Stack(
alignment: Alignment.topCenter,
children: [
Positioned(
top: 164,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Container(
color: Colors.white,
width: 335,
height: 365,
child: Column(
children: [
Expanded(
flex: 135,
child: Container(
constraints: BoxConstraints.expand(),
color: Colors.grey,
child: Column(
children: [
Container(
margin: EdgeInsets.only(top: 50),
child: Text(
"Name",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
)),
Container(
margin: EdgeInsets.only(
top: 20, left: 5, right: 5),
child: Text(
"desc",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
fontSize: 20),
overflow: TextOverflow.ellipsis,
maxLines: 1,
)),
],
),
)),
Expanded(
flex: 231,
child: Container(
color: Colors.white,
child: Column(
children: [
Container(
margin: EdgeInsets.only(top: 18),
child: QrImage(
data: _barCodeString,
version: QrVersions.auto,
padding: EdgeInsets.all(5),
size: qrCodeImageWitdh,
backgroundColor: Colors.white,
errorCorrectionLevel:
QrErrorCorrectLevel.H,
embeddedImage:
AssetImage('assets/dash.png'),
embeddedImageStyle: QrEmbeddedImageStyle(
size: Size(qrCodeImageWitdh / 5.0,
qrCodeImageWitdh / 5.0),
),
),
),
Container(
margin: EdgeInsets.only(top: 15),
child: Text(
"time",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
fontSize: 14),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
Container(
margin: EdgeInsets.only(top: 5),
child: Text(
"text",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
fontSize: 13),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
],
),
)),
],
),
),
),
),
Positioned(
top: 127,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular((topImageWidth + 4) / 2.0)),
border: Border.all(color: Colors.white, width: 2),
),
child: Container(
height: topImageWidth,
width: topImageWidth,
child: CircleAvatar(
radius: topImageWidth / 2.0,
child: Image.asset(
"assets/dash.png",
)),
),
),
),
Positioned(
bottom: 31,
child: Image.asset(
"assets/dash.png",
height: 21,
fit: BoxFit.contain,
),
),
],
),
),
),
);
}
}
Looks like you're on a previous Flutter stable, Can you please upgrade to the latest and please provide this image |
@TahaTesser Previously closed issue that seems to be same problem:
code sample
flutter doctor -v
|
Hi @BaroneX Previewcode sampleimport 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:qr_flutter/qr_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: QrCodeWidget(),
),
);
}
}
class QrCodeWidget extends StatefulWidget {
QrCodeWidget({Key key}) : super(key: key);
@override
_QrCodeWidgetState createState() => _QrCodeWidgetState();
}
class _QrCodeWidgetState extends State<QrCodeWidget> {
final ScrollController controller =
ScrollController(initialScrollOffset: -1, keepScrollOffset: true);
ui.Image image;
GlobalKey repaintWidgetKey = GlobalKey();
String _barCodeString = "123456";
_saveImage() async {
PermissionStatus permission = await Permission.photos.request();
bool isPhotoGranted = (permission.isUndetermined ||
permission.isRestricted ||
permission.isGranted);
if (!isPhotoGranted) {
return;
}
ByteData sourceByteData = await _capturePngToByteData();
final result = await ImageGallerySaver.saveImage(
sourceByteData.buffer.asUint8List(),
quality: 60,
name: "hello");
if (result["isSuccess"]) {
print("success");
} else {
print("fail");
}
}
Future<ByteData> _capturePngToByteData() async {
try {
RenderRepaintBoundary boundary =
repaintWidgetKey.currentContext.findRenderObject();
double dpr = ui.window.devicePixelRatio;
ui.Image image = await boundary.toImage(pixelRatio: dpr);
ByteData _byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
return _byteData;
} catch (e) {
print(e);
}
return null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.green,
body: Stack(
children: <Widget>[
Container(
child: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: Container(
child: GestureDetector(
child: Container(
width: 110,
alignment: Alignment.center,
margin: EdgeInsets.only(top: 20),
height: 35,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(19)),
color: Colors.transparent,
border:
Border.all(color: Colors.white, width: 0.5)),
child: Text(
"save",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
onTap: () {
_saveImage();
},
),
),
),
],
controller: controller,
),
),
_renderQrCaptureView(),
],
),
);
}
_renderQrCaptureView() {
double topImageWidth = 75.0;
double qrCodeImageWitdh = 135;
return Positioned(
top: MediaQuery.of(context).size.height,
child: RepaintBoundary(
key: repaintWidgetKey,
child: Container(
width: MediaQuery.of(context).size.width,
height: 667,
decoration: BoxDecoration(
color: Colors.black,
image: DecorationImage(
image: AssetImage("res/images/dash.png"), fit: BoxFit.fill),
),
child: Stack(
alignment: Alignment.topCenter,
children: [
Positioned(
top: 164,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Container(
color: Colors.white,
width: 335,
height: 365,
child: Column(
children: [
Expanded(
flex: 135,
child: Container(
constraints: BoxConstraints.expand(),
color: Colors.grey,
child: Column(
children: [
Container(
margin: EdgeInsets.only(top: 50),
child: Text(
"Name",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
)),
Container(
margin: EdgeInsets.only(
top: 20, left: 5, right: 5),
child: Text(
"desc",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
fontSize: 20),
overflow: TextOverflow.ellipsis,
maxLines: 1,
)),
],
),
)),
Expanded(
flex: 231,
child: Container(
color: Colors.white,
child: Column(
children: [
Container(
margin: EdgeInsets.only(top: 18),
child: QrImage(
data: _barCodeString,
version: QrVersions.auto,
padding: EdgeInsets.all(5),
size: qrCodeImageWitdh,
backgroundColor: Colors.white,
errorCorrectionLevel:
QrErrorCorrectLevel.H,
embeddedImage: AssetImage(
'res/images/icon_default_portrait.png'),
embeddedImageStyle: QrEmbeddedImageStyle(
size: Size(qrCodeImageWitdh / 5.0,
qrCodeImageWitdh / 5.0),
),
),
),
Container(
margin: EdgeInsets.only(top: 15),
child: Text(
"time",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
fontSize: 14),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
Container(
margin: EdgeInsets.only(top: 5),
child: Text(
"text",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
fontSize: 13),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
],
),
)),
],
),
),
),
),
Positioned(
top: 127,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular((topImageWidth + 4) / 2.0)),
border: Border.all(color: Colors.white, width: 2),
),
child: Container(
height: topImageWidth,
width: topImageWidth,
child: CircleAvatar(
radius: topImageWidth / 2.0,
child: Image.asset(
"res/images/icon_default_portrait.png",
)),
),
),
),
Positioned(
bottom: 31,
child: Image.asset(
"res/images/icon_default_portrait.png",
height: 21,
fit: BoxFit.contain,
),
),
],
),
),
),
);
}
}
flutter doctor -v[✓] Flutter (Channel stable, 1.22.6, on macOS 11.2 20D64 darwin-x64, locale en-GB)
• Flutter version 1.22.6 at /Users/tahatesser/Code/flutter_stable
• Framework revision 9b2d32b605 (2 weeks ago), 2021-01-22 14:36:39 -0800
• Engine revision 2f0af37152
• Dart version 2.10.5
[✗] Android toolchain - develop for Android devices
✗ Unable to locate Android SDK.
Install Android Studio from: https://developer.android.com/studio/index.html
On first launch it will assist you in installing the Android SDK components.
(or visit https://flutter.dev/docs/get-started/install/macos#android-setup for detailed instructions).
If the Android SDK has been installed to a custom location, set ANDROID_SDK_ROOT to that location.
You may also want to add it to your PATH environment variable.
[!] Xcode - develop for iOS and macOS (Xcode 12.4)
• Xcode at /Volumes/Extreme/Xcode.app/Contents/Developer
• Xcode 12.4, Build version 12D4e
✗ CocoaPods not installed.
CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin usage on the Dart side.
Without CocoaPods, plugins will not work on iOS or macOS.
For more info, see https://flutter.dev/platform-plugins
To install:
sudo gem install cocoapods
[!] Android Studio (version 4.1)
• Android Studio at /Applications/Android Studio.app/Contents
✗ Flutter plugin not installed; this adds Flutter specific functionality.
✗ Dart plugin not installed; this adds Dart specific functionality.
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
[✓] VS Code (version 1.53.0)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.19.0
[✓] Connected device (1 available)
• Taha’s iPhone (mobile) • 00008020-001059882212002E • ios • iOS 14.4
! Doctor found issues in 3 categories. [✓] Flutter (Channel beta, 1.26.0-17.3.pre, on macOS 11.2 20D64 darwin-x64, locale en-GB)
• Flutter version 1.26.0-17.3.pre at /Users/tahatesser/Code/flutter_beta
• Framework revision 4b50ca7f7f (3 days ago), 2021-02-04 19:44:27 -0800
• Engine revision 2c527d6c7e
• Dart version 2.12.0 (build 2.12.0-259.8.beta)
[✗] Android toolchain - develop for Android devices
✗ Unable to locate Android SDK.
Install Android Studio from: https://developer.android.com/studio/index.html
On first launch it will assist you in installing the Android SDK components.
(or visit https://flutter.dev/docs/get-started/install/macos#android-setup for detailed instructions).
If the Android SDK has been installed to a custom location, please use
`flutter config --android-sdk` to update to that location.
[!] Xcode - develop for iOS and macOS
• Xcode at /Volumes/Extreme/Xcode.app/Contents/Developer
• Xcode 12.4, Build version 12D4e
✗ CocoaPods not installed.
CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin usage on the Dart side.
Without CocoaPods, plugins will not work on iOS or macOS.
For more info, see https://flutter.dev/platform-plugins
To install see https://guides.cocoapods.org/using/getting-started.html#installation for instructions.
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 4.1)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
[✓] VS Code (version 1.53.0)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.19.0
[✓] Connected device (2 available)
• Taha’s iPhone (mobile) • 00008020-001059882212002E • ios • iOS 14.4
• Chrome (web) • chrome • web-javascript • Google Chrome 88.0.4324.146
! Doctor found issues in 2 categories. [✓] Flutter (Channel master, 1.26.0-18.0.pre.212, on macOS 11.2 20D64 darwin-x64, locale en-GB)
• Flutter version 1.26.0-18.0.pre.212 at /Users/tahatesser/Code/flutter_master
• Framework revision 02d441ea55 (2 days ago), 2021-02-05 17:17:12 -0800
• Engine revision b04955656c
• Dart version 2.13.0 (build 2.13.0-0.0.dev)
[✗] Android toolchain - develop for Android devices
✗ Unable to locate Android SDK.
Install Android Studio from: https://developer.android.com/studio/index.html
On first launch it will assist you in installing the Android SDK components.
(or visit https://flutter.dev/docs/get-started/install/macos#android-setup for detailed instructions).
If the Android SDK has been installed to a custom location, please use
`flutter config --android-sdk` to update to that location.
[!] Xcode - develop for iOS and macOS
• Xcode at /Volumes/Extreme/Xcode.app/Contents/Developer
• Xcode 12.4, Build version 12D4e
✗ CocoaPods not installed.
CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin usage on the Dart side.
Without CocoaPods, plugins will not work on iOS or macOS.
For more info, see https://flutter.dev/platform-plugins
To install see https://guides.cocoapods.org/using/getting-started.html#installation for instructions.
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 4.1)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
[✓] VS Code (version 1.53.0)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.19.0
[✓] Connected device (3 available)
• Taha’s iPhone (mobile) • 00008020-001059882212002E • ios • iOS 14.4
• macOS (desktop) • macos • darwin-x64 • macOS 11.2 20D64 darwin-x64
• Chrome (web) • chrome • web-javascript • Google Chrome 88.0.4324.146
! Doctor found issues in 2 categories. Can you please upgrade to the latest Thank you |
@TahaTesser |
Hi @BaroneX |
@TahaTesser flutter doctor -v
code sample
|
#75316 (comment) and #75316 (comment) seem to contradict one another. @TahaTesser are you able to reproduce this issue on all versions? I think we have a theory of why this could be happening. When the "authorization dialog" pops up, the application resigns active and we must suspend all rendering. However, to snapshot the scene, we have to switch to the onscreen context which is now disabled by the sync switch (cc @gaaclarke). It might be easier to to reproduce this issue in the presence of the dialog. If we can verify this, we'll probably have to switch to snapshotting on the IO context while in the background or rework the sync switch. |
This sounds like |
@chinmaygarde |
I ran into the same problem, it occurs when multiple engines are used. When engine1 is used and recycled, then engine2 is created, and getImage in engine2, the problem occurs. |
when use EngineGroup , the problem occurs. The Image widget will gone. |
If this works on master, flutter/engine#25196 may have fixed this based on my previous comment. |
Android platform has the same problem,stable branch 2.2.2, only occurs when using Engine Group on first engine be distroyed and the second engine created |
Use of flutterenginegroup causes the Image elements in the screenshot to disappear. In IOS, I solve this problem by pre initializing an engine in appdelegate to host the pages that need screenshots. In Android, there is no problem,You can get the perfect screenshot |
I suspect that on iOS we'll run into issues where an image was created as GPU resident in the foreground, and then snapshotting happens in the background in a CPU context and can't render the GPU resident image into the snapshot... |
I am trying to get screenshot but on Web |
same issue |
3.7.1 solved this problem |
I cannot reproduce this issue on the latest version of flutter using the code sample in #75316 (comment) updated for the latest version of flutter. I tried it over 150 times and in all cases, the images were rendered properly. recordingIMG_4720.MP4Given that the bug no longer reproduces, I'll be closing the issue. If anyone is still facing this issue on the latest versions of flutter, kindly file a new issue and provide detailed steps to reproduce the issue. Thank you code sampleimport 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:qr_flutter/qr_flutter.dart';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: const QrCodeWidget(),
),
);
}
}
class QrCodeWidget extends StatefulWidget {
const QrCodeWidget({Key? key}) : super(key: key);
@override
State<QrCodeWidget> createState() => _QrCodeWidgetState();
}
class _QrCodeWidgetState extends State<QrCodeWidget> {
final ScrollController controller =
ScrollController(initialScrollOffset: -1, keepScrollOffset: true);
late ui.Image image;
GlobalKey repaintWidgetKey = GlobalKey();
String _barCodeString = "123456";
_saveImage() async {
PermissionStatus permission = await Permission.photos.request();
bool isPhotoGranted = (permission.isPermanentlyDenied ||
permission.isRestricted ||
permission.isGranted);
if (!isPhotoGranted) {
return;
}
ByteData? sourceByteData = await _capturePngToByteData();
if (sourceByteData == null) {
return;
}
final result = await ImageGallerySaver.saveImage(
sourceByteData.buffer.asUint8List(),
quality: 60,
name: "hello");
if (result["isSuccess"]) {
print("success");
} else {
print("fail");
}
}
Future<ByteData?> _capturePngToByteData() async {
try {
RenderRepaintBoundary boundary = repaintWidgetKey.currentContext!
.findRenderObject() as RenderRepaintBoundary;
double dpr = ui.window.devicePixelRatio;
ui.Image image = await boundary.toImage(pixelRatio: dpr);
ByteData? _byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
return _byteData;
} catch (e) {
print(e);
}
return null;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.green,
body: Stack(
children: <Widget>[
CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: GestureDetector(
child: Container(
width: 110,
alignment: Alignment.center,
margin: const EdgeInsets.only(top: 20),
height: 35,
decoration: BoxDecoration(
borderRadius:
const BorderRadius.all(Radius.circular(19)),
color: Colors.transparent,
border: Border.all(color: Colors.white, width: 0.5)),
child: const Text(
"save",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
onTap: () {
_saveImage();
},
),
),
],
controller: controller,
),
_renderQrCaptureView(),
],
),
);
}
_renderQrCaptureView() {
const assetImage = AssetImage("res/images/icon_default_portrait.png");
final imageAsset = Image.asset("res/images/icon_default_portrait.png");
double topImageWidth = 75.0;
double qrCodeImageWitdh = 135;
return Positioned(
top: MediaQuery.of(context).size.height,
child: RepaintBoundary(
key: repaintWidgetKey,
child: Container(
width: MediaQuery.of(context).size.width,
height: 667,
decoration: const BoxDecoration(
color: Colors.black,
image: DecorationImage(image: assetImage, fit: BoxFit.fill),
),
child: Stack(
alignment: Alignment.topCenter,
children: [
Positioned(
top: 164,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Container(
color: Colors.white,
width: 335,
height: 365,
child: Column(
children: [
Expanded(
flex: 135,
child: Container(
constraints: const BoxConstraints.expand(),
color: Colors.grey,
child: Column(
children: [
Container(
margin: const EdgeInsets.only(top: 50),
child: const Text(
"Name",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
)),
Container(
margin: const EdgeInsets.only(
top: 20, left: 5, right: 5),
child: const Text(
"desc",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
fontSize: 20),
overflow: TextOverflow.ellipsis,
maxLines: 1,
)),
],
),
)),
Expanded(
flex: 231,
child: Container(
color: Colors.white,
child: Column(
children: [
Container(
margin: const EdgeInsets.only(top: 18),
child: QrImage(
data: _barCodeString,
version: QrVersions.auto,
padding: const EdgeInsets.all(5),
size: qrCodeImageWitdh,
backgroundColor: Colors.white,
errorCorrectionLevel:
QrErrorCorrectLevel.H,
embeddedImage: assetImage,
embeddedImageStyle: QrEmbeddedImageStyle(
size: Size(qrCodeImageWitdh / 5.0,
qrCodeImageWitdh / 5.0),
),
),
),
Container(
margin: const EdgeInsets.only(top: 15),
child: const Text(
"time",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
fontSize: 14),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
Container(
margin: const EdgeInsets.only(top: 5),
child: const Text(
"text",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w400,
fontSize: 13),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
],
),
)),
],
),
),
),
),
Positioned(
top: 127,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular((topImageWidth + 4) / 2.0)),
border: Border.all(color: Colors.white, width: 2),
),
child: SizedBox(
height: topImageWidth,
width: topImageWidth,
child: CircleAvatar(
radius: topImageWidth / 2.0, child: imageAsset),
),
),
),
Positioned(
bottom: 31,
child: Image.asset(
"res/images/icon_default_portrait.png",
height: 21,
fit: BoxFit.contain,
),
),
],
),
),
),
);
}
} flutter doctor -v
|
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, including the output of |
Steps to Reproduce
1.Use RepaintBoundary widget, with an Image as a child.
2.Use RenderRepaintBoundary to generate screenshots
3.Sometimes the image component is not rendered in the screenshot
Expected results:
the image component is rendered in the screenshot
Actual results:
Sometimes the image component is not rendered in the screenshot
The user avatar and background image are not rendered in the screenshot
Devices
iPhone 12(14.3)
Logs
The text was updated successfully, but these errors were encountered: