Flutter中截圖的主要類是RepaintBoundary。
廢話不多說,直接上代碼:
import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; class WidgetShot extends StatefulWidget { final Widget child; final ShotController controller; WidgetShot({@required this.child, this.controller}); @override _WidgetShotState createState() => _WidgetShotState(); } class _WidgetShotState extends State<WidgetShot> { GlobalKey<_OverRepaintBoundaryState> globalKey = GlobalKey(); @override void initState() { super.initState(); if (widget.controller != null) { widget.controller.setGlobalKey(globalKey); } } @override Widget build(BuildContext context) { return SingleChildScrollView( scrollDirection: Axis.vertical, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: _OverRepaintBoundary( key: globalKey, child: RepaintBoundary( child: widget.child, ), ), ), ); } } 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; } } class ShotController { GlobalKey<_OverRepaintBoundaryState> globalKey; Future<Uint8List> makeImageUint8List() async { RenderRepaintBoundary boundary = globalKey.currentContext.findRenderObject(); // 這個可以獲取當前設備的像素比 var dpr = ui.window.devicePixelRatio; ui.Image image = await boundary.toImage(pixelRatio: dpr); ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png); Uint8List pngBytes = byteData.buffer.asUint8List(); return pngBytes; } setGlobalKey(GlobalKey<_OverRepaintBoundaryState> globalKey) { this.globalKey = globalKey; } }
測試使用:
import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_app/widget_shot.dart'; void main() => 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: 'widget shot demo'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { // 關鍵是創建Controller ShotController shotController = new ShotController(); int _counter = 0; void _incrementCounter() async { setState(() { _counter++; }); // 將widget生成Uint8List傳遞給Image即可 Uint8List pngBytes = await shotController.makeImageUint8List(); Navigator.push(context, new MaterialPageRoute(builder: (_) { return Scaffold( appBar: AppBar( title: Text('shot widget'), ), body: Image.memory(pngBytes), ); })); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: WidgetShot( controller: shotController, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'You have pushed the button this many times:', ), Offstage( offstage: true, child: Text('offstage demo'), ), Text( '$_counter', style: Theme.of(context).textTheme.display1, ), ], ), ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), // This trailing comma makes auto-formatting nicer for build methods. ); } }