diff --git a/examples/travel_app/lib/src/catalog/checkbox_filter_chips_input.dart b/examples/travel_app/lib/src/catalog/checkbox_filter_chips_input.dart index be557b21b..0c50b5dcd 100644 --- a/examples/travel_app/lib/src/catalog/checkbox_filter_chips_input.dart +++ b/examples/travel_app/lib/src/catalog/checkbox_filter_chips_input.dart @@ -101,62 +101,55 @@ final checkboxFilterChipsInput = CatalogItem( ] ''', ], - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final checkboxFilterChipsData = _CheckboxFilterChipsInputData.fromMap( - data as Map, + widgetBuilder: (context) { + final checkboxFilterChipsData = _CheckboxFilterChipsInputData.fromMap( + context.data as Map, + ); + IconData? icon; + if (checkboxFilterChipsData.iconName != null) { + try { + icon = iconFor( + TravelIcon.values.byName(checkboxFilterChipsData.iconName!), ); - IconData? icon; - if (checkboxFilterChipsData.iconName != null) { - try { - icon = iconFor( - TravelIcon.values.byName(checkboxFilterChipsData.iconName!), - ); - } catch (e) { - developer.log( - 'Invalid icon name: ${checkboxFilterChipsData.iconName}', - name: 'CheckboxFilterChipsInput', - error: e, - ); - icon = null; - } - } + } catch (e) { + developer.log( + 'Invalid icon name: ${checkboxFilterChipsData.iconName}', + name: 'CheckboxFilterChipsInput', + error: e, + ); + icon = null; + } + } - final selectedOptionsRef = checkboxFilterChipsData.selectedOptions; - final notifier = dataContext.subscribeToObjectArray(selectedOptionsRef); + final selectedOptionsRef = checkboxFilterChipsData.selectedOptions; + final notifier = context.dataContext.subscribeToObjectArray( + selectedOptionsRef, + ); - return ValueListenableBuilder?>( - valueListenable: notifier, - builder: (context, currentSelectedValues, child) { - final selectedOptionsSet = (currentSelectedValues ?? []) - .cast() - .toSet(); - return _CheckboxFilterChip( - chipLabel: checkboxFilterChipsData.chipLabel, - options: checkboxFilterChipsData.options, - icon: icon, - selectedOptions: selectedOptionsSet, - onChanged: (newSelectedOptions) { - final path = selectedOptionsRef['path'] as String?; - if (path != null) { - dataContext.update( - DataPath(path), - newSelectedOptions.toList(), - ); - } - }, - ); + return ValueListenableBuilder?>( + valueListenable: notifier, + builder: (buildContext, currentSelectedValues, child) { + final selectedOptionsSet = (currentSelectedValues ?? []) + .cast() + .toSet(); + return _CheckboxFilterChip( + chipLabel: checkboxFilterChipsData.chipLabel, + options: checkboxFilterChipsData.options, + icon: icon, + selectedOptions: selectedOptionsSet, + onChanged: (newSelectedOptions) { + final path = selectedOptionsRef['path'] as String?; + if (path != null) { + context.dataContext.update( + DataPath(path), + newSelectedOptions.toList(), + ); + } }, ); }, + ); + }, ); class _CheckboxFilterChip extends StatelessWidget { diff --git a/examples/travel_app/lib/src/catalog/date_input_chip.dart b/examples/travel_app/lib/src/catalog/date_input_chip.dart index 51b7bbda4..6f2e4d150 100644 --- a/examples/travel_app/lib/src/catalog/date_input_chip.dart +++ b/examples/travel_app/lib/src/catalog/date_input_chip.dart @@ -128,33 +128,26 @@ final dateInputChip = CatalogItem( ] ''', ], - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final datePickerData = _DatePickerData.fromMap(data as JsonMap); - final notifier = dataContext.subscribeToString(datePickerData.value); - final path = datePickerData.value?['path'] as String?; + widgetBuilder: (context) { + final datePickerData = _DatePickerData.fromMap(context.data as JsonMap); + final notifier = context.dataContext.subscribeToString( + datePickerData.value, + ); + final path = datePickerData.value?['path'] as String?; - return ValueListenableBuilder( - valueListenable: notifier, - builder: (context, currentValue, child) { - return _DateInputChip( - initialValue: currentValue, - label: datePickerData.label, - onChanged: (newValue) { - if (path != null) { - dataContext.update(DataPath(path), newValue); - } - }, - ); + return ValueListenableBuilder( + valueListenable: notifier, + builder: (buildContext, currentValue, child) { + return _DateInputChip( + initialValue: currentValue, + label: datePickerData.label, + onChanged: (newValue) { + if (path != null) { + context.dataContext.update(DataPath(path), newValue); + } }, ); }, + ); + }, ); diff --git a/examples/travel_app/lib/src/catalog/information_card.dart b/examples/travel_app/lib/src/catalog/information_card.dart index ac4486b1e..6372f7ba9 100644 --- a/examples/travel_app/lib/src/catalog/information_card.dart +++ b/examples/travel_app/lib/src/catalog/information_card.dart @@ -82,36 +82,27 @@ final informationCard = CatalogItem( ] ''', ], - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final cardData = _InformationCardData.fromMap( - data as Map, - ); - final imageChild = cardData.imageChildId != null - ? buildChild(cardData.imageChildId!) - : null; + widgetBuilder: (context) { + final cardData = _InformationCardData.fromMap( + context.data as Map, + ); + final imageChild = cardData.imageChildId != null + ? context.buildChild(cardData.imageChildId!) + : null; - final titleNotifier = dataContext.subscribeToString(cardData.title); - final subtitleNotifier = dataContext.subscribeToString( - cardData.subtitle, - ); - final bodyNotifier = dataContext.subscribeToString(cardData.body); + final titleNotifier = context.dataContext.subscribeToString(cardData.title); + final subtitleNotifier = context.dataContext.subscribeToString( + cardData.subtitle, + ); + final bodyNotifier = context.dataContext.subscribeToString(cardData.body); - return _InformationCard( - imageChild: imageChild, - titleNotifier: titleNotifier, - subtitleNotifier: subtitleNotifier, - bodyNotifier: bodyNotifier, - ); - }, + return _InformationCard( + imageChild: imageChild, + titleNotifier: titleNotifier, + subtitleNotifier: subtitleNotifier, + bodyNotifier: bodyNotifier, + ); + }, ); class _InformationCard extends StatelessWidget { diff --git a/examples/travel_app/lib/src/catalog/input_group.dart b/examples/travel_app/lib/src/catalog/input_group.dart index 213cf9699..2a4e17b4a 100644 --- a/examples/travel_app/lib/src/catalog/input_group.dart +++ b/examples/travel_app/lib/src/catalog/input_group.dart @@ -118,71 +118,64 @@ final inputGroup = CatalogItem( ], name: 'InputGroup', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final inputGroupData = _InputGroupData.fromMap( - data as Map, - ); + widgetBuilder: (context) { + final inputGroupData = _InputGroupData.fromMap( + context.data as Map, + ); - final notifier = dataContext.subscribeToString( - inputGroupData.submitLabel, - ); + final notifier = context.dataContext.subscribeToString( + inputGroupData.submitLabel, + ); - final children = inputGroupData.children; - final actionData = inputGroupData.action; - final name = actionData['name'] as String; - final contextDefinition = - (actionData['context'] as List?) ?? []; + final children = inputGroupData.children; + final actionData = inputGroupData.action; + final name = actionData['name'] as String; + final contextDefinition = + (actionData['context'] as List?) ?? []; - return Card( - color: Theme.of(context).colorScheme.primaryContainer, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Wrap( - runSpacing: 16.0, - spacing: 8.0, - children: children.map(buildChild).toList(), - ), - const SizedBox(height: 16.0), - ValueListenableBuilder( - valueListenable: notifier, - builder: (context, submitLabel, child) { - return ElevatedButton( - onPressed: () { - final resolvedContext = resolveContext( - dataContext, - contextDefinition, - ); - dispatchEvent( - UserActionEvent( - name: name, - sourceComponentId: id, - context: resolvedContext, - ), - ); - }, - child: Text(submitLabel ?? ''), - style: ElevatedButton.styleFrom( - backgroundColor: Theme.of(context).colorScheme.primary, - foregroundColor: Colors.white, + return Card( + color: Theme.of(context.buildContext).colorScheme.primaryContainer, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + runSpacing: 16.0, + spacing: 8.0, + children: children.map(context.buildChild).toList(), + ), + const SizedBox(height: 16.0), + ValueListenableBuilder( + valueListenable: notifier, + builder: (builderContext, submitLabel, child) { + return ElevatedButton( + onPressed: () { + final resolvedContext = resolveContext( + context.dataContext, + contextDefinition, + ); + context.dispatchEvent( + UserActionEvent( + name: name, + sourceComponentId: context.id, + context: resolvedContext, ), ); }, - ), - ], + child: Text(submitLabel ?? ''), + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of( + builderContext, + ).colorScheme.primary, + foregroundColor: Colors.white, + ), + ); + }, ), - ), - ); - }, + ], + ), + ), + ); + }, ); diff --git a/examples/travel_app/lib/src/catalog/itinerary.dart b/examples/travel_app/lib/src/catalog/itinerary.dart index 6cac200df..3e529fc22 100644 --- a/examples/travel_app/lib/src/catalog/itinerary.dart +++ b/examples/travel_app/lib/src/catalog/itinerary.dart @@ -219,39 +219,30 @@ final itinerary = CatalogItem( ] ''', ], - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final itineraryData = _ItineraryData.fromMap( - data as Map, - ); + widgetBuilder: (context) { + final itineraryData = _ItineraryData.fromMap( + context.data as Map, + ); - final titleNotifier = dataContext.subscribeToString( - itineraryData.title, - ); - final subheadingNotifier = dataContext.subscribeToString( - itineraryData.subheading, - ); - final imageChild = buildChild(itineraryData.imageChildId); + final titleNotifier = context.dataContext.subscribeToString( + itineraryData.title, + ); + final subheadingNotifier = context.dataContext.subscribeToString( + itineraryData.subheading, + ); + final imageChild = context.buildChild(itineraryData.imageChildId); - return _Itinerary( - titleNotifier: titleNotifier, - subheadingNotifier: subheadingNotifier, - imageChild: imageChild, - days: itineraryData.days, - widgetId: id, - buildChild: buildChild, - dispatchEvent: dispatchEvent, - dataContext: dataContext, - ); - }, + return _Itinerary( + titleNotifier: titleNotifier, + subheadingNotifier: subheadingNotifier, + imageChild: imageChild, + days: itineraryData.days, + widgetId: context.id, + buildChild: context.buildChild, + dispatchEvent: context.dispatchEvent, + dataContext: context.dataContext, + ); + }, ); class _Itinerary extends StatelessWidget { diff --git a/examples/travel_app/lib/src/catalog/listings_booker.dart b/examples/travel_app/lib/src/catalog/listings_booker.dart index f82034c93..0159bf1a9 100644 --- a/examples/travel_app/lib/src/catalog/listings_booker.dart +++ b/examples/travel_app/lib/src/catalog/listings_booker.dart @@ -52,38 +52,29 @@ extension type _ListingsBookerData.fromMap(Map _json) { final listingsBooker = CatalogItem( name: 'ListingsBooker', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final listingsBookerData = _ListingsBookerData.fromMap( - data as Map, - ); + widgetBuilder: (context) { + final listingsBookerData = _ListingsBookerData.fromMap( + context.data as Map, + ); - final itineraryNameNotifier = dataContext.subscribeToString( - listingsBookerData.itineraryName, - ); + final itineraryNameNotifier = context.dataContext.subscribeToString( + listingsBookerData.itineraryName, + ); - return ValueListenableBuilder( - valueListenable: itineraryNameNotifier, - builder: (context, itineraryName, _) { - return _ListingsBooker( - listingSelectionIds: listingsBookerData.listingSelectionIds, - itineraryName: itineraryName ?? '', - dispatchEvent: dispatchEvent, - widgetId: id, - modifyAction: listingsBookerData.modifyAction, - dataContext: dataContext, - ); - }, + return ValueListenableBuilder( + valueListenable: itineraryNameNotifier, + builder: (builderContext, itineraryName, _) { + return _ListingsBooker( + listingSelectionIds: listingsBookerData.listingSelectionIds, + itineraryName: itineraryName ?? '', + dispatchEvent: context.dispatchEvent, + widgetId: context.id, + modifyAction: listingsBookerData.modifyAction, + dataContext: context.dataContext, ); }, + ); + }, exampleData: [ () { final start1 = DateTime.now().add(const Duration(days: 5)); diff --git a/examples/travel_app/lib/src/catalog/options_filter_chip_input.dart b/examples/travel_app/lib/src/catalog/options_filter_chip_input.dart index b500f2054..5c6c846de 100644 --- a/examples/travel_app/lib/src/catalog/options_filter_chip_input.dart +++ b/examples/travel_app/lib/src/catalog/options_filter_chip_input.dart @@ -93,51 +93,42 @@ final optionsFilterChipInput = CatalogItem( ] ''', ], - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final optionsFilterChipData = _OptionsFilterChipInputData.fromMap( - data as Map, + widgetBuilder: (context) { + final optionsFilterChipData = _OptionsFilterChipInputData.fromMap( + context.data as Map, + ); + IconData? icon; + if (optionsFilterChipData.iconName != null) { + try { + icon = iconFor( + TravelIcon.values.byName(optionsFilterChipData.iconName!), ); - IconData? icon; - if (optionsFilterChipData.iconName != null) { - try { - icon = iconFor( - TravelIcon.values.byName(optionsFilterChipData.iconName!), - ); - } catch (e) { - icon = null; - } - } + } catch (e) { + icon = null; + } + } - final valueRef = optionsFilterChipData.value; - final path = valueRef?['path'] as String?; - final notifier = dataContext.subscribeToString(valueRef); - - return ValueListenableBuilder( - valueListenable: notifier, - builder: (context, currentValue, child) { - return _OptionsFilterChip( - chipLabel: optionsFilterChipData.chipLabel, - options: optionsFilterChipData.options, - icon: icon, - value: currentValue, - onChanged: (newValue) { - if (path != null && newValue != null) { - dataContext.update(DataPath(path), newValue); - } - }, - ); + final valueRef = optionsFilterChipData.value; + final path = valueRef?['path'] as String?; + final notifier = context.dataContext.subscribeToString(valueRef); + + return ValueListenableBuilder( + valueListenable: notifier, + builder: (builderContext, currentValue, child) { + return _OptionsFilterChip( + chipLabel: optionsFilterChipData.chipLabel, + options: optionsFilterChipData.options, + icon: icon, + value: currentValue, + onChanged: (newValue) { + if (path != null && newValue != null) { + context.dataContext.update(DataPath(path), newValue); + } }, ); }, + ); + }, ); class _OptionsFilterChip extends StatefulWidget { diff --git a/examples/travel_app/lib/src/catalog/tabbed_sections.dart b/examples/travel_app/lib/src/catalog/tabbed_sections.dart index 26f7bcf13..16455ca50 100644 --- a/examples/travel_app/lib/src/catalog/tabbed_sections.dart +++ b/examples/travel_app/lib/src/catalog/tabbed_sections.dart @@ -102,29 +102,22 @@ final tabbedSections = CatalogItem( ] ''', ], - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final tabbedSectionsData = _TabbedSectionsData.fromMap( - data as Map, - ); - final sections = tabbedSectionsData.sections.map((section) { - final titleNotifier = dataContext.subscribeToString(section.title); - return _TabSectionData( - titleNotifier: titleNotifier, - childId: section.childId, - ); - }).toList(); - - return _TabbedSections(sections: sections, buildChild: buildChild); - }, + widgetBuilder: (context) { + final tabbedSectionsData = _TabbedSectionsData.fromMap( + context.data as Map, + ); + final sections = tabbedSectionsData.sections.map((section) { + final titleNotifier = context.dataContext.subscribeToString( + section.title, + ); + return _TabSectionData( + titleNotifier: titleNotifier, + childId: section.childId, + ); + }).toList(); + + return _TabbedSections(sections: sections, buildChild: context.buildChild); + }, ); class _TabSectionData { diff --git a/examples/travel_app/lib/src/catalog/text_input_chip.dart b/examples/travel_app/lib/src/catalog/text_input_chip.dart index d4cb9f92b..1b3785e92 100644 --- a/examples/travel_app/lib/src/catalog/text_input_chip.dart +++ b/examples/travel_app/lib/src/catalog/text_input_chip.dart @@ -71,40 +71,31 @@ final textInputChip = CatalogItem( ] ''', ], - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final textInputChipData = _TextInputChipData.fromMap( - data as Map, - ); + widgetBuilder: (context) { + final textInputChipData = _TextInputChipData.fromMap( + context.data as Map, + ); - final valueRef = textInputChipData.value; - final path = valueRef?['path'] as String?; - final notifier = dataContext.subscribeToString(valueRef); + final valueRef = textInputChipData.value; + final path = valueRef?['path'] as String?; + final notifier = context.dataContext.subscribeToString(valueRef); - return ValueListenableBuilder( - valueListenable: notifier, - builder: (context, currentValue, child) { - return _TextInputChip( - label: textInputChipData.label, - value: currentValue, - obscured: textInputChipData.obscured, - onChanged: (newValue) { - if (path != null) { - dataContext.update(DataPath(path), newValue); - } - }, - ); + return ValueListenableBuilder( + valueListenable: notifier, + builder: (builderContext, currentValue, child) { + return _TextInputChip( + label: textInputChipData.label, + value: currentValue, + obscured: textInputChipData.obscured, + onChanged: (newValue) { + if (path != null) { + context.dataContext.update(DataPath(path), newValue); + } }, ); }, + ); + }, ); class _TextInputChip extends StatefulWidget { diff --git a/examples/travel_app/lib/src/catalog/trailhead.dart b/examples/travel_app/lib/src/catalog/trailhead.dart index b2e117f56..ac4f62665 100644 --- a/examples/travel_app/lib/src/catalog/trailhead.dart +++ b/examples/travel_app/lib/src/catalog/trailhead.dart @@ -70,27 +70,18 @@ final trailhead = CatalogItem( ] ''', ], - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final trailheadData = _TrailheadData.fromMap( - data as Map, - ); - return _Trailhead( - topics: trailheadData.topics, - action: trailheadData.action, - widgetId: id, - dispatchEvent: dispatchEvent, - dataContext: dataContext, - ); - }, + widgetBuilder: (context) { + final trailheadData = _TrailheadData.fromMap( + context.data as Map, + ); + return _Trailhead( + topics: trailheadData.topics, + action: trailheadData.action, + widgetId: context.id, + dispatchEvent: context.dispatchEvent, + dataContext: context.dataContext, + ); + }, ); class _Trailhead extends StatelessWidget { diff --git a/examples/travel_app/lib/src/catalog/travel_carousel.dart b/examples/travel_app/lib/src/catalog/travel_carousel.dart index 0aebe5e10..98719a2f0 100644 --- a/examples/travel_app/lib/src/catalog/travel_carousel.dart +++ b/examples/travel_app/lib/src/catalog/travel_carousel.dart @@ -65,48 +65,41 @@ final _schema = S.object( final travelCarousel = CatalogItem( name: 'TravelCarousel', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final carouselData = _TravelCarouselData.fromMap( - (data as Map).cast(), - ); + widgetBuilder: (context) { + final carouselData = _TravelCarouselData.fromMap( + context.data as Map, + ); - final titleNotifier = dataContext.subscribeToString(carouselData.title); + final titleNotifier = context.dataContext.subscribeToString( + carouselData.title, + ); - final items = carouselData.items.map((item) { - final descriptionNotifier = dataContext.subscribeToString( - item.description, - ); + final items = carouselData.items.map((item) { + final descriptionNotifier = context.dataContext.subscribeToString( + item.description, + ); - return _TravelCarouselItemData( - descriptionNotifier: descriptionNotifier, - imageChild: buildChild(item.imageChildId), - listingSelectionId: item.listingSelectionId, - action: item.action, - ); - }).toList(); + return _TravelCarouselItemData( + descriptionNotifier: descriptionNotifier, + imageChild: context.buildChild(item.imageChildId), + listingSelectionId: item.listingSelectionId, + action: item.action, + ); + }).toList(); - return ValueListenableBuilder( - valueListenable: titleNotifier, - builder: (context, title, _) { - return _TravelCarousel( - title: title, - items: items, - widgetId: id, - dispatchEvent: dispatchEvent, - dataContext: dataContext, - ); - }, + return ValueListenableBuilder( + valueListenable: titleNotifier, + builder: (builderContext, title, _) { + return _TravelCarousel( + title: title, + items: items, + widgetId: context.id, + dispatchEvent: context.dispatchEvent, + dataContext: context.dataContext, ); }, + ); + }, exampleData: [_inspirationExample, _hotelExample], ); diff --git a/examples/travel_app/test/checkbox_filter_chips_input_test.dart b/examples/travel_app/test/checkbox_filter_chips_input_test.dart index 8d5f48117..cfd0f3ae9 100644 --- a/examples/travel_app/test/checkbox_filter_chips_input_test.dart +++ b/examples/travel_app/test/checkbox_filter_chips_input_test.dart @@ -18,20 +18,23 @@ void main() { builder: (context) { return Center( child: checkboxFilterChipsInput.widgetBuilder( - data: { - 'chipLabel': 'Amenities', - 'options': ['Wifi', 'Pool', 'Gym'], - 'selectedOptions': { - 'literalArray': ['Wifi', 'Gym'], + CatalogItemContext( + data: { + 'chipLabel': 'Amenities', + 'options': ['Wifi', 'Pool', 'Gym'], + 'selectedOptions': { + 'literalArray': ['Wifi', 'Gym'], + }, + 'iconName': 'hotel', }, - 'iconName': 'hotel', - }, - id: 'test', - buildChild: (_, [_]) => const SizedBox(), - dispatchEvent: (_) {}, - context: context, - dataContext: DataContext(DataModel(), '/'), - getComponent: (String componentId) => null, + id: 'test', + buildChild: (_, [_]) => const SizedBox(), + dispatchEvent: (_) {}, + buildContext: context, + dataContext: DataContext(DataModel(), '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ), ); }, diff --git a/examples/travel_app/test/date_input_chip_test.dart b/examples/travel_app/test/date_input_chip_test.dart index 618398472..3dbb7e4d6 100644 --- a/examples/travel_app/test/date_input_chip_test.dart +++ b/examples/travel_app/test/date_input_chip_test.dart @@ -19,16 +19,19 @@ void main() { body: Builder( builder: (context) { return dateInputChip.widgetBuilder( - data: { - 'value': {'literalString': '2025-09-20'}, - 'label': 'Test Date', - }, - id: 'test_chip', - buildChild: (data, [_]) => const SizedBox(), - dispatchEvent: (event) {}, - context: context, - dataContext: DataContext(dataModel, '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: { + 'value': {'literalString': '2025-09-20'}, + 'label': 'Test Date', + }, + id: 'test_chip', + buildChild: (data, [_]) => const SizedBox(), + dispatchEvent: (event) {}, + buildContext: context, + dataContext: DataContext(dataModel, '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); }, ), @@ -51,16 +54,19 @@ void main() { body: Builder( builder: (context) { return dateInputChip.widgetBuilder( - data: { - 'value': {'path': '/testDate'}, - 'label': 'Test Date', - }, - id: 'test_chip', - buildChild: (data, [_]) => const SizedBox(), - dispatchEvent: (event) {}, - context: context, - dataContext: DataContext(dataModel, '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: { + 'value': {'path': '/testDate'}, + 'label': 'Test Date', + }, + id: 'test_chip', + buildChild: (data, [_]) => const SizedBox(), + dispatchEvent: (event) {}, + buildContext: context, + dataContext: DataContext(dataModel, '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); }, ), @@ -88,16 +94,19 @@ void main() { body: Builder( builder: (context) { return dateInputChip.widgetBuilder( - data: { - 'value': {'path': '/testDate'}, - 'label': 'Test Date', - }, - id: 'test_chip', - buildChild: (data, [_]) => const SizedBox(), - dispatchEvent: (event) {}, - context: context, - dataContext: DataContext(dataModel, '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: { + 'value': {'path': '/testDate'}, + 'label': 'Test Date', + }, + id: 'test_chip', + buildChild: (data, [_]) => const SizedBox(), + dispatchEvent: (event) {}, + buildContext: context, + dataContext: DataContext(dataModel, '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); }, ), @@ -126,16 +135,19 @@ void main() { body: Builder( builder: (context) { return dateInputChip.widgetBuilder( - data: { - 'value': {'path': '/testDate'}, - 'label': 'Test Date', - }, - id: 'test_chip', - buildChild: (data, [_]) => const SizedBox(), - dispatchEvent: (event) {}, - context: context, - dataContext: DataContext(dataModel, '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: { + 'value': {'path': '/testDate'}, + 'label': 'Test Date', + }, + id: 'test_chip', + buildChild: (data, [_]) => const SizedBox(), + dispatchEvent: (event) {}, + buildContext: context, + dataContext: DataContext(dataModel, '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); }, ), diff --git a/examples/travel_app/test/input_group_test.dart b/examples/travel_app/test/input_group_test.dart index 63e57b729..0baac8893 100644 --- a/examples/travel_app/test/input_group_test.dart +++ b/examples/travel_app/test/input_group_test.dart @@ -27,15 +27,18 @@ void main() { body: Builder( builder: (context) { return inputGroup.widgetBuilder( - data: data, - id: 'testId', - buildChild: buildChild, - dispatchEvent: (event) { - dispatchedEvent = event; - }, - context: context, - dataContext: DataContext(DataModel(), '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: data, + id: 'testId', + buildChild: buildChild, + dispatchEvent: (event) { + dispatchedEvent = event; + }, + buildContext: context, + dataContext: DataContext(DataModel(), '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); }, ), @@ -73,13 +76,16 @@ void main() { body: Builder( builder: (context) { return inputGroup.widgetBuilder( - data: data, - id: 'testId', - buildChild: (_, [_]) => const SizedBox.shrink(), - dispatchEvent: (UiEvent _) {}, - context: context, - dataContext: DataContext(DataModel(), '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: data, + id: 'testId', + buildChild: (_, [_]) => const SizedBox.shrink(), + dispatchEvent: (UiEvent _) {}, + buildContext: context, + dataContext: DataContext(DataModel(), '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); }, ), diff --git a/examples/travel_app/test/itinerary_test.dart b/examples/travel_app/test/itinerary_test.dart index 157c44610..66bfcbf79 100644 --- a/examples/travel_app/test/itinerary_test.dart +++ b/examples/travel_app/test/itinerary_test.dart @@ -47,13 +47,16 @@ void main() { }; final itineraryWidget = itinerary.widgetBuilder( - data: testData, - id: 'itinerary1', - buildChild: (data, [_]) => SizedBox(key: Key(data)), - dispatchEvent: mockDispatchEvent, - context: tester.element(find.byType(Container)), - dataContext: DataContext(DataModel(), '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: testData, + id: 'itinerary1', + buildChild: (data, [_]) => SizedBox(key: Key(data)), + dispatchEvent: mockDispatchEvent, + buildContext: tester.element(find.byType(Container)), + dataContext: DataContext(DataModel(), '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); // 2. Pump the widget diff --git a/examples/travel_app/test/options_filter_chip_input_test.dart b/examples/travel_app/test/options_filter_chip_input_test.dart index 650a9a106..a9b3b7175 100644 --- a/examples/travel_app/test/options_filter_chip_input_test.dart +++ b/examples/travel_app/test/options_filter_chip_input_test.dart @@ -26,13 +26,16 @@ void main() { body: Builder( builder: (context) { return optionsFilterChipInput.widgetBuilder( - data: data, - id: 'testId', - buildChild: (_, [_]) => const SizedBox.shrink(), - dispatchEvent: (event) {}, - context: context, - dataContext: DataContext(dataModel, '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: data, + id: 'testId', + buildChild: (_, [_]) => const SizedBox.shrink(), + dispatchEvent: (event) {}, + buildContext: context, + dataContext: DataContext(dataModel, '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); }, ), @@ -85,13 +88,16 @@ void main() { body: Builder( builder: (context) { return optionsFilterChipInput.widgetBuilder( - data: data, - id: 'testId', - buildChild: (_, [_]) => const SizedBox.shrink(), - dispatchEvent: (event) {}, - context: context, - dataContext: DataContext(dataModel, '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: data, + id: 'testId', + buildChild: (_, [_]) => const SizedBox.shrink(), + dispatchEvent: (event) {}, + buildContext: context, + dataContext: DataContext(dataModel, '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); }, ), diff --git a/examples/travel_app/test/tabbed_sections_test.dart b/examples/travel_app/test/tabbed_sections_test.dart index c77bfa6cb..ca3c6b322 100644 --- a/examples/travel_app/test/tabbed_sections_test.dart +++ b/examples/travel_app/test/tabbed_sections_test.dart @@ -47,13 +47,16 @@ void main() { child: Builder( builder: (context) { return catalogItem.widgetBuilder( - data: data, - id: 'testId', - buildChild: mockBuildChild, - dispatchEvent: (event) {}, - context: context, - dataContext: DataContext(DataModel(), '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: data, + id: 'testId', + buildChild: mockBuildChild, + dispatchEvent: (event) {}, + buildContext: context, + dataContext: DataContext(DataModel(), '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); }, ), diff --git a/examples/travel_app/test/trailhead_test.dart b/examples/travel_app/test/trailhead_test.dart index a8edd7fc6..3b973e8ba 100644 --- a/examples/travel_app/test/trailhead_test.dart +++ b/examples/travel_app/test/trailhead_test.dart @@ -27,15 +27,18 @@ void main() { body: Builder( builder: (context) { return trailhead.widgetBuilder( - data: data, - id: 'testId', - buildChild: (_, [_]) => const SizedBox.shrink(), - dispatchEvent: (event) { - dispatchedEvent = event; - }, - context: context, - dataContext: DataContext(DataModel(), '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: data, + id: 'testId', + buildChild: (_, [_]) => const SizedBox.shrink(), + dispatchEvent: (event) { + dispatchedEvent = event; + }, + buildContext: context, + dataContext: DataContext(DataModel(), '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); }, ), @@ -70,13 +73,16 @@ void main() { body: Builder( builder: (context) { return trailhead.widgetBuilder( - data: data, - id: 'testId', - buildChild: (_, [_]) => const SizedBox.shrink(), - dispatchEvent: (event) {}, - context: context, - dataContext: DataContext(DataModel(), '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: data, + id: 'testId', + buildChild: (_, [_]) => const SizedBox.shrink(), + dispatchEvent: (event) {}, + buildContext: context, + dataContext: DataContext(DataModel(), '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); }, ), diff --git a/examples/travel_app/test/travel_carousel_test.dart b/examples/travel_app/test/travel_carousel_test.dart index 1ad9c6d3b..52e82e876 100644 --- a/examples/travel_app/test/travel_carousel_test.dart +++ b/examples/travel_app/test/travel_carousel_test.dart @@ -40,15 +40,18 @@ void main() { body: Builder( builder: (context) { return travelCarousel.widgetBuilder( - data: data, - id: 'testId', - buildChild: buildChild, - dispatchEvent: (event) { - dispatchedEvent = event; - }, - context: context, - dataContext: DataContext(DataModel(), '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: data, + id: 'testId', + buildChild: buildChild, + dispatchEvent: (event) { + dispatchedEvent = event; + }, + buildContext: context, + dataContext: DataContext(DataModel(), '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); }, ), @@ -102,15 +105,18 @@ void main() { body: Builder( builder: (context) { return travelCarousel.widgetBuilder( - data: data, - id: 'testId', - buildChild: buildChild, - dispatchEvent: (event) { - dispatchedEvent = event; - }, - context: context, - dataContext: DataContext(DataModel(), '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: data, + id: 'testId', + buildChild: buildChild, + dispatchEvent: (event) { + dispatchedEvent = event; + }, + buildContext: context, + dataContext: DataContext(DataModel(), '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); }, ), @@ -139,13 +145,16 @@ void main() { body: Builder( builder: (context) { return travelCarousel.widgetBuilder( - data: data, - id: 'testId', - buildChild: (data, [_]) => Text(data), - dispatchEvent: (event) {}, - context: context, - dataContext: DataContext(DataModel(), '/'), - getComponent: (String componentId) => null, + CatalogItemContext( + data: data, + id: 'testId', + buildChild: (data, [_]) => Text(data), + dispatchEvent: (event) {}, + buildContext: context, + dataContext: DataContext(DataModel(), '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ); }, ), diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/audio_player.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/audio_player.dart index 755164898..955604fb7 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/audio_player.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/audio_player.dart @@ -28,21 +28,12 @@ final _schema = S.object( final audioPlayer = CatalogItem( name: 'AudioPlayer', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - return ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 200, maxHeight: 100), - child: const Placeholder(child: Center(child: Text('AudioPlayer'))), - ); - }, + widgetBuilder: (context) { + return ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 200, maxHeight: 100), + child: const Placeholder(child: Center(child: Text('AudioPlayer'))), + ); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/button.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/button.dart index a1e221c50..438f03aad 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/button.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/button.dart @@ -60,52 +60,41 @@ extension type _ButtonData.fromMap(JsonMap _json) { final button = CatalogItem( name: 'Button', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final buttonData = _ButtonData.fromMap(data as JsonMap); - final child = buildChild(buttonData.child); - final actionData = buttonData.action; - final actionName = actionData['name'] as String; - final contextDefinition = - (actionData['context'] as List?) ?? []; + widgetBuilder: (context) { + final buttonData = _ButtonData.fromMap(context.data as JsonMap); + final child = context.buildChild(buttonData.child); + final actionData = buttonData.action; + final actionName = actionData['name'] as String; + final contextDefinition = + (actionData['context'] as List?) ?? []; - genUiLogger.info('Building Button with child: ${buttonData.child}'); - final colorScheme = Theme.of(context).colorScheme; - final primary = buttonData.primary; + genUiLogger.info('Building Button with child: ${buttonData.child}'); + final colorScheme = Theme.of(context.buildContext).colorScheme; + final primary = buttonData.primary; - return ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: primary - ? colorScheme.primary - : colorScheme.surface, - foregroundColor: primary - ? colorScheme.onPrimary - : colorScheme.onSurface, + return ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: primary ? colorScheme.primary : colorScheme.surface, + foregroundColor: primary + ? colorScheme.onPrimary + : colorScheme.onSurface, + ), + onPressed: () { + final resolvedContext = resolveContext( + context.dataContext, + contextDefinition, + ); + context.dispatchEvent( + UserActionEvent( + name: actionName, + sourceComponentId: context.id, + context: resolvedContext, ), - onPressed: () { - final resolvedContext = resolveContext( - dataContext, - contextDefinition, - ); - dispatchEvent( - UserActionEvent( - name: actionName, - sourceComponentId: id, - context: resolvedContext, - ), - ); - }, - child: child, ); }, + child: child, + ); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/card.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/card.dart index 8453ede63..db3e97f1f 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/card.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/card.dart @@ -33,19 +33,10 @@ extension type _CardData.fromMap(JsonMap _json) { final card = CatalogItem( name: 'Card', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final cardData = _CardData.fromMap(data as JsonMap); - return Card(child: buildChild(cardData.child)); - }, + widgetBuilder: (context) { + final cardData = _CardData.fromMap(context.data as JsonMap); + return Card(child: context.buildChild(cardData.child)); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/check_box.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/check_box.dart index 7455d1dfc..d7331afd9 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/check_box.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/check_box.dart @@ -40,40 +40,35 @@ extension type _CheckBoxData.fromMap(JsonMap _json) { final checkBox = CatalogItem( name: 'CheckBox', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final checkBoxData = _CheckBoxData.fromMap(data as JsonMap); - final labelNotifier = dataContext.subscribeToString(checkBoxData.label); - final valueNotifier = dataContext.subscribeToBool(checkBoxData.value); - return ValueListenableBuilder( - valueListenable: labelNotifier, - builder: (context, label, child) { - return ValueListenableBuilder( - valueListenable: valueNotifier, - builder: (context, value, child) { - return CheckboxListTile( - title: Text(label ?? ''), - value: value ?? false, - onChanged: (newValue) { - final path = checkBoxData.value['path'] as String?; - if (path != null) { - dataContext.update(DataPath(path), newValue); - } - }, - ); + widgetBuilder: (context) { + final checkBoxData = _CheckBoxData.fromMap(context.data as JsonMap); + final labelNotifier = context.dataContext.subscribeToString( + checkBoxData.label, + ); + final valueNotifier = context.dataContext.subscribeToBool( + checkBoxData.value, + ); + return ValueListenableBuilder( + valueListenable: labelNotifier, + builder: (bcontext, label, child) { + return ValueListenableBuilder( + valueListenable: valueNotifier, + builder: (bcontext, value, child) { + return CheckboxListTile( + title: Text(label ?? ''), + value: value ?? false, + onChanged: (newValue) { + final path = checkBoxData.value['path'] as String?; + if (path != null) { + context.dataContext.update(DataPath(path), newValue); + } }, ); }, ); }, + ); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/column.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/column.dart index d9f41ad5c..cbd3ebb41 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/column.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/column.dart @@ -105,69 +105,51 @@ CrossAxisAlignment _parseCrossAxisAlignment(String? alignment) { final column = CatalogItem( name: 'Column', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final columnData = _ColumnData.fromMap(data as JsonMap); - return ComponentChildrenBuilder( - childrenData: columnData.children, - dataContext: dataContext, - buildChild: buildChild, - getComponent: getComponent, - explicitListBuilder: - (childIds, buildChild, getComponent, dataContext) { - return Column( - mainAxisAlignment: _parseMainAxisAlignment( - columnData.distribution, - ), - crossAxisAlignment: _parseCrossAxisAlignment( - columnData.alignment, - ), - mainAxisSize: MainAxisSize.min, - children: childIds - .map( - (componentId) => buildWeightedChild( - componentId: componentId, - dataContext: dataContext, - buildChild: buildChild, - component: getComponent(componentId), - ), - ) - .toList(), - ); - }, - templateListWidgetBuilder: (context, list, componentId, dataBinding) { - return Column( - mainAxisAlignment: _parseMainAxisAlignment( - columnData.distribution, - ), - crossAxisAlignment: _parseCrossAxisAlignment( - columnData.alignment, + widgetBuilder: (context) { + final columnData = _ColumnData.fromMap(context.data as JsonMap); + return ComponentChildrenBuilder( + childrenData: columnData.children, + dataContext: context.dataContext, + buildChild: context.buildChild, + getComponent: context.getComponent, + explicitListBuilder: (childIds, buildChild, getComponent, dataContext) { + return Column( + mainAxisAlignment: _parseMainAxisAlignment(columnData.distribution), + crossAxisAlignment: _parseCrossAxisAlignment(columnData.alignment), + mainAxisSize: MainAxisSize.min, + children: childIds + .map( + (componentId) => buildWeightedChild( + componentId: componentId, + dataContext: dataContext, + buildChild: buildChild, + component: getComponent(componentId), + ), + ) + .toList(), + ); + }, + templateListWidgetBuilder: (bcontext, list, componentId, dataBinding) { + return Column( + mainAxisAlignment: _parseMainAxisAlignment(columnData.distribution), + crossAxisAlignment: _parseCrossAxisAlignment(columnData.alignment), + mainAxisSize: MainAxisSize.min, + children: [ + for (var i = 0; i < list.length; i++) ...[ + buildWeightedChild( + componentId: componentId, + dataContext: context.dataContext.nested( + DataPath('$dataBinding/$i'), + ), + buildChild: context.buildChild, + component: context.getComponent(componentId), ), - mainAxisSize: MainAxisSize.min, - children: [ - for (var i = 0; i < list.length; i++) ...[ - buildWeightedChild( - componentId: componentId, - dataContext: dataContext.nested( - DataPath('$dataBinding/$i'), - ), - buildChild: buildChild, - component: getComponent(componentId), - ), - ], - ], - ); - }, + ], + ], ); }, + ); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/date_time_input.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/date_time_input.dart index 234df414d..6ecb0634d 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/date_time_input.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/date_time_input.dart @@ -59,56 +59,55 @@ extension type _DateTimeInputData.fromMap(JsonMap _json) { final dateTimeInput = CatalogItem( name: 'DateTimeInput', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final dateTimeInputData = _DateTimeInputData.fromMap(data as JsonMap); - final valueNotifier = dataContext.subscribeToString( - dateTimeInputData.value, - ); + widgetBuilder: (context) { + final dateTimeInputData = _DateTimeInputData.fromMap( + context.data as JsonMap, + ); + final valueNotifier = context.dataContext.subscribeToString( + dateTimeInputData.value, + ); - return ValueListenableBuilder( - valueListenable: valueNotifier, - builder: (context, value, child) { - return ListTile( - title: Text(value ?? 'Select a date/time'), - onTap: () async { - final path = dateTimeInputData.value['path'] as String?; - if (path == null) { - return; - } - if (dateTimeInputData.enableDate) { - final date = await showDatePicker( - context: context, - initialDate: DateTime.now(), - firstDate: DateTime(2000), - lastDate: DateTime(2100), - ); - if (date != null) { - dataContext.update(DataPath(path), date.toIso8601String()); - } - } - if (dateTimeInputData.enableTime) { - final time = await showTimePicker( - context: context, - initialTime: TimeOfDay.now(), - ); - if (time != null) { - dataContext.update(DataPath(path), time.format(context)); - } - } - }, - ); + return ValueListenableBuilder( + valueListenable: valueNotifier, + builder: (bcontext, value, child) { + return ListTile( + title: Text(value ?? 'Select a date/time'), + onTap: () async { + final path = dateTimeInputData.value['path'] as String?; + if (path == null) { + return; + } + if (dateTimeInputData.enableDate) { + final date = await showDatePicker( + context: context.buildContext, + initialDate: DateTime.now(), + firstDate: DateTime(2000), + lastDate: DateTime(2100), + ); + if (date != null) { + context.dataContext.update( + DataPath(path), + date.toIso8601String(), + ); + } + } + if (dateTimeInputData.enableTime) { + final time = await showTimePicker( + context: context.buildContext, + initialTime: TimeOfDay.now(), + ); + if (time != null) { + context.dataContext.update( + DataPath(path), + time.format(context.buildContext), + ); + } + } }, ); }, + ); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/divider.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/divider.dart index 2a0e8ce06..72800596d 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/divider.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/divider.dart @@ -32,22 +32,13 @@ extension type _DividerData.fromMap(JsonMap _json) { final divider = CatalogItem( name: 'Divider', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final dividerData = _DividerData.fromMap(data as JsonMap); - if (dividerData.axis == 'vertical') { - return const VerticalDivider(); - } - return const Divider(); - }, + widgetBuilder: (context) { + final dividerData = _DividerData.fromMap(context.data as JsonMap); + if (dividerData.axis == 'vertical') { + return const VerticalDivider(); + } + return const Divider(); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/heading.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/heading.dart index 282116210..30e7841d1 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/heading.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/heading.dart @@ -39,35 +39,26 @@ extension type _HeadingData.fromMap(JsonMap _json) { final heading = CatalogItem( name: 'Heading', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final headingData = _HeadingData.fromMap(data as JsonMap); - final notifier = dataContext.subscribeToString(headingData.text); + widgetBuilder: (context) { + final headingData = _HeadingData.fromMap(context.data as JsonMap); + final notifier = context.dataContext.subscribeToString(headingData.text); - return ValueListenableBuilder( - valueListenable: notifier, - builder: (context, currentValue, child) { - final textTheme = Theme.of(context).textTheme; - final style = switch (headingData.level) { - '1' => textTheme.headlineLarge, - '2' => textTheme.headlineMedium, - '3' => textTheme.headlineSmall, - '4' => textTheme.titleLarge, - '5' => textTheme.titleMedium, - _ => textTheme.titleSmall, - }; - return Text(currentValue ?? '', style: style); - }, - ); + return ValueListenableBuilder( + valueListenable: notifier, + builder: (bcontext, currentValue, child) { + final textTheme = Theme.of(bcontext).textTheme; + final style = switch (headingData.level) { + '1' => textTheme.headlineLarge, + '2' => textTheme.headlineMedium, + '3' => textTheme.headlineSmall, + '4' => textTheme.titleLarge, + '5' => textTheme.titleMedium, + _ => textTheme.titleSmall, + }; + return Text(currentValue ?? '', style: style); }, + ); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/icon.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/icon.dart index 0f7270140..75fa9d016 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/icon.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/icon.dart @@ -90,44 +90,33 @@ enum AvailableIcons { final icon = CatalogItem( name: 'Icon', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final iconData = _IconData.fromMap(data as JsonMap); - final literalName = iconData.literalName; - final namePath = iconData.namePath; - - if (literalName != null) { - final icon = - AvailableIcons.fromName(literalName)?.iconData ?? - Icons.broken_image; - return Icon(icon); - } + widgetBuilder: (context) { + final iconData = _IconData.fromMap(context.data as JsonMap); + final literalName = iconData.literalName; + final namePath = iconData.namePath; + + if (literalName != null) { + final icon = + AvailableIcons.fromName(literalName)?.iconData ?? Icons.broken_image; + return Icon(icon); + } - if (namePath == null) { - return const Icon(Icons.broken_image); - } + if (namePath == null) { + return const Icon(Icons.broken_image); + } + + final notifier = context.dataContext.subscribe(DataPath(namePath)); - final notifier = dataContext.subscribe(DataPath(namePath)); - - return ValueListenableBuilder( - valueListenable: notifier, - builder: (context, currentValue, child) { - final iconName = currentValue ?? ''; - final icon = - AvailableIcons.fromName(iconName)?.iconData ?? - Icons.broken_image; - return Icon(icon); - }, - ); + return ValueListenableBuilder( + valueListenable: notifier, + builder: (bcontext, currentValue, child) { + final iconName = currentValue ?? ''; + final icon = + AvailableIcons.fromName(iconName)?.iconData ?? Icons.broken_image; + return Icon(icon); }, + ); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/image.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/image.dart index eaf7950c5..3bd2eae7c 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/image.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/image.dart @@ -64,40 +64,32 @@ final image = CatalogItem( ] ''', ], - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final imageData = _ImageData.fromMap(data as JsonMap); - final notifier = dataContext.subscribeToString(imageData.url); + widgetBuilder: (context) { + final imageData = _ImageData.fromMap(context.data as JsonMap); + final notifier = context.dataContext.subscribeToString(imageData.url); - return ValueListenableBuilder( - valueListenable: notifier, - builder: (context, currentLocation, child) { - final location = currentLocation; - if (location == null || location.isEmpty) { - genUiLogger.warning( - 'Image widget created with no URL at path: ${dataContext.path}', - ); - return const SizedBox.shrink(); - } - final fit = imageData.fit; + return ValueListenableBuilder( + valueListenable: notifier, + builder: (bcontext, currentLocation, child) { + final location = currentLocation; + if (location == null || location.isEmpty) { + genUiLogger.warning( + 'Image widget created with no URL at path: ' + '${context.dataContext.path}', + ); + return const SizedBox.shrink(); + } + final fit = imageData.fit; - late Widget child; + late Widget bchild; - if (location.startsWith('assets/')) { - child = Image.asset(location, fit: fit); - } else { - child = Image.network(location, fit: fit); - } - return SizedBox(width: 150, height: 150, child: child); - }, - ); + if (location.startsWith('assets/')) { + bchild = Image.asset(location, fit: fit); + } else { + bchild = Image.network(location, fit: fit); + } + return SizedBox(width: 150, height: 150, child: bchild); }, + ); + }, ); diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/list.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/list.dart index d80d3ac62..7147410d3 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/list.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/list.dart @@ -51,53 +51,41 @@ extension type _ListData.fromMap(JsonMap _json) { final list = CatalogItem( name: 'List', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final listData = _ListData.fromMap(data as JsonMap); - final direction = listData.direction == 'horizontal' - ? Axis.horizontal - : Axis.vertical; - return ComponentChildrenBuilder( - childrenData: listData.children, - dataContext: dataContext, - buildChild: buildChild, - getComponent: getComponent, - explicitListBuilder: - (childIds, buildChild, getComponent, dataContext) { - return ListView( - shrinkWrap: true, - scrollDirection: direction, - children: childIds - .map((id) => buildChild(id, dataContext)) - .toList(), - ); - }, - templateListWidgetBuilder: - (context, Map data, componentId, dataBinding) { - final values = data.values.toList(); - final keys = data.keys.toList(); - return ListView.builder( - shrinkWrap: true, - scrollDirection: direction, - itemCount: values.length, - itemBuilder: (context, index) { - final itemDataContext = dataContext.nested( - DataPath('$dataBinding/${keys[index]}'), - ); - return buildChild(componentId, itemDataContext); - }, - ); - }, + widgetBuilder: (context) { + final listData = _ListData.fromMap(context.data as JsonMap); + final direction = listData.direction == 'horizontal' + ? Axis.horizontal + : Axis.vertical; + return ComponentChildrenBuilder( + childrenData: listData.children, + dataContext: context.dataContext, + buildChild: context.buildChild, + getComponent: context.getComponent, + explicitListBuilder: (childIds, buildChild, getComponent, dataContext) { + return ListView( + shrinkWrap: true, + scrollDirection: direction, + children: childIds.map((id) => buildChild(id, dataContext)).toList(), ); }, + templateListWidgetBuilder: + (bcontext, Map data, componentId, dataBinding) { + final values = data.values.toList(); + final keys = data.keys.toList(); + return ListView.builder( + shrinkWrap: true, + scrollDirection: direction, + itemCount: values.length, + itemBuilder: (bcontext, index) { + final itemDataContext = context.dataContext.nested( + DataPath('$dataBinding/${keys[index]}'), + ); + return context.buildChild(componentId, itemDataContext); + }, + ); + }, + ); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/modal.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/modal.dart index e7a2dd4ec..827edf931 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/modal.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/modal.dart @@ -50,19 +50,10 @@ extension type _ModalData.fromMap(JsonMap _json) { final modal = CatalogItem( name: 'Modal', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final modalData = _ModalData.fromMap(data as JsonMap); - return buildChild(modalData.entryPointChild); - }, + widgetBuilder: (context) { + final modalData = _ModalData.fromMap(context.data as JsonMap); + return context.buildChild(modalData.entryPointChild); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/multiple_choice.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/multiple_choice.dart index ea2efbf74..d0222e106 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/multiple_choice.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/multiple_choice.dart @@ -60,60 +60,51 @@ extension type _MultipleChoiceData.fromMap(JsonMap _json) { final multipleChoice = CatalogItem( name: 'MultipleChoice', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final multipleChoiceData = _MultipleChoiceData.fromMap(data as JsonMap); - final selectionsNotifier = dataContext.subscribeToObjectArray( - multipleChoiceData.selections, - ); + widgetBuilder: (context) { + final multipleChoiceData = _MultipleChoiceData.fromMap( + context.data as JsonMap, + ); + final selectionsNotifier = context.dataContext.subscribeToObjectArray( + multipleChoiceData.selections, + ); - return ValueListenableBuilder?>( - valueListenable: selectionsNotifier, - builder: (context, selections, child) { - return Column( - children: multipleChoiceData.options.map((option) { - final labelNotifier = dataContext.subscribeToString( - option['label'] as JsonMap, - ); - final value = option['value'] as String; - return ValueListenableBuilder( - valueListenable: labelNotifier, - builder: (context, label, child) { - return CheckboxListTile( - title: Text(label ?? ''), - value: selections?.contains(value) ?? false, - onChanged: (newValue) { - final path = - multipleChoiceData.selections['path'] as String?; - if (path == null) { - return; - } - final newSelections = List.from( - selections ?? [], - ); - if (newValue ?? false) { - newSelections.add(value); - } else { - newSelections.remove(value); - } - dataContext.update(DataPath(path), newSelections); - }, - ); + return ValueListenableBuilder?>( + valueListenable: selectionsNotifier, + builder: (bcontext, selections, child) { + return Column( + children: multipleChoiceData.options.map((option) { + final labelNotifier = context.dataContext.subscribeToString( + option['label'] as JsonMap, + ); + final value = option['value'] as String; + return ValueListenableBuilder( + valueListenable: labelNotifier, + builder: (bcontext, label, child) { + return CheckboxListTile( + title: Text(label ?? ''), + value: selections?.contains(value) ?? false, + onChanged: (newValue) { + final path = + multipleChoiceData.selections['path'] as String?; + if (path == null) { + return; + } + final newSelections = List.from(selections ?? []); + if (newValue ?? false) { + newSelections.add(value); + } else { + newSelections.remove(value); + } + context.dataContext.update(DataPath(path), newSelections); }, ); - }).toList(), + }, ); - }, + }).toList(), ); }, + ); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/row.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/row.dart index ac317ef30..ac563a077 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/row.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/row.dart @@ -105,65 +105,51 @@ CrossAxisAlignment _parseCrossAxisAlignment(String? alignment) { final row = CatalogItem( name: 'Row', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final rowData = _RowData.fromMap(data as JsonMap); - return ComponentChildrenBuilder( - childrenData: rowData.children, - dataContext: dataContext, - buildChild: buildChild, - getComponent: getComponent, - explicitListBuilder: - (childIds, buildChild, getComponent, dataContext) { - return Row( - mainAxisAlignment: _parseMainAxisAlignment( - rowData.distribution, - ), - crossAxisAlignment: _parseCrossAxisAlignment( - rowData.alignment, - ), - mainAxisSize: MainAxisSize.min, - children: childIds - .map( - (componentId) => buildWeightedChild( - componentId: componentId, - dataContext: dataContext, - buildChild: buildChild, - component: getComponent(componentId), - ), - ) - .toList(), - ); - }, - templateListWidgetBuilder: (context, list, componentId, dataBinding) { - return Row( - mainAxisAlignment: _parseMainAxisAlignment(rowData.distribution), - crossAxisAlignment: _parseCrossAxisAlignment(rowData.alignment), - mainAxisSize: MainAxisSize.min, - children: [ - for (var i = 0; i < list.length; i++) ...[ - buildWeightedChild( - componentId: componentId, - dataContext: dataContext.nested( - DataPath('$dataBinding/$i'), - ), - buildChild: buildChild, - component: getComponent(componentId), - ), - ], - ], - ); - }, + widgetBuilder: (context) { + final rowData = _RowData.fromMap(context.data as JsonMap); + return ComponentChildrenBuilder( + childrenData: rowData.children, + dataContext: context.dataContext, + buildChild: context.buildChild, + getComponent: context.getComponent, + explicitListBuilder: (childIds, buildChild, getComponent, dataContext) { + return Row( + mainAxisAlignment: _parseMainAxisAlignment(rowData.distribution), + crossAxisAlignment: _parseCrossAxisAlignment(rowData.alignment), + mainAxisSize: MainAxisSize.min, + children: childIds + .map( + (componentId) => buildWeightedChild( + componentId: componentId, + dataContext: dataContext, + buildChild: buildChild, + component: getComponent(componentId), + ), + ) + .toList(), ); }, + templateListWidgetBuilder: (bcontext, list, componentId, dataBinding) { + return Row( + mainAxisAlignment: _parseMainAxisAlignment(rowData.distribution), + crossAxisAlignment: _parseCrossAxisAlignment(rowData.alignment), + mainAxisSize: MainAxisSize.min, + children: [ + for (var i = 0; i < list.length; i++) ...[ + buildWeightedChild( + componentId: componentId, + dataContext: context.dataContext.nested( + DataPath('$dataBinding/$i'), + ), + buildChild: context.buildChild, + component: context.getComponent(componentId), + ), + ], + ], + ); + }, + ); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/slider.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/slider.dart index 7b53b4a2c..2766a65a4 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/slider.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/slider.dart @@ -50,39 +50,30 @@ extension type _SliderData.fromMap(JsonMap _json) { final slider = CatalogItem( name: 'Slider', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final sliderData = _SliderData.fromMap(data as JsonMap); - final valueNotifier = dataContext.subscribeToValue( - sliderData.value, - 'literalNumber', - ); + widgetBuilder: (CatalogItemContext context) { + final sliderData = _SliderData.fromMap(context.data as JsonMap); + final valueNotifier = context.dataContext.subscribeToValue( + sliderData.value, + 'literalNumber', + ); - return ValueListenableBuilder( - valueListenable: valueNotifier, - builder: (context, value, child) { - return Slider( - value: (value ?? 0.0).toDouble(), - min: sliderData.minValue, - max: sliderData.maxValue, - onChanged: (newValue) { - final path = sliderData.value['path'] as String?; - if (path != null) { - dataContext.update(DataPath(path), newValue); - } - }, - ); + return ValueListenableBuilder( + valueListenable: valueNotifier, + builder: (_, value, child) { + return Slider( + value: (value ?? 0.0).toDouble(), + min: sliderData.minValue, + max: sliderData.maxValue, + onChanged: (newValue) { + final path = sliderData.value['path'] as String?; + if (path != null) { + context.dataContext.update(DataPath(path), newValue); + } }, ); }, + ); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/tabs.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/tabs.dart index 13b011028..32056896c 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/tabs.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/tabs.dart @@ -45,45 +45,36 @@ extension type _TabsData.fromMap(JsonMap _json) { final tabs = CatalogItem( name: 'Tabs', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final tabsData = _TabsData.fromMap(data as JsonMap); - return DefaultTabController( - length: tabsData.tabItems.length, - child: Column( - children: [ - TabBar( - tabs: tabsData.tabItems.map((tabItem) { - final titleNotifier = dataContext.subscribeToString( - tabItem['title'] as JsonMap, - ); - return ValueListenableBuilder( - valueListenable: titleNotifier, - builder: (context, title, child) { - return Tab(text: title ?? ''); - }, - ); - }).toList(), - ), - Expanded( - child: TabBarView( - children: tabsData.tabItems.map((tabItem) { - return buildChild(tabItem['child'] as String); - }).toList(), - ), - ), - ], + widgetBuilder: (context) { + final tabsData = _TabsData.fromMap(context.data as JsonMap); + return DefaultTabController( + length: tabsData.tabItems.length, + child: Column( + children: [ + TabBar( + tabs: tabsData.tabItems.map((tabItem) { + final titleNotifier = context.dataContext.subscribeToString( + tabItem['title'] as JsonMap, + ); + return ValueListenableBuilder( + valueListenable: titleNotifier, + builder: (bcontext, title, child) { + return Tab(text: title ?? ''); + }, + ); + }).toList(), + ), + Expanded( + child: TabBarView( + children: tabsData.tabItems.map((tabItem) { + return context.buildChild(tabItem['child'] as String); + }).toList(), + ), ), - ); - }, + ], + ), + ); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/text.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/text.dart index 3f1c605d9..bafc4ec7c 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/text.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/text.dart @@ -52,27 +52,18 @@ final text = CatalogItem( ] ''', ], - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final textData = _TextData.fromMap(data as JsonMap); - final notifier = dataContext.subscribeToString(textData.text); + widgetBuilder: (context) { + final textData = _TextData.fromMap(context.data as JsonMap); + final notifier = context.dataContext.subscribeToString(textData.text); - return ValueListenableBuilder( - valueListenable: notifier, - builder: (context, currentValue, child) { - return Text( - currentValue ?? '', - style: Theme.of(context).textTheme.bodyMedium, - ); - }, + return ValueListenableBuilder( + valueListenable: notifier, + builder: (bcontext, currentValue, child) { + return Text( + currentValue ?? '', + style: Theme.of(bcontext).textTheme.bodyMedium, ); }, + ); + }, ); diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/text_field.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/text_field.dart index 2544496d9..287f818af 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/text_field.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/text_field.dart @@ -165,65 +165,55 @@ final textField = CatalogItem( ] ''', ], - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - final textFieldData = _TextFieldData.fromMap(data as JsonMap); - final valueRef = textFieldData.text; - final path = valueRef?['path'] as String?; - final notifier = dataContext.subscribeToString(valueRef); - final labelNotifier = dataContext.subscribeToString( - textFieldData.label, - ); + widgetBuilder: (context) { + final textFieldData = _TextFieldData.fromMap(context.data as JsonMap); + final valueRef = textFieldData.text; + final path = valueRef?['path'] as String?; + final notifier = context.dataContext.subscribeToString(valueRef); + final labelNotifier = context.dataContext.subscribeToString( + textFieldData.label, + ); - return ValueListenableBuilder( - valueListenable: notifier, - builder: (context, currentValue, child) { - return ValueListenableBuilder( - valueListenable: labelNotifier, - builder: (context, label, child) { - return _TextField( - initialValue: currentValue ?? '', - label: label, - textFieldType: textFieldData.textFieldType, - validationRegexp: textFieldData.validationRegexp, - onChanged: (newValue) { - if (path != null) { - dataContext.update(DataPath(path), newValue); - } - }, - onSubmitted: (newValue) { - final actionData = textFieldData.onSubmittedAction; - if (actionData == null) { - return; - } - final actionName = actionData['name'] as String; - final contextDefinition = - (actionData['context'] as List?) ?? - []; - final resolvedContext = resolveContext( - dataContext, - contextDefinition, - ); - dispatchEvent( - UserActionEvent( - name: actionName, - sourceComponentId: id, - context: resolvedContext, - ), - ); - }, + return ValueListenableBuilder( + valueListenable: notifier, + builder: (bcontext, currentValue, child) { + return ValueListenableBuilder( + valueListenable: labelNotifier, + builder: (bcontext, label, child) { + return _TextField( + initialValue: currentValue ?? '', + label: label, + textFieldType: textFieldData.textFieldType, + validationRegexp: textFieldData.validationRegexp, + onChanged: (newValue) { + if (path != null) { + context.dataContext.update(DataPath(path), newValue); + } + }, + onSubmitted: (newValue) { + final actionData = textFieldData.onSubmittedAction; + if (actionData == null) { + return; + } + final actionName = actionData['name'] as String; + final contextDefinition = + (actionData['context'] as List?) ?? []; + final resolvedContext = resolveContext( + context.dataContext, + contextDefinition, + ); + context.dispatchEvent( + UserActionEvent( + name: actionName, + sourceComponentId: context.id, + context: resolvedContext, + ), ); }, ); }, ); }, + ); + }, ); diff --git a/packages/flutter_genui/lib/src/catalog/core_widgets/video.dart b/packages/flutter_genui/lib/src/catalog/core_widgets/video.dart index 94c74161b..66800fc34 100644 --- a/packages/flutter_genui/lib/src/catalog/core_widgets/video.dart +++ b/packages/flutter_genui/lib/src/catalog/core_widgets/video.dart @@ -28,21 +28,12 @@ final _schema = S.object( final video = CatalogItem( name: 'Video', dataSchema: _schema, - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - return ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 200, maxHeight: 100), - child: const Placeholder(child: Center(child: Text('Video'))), - ); - }, + widgetBuilder: (context) { + return ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 200, maxHeight: 100), + child: const Placeholder(child: Center(child: Text('Video'))), + ); + }, exampleData: [ () => ''' [ diff --git a/packages/flutter_genui/lib/src/core/genui_surface.dart b/packages/flutter_genui/lib/src/core/genui_surface.dart index 876a0314e..ae5126334 100644 --- a/packages/flutter_genui/lib/src/core/genui_surface.dart +++ b/packages/flutter_genui/lib/src/core/genui_surface.dart @@ -91,6 +91,7 @@ class _GenUiSurfaceState extends State { context: context, dataContext: dataContext, getComponent: (String componentId) => definition.components[componentId], + surfaceId: widget.surfaceId, ); } diff --git a/packages/flutter_genui/lib/src/model/catalog.dart b/packages/flutter_genui/lib/src/model/catalog.dart index 576ba6aaf..af697c1ce 100644 --- a/packages/flutter_genui/lib/src/model/catalog.dart +++ b/packages/flutter_genui/lib/src/model/catalog.dart @@ -70,6 +70,7 @@ class Catalog { required BuildContext context, required DataContext dataContext, required GetComponentCallback getComponent, + required String surfaceId, }) { final widgetType = widgetData.keys.firstOrNull; final item = items.firstWhereOrNull((item) => item.name == widgetType); @@ -80,14 +81,17 @@ class Catalog { genUiLogger.info('Building widget ${item.name} with id $id'); return item.widgetBuilder( - data: JsonMap.from(widgetData[widgetType]! as Map), - id: id, - buildChild: (String childId, [DataContext? childDataContext]) => - buildChild(childId, childDataContext ?? dataContext), - dispatchEvent: dispatchEvent, - context: context, - dataContext: dataContext, - getComponent: getComponent, + CatalogItemContext( + data: JsonMap.from(widgetData[widgetType]! as Map), + id: id, + buildChild: (String childId, [DataContext? childDataContext]) => + buildChild(childId, childDataContext ?? dataContext), + dispatchEvent: dispatchEvent, + buildContext: context, + dataContext: dataContext, + getComponent: getComponent, + surfaceId: surfaceId, + ), ); } diff --git a/packages/flutter_genui/lib/src/model/catalog_item.dart b/packages/flutter_genui/lib/src/model/catalog_item.dart index d1d74739d..5479be87c 100644 --- a/packages/flutter_genui/lib/src/model/catalog_item.dart +++ b/packages/flutter_genui/lib/src/model/catalog_item.dart @@ -23,23 +23,29 @@ typedef ChildBuilderCallback = typedef ExampleBuilderCallback = String Function(); /// A callback that builds a widget for a catalog item. -typedef CatalogWidgetBuilder = - Widget Function({ - // The actual deserialized JSON data for this widget. The format of this - // data will exactly match [CatalogItem.dataSchema]. - required Object data, - // The ID of this widget. - required String id, - // A function used to build a child based on the given ID. - required ChildBuilderCallback buildChild, - // A function used to dispatch an event. - required DispatchEventCallback dispatchEvent, - required BuildContext context, - // The current data context for this widget. - required DataContext dataContext, - // A function to retrieve a component definition by its ID. - required GetComponentCallback getComponent, - }); +typedef CatalogWidgetBuilder = Widget Function(CatalogItemContext itemContext); + +class CatalogItemContext { + CatalogItemContext({ + required this.data, + required this.id, + required this.buildChild, + required this.dispatchEvent, + required this.buildContext, + required this.dataContext, + required this.getComponent, + required this.surfaceId, + }); + + final Object data; + final String id; + final ChildBuilderCallback buildChild; + final DispatchEventCallback dispatchEvent; + final BuildContext buildContext; + final DataContext dataContext; + final GetComponentCallback getComponent; + final String surfaceId; +} /// Defines a UI layout type, its schema, and how to build its widget. @immutable diff --git a/packages/flutter_genui/test/catalog_test.dart b/packages/flutter_genui/test/catalog_test.dart index 78f5b7a40..e73f6e825 100644 --- a/packages/flutter_genui/test/catalog_test.dart +++ b/packages/flutter_genui/test/catalog_test.dart @@ -35,6 +35,7 @@ void main() { context: context, dataContext: DataContext(DataModel(), '/'), getComponent: (String componentId) => null, + surfaceId: 'surfaceId', ); }, ), @@ -80,6 +81,7 @@ void main() { context: context, dataContext: DataContext(DataModel(), '/'), getComponent: (String componentId) => null, + surfaceId: 'surfaceId', ); expect(widget, isA()); return widget; diff --git a/packages/flutter_genui/test/core/ui_tools_test.dart b/packages/flutter_genui/test/core/ui_tools_test.dart index 379398836..2da444142 100644 --- a/packages/flutter_genui/test/core/ui_tools_test.dart +++ b/packages/flutter_genui/test/core/ui_tools_test.dart @@ -26,18 +26,9 @@ void main() { catalog: Catalog([ CatalogItem( name: 'Text', - widgetBuilder: - ({ - required data, - required id, - required buildChild, - required dispatchEvent, - required context, - required dataContext, - required getComponent, - }) { - return const Text(''); - }, + widgetBuilder: (_) { + return const Text(''); + }, dataSchema: Schema.object(properties: {}), ), ]), diff --git a/packages/flutter_genui/test/image_test.dart b/packages/flutter_genui/test/image_test.dart index 6a4ef906d..4922e4c64 100644 --- a/packages/flutter_genui/test/image_test.dart +++ b/packages/flutter_genui/test/image_test.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_genui/src/catalog/core_widgets/image.dart'; +import 'package:flutter_genui/src/model/catalog_item.dart'; import 'package:flutter_genui/src/model/data_model.dart'; import 'package:flutter_genui/src/model/ui_models.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -19,18 +20,21 @@ void main() { home: Builder( builder: (context) => Scaffold( body: image.widgetBuilder( - data: { - 'url': { - 'literalString': - 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png', + CatalogItemContext( + data: { + 'url': { + 'literalString': + 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png', + }, }, - }, - id: 'test_image', - buildChild: (_, [_]) => const SizedBox(), - dispatchEvent: (UiEvent event) {}, - context: context, - dataContext: DataContext(DataModel(), '/'), - getComponent: (String componentId) => null, + id: 'test_image', + buildChild: (_, [_]) => const SizedBox(), + dispatchEvent: (UiEvent event) {}, + buildContext: context, + dataContext: DataContext(DataModel(), '/'), + getComponent: (String componentId) => null, + surfaceId: 'surface1', + ), ), ), ),