import 'package:dynamic_sdk/dynamic_sdk.dart';
import 'package:flutter/material.dart';
class TrustedDevicesScreen extends StatefulWidget {
const TrustedDevicesScreen({super.key});
@override
State<TrustedDevicesScreen> createState() => _TrustedDevicesScreenState();
}
class _TrustedDevicesScreenState extends State<TrustedDevicesScreen> {
List<RegisteredDevice>? _devices;
bool _isLoading = true;
String? _error;
@override
void initState() {
super.initState();
_loadDevices();
}
Future<void> _loadDevices() async {
setState(() {
_isLoading = true;
_error = null;
});
try {
final devices = await DynamicSDK.instance.deviceRegistration
.getRegisteredDevices();
setState(() => _devices = devices);
} catch (e) {
setState(() => _error = 'Failed to load devices: $e');
} finally {
setState(() => _isLoading = false);
}
}
Future<void> _revokeDevice(String deviceId) async {
try {
await DynamicSDK.instance.deviceRegistration.revokeRegisteredDevice(
deviceRegistrationId: deviceId,
);
await _loadDevices();
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to revoke device: $e')),
);
}
}
Future<void> _revokeAllDevices() async {
try {
await DynamicSDK.instance.deviceRegistration.revokeAllRegisteredDevices();
await _loadDevices();
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to revoke all devices: $e')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Trusted Devices')),
body: RefreshIndicator(
onRefresh: _loadDevices,
child: _buildBody(),
),
);
}
Widget _buildBody() {
if (_isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (_error != null) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_error!, style: const TextStyle(color: Colors.red)),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _loadDevices,
child: const Text('Retry'),
),
],
),
);
}
final devices = _devices ?? [];
if (devices.isEmpty) {
return const Center(child: Text('No trusted devices registered'));
}
return ListView(
padding: const EdgeInsets.all(16),
children: [
...devices.map((device) => _TrustedDeviceCard(
device: device,
onRemove: () => _revokeDevice(device.id),
)),
if (devices.length > 1) ...[
const SizedBox(height: 16),
ElevatedButton(
onPressed: _revokeAllDevices,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
child: const Text('Remove All Devices'),
),
],
],
);
}
}
class _TrustedDeviceCard extends StatelessWidget {
const _TrustedDeviceCard({
required this.device,
required this.onRemove,
});
final RegisteredDevice device;
final VoidCallback onRemove;
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
device.displayText ?? 'Unknown Device',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(width: 8),
if (device.isCurrentDevice == true)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2,
),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
borderRadius: BorderRadius.circular(4),
),
child: const Text(
'This Device',
style: TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
),
],
),
if (device.type != null) ...[
const SizedBox(height: 4),
Text(
device.type!,
style: Theme.of(context).textTheme.bodySmall,
),
],
const SizedBox(height: 4),
Text(
'Registered: ${device.createdAt}',
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 12),
ElevatedButton(
onPressed: onRemove,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
minimumSize: const Size.fromHeight(40),
),
child: const Text('Remove'),
),
],
),
),
);
}
}