nexabase_flutter_sdk 1.0.2
nexabase_flutter_sdk: ^1.0.2 copied to clipboard
SDK oficial de Flutter para Nexabase - Base de datos en tiempo real para aplicaciones Flutter
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:nexabase_flutter_sdk/nexabase_flutter_sdk.dart';
import 'dart:typed_data';
void main() {
runApp(const NexabaseExampleApp());
}
class NexabaseExampleApp extends StatelessWidget {
const NexabaseExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Nexabase SDK Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool _isInitialized = false;
bool _isLoading = false;
String _status = 'Inicializando...';
List<NexabaseRecord> _records = [];
NexabaseUser? _currentUser;
String _selectedCollection = 'usuarios';
@override
void initState() {
super.initState();
_initializeNexabase();
}
Future<void> _initializeNexabase() async {
setState(() {
_isLoading = true;
_status = 'Conectando a Nexabase...';
});
try {
// Inicializar el cliente
await NexabaseClient.instance.initialize(
AuthConfig(
baseUrl: 'http://localhost:3000', // Cambia por tu URL
apiKey:
'nxb_7550817293880fcfa8b4851f0903804aac684eed185c719e39645e6d25eb1d01', // Cambia por tu API key
enableDebugLogs: true,
),
);
setState(() {
_isInitialized = true;
_status = '✅ Conectado a Nexabase';
});
// Escuchar cambios de autenticación
NexabaseClient.instance.auth.authStateChanges.listen((authState) {
setState(() {
_currentUser = authState.user;
if (authState.isAuthenticated) {
_status = '✅ Usuario autenticado: ${authState.user?.email}';
} else {
_status = '⚠️ Usuario no autenticado';
}
});
});
// Cargar datos iniciales
await _loadRecords();
} catch (e) {
setState(() {
_status = '❌ Error: $e';
});
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _authenticateUser() async {
setState(() {
_isLoading = true;
_status = 'Autenticando usuario...';
});
try {
// Intenta hacer login o registro
final user = await NexabaseClient.instance.auth.login(
email: 'demo@ejemplo.com',
password: 'password123',
);
setState(() {
_currentUser = user;
_status = '✅ Usuario autenticado: ${user.email}';
});
} catch (e) {
// Si falla el login, intentar registro
try {
final user = await NexabaseClient.instance.auth.register(
email: 'demo@ejemplo.com',
password: 'password123',
firstName: 'Demo',
lastName: 'User',
);
setState(() {
_currentUser = user;
_status = '✅ Usuario registrado: ${user.email}';
});
} catch (registerError) {
setState(() {
_status = '❌ Error de autenticación: $registerError';
});
}
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _loadRecords() async {
if (!_isInitialized) return;
setState(() {
_isLoading = true;
_status = 'Cargando registros de $_selectedCollection...';
});
try {
final response = await NexabaseClient.instance.database.getRecords(
_selectedCollection,
options: QueryOptions(
limit: 10,
orderBy: 'created_at',
order: SortDirection.desc,
),
);
setState(() {
_records = response.records;
_status =
'✅ ${response.records.length} registros cargados de $_selectedCollection';
});
} catch (e) {
setState(() {
_status = '❌ Error al cargar registros: $e';
});
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _loadRecordsAdvanced() async {
if (!_isInitialized) return;
setState(() {
_isLoading = true;
_status = 'Cargando registros avanzados de $_selectedCollection...';
});
try {
final response =
await NexabaseClient.instance.database.getRecordsAdvanced(
_selectedCollection,
page: 1,
limit: 10,
search: 'demo',
filters: [
CollectionFilter(
field: 'activo',
operator: FilterOperator.equals,
value: true,
),
],
sort: [
CollectionSort(
field: 'created_at',
direction: SortDirection.desc,
),
],
);
setState(() {
_records = response.records;
_status = '✅ ${response.records.length} registros avanzados cargados';
});
} catch (e) {
setState(() {
_status = '❌ Error al cargar registros avanzados: $e';
});
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _createRecord() async {
if (!_isInitialized) return;
setState(() {
_isLoading = true;
_status = 'Creando registro en $_selectedCollection...';
});
try {
final newRecord = await NexabaseClient.instance.database.createRecord(
_selectedCollection,
{
'nombre': 'Usuario Demo ${DateTime.now().millisecondsSinceEpoch}',
'email': 'demo${DateTime.now().millisecondsSinceEpoch}@ejemplo.com',
'activo': true,
'metadata': {
'source': 'flutter_sdk_demo',
'timestamp': DateTime.now().toIso8601String(),
},
},
);
setState(() {
_records.insert(0, newRecord);
_status = '✅ Registro creado: ${newRecord.id}';
});
} catch (e) {
setState(() {
_status = '❌ Error al crear registro: $e';
});
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _startRealtimeSubscription() async {
if (!_isInitialized) return;
try {
// Habilitar tiempo real para la colección
await NexabaseClient.instance.realtime
.enableRealtimeForCollection(_selectedCollection);
// Conectar al servicio de tiempo real
await NexabaseClient.instance.realtime.connect();
// Suscribirse a cambios en tiempo real
NexabaseClient.instance.realtime.subscribe(_selectedCollection).listen(
(event) {
setState(() {
_status =
'🔄 Evento en tiempo real: ${event.type.name} en ${event.collection}';
});
// Recargar datos cuando hay cambios
_loadRecords();
},
onError: (error) {
setState(() {
_status = '❌ Error en tiempo real: $error';
});
},
);
setState(() {
_status =
'📡 Suscrito a cambios en tiempo real para $_selectedCollection';
});
} catch (e) {
setState(() {
_status = '❌ Error al configurar tiempo real: $e';
});
}
}
Future<void> _testStorageUpload() async {
if (!_isInitialized) return;
setState(() {
_isLoading = true;
_status = 'Probando subida de archivo...';
});
try {
// Crear un archivo de prueba
final testData =
'Contenido de prueba desde Flutter SDK - ${DateTime.now()}';
final bytes = Uint8List.fromList(testData.codeUnits);
final uploadedFile = await NexabaseClient.instance.storage.uploadBytes(
bytes: bytes,
fileName: 'test_flutter_sdk.txt',
mimeType: 'text/plain',
folder: 'demo',
);
setState(() {
_status =
'✅ Archivo subido: ${uploadedFile.originalName} (${uploadedFile.sizeFormatted})';
});
} catch (e) {
setState(() {
_status = '❌ Error al subir archivo: $e';
});
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _getStorageStats() async {
if (!_isInitialized) return;
setState(() {
_isLoading = true;
_status = 'Obteniendo estadísticas de almacenamiento...';
});
try {
final usage = await NexabaseClient.instance.storage.getTenantUsage();
setState(() {
_status =
'📊 Storage: ${usage.storage.usedGb.toStringAsFixed(2)}GB/${usage.storage.limitGb}GB (${usage.storage.usagePercentage.toStringAsFixed(1)}%)';
});
} catch (e) {
setState(() {
_status = '❌ Error al obtener estadísticas: $e';
});
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _getRealtimeStats() async {
if (!_isInitialized) return;
setState(() {
_isLoading = true;
_status = 'Obteniendo estadísticas de tiempo real...';
});
try {
final stats = await NexabaseClient.instance.realtime.getStats();
setState(() {
_status =
'📊 Tiempo Real: ${stats.totalConnections} conexiones, ${stats.activeCollections} colecciones activas';
});
} catch (e) {
setState(() {
_status = '❌ Error al obtener estadísticas de tiempo real: $e';
});
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _checkHealth() async {
if (!_isInitialized) return;
setState(() {
_isLoading = true;
_status = 'Verificando salud del sistema...';
});
try {
final health = await NexabaseClient.instance.checkHealth();
setState(() {
_status =
'🏥 Salud: ${health.isHealthy ? "✅ Saludable" : "❌ Con problemas"} - ${health.responseTime.inMilliseconds}ms';
});
} catch (e) {
setState(() {
_status = '❌ Error al verificar salud: $e';
});
} finally {
setState(() {
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Nexabase SDK Demo'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Estado actual
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Estado de Conexión',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Text(_status),
if (_currentUser != null) ...[
const SizedBox(height: 8),
Text(
'Usuario: ${_currentUser!.fullName} (${_currentUser!.role})'),
],
if (_isLoading) ...[
const SizedBox(height: 8),
const LinearProgressIndicator(),
],
],
),
),
),
const SizedBox(height: 16),
// Selector de colección
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Colección',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
DropdownButton<String>(
value: _selectedCollection,
isExpanded: true,
items: const [
DropdownMenuItem(
value: 'usuarios', child: Text('usuarios')),
DropdownMenuItem(
value: 'productos', child: Text('productos')),
DropdownMenuItem(
value: 'pedidos', child: Text('pedidos')),
],
onChanged: (value) {
if (value != null) {
setState(() {
_selectedCollection = value;
});
_loadRecords();
}
},
),
],
),
),
),
const SizedBox(height: 16),
// Botones de acción - Fila 1
Wrap(
spacing: 8,
runSpacing: 8,
children: [
ElevatedButton(
onPressed:
_isInitialized && !_isLoading ? _authenticateUser : null,
child: const Text('Autenticar'),
),
ElevatedButton(
onPressed:
_isInitialized && !_isLoading ? _loadRecords : null,
child: const Text('Cargar'),
),
ElevatedButton(
onPressed: _isInitialized && !_isLoading
? _loadRecordsAdvanced
: null,
child: const Text('Avanzado'),
),
ElevatedButton(
onPressed:
_isInitialized && !_isLoading ? _createRecord : null,
child: const Text('Crear'),
),
],
),
const SizedBox(height: 8),
// Botones de acción - Fila 2
Wrap(
spacing: 8,
runSpacing: 8,
children: [
ElevatedButton(
onPressed: _isInitialized ? _startRealtimeSubscription : null,
child: const Text('Tiempo Real'),
),
ElevatedButton(
onPressed:
_isInitialized && !_isLoading ? _testStorageUpload : null,
child: const Text('Upload'),
),
ElevatedButton(
onPressed:
_isInitialized && !_isLoading ? _getStorageStats : null,
child: const Text('Storage'),
),
ElevatedButton(
onPressed:
_isInitialized && !_isLoading ? _getRealtimeStats : null,
child: const Text('RT Stats'),
),
ElevatedButton(
onPressed:
_isInitialized && !_isLoading ? _checkHealth : null,
child: const Text('Salud'),
),
],
),
const SizedBox(height: 16),
// Lista de registros
Expanded(
child: Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Registros de $_selectedCollection (${_records.length})',
style: Theme.of(context).textTheme.titleMedium,
),
),
Expanded(
child: _records.isEmpty
? const Center(
child: Text('No hay registros para mostrar'),
)
: ListView.builder(
itemCount: _records.length,
itemBuilder: (context, index) {
final record = _records[index];
return ListTile(
title: Text(
record.getValue<String>('nombre') ??
record.getValue<String>('name') ??
'Sin nombre'),
subtitle: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text('ID: ${record.id}'),
if (record.getValue<String>('email') !=
null)
Text(
'Email: ${record.getValue<String>('email')}'),
if (record.createdAt != null)
Text(
'Creado: ${record.createdAt!.toLocal().toString().split('.').first}'),
],
),
trailing:
record.getValue<bool>('activo') == true
? const Icon(Icons.check_circle,
color: Colors.green)
: const Icon(Icons.cancel,
color: Colors.red),
isThreeLine: true,
);
},
),
),
],
),
),
),
],
),
),
);
}
@override
void dispose() {
// Limpiar recursos al cerrar la app
NexabaseClient.instance.dispose();
super.dispose();
}
}