buildContractScene function
Builds the neutral contract scene for a LibraryDriver: the
AppBar + element A (FAB, increments SceneSpec.aTaps) + the ListView of
12 rows (element B at kSceneBRow) + the scroll margin, all under a
MaterialApp. The consumer's driver supplies only the library-specific
parts:
wrapRow: called once per row with the neutral row; the driver wraps the rows its solution anchors (A/B are always rows kSceneBRow and the driver's own second target). Called for every row so a driver can also replace row content if its solution needs it.wrapHome: wraps the wholeMaterialApp.home— for solutions that need a live context / host above the scene (e.g. tcm's context capture). Receives the neutralScaffold.navigatorObservers/appBuilder: app-level wiring some solutions need (dialog/toast hosts mount their overlay through MaterialApp'snavigatorObservers/builder). Passed through verbatim; the base scene (S1) must leave them null so it does not touch the solution.
withLibrary selects the with-library vs the base scene; the base scene
(S1) must not touch the solution at all, so the driver must return the
neutral row/home unchanged when withLibrary is false.
Implementation
Widget buildContractScene(
SceneSpec spec, {
required bool withLibrary,
required Widget Function(int index, Widget row) wrapRow,
Widget Function(Widget home)? wrapHome,
ThemeData? theme,
List<NavigatorObserver>? navigatorObservers,
Widget Function(BuildContext, Widget?)? appBuilder,
}) {
Widget neutralRow(int index) => SizedBox(
key: Key(sceneRowKey(index)),
height: kSceneRowHeight,
child: Align(
alignment: Alignment.centerLeft,
child: Text('Row $index'),
),
);
final list = ListView(
key: const Key(kSceneListKey),
controller: spec.listScroll,
children: [
for (var i = 0; i < kSceneRowCount; i++) wrapRow(i, neutralRow(i)),
const SizedBox(height: kSceneScrollMargin),
],
);
final scaffold = Scaffold(
appBar: AppBar(title: const Text('Contract scene')),
floatingActionButton: FloatingActionButton(
key: const Key(kSceneAKey),
onPressed: () => spec.aTaps.value++, // S6: tap on A after hide
child: const Icon(Icons.add),
),
body: list,
);
return MaterialApp(
theme: theme ??
ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
useMaterial3: true,
),
navigatorObservers: navigatorObservers ?? const [],
builder: appBuilder,
home: wrapHome != null ? wrapHome(scaffold) : scaffold,
);
}