app_check_secure 0.0.1
app_check_secure: ^0.0.1 copied to clipboard
A simple, production-ready Flutter package that completely abstracts Firebase App Check for Android, iOS, and Web.
example/lib/main.dart
import 'dart:async';
import 'package:app_check_secure/app_check_secure.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
String? startupError;
try {
// 1) Firebase initialization (required before App Check).
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
// Replace these with your own values from Firebase Console.
// - recaptchaSiteKey: App Check → your Web app → reCAPTCHA site key (production)
// - *DebugToken: App Check → Manage debug tokens (debug / CI only)
const recaptchaSiteKey = 'YOUR_RECAPTCHA_SITE_KEY';
const webDebugToken = 'YOUR_WEB_APP_CHECK_DEBUG_TOKEN';
const androidDebugToken = 'YOUR_ANDROID_APP_CHECK_DEBUG_TOKEN';
const iosDebugToken = 'YOUR_IOS_APP_CHECK_DEBUG_TOKEN';
// 2) App Check — platforms default to false; enable only what you need.
// debug: true → uses *DebugToken (debug provider)
// debug: false → uses recaptchaSiteKey (reCAPTCHA)
await AppCheckSecure.initialize(
config: AppCheckConfig(
debug: kDebugMode,
enableWeb: true,
webSiteKey: recaptchaSiteKey,
webDebugToken: webDebugToken,
// Uncomment when testing Android / iOS:
// enableAndroid: true,
// enableIos: true,
// androidDebugToken: androidDebugToken,
// iosDebugToken: iosDebugToken,
),
);
} catch (error) {
startupError = error.toString();
}
runApp(AppCheckExampleApp(startupError: startupError));
}
/// Example application demonstrating [AppCheckSecure].
class AppCheckExampleApp extends StatelessWidget {
/// Creates the example app.
const AppCheckExampleApp({super.key, this.startupError});
/// Non-null when Firebase or App Check failed during startup.
final String? startupError;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'app_check_secure example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF0B6E4F)),
useMaterial3: true,
),
home: AppCheckDemoPage(startupError: startupError),
);
}
}
/// Demo page for token generation, refresh, clear, and listening.
class AppCheckDemoPage extends StatefulWidget {
/// Creates the demo page.
const AppCheckDemoPage({super.key, this.startupError});
/// Startup error message, if any.
final String? startupError;
@override
State<AppCheckDemoPage> createState() => _AppCheckDemoPageState();
}
class _AppCheckDemoPageState extends State<AppCheckDemoPage> {
StreamSubscription<String?>? _tokenSubscription;
String? _token;
String? _lastEvent;
String? _error;
bool _busy = false;
@override
void initState() {
super.initState();
_token = AppCheckSecure.currentToken;
if (AppCheckSecure.isInitialized) {
_tokenSubscription = AppCheckSecure.onTokenChanged.listen((token) {
setState(() {
_token = token;
_lastEvent =
'onTokenChanged → ${token == null ? 'null' : _short(token)}';
});
});
}
}
@override
void dispose() {
_tokenSubscription?.cancel();
super.dispose();
}
String _platformLabel() {
if (kIsWeb) {
return 'Web';
}
return switch (defaultTargetPlatform) {
TargetPlatform.android => 'Android',
TargetPlatform.iOS => 'iOS',
_ => defaultTargetPlatform.name,
};
}
String _short(String value) {
if (value.length <= 48) {
return value;
}
return '${value.substring(0, 24)}…${value.substring(value.length - 12)}';
}
Future<void> _run(Future<void> Function() action) async {
setState(() {
_busy = true;
_error = null;
});
try {
await action();
} on AppCheckException catch (error) {
setState(() => _error = error.toString());
} catch (error) {
setState(() => _error = error.toString());
} finally {
if (mounted) {
setState(() => _busy = false);
}
}
}
@override
Widget build(BuildContext context) {
final startupError = widget.startupError;
return Scaffold(
appBar: AppBar(
title: const Text('app_check_secure'),
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
Text(
'Platform: ${_platformLabel()}',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Text('Initialized: ${AppCheckSecure.isInitialized}'),
if (startupError != null) ...[
const SizedBox(height: 12),
Card(
color: Theme.of(context).colorScheme.errorContainer,
child: Padding(
padding: const EdgeInsets.all(12),
child: Text(
'Startup error:\n$startupError\n\n'
'Configure Firebase with `flutterfire configure` in the '
'example/ directory, then rebuild. For web, also pass '
'--dart-define=RECAPTCHA_SITE_KEY=...',
),
),
),
],
const SizedBox(height: 16),
Text('Current token', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 8),
SelectableText(_token == null ? 'null' : _token!),
if (_lastEvent != null) ...[
const SizedBox(height: 12),
Text('Last stream event: $_lastEvent'),
],
if (_error != null) ...[
const SizedBox(height: 12),
Text(
_error!,
style: TextStyle(color: Theme.of(context).colorScheme.error),
),
],
const SizedBox(height: 24),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
FilledButton(
onPressed: !AppCheckSecure.isInitialized || _busy
? null
: () => _run(() async {
final token = await AppCheckSecure.getToken();
setState(() {
_token = token;
_lastEvent =
'getToken → ${token == null ? 'null' : _short(token)}';
});
}),
child: const Text('Get token'),
),
FilledButton.tonal(
onPressed: !AppCheckSecure.isInitialized || _busy
? null
: () => _run(() async {
final token = await AppCheckSecure.refreshToken();
setState(() {
_token = token;
_lastEvent =
'refreshToken → ${token == null ? 'null' : _short(token)}';
});
}),
child: const Text('Refresh token'),
),
OutlinedButton(
onPressed: !AppCheckSecure.isInitialized || _busy
? null
: () => _run(() async {
await AppCheckSecure.clearToken();
setState(() {
_token = AppCheckSecure.currentToken;
_lastEvent = 'clearToken → null';
});
}),
child: const Text('Clear token'),
),
],
),
const SizedBox(height: 32),
Text('Notes', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 8),
const Text(
'• Platforms default to false — set enableAndroid / enableIos / enableWeb.\n'
'• Set debug: true/false — package picks providers.\n'
'• Optional androidDebugToken / iosDebugToken / webDebugToken (or setters).\n'
'• Web production: reCAPTCHA — requires webSiteKey.\n'
'• Register debug tokens in Firebase Console → App Check.',
),
],
),
);
}
}