Optare v1.0 is now available. Get started →
SDKs
Flutter

Flutter SDK Quick Start

The official Flutter SDK for Optare ID provides OAuth 2.0 authentication with PKCE, secure token storage, and user management for iOS and Android apps.

Installation

Add to your pubspec.yaml:

dependencies:
  optareid_flutter: ^0.1.3

Run:

flutter pub get

Platform Configuration

iOS Setup

Add URL scheme to ios/Runner/Info.plist:

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
  </dict>
</array>

Android Setup

Add intent filter to android/app/src/main/AndroidManifest.xml:

<activity ...>
  <intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <data android:scheme="myapp" android:host="callback"/>
  </intent-filter>
</activity>

Initialize SDK

import 'package:optareid_flutter/optareid_flutter.dart';
 
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  await OptareAuth.init(
    clientId: 'your-client-id',
    redirectUri: 'myapp://callback',
  );
  
  runApp(const MyApp());
}

Basic Usage

Login

ElevatedButton(
  onPressed: () => OptareAuth.login(),
  child: const Text('Sign in with Optare'),
)

Check Authentication

Widget build(BuildContext context) {
  if (OptareAuth.isAuthenticated) {
    return HomeScreen(user: OptareAuth.currentUser);
  }
  return LoginScreen();
}

Get User Profile

final user = await OptareAuth.getUser();
print('Welcome, ${user?.name}');
print('Email: ${user?.email}');
print('Organization: ${user?.organizationId}');

Check Licenses

if (OptareAuth.hasLicense('premium-plan')) {
  // Show premium features
}
 
if (OptareAuth.hasEntitlement('export-data')) {
  // Enable export button
}

Get Organizations

final orgs = await OptareAuth.getOrganizations();
for (final org in orgs) {
  print('${org.name} - Role: ${org.role}');
}

Get Licenses (Server-Side Validation)

final licenses = await OptareAuth.getLicenses();
for (final license in licenses) {
  if (license.isValid && license.hasFeature('premium')) {
    // User has premium access
  }
}

Update Profile

final updated = await OptareAuth.updateProfile(
  name: 'New Name',
  avatarUrl: 'https://example.com/avatar.jpg',
);

Logout

await OptareAuth.logout();
Navigator.pushReplacement(context, 
  MaterialPageRoute(builder: (_) => LoginScreen()));

Configuration Options

OptionTypeDefaultDescription
clientIdStringRequiredOAuth client ID from Optare console
redirectUriStringRequiredDeep link callback URI
issuerStringhttps://id.optare.oneOptare ID server URL
scopesList<String>['openid', 'email', 'profile']OAuth scopes
storageTokenStorageSecureTokenStorage()Token storage adapter

User Object

class OptareUser {
  String id;           // Unique user ID
  String? email;       // Email address
  String? name;        // Display name
  String? picture;     // Profile picture URL
  String? organizationId;  // Organization ID
  List<String> licenses;   // Product licenses
  List<String> entitlements; // Feature entitlements
  List<String> roles;       // User roles
}

Error Handling

try {
  await OptareAuth.login();
} on OptareAuthException catch (e) {
  showSnackBar('Authentication failed: ${e.message}');
} on OptareNetworkException catch (e) {
  showSnackBar('Network error: ${e.message}');
}

Complete Example

import 'package:flutter/material.dart';
import 'package:optareid_flutter/optareid_flutter.dart';
 
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  await OptareAuth.init(
    clientId: 'your-client-id',
    redirectUri: 'myapp://callback',
  );
  
  runApp(const MyApp());
}
 
class MyApp extends StatelessWidget {
  const MyApp({super.key});
 
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      home: OptareAuth.isAuthenticated 
          ? const HomeScreen() 
          : const LoginScreen(),
    );
  }
}
 
class LoginScreen extends StatelessWidget {
  const LoginScreen({super.key});
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () => OptareAuth.login(),
          child: const Text('Sign in with Optare'),
        ),
      ),
    );
  }
}
 
class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});
 
  @override
  Widget build(BuildContext context) {
    final user = OptareAuth.currentUser;
    
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
        actions: [
          IconButton(
            icon: const Icon(Icons.logout),
            onPressed: () async {
              await OptareAuth.logout();
              Navigator.pushReplacement(
                context,
                MaterialPageRoute(builder: (_) => const LoginScreen()),
              );
            },
          ),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (user?.picture != null)
              CircleAvatar(
                backgroundImage: NetworkImage(user!.picture!),
                radius: 50,
              ),
            const SizedBox(height: 16),
            Text('Welcome, ${user?.name ?? 'User'}!'),
            Text(user?.email ?? ''),
          ],
        ),
      ),
    );
  }
}

Troubleshooting

Android Build Error (Pre-v0.1.3)

If you see incorrect androidPackage: one.optare.optareid_flutter, upgrade to v0.1.3:

flutter pub upgrade optareid_flutter

Deep Links Not Working

Ensure your redirect URI scheme matches:

  1. pubspec.yamlredirectUri parameter
  2. android/app/src/main/AndroidManifest.xmlandroid:scheme
  3. ios/Runner/Info.plistCFBundleURLSchemes
  4. Optare Dashboard → OAuth Client redirect URIs

Next Steps