Skip to Content
Apso is in public beta. Get started

Flutter integration

Flutter applications connect to Apso through the generated REST API. Use an end-user access token in the mobile client and keep service API keys in a trusted server environment.

Add dependencies

flutter pub add http flutter_secure_storage

Create the client

lib/api_client.dart
import 'dart:convert'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:http/http.dart' as http; class ApiException implements Exception { final int statusCode; final String message; ApiException(this.statusCode, this.message); } class ApiClient { ApiClient({required this.baseUrl}); final String baseUrl; final FlutterSecureStorage _storage = const FlutterSecureStorage(); Future<Map<String, String>> _headers() async { final token = await _storage.read(key: 'access_token'); return { 'Content-Type': 'application/json', if (token != null) 'Authorization': 'Bearer $token', }; } Future<dynamic> get(String path) async { final response = await http.get( Uri.parse('$baseUrl$path'), headers: await _headers(), ); return _decode(response); } Future<dynamic> post(String path, Map<String, dynamic> body) async { final response = await http.post( Uri.parse('$baseUrl$path'), headers: await _headers(), body: jsonEncode(body), ); return _decode(response); } dynamic _decode(http.Response response) { if (response.statusCode >= 200 && response.statusCode < 300) { return response.body.isEmpty ? null : jsonDecode(response.body); } throw ApiException(response.statusCode, 'API request failed'); } }

Do not embed an Apso service API key in the Flutter application. Mobile users can extract values shipped in the bundle.

Model a paginated response

lib/project.dart
class Project { Project({required this.id, required this.name}); final int id; final String name; factory Project.fromJson(Map<String, dynamic> json) { return Project(id: json['id'] as int, name: json['name'] as String); } } class ProjectPage { ProjectPage({required this.data, required this.total}); final List<Project> data; final int total; factory ProjectPage.fromJson(Map<String, dynamic> json) { return ProjectPage( data: (json['data'] as List) .map((item) => Project.fromJson(item as Map<String, dynamic>)) .toList(), total: json['total'] as int, ); } }

Query and create projects

lib/project_service.dart
import 'api_client.dart'; import 'project.dart'; class ProjectService { ProjectService(this.client); final ApiClient client; Future<ProjectPage> list() async { final json = await client.get('/Projects?limit=20&page=1'); return ProjectPage.fromJson(json as Map<String, dynamic>); } Future<Project> create(String name) async { final json = await client.post('/Projects', { 'name': name, 'status': 'Active', }); return Project.fromJson(json as Map<String, dynamic>); } }

Store and clear the user token

const storage = FlutterSecureStorage(); await storage.write(key: 'access_token', value: accessToken); await storage.delete(key: 'access_token');

The access token must come from an authentication flow configured for the generated backend. See Bring Your Own Auth.

Mobile checklist

  • Use HTTPS outside local development.
  • Store user tokens in platform-backed secure storage.
  • Clear expired credentials after 401 responses.
  • Handle offline, timeout, empty, and retry states.
  • Keep service API keys in a server or BFF.
  • Validate tenant scope on every protected request.
Last updated on