我正在使用 RepaintBoundary 来获取当前小部件的屏幕截图,即 listView 。但是它仅捕获当时在屏幕上可见的内容。
RepaintBoundary( key: src, child: ListView(padding: EdgeInsets.only(left: 10.0), scrollDirection: Axis.horizontal, children: <Widget>[ Align( alignment: Alignment(-0.8, -0.2), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: listLabel(orientation), ) ), Padding(padding: EdgeInsets.all(5.0)), Align( alignment: FractionalOffset(0.3, 0.5), child: Container( height: orientation == Orientation.portrait? 430.0: 430.0*0.7, decoration: BoxDecoration( border: Border(left: BorderSide(color: Colors.black)) ), //width: 300.0, child: Wrap( direction: Axis.vertical, //runSpacing: 10.0, children: colWidget(orientation), ) ) ), Padding(padding: EdgeInsets.all(5.0)), Column( mainAxisAlignment: MainAxisAlignment.center, children: listLabel(orientation), ) ], ), );
屏幕截图功能:
Future screenshot() async { RenderRepaintBoundary boundary = src.currentContext.findRenderObject(); ui.Image image = await boundary.toImage(); ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png); Uint8List pngBytes = byteData.buffer.asUint8List(); print(pngBytes); final directory = (await getExternalStorageDirectory()).path; File imgFile =new File('$directory/layout2.pdf'); imgFile.writeAsBytes(pngBytes); }
有什么办法,以便我可以捕获整个listView,即不仅捕获屏幕上不可见的内容,还捕获可滚动内容。或者,如果整个窗口小部件太大而无法容纳图片,则可以将其捕获到多个图像中。
这让我很好奇是否可行,因此我做了一个快速的模型来证明它确实有效。但是请注意,这样做实际上是在故意破坏flutter进行优化的工作,因此您实际上不应在绝对必要的地方使用它。
无论如何,这是代码:
import 'dart:math'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/rendering.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; @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(); 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: Column( children: List.generate( 30, (i) => Container( color: Color.fromRGBO(random.nextInt(256), random.nextInt(256), random.nextInt(256), 1.0), height: 100, ), ), ), ), ), ); } } 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; } }
它正在做的是一个滚动视图,其中封装了列表(列),并确保repaintBoundary在列周围。对于使用列表的代码,它根本无法捕获所有子项,因为列表本质上是repaintBoundary本身。
特别注意’overRepaintKey’和OverRepaintBoundary。通过遍历渲染子代,您可能可以不使用它而逃脱,但这使它变得容易得多。