pilot_ui 0.0.13
pilot_ui: ^0.0.13 copied to clipboard
A dynamic, theme-aware JSON UI rendering engine for Flutter with extensible parsers, action handlers, and ready-to-use smart dispatch & RTS action components.
Pilot UI 🚀 #
A powerful, dynamic, theme-aware JSON UI compilation and rendering engine for Flutter. Build interactive, responsive widget trees directly from JSON schemas or server-driven definitions, complete with state management, expression evaluation, extensible registries, and specialized UI components.
✨ Features #
- 🧱 Server-Driven UI (SDUI): Render complete Flutter widget trees directly from JSON payloads.
- ⚡ Extensible Registry: Register your own custom widget parsers and action handlers seamlessly via
PilotRegistry. - 🔄 Reactive State Management: Built-in state context (
PilotStateContext) and expression evaluator (${state.myValue}) for interactive UI flows. - 🎨 Theme-Aware & Responsive: Dynamic theme resolution (colors, typography, elevation) and responsive props adapting to device screen sizes and breakpoints.
- 📝 Lightweight Markdown Engine: Built-in
PilotMarkdownTextsupporting bold (**), italic (*), inline code (`), bullet lists (-), headers (#), and hyperlinks ([text](url)) without third-party dependencies. - 📦 Rich Widget Parser Suite:
- Layout:
Container,Column,Row,Flex,Stack,ListView,Expanded,Wrap,SafeArea - Display:
Text,Markdown/MarkdownText,Icon,CachedImage,Card,Avatar,Badge,ProgressIndicator,GlassContainer - Inputs & Buttons:
TextField,Checkbox,ElevatedButton,TextButton - Structure & Nav:
AppBar,BottomNavigationBar,FloatingActionButton,PageView,TabView
- Layout:
- 🎯 Action Routing: Declarative action handling for
showDialog,showBottomSheet,setState, and custom event emission (emitEvent). - 🚚 Specialized Dispatch & Fleet Components:
RtsActionCard: Ready-to-use, interactive Return-to-Station request cards featuring animated countdown expiry progress bars, integrated driver contact bar, audio note player, dark mode support, markdown text formatting, expandable/collapsible animation, severity badges, interactive AI Insights callouts, header edit callbacks, and dynamic action buttons.DriverContactBar: Direct calling interface with driver avatars, phone number formatting, native multi-app chooser support (Phone, Google Voice, Truecaller), and dark theme support.PilotAudioPlayer: Sleek, seekable audio player for driver voice notes with play/pause controls, slider scrubbing, tabular time formatting, and stream handling.DynamicActionButtons: Configurable action trigger buttons with icon matching, async loading states, dark theme palettes, and customizable colors.
📦 Installation #
Add pilot_ui to your pubspec.yaml dependencies:
dependencies:
flutter:
sdk: flutter
pilot_ui: ^0.0.13
Or run:
flutter pub add pilot_ui
🚀 Quick Start #
1. Basic JSON Widget Rendering #
Use PilotBuilder to compile a JSON UI specification into live Flutter widgets:
import 'package:flutter/material.dart';
import 'package:pilot_ui/pilot_ui.dart';
class DynamicScreen extends StatelessWidget {
const DynamicScreen({super.key});
final Map<String, dynamic> jsonUi = const {
"type": "container",
"children": [
{
"type": "column",
"children": [
{
"type": "text",
"properties": {
"text": "Hello, Pilot UI!",
"style": {
"fontSize": 22.0,
"fontWeight": "bold",
"color": "#7C4DFF"
}
}
},
{
"type": "markdown",
"properties": {
"text": "Render **bold**, *italic*, `code`, and [links](https://flutter.dev) easily!"
}
},
{
"type": "elevated_button",
"properties": {
"label": "Click Me"
},
"actions": {
"onTap": {
"action": "showDialog",
"params": {
"title": "Welcome",
"message": "Rendered dynamically from JSON!"
}
}
}
}
]
}
]
};
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Dynamic UI')),
body: PilotBuilder(
json: jsonUi,
initialState: const {
'user': 'Driver #42',
},
onEvent: (eventName, payload) {
debugPrint('Event emitted: $eventName -> $payload');
},
),
);
}
}
📝 Markdown Text: PilotMarkdownText #
Render markdown formatting in Flutter directly or via JSON without needing bulky markdown libraries. Hyperlinks ([label](url)) and raw URLs are automatically clickable and open in the default browser/external application:
import 'package:pilot_ui/pilot_ui.dart';
PilotMarkdownText(
"Driver **Aaron Pouncy** requested *RTS*. Read the [guidelines](https://example.com).",
style: const TextStyle(fontSize: 14, color: Colors.black87),
// Optional custom link tap callback; if omitted, URLs launch automatically
onLinkTap: (url) {
debugPrint('Navigating to: $url');
},
)
JSON definition:
{
"type": "markdown",
"properties": {
"text": "### Notice\n- **Driver ID**: #42\n- **Status**: *En Route*\n- Visit [Support Portal](https://support.fleetglide.io)",
"style": {
"fontSize": 14.0,
"color": "#1E293B"
}
}
}
📞 Driver Contact Bar: DriverContactBar #
Quickly connect dispatchers to drivers with phone number normalization, one-tap calling, and native multi-app chooser support:
import 'package:pilot_ui/pilot_ui.dart';
DriverContactBar(
driverName: "Abas Ali",
driverPhone: "+1 (555) 234-5678",
isDarkMode: false,
onCall: () {
debugPrint('Initiating call to driver');
},
)
Multi-App Calling Chooser (Android) #
On Android devices, tapping the Call button automatically triggers Android's system chooser dialog (Intent.createChooser with ACTION_DIAL), allowing users to pick from any installed calling or dialer application (e.g. Phone, Google Voice, Truecaller) rather than being locked to only the default dialer.
Android 11+ Configuration
To ensure Android allows querying installed calling apps under Android 11+ (API 30+) package visibility rules, add the following <queries> entries inside <manifest> in your android/app/src/main/AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<queries>
<intent>
<action android:name="android.intent.action.DIAL" />
<data android:scheme="tel" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="tel" />
</intent>
</queries>
...
</manifest>
Multi-App Calling Chooser (iOS) #
On iOS, if Google Voice is installed on the user's device, tapping the Call button automatically presents a native CupertinoActionSheet prompting the user to choose between:
- 📞 Phone (Default): Directly dials via Apple's native cellular dialer (
tel:<phone>). - 🎙️ Google Voice: Copies the phone number to the iOS clipboard for effortless pasting and opens Google Voice (
googlevoice://).
If Google Voice is not installed, it seamlessly places the call using the native Phone dialer without interrupting the user.
iOS Configuration
Add googlevoice and tel under LSApplicationQueriesSchemes in your ios/Runner/Info.plist:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>tel</string>
<string>googlevoice</string>
</array>
🎵 Voice Note & Audio Player: PilotAudioPlayer #
Play voice notes and driver audio clips with seek scrubbing and streaming status:
import 'package:pilot_ui/pilot_ui.dart';
PilotAudioPlayer(
audioUrl: "https://example.com/audio/driver_note.mp3",
title: "Driver Voice Note",
autoPlay: false,
)
🚚 Specialized Components: RtsActionCard #
pilot_ui includes high-fidelity operational components for logistics and dispatch feeds with countdown expiry timers, driver contact bars, audio note players, markdown formatting, AI Insights callouts, and dark theme support:
import 'package:flutter/material.dart';
import 'package:pilot_ui/pilot_ui.dart';
Widget buildEventCard(Map<String, dynamic> eventJson, BuildContext context) {
return RtsActionCard(
data: eventJson,
initiallyExpanded: false, // Collapsed by default; tap to expand
isDarkMode: Theme.of(context).brightness == Brightness.dark, // Optional manual dark mode override
showEdit: true,
onEdit: () => debugPrint('Edit card tapped'),
onCallDriver: () => debugPrint('Driver call triggered'),
onLinkTap: (url) => debugPrint('Tapped link in card: $url'),
onButtonTap: (action) {
debugPrint('Triggered: ${action?.actionType} with payload: ${action?.actionPayload}');
},
);
}
You can also render RtsActionCard dynamically inside your JSON definitions:
// Register specialized components before building
PilotComponents.registerAll();
{
"type": "rts_action_card",
"title": "**Driver RTS Request** - 50 Undeliverables",
"severity": "warning",
"status": "pending",
"description": "Driver **Aaron** requested early return to station. Reason: `Heavy Traffic`.",
"ai_diagnosis": "High volume of remaining packages. Recommend review.",
"audio_url": "https://example.com/audio/voice_note.mp3",
"expires_at": "2026-09-22T14:45:00Z",
"expires_in_seconds": 900,
"ui_schema": {
"data": {
"driver_name": "Aaron Pouncy",
"driver_phone": "+1 (555) 019-2834"
}
},
"actions": [
{
"action_type": "APPROVE_RTS",
"action_label": "⚡ Approve Return to Station",
"action_payload": {"status": "approved"}
},
{
"action_type": "REJECT_RTS",
"action_label": "❌ Reject Return",
"action_payload": {"status": "rejected"}
}
]
}
🛠️ Extending Pilot UI #
Registering Custom Widget Parsers #
Implement BaseWidgetParser and register it with PilotRegistry:
import 'package:flutter/material.dart';
import 'package:pilot_ui/pilot_ui.dart';
class CustomHeaderParser extends BaseWidgetParser {
@override
Widget parse(JsonWidgetData data, BuildContext context) {
final title = data.props['title'] ?? '';
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.deepPurple.shade50,
borderRadius: BorderRadius.circular(8),
),
child: Text(
title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
);
}
}
// In your app initialization:
PilotRegistry.registerWidget('custom_header', CustomHeaderParser());
Registering Custom Action Handlers #
Implement BaseActionHandler and register it with PilotRegistry:
import 'package:flutter/material.dart';
import 'package:pilot_ui/pilot_ui.dart';
class AnalyticsActionHandler extends BaseActionHandler {
@override
void handle(ActionModel action, BuildContext context) {
final eventName = action.params['event_name'];
debugPrint('Analytics tracked: $eventName');
}
}
// In your app initialization:
PilotRegistry.registerAction('trackAnalytics', AnalyticsActionHandler());
📂 Example App #
An interactive dashboard example is available in the example/ directory.
To run the example:
cd example
flutter run
📄 License #
This project is licensed under the MIT License.