The scene surface and its status screens
Open the live demo · Read the source · View on GitHub
Every viewport in this application, including the one this page would use if
it did not override its own body, is built from the same two pieces:
SceneSurface renders a scene into a widget, and DidNotStart is what shows
instead when opening a renderer failed.
Step 1: A scene to draw #
@override
Scene build(DemoContext context) {
final material = f3d.Material(
name: 'ball',
baseColor: Vector4(0.6, 0.7, 0.9, 1.0),
);
final ball = MeshNode(
DeviceMesh.upload(context.device, SphereShape(segments: 24).build()),
material,
);
_scene = Scene()
..add(ball)
..add(
LightNode(name: 'sun', intensity: 3.0)
..setLocalForward(Vector3(-0.4, -1.0, -0.3)),
);
return _scene;
}
Step 2: Wrap it in a surface #
SceneSurface calls onBeforeFrame, then settings, then renders, and
hands the result to presentFrame — the one function every backend can
answer, since a backend whose frame is composited elsewhere has no image of
its own to paint.
@override
Widget? customBody(BuildContext buildContext, DemoContext context) =>
SceneSurface(
renderer: context.renderer,
scene: _scene,
view: context.view,
settings: () => const RenderSettings(),
onBeforeFrame: () {},
presentFrame: presentFrame,
);
Step 3: What failure looks like #
Five applications each wrote their own screen for "the renderer never
opened"; DidNotStart is the one that is left, with room for an
application to add what the bare error does not say.
// What an application shows instead, when the renderer never opened.
final failure = DidNotStart(
StateError('no shader bundle for this build'),
explaining: 'run tool/build_shaders.sh first',
);
The explanation an application supplies is shown alongside the error, verbatim.