Dart/Flutter SDK
Build cross-platform mobile apps with Flutter that integrate with Staqr Platform and Commerce APIs.
Security Notice
Never embed API keys directly in mobile applications.
Instead, use one of these patterns:
- OAuth2/OIDC user authentication
- Backend proxy for sensitive API calls
- Token-based authentication with refresh
See Mobile Security for detailed guidance.
Installation
Add to your pubspec.yaml:
dependencies:
staqr_api_client: ^1.0.0
Then run:
dart pub get
Quick Start
import 'package:staqr_api_client/staqr_api_client.dart';
void main() async {
// Configuration - token from secure source (not hardcoded!)
final client = StaqrApiClient(
baseUrl: 'https://api.staqr.com',
accessToken: await SecureStorage.getToken(),
);
try {
// List flows
final flows = await client.getFlowsApi().listFlows(limit: 50);
print('Found ${flows.length} flows');
for (final flow in flows) {
print(' - ${flow.name} (${flow.id})');
}
} catch (e) {
print('Error: $e');
}
}
Available Packages
| Package | API | Platform Support |
|---|---|---|
staqr_api_client | Staqr Platform | iOS, Android, Web |
staqr_commerce_v0 | Commerce v0 (Legacy) | iOS, Android, Web |
staqr_commerce_v1 | Commerce v1 | iOS, Android, Web |
staqr_commerce_v2 | Commerce v2 | iOS, Android, Web |
Authentication
Secure Token Storage
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class SecureTokenService {
final FlutterSecureStorage _storage = const FlutterSecureStorage();
Future<void> saveToken(String token) async {
await _storage.write(key: 'staqr_access_token', value: token);
}
Future<String?> getToken() async {
return await _storage.read(key: 'staqr_access_token');
}
Future<void> deleteToken() async {
await _storage.delete(key: 'staqr_access_token');
}
}
OAuth2 Integration
import 'package:flutter_appauth/flutter_appauth.dart';
class AuthService {
final FlutterAppAuth _appAuth = const FlutterAppAuth();
Future<String?> authenticate() async {
final result = await _appAuth.authorizeAndExchangeCode(
AuthorizationTokenRequest(
'your-client-id',
'your-redirect-uri',
issuer: 'https://auth.staqr.com',
scopes: ['openid', 'profile', 'email'],
),
);
return result?.accessToken;
}
}
Flutter State Management
With Provider
import 'package:provider/provider.dart';
import 'package:staqr_api_client/staqr_api_client.dart';
class StaqrProvider extends ChangeNotifier {
StaqrApiClient? _client;
List<Flow> _flows = [];
bool _isLoading = false;
List<Flow> get flows => _flows;
bool get isLoading => _isLoading;
Future<void> initialize(String token) async {
_client = StaqrApiClient(
baseUrl: 'https://api.staqr.com',
accessToken: token,
);
notifyListeners();
}
Future<void> loadFlows() async {
if (_client == null) return;
_isLoading = true;
notifyListeners();
try {
_flows = await _client!.getFlowsApi().listFlows(limit: 100);
} catch (e) {
print('Error loading flows: $e');
} finally {
_isLoading = false;
notifyListeners();
}
}
}
// Usage in widget
class FlowsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<StaqrProvider>(
builder: (context, provider, child) {
if (provider.isLoading) {
return const CircularProgressIndicator();
}
return ListView.builder(
itemCount: provider.flows.length,
itemBuilder: (context, index) {
final flow = provider.flows[index];
return ListTile(
title: Text(flow.name),
subtitle: Text(flow.id),
);
},
);
},
);
}
}
With Riverpod
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:staqr_api_client/staqr_api_client.dart';
// Token provider (from secure storage)
final tokenProvider = FutureProvider<String?>((ref) async {
final storage = FlutterSecureStorage();
return await storage.read(key: 'staqr_access_token');
});
// Staqr client provider
final staqrClientProvider = Provider<StaqrApiClient?>((ref) {
final tokenAsync = ref.watch(tokenProvider);
return tokenAsync.when(
data: (token) => token != null
? StaqrApiClient(baseUrl: 'https://api.staqr.com', accessToken: token)
: null,
loading: () => null,
error: (_, __) => null,
);
});
// Flows provider
final flowsProvider = FutureProvider<List<Flow>>((ref) async {
final client = ref.watch(staqrClientProvider);
if (client == null) return [];
return await client.getFlowsApi().listFlows(limit: 100);
});
With Bloc
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:staqr_api_client/staqr_api_client.dart';
// Events
abstract class FlowsEvent {}
class LoadFlows extends FlowsEvent {}
class RefreshFlows extends FlowsEvent {}
// States
abstract class FlowsState {}
class FlowsInitial extends FlowsState {}
class FlowsLoading extends FlowsState {}
class FlowsLoaded extends FlowsState {
final List<Flow> flows;
FlowsLoaded(this.flows);
}
class FlowsError extends FlowsState {
final String message;
FlowsError(this.message);
}
// Bloc
class FlowsBloc extends Bloc<FlowsEvent, FlowsState> {
final StaqrApiClient _client;
FlowsBloc(this._client) : super(FlowsInitial()) {
on<LoadFlows>(_onLoadFlows);
on<RefreshFlows>(_onRefreshFlows);
}
Future<void> _onLoadFlows(LoadFlows event, Emitter<FlowsState> emit) async {
emit(FlowsLoading());
try {
final flows = await _client.getFlowsApi().listFlows(limit: 100);
emit(FlowsLoaded(flows));
} catch (e) {
emit(FlowsError(e.toString()));
}
}
Future<void> _onRefreshFlows(RefreshFlows event, Emitter<FlowsState> emit) async {
try {
final flows = await _client.getFlowsApi().listFlows(limit: 100);
emit(FlowsLoaded(flows));
} catch (e) {
emit(FlowsError(e.toString()));
}
}
}
Error Handling
import 'package:dio/dio.dart';
import 'package:staqr_api_client/staqr_api_client.dart';
Future<void> safeApiCall() async {
try {
final flows = await client.getFlowsApi().listFlows();
// Handle success
} on DioException catch (e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
case DioExceptionType.sendTimeout:
case DioExceptionType.receiveTimeout:
// Handle timeout
showError('Connection timed out. Please try again.');
break;
case DioExceptionType.badResponse:
final statusCode = e.response?.statusCode;
if (statusCode == 401) {
// Token expired - refresh or re-authenticate
await refreshToken();
} else if (statusCode == 403) {
showError('You do not have permission to perform this action.');
} else if (statusCode == 404) {
showError('Resource not found.');
} else {
showError('Server error. Please try again later.');
}
break;
case DioExceptionType.cancel:
// Request was cancelled
break;
default:
showError('Network error. Please check your connection.');
}
} catch (e) {
showError('An unexpected error occurred.');
}
}
Offline Support
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:hive_flutter/hive_flutter.dart';
class OfflineAwareStaqrService {
final StaqrApiClient _client;
final Box<List<Flow>> _cache;
OfflineAwareStaqrService(this._client, this._cache);
Future<List<Flow>> getFlows({bool forceRefresh = false}) async {
final connectivity = await Connectivity().checkConnectivity();
final isOnline = connectivity != ConnectivityResult.none;
if (isOnline && (forceRefresh || !_cache.containsKey('flows'))) {
// Fetch from API and cache
final flows = await _client.getFlowsApi().listFlows();
await _cache.put('flows', flows);
return flows;
}
// Return cached data
return _cache.get('flows') ?? [];
}
}
Requirements
- Dart SDK >= 2.17.0
- Flutter >= 3.0.0 (for Flutter apps)
Dependencies
The SDK uses:
diofor HTTP requestsjson_serializablefor JSON parsingbuilt_valuefor immutable models
Next Steps
- Mobile Security Guide - Critical security patterns
- Authentication Guide - Token management
- API Reference - Full endpoint documentation