Flutter Quickstart
Use this guide to add ThunderID authentication to a Flutter application using the thunderid_flutter package.
What You Will Learn
- Create a new Flutter project
- Install the
thunderid_flutterpackage
- Add working sign-in and sign-out using Flutter widgets
- Display the signed-in user's name
Prerequisites
- About 20 minutes
- Flutter 3.16+ and Dart 3.2+
- iOS 16+ or Android API 26+ target device or simulator
Check out the complete Flutter Quickstart Sample in the ThunderID repository.
Run ThunderID
Start a local ThunderID instance. Pick the method that works best for you:
Requires Node.js 18+
Full install guide →Once it's running, the console is available at https://localhost:8090/console.
Create an Application
-
Sign in to the Console.
Test UserIf you used the default setup, sign in to the Console as
adminwith the password generated during setup and printed to the setup output (unless you supplied your own). -
Navigate to Applications, and click Add Application.
-
Under Application Type, select Mobile App.
-
Enter a name (e.g.
My Flutter App) and create an application. The rest of the settings can stay at their defaults. -
Copy the Client ID from the General tab. You'll add the redirect URI in the next step after configuring your URL scheme.
Create a Flutter App
Create a new Flutter project:
flutter create my_app
cd my_app
If you already have an existing Flutter project, skip this step.
Install thunderid_flutter Package
Add the package to your pubspec.yaml:
dependencies:
thunderid_flutter: ^0.1.0
Then install it:
flutter pub get
Configure a Callback URL Scheme
ThunderID redirects back to your app after sign-in and sign-out using a custom URL scheme. You need to register this scheme on both platforms.
iOS
Open ios/Runner/Info.plist and add a URL scheme under URL Types:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>dev.thunderid.app</string>
</array>
</dict>
</array>
Android
Open android/app/src/main/AndroidManifest.xml and add an intent filter inside your <activity> tag:
<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="dev.thunderid.app"
android:host="callback" />
</intent-filter>
Register the URIs in the console
In the ThunderID console, open your registered application and add the following as an Allowed Redirect URI:
dev.thunderid.app://callback
Add the same value as an Allowed Post-Logout Redirect URI:
dev.thunderid.app://logout
Initialize the SDK
Wrap your root widget with ThunderIDProvider in lib/main.dart:
import 'package:flutter/material.dart';
import 'package:thunderid_flutter/thunderid_flutter.dart';
void main() {
runApp(
ThunderIDProvider(
config: ThunderIDConfig(
baseUrl: 'https://localhost:8090',
clientId: '<your-client-id>',
scopes: const ['openid', 'profile', 'email'],
afterSignInUrl: 'dev.thunderid.app://callback',
afterSignOutUrl: 'dev.thunderid.app://logout',
applicationId: '<your-application-id>',
),
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: RootScreen(),
);
}
}
Replace <your-client-id> with the Client ID and <your-application-id> with the Application ID from your ThunderID application settings.
Configuration Parameters
| Parameter | Description |
|---|---|
baseUrl | Your ThunderID instance URL. Must use HTTPS. |
clientId | The Client ID from your ThunderID application |
scopes | OAuth 2.0 scopes to request. Include 'openid' at minimum. |
afterSignInUrl | The redirect URI to return to after sign-in |
afterSignOutUrl | The redirect URI to return to after sign-out |
applicationId | The Application ID used for embedded (app-native) sign-in flows |
Add Sign-In and Sign-Out
Create a root screen that reads auth state from ThunderIDProvider.of(context) and routes to either your auth or home screen.
Create lib/root_screen.dart:
import 'package:flutter/material.dart';
import 'package:thunderid_flutter/thunderid_flutter.dart';
import 'auth_screen.dart';
import 'home_screen.dart';
class RootScreen extends StatelessWidget {
const RootScreen({super.key});
@override
Widget build(BuildContext context) {
final thunder = ThunderIDProvider.of(context);
if (!thunder.initialized || thunder.isLoading) {
return const Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
return thunder.isSignedIn ? const HomeScreen() : const AuthScreen();
}
}
Create lib/auth_screen.dart to display the sign-in form:
import 'package:flutter/material.dart';
import 'package:thunderid_flutter/thunderid_flutter.dart';
enum _AuthMode { signIn, signUp }
class AuthScreen extends StatefulWidget {
const AuthScreen({super.key});
@override
State<AuthScreen> createState() => _AuthScreenState();
}
class _AuthScreenState extends State<AuthScreen> {
_AuthMode _mode = _AuthMode.signIn;
@override
Widget build(BuildContext context) {
final thunder = ThunderIDProvider.of(context);
final applicationId = thunder.config?.applicationId ?? '';
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 40),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SegmentedButton<_AuthMode>(
segments: const [
ButtonSegment(value: _AuthMode.signIn, label: Text('Sign In')),
ButtonSegment(value: _AuthMode.signUp, label: Text('Create Account')),
],
selected: {_mode},
onSelectionChanged: (s) => setState(() => _mode = s.first),
),
const SizedBox(height: 28),
if (_mode == _AuthMode.signIn)
SignIn(applicationId: applicationId)
else
SignUp(applicationId: applicationId),
],
),
),
),
);
}
}
Display User Profile Information
Create lib/home_screen.dart to show the authenticated user's name and a sign-out button:
import 'package:flutter/material.dart';
import 'package:thunderid_flutter/thunderid_flutter.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
final thunder = ThunderIDProvider.of(context);
final user = thunder.user;
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (user != null) ...[
Text(
'Welcome, ${user.displayName ?? user.email ?? 'User'}!',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
user.email ?? '',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 24),
],
SignOutButton(),
],
),
),
);
}
}
Run the App
Start an iOS simulator or connect an Android device, then run:
flutter run
You'll need a user to sign in with. If you haven't created one yet, open https://localhost:8090/console, navigate to Users, and add a test user with an email and password.
You should see the sign-in form. Enter your test user credentials and tap Submit. After successful authentication, the home screen displays the user's name and a Sign Out button.