runRenderDriver function
Runs the render guest: loads the bundle's fonts, runs registrar, and
serves render requests over stdio until stdin closes.
The bundle's generated main is one line — runRenderDriver(registerRenders)
— and RenderPool is the other end of the wire. stdout is the protocol:
the app's own prints are redirected to stderr, and protocol lines carry
the marker so an unredirectable stray line cannot corrupt a reply.
Implementation
Future<void> runRenderDriver(void Function(RenderHost host) registrar) async {
WidgetsFlutterBinding.ensureInitialized();
var bundleDir =
Platform.environment['FW_RENDER_BUNDLE'] ?? Directory.current.path;
var fonts = await _loadFonts(bundleDir);
var bindings = RenderBindings();
registrar(bindings);
void reply(Map<String, Object?> message) {
// The leading newline is insurance: app code that wrote to stdout
// without a trailing newline would otherwise glue this reply onto its
// line, and a marker that is not at line start is just a log to the
// pool. The empty line it usually produces is dropped on the other end.
stdout.write('\n$renderProtocolMarker${jsonEncode(message)}\n');
}
List<Map<String, Object?>> points() => [
for (var bound in bindings.entries)
RenderPointInfo(
name: bound.point.name,
kind: switch (bound) {
BoundWidgetRender() => RenderPointKind.widget,
BoundDocumentRender() => RenderPointKind.document,
},
).toJson(),
];
reply({
'event': 'ready',
'protocol': renderProtocolVersion,
'points': points(),
});
await runZoned(
() async {
await for (var line
in stdin.transform(utf8.decoder).transform(const LineSplitter())) {
Map<String, Object?> request;
try {
request = (jsonDecode(line) as Map).cast<String, Object?>();
} catch (_) {
continue;
}
var id = request['id'];
switch (request['method']) {
case 'exit':
reply({'id': id, 'result': <String, Object?>{}});
exit(0);
case 'list':
reply({
'id': id,
'result': {'points': points()},
});
case 'render':
try {
reply({
'id': id,
'result': await _render(bindings, fonts, request),
});
} catch (error, stack) {
reply({
'id': id,
'error': {'message': '$error', 'stack': '$stack'},
});
}
default:
reply({
'id': id,
'error': {'message': 'unknown method "${request['method']}"'},
});
}
}
// stdin closed: the pool is gone.
exit(0);
},
zoneSpecification: ZoneSpecification(
print: (_, _, _, line) => stderr.writeln(line),
),
);
}