runRenderDriver function

Future<void> runRenderDriver(
  1. void registrar(
    1. RenderHost host
    )
)

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),
    ),
  );
}