Flutter SDK
The Flutter SDK is generated from the OpenAPI spec and focuses on client-side features: auth, realtime, and storage. For database, cache, and queue operations, call your server-side Logic Modules via the API.
⚠️
Beta — The Flutter SDK is in active beta. APIs may change between minor versions.
What you can build
- Mobile apps with auth — Registration, login, OTP, password reset on iOS and Android
- Real-time chat — Live messaging with presence indicators and message history
- Collaborative features — Shared lists, collaborative editing, live cursors
- Photo/file uploads — Camera capture to CDN with progress tracking
- Live dashboards — Real-time data feeds on mobile and tablet
- Cross-platform apps — Single codebase for iOS, Android, web, and desktop
Install
Add to pubspec.yaml:
dependencies:
aerostack: ^1.0.0flutter pub getInitialize
import 'package:aerostack/aerostack.dart';
final client = AerostackClient(
projectId: 'your-project-id',
apiKey: 'your-public-api-key',
baseUrl: 'https://api.aerostack.dev/v1',
);Auth
Full authentication lifecycle — registration, login, OTP, password reset, and email verification:
// Register
final result = await client.auth.register(
email: 'jane@example.com',
password: 'securePassword123',
name: 'Jane Doe',
);
if (result.requiresVerification) {
// Show "check your email" screen
}
// Login
final result = await client.auth.login(
email: 'jane@example.com',
password: 'securePassword123',
);
final accessToken = result.accessToken;
// Passwordless OTP
await client.auth.sendOtp(email: 'jane@example.com');
final result = await client.auth.verifyOtp(
email: 'jane@example.com',
code: '482916',
);
// Get current user
final user = await client.auth.me(accessToken: accessToken);
// Password reset
await client.auth.requestPasswordReset(email: 'jane@example.com');
await client.auth.resetPassword(token: resetToken, newPassword: 'newPassword456');
// Email verification
await client.auth.verifyEmail(token: verificationToken);
// Logout
await client.auth.logout(accessToken: accessToken, refreshToken: refreshToken);AuthResult type
class AuthResult {
final String? accessToken;
final String? refreshToken;
final int? expiresAt;
final User? user;
final bool requiresVerification;
}
class User {
final String id;
final String email;
final String? name;
final String? avatarUrl;
final bool emailVerified;
final Map<String, dynamic>? customFields;
}Realtime
Pub/sub channels with presence tracking and message history:
final channel = client.realtime.channel('chat/general');
// Listen for messages
channel.on('message', (payload) {
print('New message: ${payload.data}');
setState(() {
messages.add(payload.data);
});
});
// Subscribe to DB changes
final ordersChannel = client.realtime.channel('orders');
ordersChannel.on('INSERT', (payload) {
print('New order: ${payload.data}');
});
await channel.subscribe();
await ordersChannel.subscribe();
// Publish a message
channel.publish('message', {
'text': 'Hello from Flutter!',
'userId': currentUser.id,
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
// Publish with persistence (for message history)
channel.publish('message', messageData, persist: true);
// Presence -- track who's online
channel.track({
'userId': currentUser.id,
'name': currentUser.name,
'status': 'online',
});
channel.on('presence:join', (payload) {
print('${payload.data['name']} joined');
});
channel.on('presence:leave', (payload) {
print('${payload.data['name']} left');
});
// Message history
final messages = await channel.getHistory(limit: 50);
// Cleanup
channel.untrack();
channel.unsubscribe();Storage
Upload files from the device:
import 'dart:io';
final file = File('/path/to/photo.jpg');
final result = await client.storage.upload(
file: file,
path: 'avatars/${currentUser.id}.jpg',
contentType: 'image/jpeg',
);
print(result.url); // CDN URL for the uploaded fileComplete example: chat screen
lib/screens/chat_screen.dart
import 'package:flutter/material.dart';
import 'package:aerostack/aerostack.dart';
class ChatScreen extends StatefulWidget {
final String roomId;
const ChatScreen({required this.roomId});
@override
State<ChatScreen> createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final _controller = TextEditingController();
final _messages = <Map<String, dynamic>>[];
final _online = <String, Map<String, dynamic>>{};
late final channel;
@override
void initState() {
super.initState();
_initRealtime();
}
Future<void> _initRealtime() async {
channel = client.realtime.channel('chat/${widget.roomId}');
// Load history
final history = await channel.getHistory(limit: 100);
setState(() {
_messages.addAll(history.map((h) => h.data));
});
// Track presence
channel.track({
'userId': currentUser.id,
'name': currentUser.name,
});
// Listen for new messages
channel.on('message', (payload) {
setState(() {
_messages.add(payload.data);
});
});
// Listen for presence changes
channel.on('presence:join', (payload) {
setState(() {
_online[payload.data['userId']] = payload.data;
});
});
channel.on('presence:leave', (payload) {
setState(() {
_online.remove(payload.data['userId']);
});
});
await channel.subscribe();
}
void _sendMessage() {
if (_controller.text.isEmpty) return;
channel.publish('message', {
'userId': currentUser.id,
'name': currentUser.name,
'text': _controller.text,
'timestamp': DateTime.now().millisecondsSinceEpoch,
}, persist: true);
_controller.clear();
}
@override
void dispose() {
channel.untrack();
channel.unsubscribe();
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Chat (${_online.length} online)'),
),
body: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: _messages.length,
itemBuilder: (ctx, i) {
final msg = _messages[i];
return ListTile(
title: Text(msg['name'] ?? 'Unknown'),
subtitle: Text(msg['text'] ?? ''),
);
},
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: const InputDecoration(hintText: 'Message...'),
),
),
IconButton(
icon: const Icon(Icons.send),
onPressed: _sendMessage,
),
],
),
),
],
),
);
}
}Next steps
- API Reference — Full method listing
- Auth — Detailed authentication patterns
- Realtime — Full guide for pub/sub, presence, and DB change events
- Error Handling — Error types and retry strategies