|
⚡ bloc_signals_genui_flutter"With the rigor of BLoC and the flex and speed of Signals" Declarative Flutter surface views, Material 3 catalog widgets, and 0ms reactive two-way data bindings for bloc_signals_genui and Google's A2UI (Agent-to-User Interface) protocol. |
bloc_signals_genui_flutter connects the pure-Dart A2uiSurfaceBloc state machine to Flutter's widget hierarchy via A2uiSurfaceView, A2uiFlutterCatalog, A2uiComponentContext, and GenUiControllerAdapter.
🌐 Ecosystem Packages
The BlocSignal monorepo consists of 13 modular packages:
⚡ Key Features
- 🎨
A2uiSurfaceView: Declarative Flutter widget handling the completeA2uiSurfaceStatelifecycle (SurfaceInitial,SurfaceStreaming,SurfaceReady,SurfaceSubmitting, andSurfaceError) with customizable builders and per-component error boundaries. - 🧱
A2uiFlutterCatalog(16 StandardBasicCatalogWidgets): Extensible component registry pre-populated with 16 Material 3 widgets covering A2UIBasicCatalogv0.9 and v1.0 (Text,Row,Column,List,Tabs,Modal,Card,Divider,Icon,Image,Button,TextField,CheckBox,ChoicePicker,Slider, andDateTimeInput) alongside immutable composition helpers (copyWith,copyWithout,registerAll). - ✅ Reactive
CheckRuleValidation UI: Automatically surfacesGenericBindervalidation state (isValid,validationErrors,validationResults) across form inputs (TextField,CheckBox,ChoicePicker,Slider,DateTimeInput) and disablesButtonsubmissions (onPressed: null) when check rules fail. - ⚡ Granular Signal Bindings & Two-Way Sync: Each rendered component subscribes to its own
GenericBinder.resolvedPropssignal and writes two-way input mutations viaA2uiComponentContext.updateBoundValue, updating leaf widgets andA2uiSurfaceBlocform data in 0ms onDataModelor@pathmutations without rebuilding parent containers. - 🧭 Multi-Surface & Split-Pane Support: Pin
A2uiSurfaceView(bloc: bloc, surfaceId: '...')to a specific surface or omitsurfaceIdto followbloc.activeSurfaceIdreactively across tabs and split views. - 🛡️ Defensive LLM Property Coercion: Built-in
safe_prop_parser.dartutilities (unwrapBinding,asString,asInt,asDouble,asBool,extractChildId,extractChildIds) protect the widget tree from malformed or stringified LLM JSON properties.
📦 Installation
Add bloc_signals_genui_flutter to your Flutter project:
flutter pub add bloc_signals_genui_flutter
Or declare it in your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
bloc_signals: ^1.7.0
bloc_signals_flutter: ^1.3.4
bloc_signals_genui: ^1.0.0
bloc_signals_genui_flutter: ^1.0.0
a2ui_core: ^0.3.0
🚀 Quick Start
Rendering an Interactive A2UI Surface
import 'package:bloc_signals_flutter/bloc_signals_flutter.dart';
import 'package:bloc_signals_genui/bloc_signals_genui.dart';
import 'package:bloc_signals_genui_flutter/bloc_signals_genui_flutter.dart';
import 'package:flutter/material.dart';
// Dart 3.5+ (Standard)
class AgentSurfaceScreen extends StatelessWidget {
const AgentSurfaceScreen({
required this.surfaceBloc,
super.key,
});
final A2uiSurfaceBloc surfaceBloc;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Generative UI Surface')),
body: A2uiSurfaceView(
bloc: surfaceBloc,
catalog: A2uiFlutterCatalog.standard(),
),
);
}
}
// Dart 3.13+ (Modern Primary Constructor)
class ModernAgentSurfaceScreen({
required this.surfaceBloc,
super.key,
}) extends StatelessWidget {
final A2uiSurfaceBloc surfaceBloc;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Generative UI Surface')),
body: A2uiSurfaceView(
bloc: surfaceBloc,
catalog: A2uiFlutterCatalog.standard(),
),
);
}
}
Registering Custom Catalog Widgets
Extend A2uiFlutterCatalog.standard() using copyWith (or register) to bind domain-specific A2UI components to custom Flutter widgets while reading and mutating form signals through A2uiComponentContext:
final customCatalog = A2uiFlutterCatalog.standard().copyWith({
'RatingSlider': (context, A2uiComponentContext componentContext) {
final path = componentContext.props['path'] as String? ?? '/rating';
final label = componentContext.props['label'] as String? ?? 'Rating';
final current =
(componentContext.props['value'] as num?)?.toDouble() ?? 3.0;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('$label: ${current.toStringAsFixed(1)}'),
Slider(
min: 1,
max: 5,
divisions: 4,
value: current,
onChanged: (next) => componentContext.updateFormField(path, next),
),
],
);
},
});
🛡️ License
MIT License. See LICENSE for details.
Libraries
- bloc_signals_genui_flutter
- Flutter UI bindings, declarative surface views, and catalog widgets for Generative UI (A2UI) with BlocSignal.