import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'config/config_loader.dart'; import 'data/local/local_storage_service.dart'; import 'data/local/models/item.dart'; import 'data/nostr/nostr_service.dart'; import 'data/nostr/models/nostr_keypair.dart'; import 'data/sync/sync_engine.dart'; import 'ui/relay_management/relay_management_screen.dart'; import 'ui/relay_management/relay_management_controller.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); // Load .env file (optional - falls back to defaults if not found) try { await dotenv.load(fileName: '.env'); } catch (e) { debugPrint('Note: .env file not found, using default values: $e'); } // Load configuration based on environment const String environment = String.fromEnvironment( 'ENV', defaultValue: 'dev', ); final config = ConfigLoader.load(environment); if (config.enableLogging) { debugPrint('App initialized with config: $config'); } runApp(const MyApp()); } /// The root widget of the application. class MyApp extends StatefulWidget { const MyApp({super.key}); @override State createState() => _MyAppState(); } class _MyAppState extends State { LocalStorageService? _storageService; NostrService? _nostrService; SyncEngine? _syncEngine; NostrKeyPair? _nostrKeyPair; int _itemCount = 0; bool _isInitialized = false; @override void initState() { super.initState(); _initializeStorage(); } Future _initializeStorage() async { try { _storageService = LocalStorageService(); await _storageService!.initialize(); final items = await _storageService!.getAllItems(); // Initialize Nostr service and sync engine _nostrService = NostrService(); _nostrKeyPair = _nostrService!.generateKeyPair(); _syncEngine = SyncEngine( localStorage: _storageService!, nostrService: _nostrService!, nostrKeyPair: _nostrKeyPair!, ); // Load relays from config final config = ConfigLoader.load( const String.fromEnvironment('ENV', defaultValue: 'dev'), ); for (final relayUrl in config.nostrRelays) { _nostrService!.addRelay(relayUrl); } setState(() { _itemCount = items.length; _isInitialized = true; }); } catch (e) { debugPrint('Failed to initialize storage: $e'); // Reset to null if initialization failed _storageService = null; } } Future _addTestItem() async { if (!_isInitialized || _storageService == null) return; final item = Item( id: 'test-${DateTime.now().millisecondsSinceEpoch}', data: { 'name': 'Test Item', 'timestamp': DateTime.now().toIso8601String(), }, ); await _storageService!.insertItem(item); final items = await _storageService!.getAllItems(); setState(() { _itemCount = items.length; }); } @override void dispose() { _syncEngine?.dispose(); _nostrService?.dispose(); // Only close if storage service was initialized if (_storageService != null) { try { _storageService!.close(); } catch (e) { debugPrint('Error closing storage service: $e'); } } super.dispose(); } @override Widget build(BuildContext context) { // Load config to display in UI const String environment = String.fromEnvironment( 'ENV', defaultValue: 'dev', ); final config = ConfigLoader.load(environment); return MaterialApp( title: 'App Boilerplate', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), useMaterial3: true, ), home: Scaffold( appBar: AppBar( title: const Text('App Boilerplate'), backgroundColor: Theme.of(context).colorScheme.inversePrimary, ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.check_circle_outline, size: 64, color: Colors.green, ), const SizedBox(height: 24), const Text( 'Flutter Modular App Boilerplate', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 32), Card( margin: const EdgeInsets.symmetric(horizontal: 32), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Configuration', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), _ConfigRow( label: 'Environment', value: environment.toUpperCase(), ), const SizedBox(height: 8), _ConfigRow( label: 'API Base URL', value: config.apiBaseUrl, ), const SizedBox(height: 8), _ConfigRow( label: 'Logging', value: config.enableLogging ? 'Enabled' : 'Disabled', ), ], ), ), ), const SizedBox(height: 32), if (_isInitialized) ...[ Card( margin: const EdgeInsets.symmetric(horizontal: 32), child: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ Text( 'Local Storage', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), Text( 'Items in database: $_itemCount', style: Theme.of(context).textTheme.bodyLarge, ), const SizedBox(height: 12), ElevatedButton( onPressed: _addTestItem, child: const Text('Add Test Item'), ), ], ), ), ), const SizedBox(height: 16), if (_isInitialized && _nostrService != null && _syncEngine != null) Card( margin: const EdgeInsets.symmetric(horizontal: 32), child: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ Text( 'Nostr Relay Management', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), Builder( builder: (navContext) { return ElevatedButton.icon( onPressed: () { Navigator.of(navContext).push( MaterialPageRoute( builder: (_) => RelayManagementScreen( controller: RelayManagementController( nostrService: _nostrService!, syncEngine: _syncEngine!, ), ), ), ); }, icon: const Icon(Icons.cloud), label: const Text('Manage Relays'), ); }, ), ], ), ), ), if (_isInitialized && _nostrService != null && _syncEngine != null) const SizedBox(height: 16), ], Text( 'Phase 5: Relay Management UI Complete ✓', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.grey, ), ), ], ), ), ), ); } } /// Widget to display a configuration row. class _ConfigRow extends StatelessWidget { final String label; final String value; const _ConfigRow({ required this.label, required this.value, }); @override Widget build(BuildContext context) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 100, child: Text( '$label:', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Colors.grey, ), ), ), Expanded( child: Text( value, style: Theme.of(context).textTheme.bodyMedium, ), ), ], ); } }