|
|
@@ -6,159 +6,117 @@ import 'package:flutter/material.dart'; |
|
|
import 'package:flutter/rendering.dart'; |
|
|
|
|
|
void main() { |
|
|
runApp( |
|
|
MaterialApp( |
|
|
theme: ThemeData( |
|
|
primarySwatch: Colors.indigo, |
|
|
accentColor: Colors.pinkAccent, |
|
|
), |
|
|
home: ExampleScreen(), |
|
|
), |
|
|
); |
|
|
runApp(MaterialApp( |
|
|
theme: ThemeData(primarySwatch: Colors.indigo), home: ExampleScreen())); |
|
|
} |
|
|
|
|
|
class ExampleScreen extends StatefulWidget { |
|
|
@override |
|
|
_ExampleScreenState createState() => new _ExampleScreenState(); |
|
|
} |
|
|
|
|
|
class _ExampleScreenState extends State<ExampleScreen> { |
|
|
final _captureKey = GlobalKey<CaptureWidgetState>(); |
|
|
Future<CaptureResult> _image; |
|
|
|
|
|
void _onCapturePressed() { |
|
|
setState(() { |
|
|
_image = _captureKey.currentState.captureImage(); |
|
|
}); |
|
|
} |
|
|
class CaptureResult { |
|
|
final Uint8List data; |
|
|
final int width; |
|
|
final int height; |
|
|
|
|
|
@override |
|
|
Widget build(BuildContext context) { |
|
|
return CaptureWidget( |
|
|
key: _captureKey, |
|
|
capture: Material( |
|
|
child: Padding( |
|
|
padding: const EdgeInsets.all(8.0), |
|
|
child: Column( |
|
|
mainAxisSize: MainAxisSize.min, |
|
|
children: <Widget>[ |
|
|
Text( |
|
|
'These widgets are not visible on the screen yet can still be captured by a RepaintBoundary.', |
|
|
), |
|
|
SizedBox(height: 12.0), |
|
|
Container( |
|
|
width: 25.0, |
|
|
height: 25.0, |
|
|
color: Colors.red, |
|
|
), |
|
|
], |
|
|
), |
|
|
), |
|
|
), |
|
|
child: Scaffold( |
|
|
appBar: AppBar( |
|
|
title: Text('Widget To Image Demo'), |
|
|
), |
|
|
body: FutureBuilder<CaptureResult>( |
|
|
future: _image, |
|
|
builder: (BuildContext context, AsyncSnapshot<CaptureResult> snapshot) { |
|
|
return SingleChildScrollView( |
|
|
child: Column( |
|
|
mainAxisAlignment: MainAxisAlignment.center, |
|
|
children: <Widget>[ |
|
|
Center( |
|
|
child: RaisedButton( |
|
|
child: Text('Capture Image'), |
|
|
onPressed: _onCapturePressed, |
|
|
), |
|
|
), |
|
|
if (snapshot.connectionState == ConnectionState.waiting) |
|
|
Center( |
|
|
child: CircularProgressIndicator(), |
|
|
) |
|
|
else if (snapshot.hasData) ...[ |
|
|
Text( |
|
|
'${snapshot.data.width} x ${snapshot.data.height}', |
|
|
textAlign: TextAlign.center, |
|
|
), |
|
|
Container( |
|
|
margin: const EdgeInsets.all(12.0), |
|
|
decoration: BoxDecoration( |
|
|
border: Border.all(color: Colors.grey.shade300, width: 2.0), |
|
|
), |
|
|
child: Image.memory( |
|
|
snapshot.data.data, |
|
|
scale: MediaQuery.of(context).devicePixelRatio, |
|
|
), |
|
|
), |
|
|
], |
|
|
], |
|
|
), |
|
|
); |
|
|
}, |
|
|
), |
|
|
), |
|
|
); |
|
|
} |
|
|
const CaptureResult(this.data, this.width, this.height); |
|
|
} |
|
|
|
|
|
class CaptureWidget extends StatefulWidget { |
|
|
final Widget child; |
|
|
final Widget capture; |
|
|
|
|
|
const CaptureWidget({ |
|
|
Key key, |
|
|
this.capture, |
|
|
this.child, |
|
|
}) : super(key: key); |
|
|
super.key, |
|
|
required this.capture, |
|
|
required this.child, |
|
|
}); |
|
|
|
|
|
@override |
|
|
CaptureWidgetState createState() => CaptureWidgetState(); |
|
|
State<CaptureWidget> createState() => CaptureWidgetState(); |
|
|
} |
|
|
|
|
|
class CaptureWidgetState extends State<CaptureWidget> { |
|
|
final _boundaryKey = GlobalKey(); |
|
|
|
|
|
Future<CaptureResult> captureImage() async { |
|
|
final pixelRatio = MediaQuery.of(context).devicePixelRatio; |
|
|
final boundary = _boundaryKey.currentContext.findRenderObject() as RenderRepaintBoundary; |
|
|
final image = await boundary.toImage(pixelRatio: pixelRatio); |
|
|
final data = await image.toByteData(format: ui.ImageByteFormat.png); |
|
|
return CaptureResult(data.buffer.asUint8List(), image.width, image.height); |
|
|
} |
|
|
|
|
|
@override |
|
|
Widget build(BuildContext context) { |
|
|
return LayoutBuilder( |
|
|
builder: (BuildContext context, BoxConstraints constraints) { |
|
|
Widget build(BuildContext context) => |
|
|
LayoutBuilder(builder: (context, constraints) { |
|
|
final height = constraints.maxHeight * 2; |
|
|
return Stack( |
|
|
fit: StackFit.passthrough, |
|
|
children: <Widget>[ |
|
|
widget.child, |
|
|
Positioned( |
|
|
left: 0.0, |
|
|
right: 0.0, |
|
|
return Stack(fit: StackFit.passthrough, children: <Widget>[ |
|
|
widget.child, |
|
|
Positioned( |
|
|
left: 0, |
|
|
right: 0, |
|
|
top: height, |
|
|
height: height, |
|
|
child: Center( |
|
|
child: RepaintBoundary( |
|
|
key: _boundaryKey, |
|
|
child: widget.capture, |
|
|
), |
|
|
), |
|
|
), |
|
|
], |
|
|
); |
|
|
}, |
|
|
); |
|
|
child: RepaintBoundary( |
|
|
key: _boundaryKey, child: widget.capture))), |
|
|
]); |
|
|
}); |
|
|
|
|
|
Future<CaptureResult> captureImage() async { |
|
|
final pixelRatio = MediaQuery.of(context).devicePixelRatio; |
|
|
final boundary = _boundaryKey.currentContext?.findRenderObject() |
|
|
as RenderRepaintBoundary?; |
|
|
final image = await boundary?.toImage(pixelRatio: pixelRatio); |
|
|
final data = await image?.toByteData(format: ui.ImageByteFormat.png); |
|
|
return CaptureResult( |
|
|
data!.buffer.asUint8List(), image?.width ?? 0, image?.height ?? 0); |
|
|
} |
|
|
} |
|
|
|
|
|
class CaptureResult { |
|
|
final Uint8List data; |
|
|
final int width; |
|
|
final int height; |
|
|
class ExampleScreen extends StatefulWidget { |
|
|
@override |
|
|
State<ExampleScreen> createState() => _ExampleScreenState(); |
|
|
} |
|
|
|
|
|
const CaptureResult(this.data, this.width, this.height); |
|
|
class _ExampleScreenState extends State<ExampleScreen> { |
|
|
final _captureKey = GlobalKey<CaptureWidgetState>(); |
|
|
Future<CaptureResult>? _image; |
|
|
|
|
|
@override |
|
|
Widget build(BuildContext context) => CaptureWidget( |
|
|
key: _captureKey, |
|
|
capture: Material( |
|
|
child: Padding( |
|
|
padding: const EdgeInsets.all(8), |
|
|
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[ |
|
|
const Text( |
|
|
'These widgets are not visible on the screen yet can still be captured by a RepaintBoundary.', |
|
|
), |
|
|
const SizedBox(height: 12), |
|
|
Container(width: 25, height: 25, color: Colors.red), |
|
|
]))), |
|
|
child: Scaffold( |
|
|
appBar: AppBar(title: const Text('Widget To Image Demo')), |
|
|
body: FutureBuilder<CaptureResult>( |
|
|
future: _image, |
|
|
builder: (context, snapshot) => SingleChildScrollView( |
|
|
child: Column( |
|
|
mainAxisAlignment: MainAxisAlignment.center, |
|
|
children: <Widget>[ |
|
|
Center( |
|
|
child: ElevatedButton( |
|
|
onPressed: _onCapturePressed, |
|
|
child: const Text('Capture Image'))), |
|
|
if (snapshot.connectionState == ConnectionState.waiting) |
|
|
const Center(child: CircularProgressIndicator()) |
|
|
else if (snapshot.hasData) ...[ |
|
|
Text( |
|
|
'${snapshot.data!.width} x ${snapshot.data!.height}', |
|
|
textAlign: TextAlign.center), |
|
|
for (var i = 0; i < 50; i++) |
|
|
Container( |
|
|
margin: const EdgeInsets.all(12), |
|
|
decoration: BoxDecoration( |
|
|
border: Border.all( |
|
|
color: Colors.grey.shade300, width: 2)), |
|
|
child: Image.memory(snapshot.data!.data, |
|
|
scale: MediaQuery.of(context) |
|
|
.devicePixelRatio)), |
|
|
], |
|
|
]))))); |
|
|
|
|
|
void _onCapturePressed() { |
|
|
_image = _captureKey.currentState?.captureImage(); |
|
|
setState(() {}); |
|
|
} |
|
|
} |