我需要构建一个小部件以获取其位图。我不在乎屏幕上有小部件。
所以我的问题是:我可以以某种方式在不使用屏幕视图层次结构的情况下“在侧面”构建小部件吗?
我没有找到一种方法。因此,如果不可能,我可以在屏幕上构建小部件,但实际上不显示它。
我尝试过Visibility
,但这会使RenderObject
为空。使用Offstage
时,在断言上调用toImage()
时将失败Failed assertion: line 2752 pos 12: ‘!debugNeedsPaint’: is not true.
答案 0 :(得分:3)
编辑:看起来这在Flutter的最新版本中中断了。不知道为什么,但是我想Flutter现在会在确定完全不可见叠加层时避免绘制。该方法仍然可以使用,但需要与翻译结合使用才能将其移出屏幕:https://gist.github.com/itsJoKr/ce5ec57bd6dedf74d1737c1f39481913
有人建议使用OverlayEntry
,它似乎是最好的解决方案。
您可以将OverlayEntry
置于当前屏幕下方,使其不可见,并使用maintainState: true
进行构建。
一个很大的优点是,由于它不与当前的窗口小部件树混合使用,因此实现起来更容易。
OverlayState overlayState = Overlay.of(context);
OverlayEntry entry = OverlayEntry(builder: (context) {
return RepaintBoundary(key: key, child: yourWidget,); // Using RepaintBoundary to get RenderObject and convert to image
}, maintainState: true);
overlayState.insert(entry);
// doesn't work anymore
// overlayState.rearrange([entry], above: entry); // Didn't find how to insert it at the bottom of current overlays, so this should rearrange it so that our entry is at the bottom
答案 1 :(得分:0)
这应该可以完成工作。我们创建了一个屏幕大小的区域。但是(屏幕外),因此仍可以将其捕获为树的一部分。
也可以在这里找到:https://gist.github.com/slightfoot/8eeadd8028c373df87f3a47bd4a35e36
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(
MaterialApp(
theme: ThemeData(
primarySwatch: Colors.indigo,
accentColor: Colors.pinkAccent,
),
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();
});
}
@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,
),
),
],
],
),
);
},
),
),
);
}
}
class CaptureWidget extends StatefulWidget {
final Widget child;
final Widget capture;
const CaptureWidget({
Key key,
this.capture,
this.child,
}) : super(key: key);
@override
CaptureWidgetState 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) {
final height = constraints.maxHeight * 2;
return OverflowBox(
alignment: Alignment.topLeft,
minHeight: height,
maxHeight: height,
child: Column(
children: <Widget>[
Expanded(
child: widget.child,
),
Expanded(
child: Center(
child: RepaintBoundary(
key: _boundaryKey,
child: widget.capture,
),
),
),
],
),
);
},
);
}
}
class CaptureResult {
final Uint8List data;
final int width;
final int height;
const CaptureResult(this.data, this.width, this.height);
}
答案 2 :(得分:-1)
在任何地方调用方法captureImage()
之前
if (boundary.debugNeedsPaint) {
print("Waiting for boundary to be painted.");
await Future.delayed(const Duration(milliseconds: 100));
return captureImage();
}