Skip to content

Flutter SDK

  • 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

Add to pubspec.yaml:

dependencies:
aerostack: ^1.0.0
Terminal window
flutter pub get
import 'package:aerostack/aerostack.dart';
final client = AerostackClient(
projectId: 'your-project-id',
apiKey: 'your-public-api-key',
baseUrl: 'https://api.aerostack.dev/v1',
);

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);
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;
}

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();

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 file
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,
),
],
),
),
],
),
);
}
}
  • 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