quicui_supabase 2.0.3
quicui_supabase: ^2.0.3 copied to clipboard
Supabase implementation for QuicUI framework. Provides backend integration for dynamic UI rendering, real-time synchronization, offline-first architecture, and callback action system support.
QuicUI Supabase Plugin #
Supabase implementation for the QuicUI framework. Enables dynamic UI rendering, real-time synchronization, and offline-first architecture through Supabase cloud infrastructure.
Overview #
The QuicUI Supabase plugin provides a complete backend integration implementing the DataSource interface from QuicUI core. It handles:
- Screen Management: Fetch, save, search, and delete dynamic UI screens
- Real-Time Updates: Subscribe to live screen changes via WebSocket
- Offline Support: Queue operations when offline, auto-sync when reconnected
- Conflict Resolution: Intelligent handling of concurrent modifications
- PostgreSQL Backend: Leverage Supabase's PostgreSQL database for data persistence
- Authentication: Built-in Supabase Auth integration
Installation #
- Add to
pubspec.yaml:
dependencies:
quicui_supabase: ^2.0.0
- Configure Supabase in your project:
flutter pub add supabase_flutter
Quick Start #
1. Initialize Supabase #
import 'package:supabase_flutter/supabase_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Supabase.initialize(
url: 'https://your-project.supabase.co',
anonKey: 'your-anon-key',
);
runApp(const MyApp());
}
2. Register DataSource #
import 'package:quicui/quicui.dart';
import 'package:quicui_supabase/quicui_supabase.dart';
void setupQuicUI() async {
final dataSource = SupabaseDataSource(
supabaseUrl: 'https://your-project.supabase.co',
supabaseAnonKey: 'your-anon-key',
);
DataSourceProvider.instance.register(dataSource);
await dataSource.connect();
}
3. Use with QuicUI #
final repository = ScreenRepository(
dataSource: DataSourceProvider.instance.get(),
);
final screen = await repository.getScreen('home_screen');
final renderer = UIRenderer();
final widget = renderer.renderScreen(screen);
4. Complete Cloud Backend Example #
Here's a complete example using Supabase as a cloud backend for dynamic UI:
import 'package:flutter/material.dart';
import 'package:quicui/quicui.dart';
import 'package:quicui_supabase/quicui_supabase.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Supabase first
await Supabase.initialize(
url: 'https://your-project.supabase.co',
anonKey: 'your-anon-key',
);
// Initialize with backend (optional)
final dataSource = SupabaseDataSource(
'https://your-project.supabase.co',
'your-anon-key',
);
await QuicUIService().initializeWithDataSource(dataSource);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// Now fetch dynamic UI from backend
return MaterialApp(
title: 'QuicUI with Supabase',
theme: ThemeData(primarySwatch: Colors.blue),
home: FutureBuilder<Screen>(
future: QuicUIService().fetchScreen('home_screen'),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
if (snapshot.hasError) {
return Scaffold(
appBar: AppBar(title: const Text('Error')),
body: Center(child: Text('Error: ${snapshot.error}')),
);
}
if (snapshot.hasData) {
final screen = snapshot.data!;
return UIRenderer().renderScreen(screen);
}
return const Scaffold(
body: Center(child: Text('No screen found')),
);
},
),
);
}
}
This example:
- ✅ Initializes Supabase with your credentials
- ✅ Sets up QuicUI with the Supabase backend plugin
- ✅ Fetches the 'home_screen' from your Supabase database
- ✅ Renders the dynamic UI from JSON
- ✅ Handles loading, error, and success states
For more examples, see the QuicUI main package documentation.
Usage Examples #
Fetch Screens #
final dataSource = DataSourceProvider.instance.get();
// Get single screen
final screen = await dataSource.fetchScreen('home_screen');
// Get multiple screens with pagination
final screens = await dataSource.fetchScreens(limit: 20, offset: 0);
// Search screens
final results = await dataSource.searchScreens('dashboard');
// Get total count
final count = await dataSource.getScreenCount();
Save/Update Screens #
final screen = Screen(
id: 'new_screen',
name: 'New Screen',
version: 1,
rootWidget: WidgetData(...),
metadata: null,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
isActive: true,
config: const ScreenConfig(),
);
await dataSource.saveScreen('new_screen', screen);
Real-Time Updates #
final stream = dataSource.subscribeToScreen('home_screen');
stream.listen((event) {
print('Event type: ${event.type}'); // insert, update, delete
print('Data: ${event.data.id}');
print('Timestamp: ${event.timestamp}');
});
// Unsubscribe when done
await dataSource.unsubscribe('home_screen');
Handle Offline Scenarios #
// Get pending items that failed to sync
final pendingItems = await dataSource.getPendingItems();
// Attempt to sync
final syncResult = await dataSource.syncData(pendingItems);
print('Synced: ${syncResult.synced}');
print('Failed: ${syncResult.failed}');
// Check specific errors
for (final error in syncResult.errors) {
print('Failed to ${error.operation}: ${error.error}');
}
Error Handling #
import 'package:quicui_supabase/quicui_supabase.dart';
try {
final screen = await dataSource.fetchScreen('missing');
} on ScreenNotFoundException catch (e) {
print('Screen not found: ${e.message}');
} on SupabaseDataSourceException catch (e) {
print('Supabase error: ${e.message}');
} on DataSourceException catch (e) {
print('Backend error: ${e.message}');
}
Database Schema #
The plugin expects the following Supabase tables:
screens table #
create table screens (
id text primary key,
name text not null,
version integer not null default 1,
root_widget jsonb not null,
metadata jsonb,
config jsonb,
is_active boolean default true,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now()
);
-- Create index for search
create index screens_name_search on screens using gin(name gin_trgm_ops);
sync_queue table #
create table sync_queue (
id uuid primary key default gen_random_uuid(),
screen_id text not null,
operation text not null,
screen_data jsonb,
synced boolean default false,
retry_count integer default 0,
last_error text,
created_at timestamp with time zone default now(),
updated_at timestamp with time zone default now()
);
Enable Real-Time (Supabase UI) #
- Go to Database → Replication
- Enable real-time on
screenstable - Enable UPDATE, INSERT, DELETE events
Architecture #
QuicUI App
↓
ScreenRepository
↓
DataSourceProvider (Service Locator)
↓
SupabaseDataSource (This Plugin)
↓
Supabase Backend
├─ PostgreSQL (screens, sync_queue tables)
├─ Real-Time (WebSocket subscriptions)
└─ Auth (User authentication)
Features #
Screen Management #
- ✅
fetchScreen(screenId)- Get single screen - ✅
fetchScreens(limit, offset)- Paginated list - ✅
saveScreen(screenId, screen)- Create/update - ✅
deleteScreen(screenId)- Delete screen - ✅
searchScreens(query)- Full-text search - ✅
getScreenCount()- Total screen count
Real-Time Synchronization #
- ✅
subscribeToScreen(screenId)- Stream updates - ✅
unsubscribe(screenId)- Stop listening - ✅ WebSocket connections
- ✅ Automatic reconnection
Offline Support #
- ✅
getPendingItems()- Get queued operations - ✅
syncData(items)- Batch sync - ✅
resolveConflict(conflict)- Conflict resolution
Connection Management #
- ✅
connect()- Initialize connection - ✅
disconnect()- Cleanup - ✅
isConnected()- Check status
Testing #
Run tests with:
cd quicui_supabase
flutter test
Troubleshooting #
Connection Issues #
try {
await dataSource.connect();
} on SupabaseDataSourceException catch (e) {
print('Connection failed: ${e.message}');
print('Cause: ${e.originalError}');
}
Real-Time Not Working #
- Verify real-time is enabled on
screenstable - Check WebSocket connection in browser DevTools
- Ensure Supabase URL and key are correct
- Check Supabase project status/logs
Sync Failures #
final result = await dataSource.syncData(pendingItems);
for (final error in result.errors) {
print('${error.itemId}: ${error.error}');
}
Performance Tips #
- Use pagination:
fetchScreens(limit: 20, offset: 0) - Debounce search:
searchScreens(query)can be expensive - Subscribe only to needed screens
- Call
disconnect()when app closes - Use
isConnected()to check before operations
Migration from Direct Supabase #
If you're currently using Supabase directly in QuicUI:
// Before
final supabaseService = SupabaseService(...);
final screen = await supabaseService.getScreen('home');
// After
final dataSource = SupabaseDataSource(...);
DataSourceProvider.instance.register(dataSource);
final repository = ScreenRepository(dataSource: dataSource);
final screen = await repository.getScreen('home_screen');
API Reference #
See lib/src/supabase_data_source.dart for detailed API documentation.
Support #
License #
MIT License - See LICENSE file for details.
Related Packages #
- quicui - Main QuicUI framework
- supabase_flutter - Supabase client
- logger - Logging utility
❤️ Love QuicUI? #
If you're enjoying QuicUI and the Supabase plugin, consider supporting the development!
🚀 Why Support QuicUI? #
- 📚 Comprehensive documentation and guides
- �� Fast bug fixes and issue resolution
- 💡 New features and improvements based on community feedback
- 📞 Dedicated support and guidance
- 🎯 Long-term project sustainability
☕ Buy Me a Coffee #
Your support helps keep QuicUI thriving and enables faster development of new features!
Thank you for being part of the QuicUI community! 🙏