flutter_cia 1.0.1
flutter_cia: ^1.0.1 copied to clipboard
CIA Authentication SDK for Flutter - Secure login, registration, and organization token exchange
import 'package:flutter/material.dart';
import 'package:flutter_cia/flutter_cia.dart';
import 'package:provider/provider.dart';
/// Example app demonstrating flutter_cia SDK usage
void main() {
runApp(const CIAExampleApp());
}
class CIAExampleApp extends StatelessWidget {
const CIAExampleApp({super.key});
@override
Widget build(BuildContext context) {
// Wrap with CIAAuthProvider for auth state management
return CIAAuthProvider(
baseUrl: 'https://cia.mn/api/v1', // Your CIA server URL with API version
child: MaterialApp(
title: 'CIA Auth Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const AuthWrapper(),
),
);
}
}
/// Wrapper that handles auth state routing
class AuthWrapper extends StatelessWidget {
const AuthWrapper({super.key});
@override
Widget build(BuildContext context) {
return Consumer<CIAAuthState>(
builder: (context, authState, _) {
// Show appropriate screen based on auth status
switch (authState.status) {
case CIAAuthStatus.initializing:
return const _LoadingScreen();
case CIAAuthStatus.unauthenticated:
case CIAAuthStatus.error:
return const AuthNavigator();
case CIAAuthStatus.pendingVerification:
return CIAVerificationScreen(
verificationType: authState.verificationType,
phone: authState.pendingVerificationData?['phone'],
branding: _getBranding(),
onVerificationSuccess: () {
// Navigation handled automatically by auth state change
},
onCancel: () async {
await authState.cancelVerification();
},
);
case CIAAuthStatus.authenticated:
return const HomeScreen();
}
},
);
}
}
/// Auth screens navigator (login/register)
class AuthNavigator extends StatefulWidget {
const AuthNavigator({super.key});
@override
State<AuthNavigator> createState() => _AuthNavigatorState();
}
class _AuthNavigatorState extends State<AuthNavigator> {
bool _showLogin = true;
@override
Widget build(BuildContext context) {
if (_showLogin) {
return CIALoginScreen(
branding: _getBranding(),
enablePhoneLogin: true,
onLoginSuccess: () {
// Navigation handled by auth state change
},
onRegisterTap: () => setState(() => _showLogin = false),
onForgotPasswordTap: () {
// Navigate to forgot password screen
_showForgotPasswordDialog(context);
},
);
} else {
return CIARegisterScreen(
branding: _getBranding(),
requirePhone: false,
onRegisterSuccess: () {
// Navigation handled by auth state change
},
onLoginTap: () => setState(() => _showLogin = true),
);
}
}
void _showForgotPasswordDialog(BuildContext context) {
final emailController = TextEditingController();
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Reset Password'),
content: TextField(
controller: emailController,
decoration: const InputDecoration(
labelText: 'Email',
hintText: 'Enter your email',
),
keyboardType: TextInputType.emailAddress,
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () async {
final authState = context.read<CIAAuthState>();
final success = await authState.requestPasswordReset(
email: emailController.text.trim(),
);
if (context.mounted) {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
success
? 'Password reset email sent!'
: authState.errorMessage ?? 'Failed to send reset email',
),
),
);
}
},
child: const Text('Send Reset Link'),
),
],
),
);
}
}
/// Home screen after authentication
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
final authState = context.watch<CIAAuthState>();
final user = authState.user;
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
actions: [
IconButton(
icon: const Icon(Icons.logout),
onPressed: () async {
await authState.logout();
},
),
],
),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// User info card
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Welcome!',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text('Email: ${user?.email ?? 'N/A'}'),
if (user?.name != null)
Text('Name: ${user?.displayName}'),
if (user?.phone != null)
Text('Phone: ${user?.phone}'),
const SizedBox(height: 8),
Row(
children: [
Icon(
user?.isVerified == true
? Icons.verified
: Icons.warning,
color: user?.isVerified == true
? Colors.green
: Colors.orange,
size: 16,
),
const SizedBox(width: 4),
Text(
user?.isVerified == true
? 'Verified'
: 'Not Verified',
style: TextStyle(
color: user?.isVerified == true
? Colors.green
: Colors.orange,
),
),
],
),
],
),
),
),
const SizedBox(height: 16),
// Organizations button
ElevatedButton.icon(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ChangeNotifierProvider.value(
value: authState,
child: CIAOrgSelectorScreen(
branding: _getBranding(),
showExchangeCodeOption: true,
onOrgSelected: (org) {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Selected: ${org.name}'),
),
);
},
),
),
),
);
},
icon: const Icon(Icons.business),
label: const Text('My Organizations'),
),
const SizedBox(height: 12),
// Profile button
OutlinedButton.icon(
onPressed: () {
_showProfileDialog(context, authState);
},
icon: const Icon(Icons.person),
label: const Text('Edit Profile'),
),
const SizedBox(height: 12),
// Change password button
OutlinedButton.icon(
onPressed: () {
_showChangePasswordDialog(context, authState);
},
icon: const Icon(Icons.lock),
label: const Text('Change Password'),
),
],
),
),
),
);
}
void _showProfileDialog(BuildContext context, CIAAuthState authState) {
final user = authState.user;
final nameController = TextEditingController(text: user?.name);
final phoneController = TextEditingController(text: user?.phone);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Edit Profile'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: nameController,
decoration: const InputDecoration(labelText: 'Name'),
),
const SizedBox(height: 8),
TextField(
controller: phoneController,
decoration: const InputDecoration(labelText: 'Phone'),
keyboardType: TextInputType.phone,
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () async {
final success = await authState.updateProfile(
name: nameController.text.trim(),
phone: phoneController.text.trim(),
);
if (context.mounted) {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
success ? 'Profile updated!' : authState.errorMessage ?? 'Failed',
),
),
);
}
},
child: const Text('Save'),
),
],
),
);
}
void _showChangePasswordDialog(BuildContext context, CIAAuthState authState) {
final currentPasswordController = TextEditingController();
final newPasswordController = TextEditingController();
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Change Password'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: currentPasswordController,
decoration: const InputDecoration(labelText: 'Current Password'),
obscureText: true,
),
const SizedBox(height: 8),
TextField(
controller: newPasswordController,
decoration: const InputDecoration(labelText: 'New Password'),
obscureText: true,
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () async {
final success = await authState.changePassword(
currentPassword: currentPasswordController.text,
newPassword: newPasswordController.text,
);
if (context.mounted) {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
success ? 'Password changed!' : authState.errorMessage ?? 'Failed',
),
),
);
}
},
child: const Text('Change'),
),
],
),
);
}
}
/// Loading screen
class _LoadingScreen extends StatelessWidget {
const _LoadingScreen();
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Loading...'),
],
),
),
);
}
}
/// Get branding config (customize for your app)
CIABrandingConfig _getBranding() {
return const CIABrandingConfig(
appName: 'My App',
// logoUrl: 'https://your-domain.com/logo.png',
primaryColor: Color(0xFF2563EB), // Blue
showPoweredByCIA: true,
);
}