SDK (Beta)FlutterSetup & Guide

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.0
flutter pub get

Initialize

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 file

Complete 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