import 'package:flutter/material.dart'; import 'package:rimworld_modman/modloader.dart'; import 'dart:io'; // Global variable to store loaded mods for access across the app late ModList modManager; void main() { // Initialize the mod manager modManager = ModList(path: modsRoot); // Start the app runApp(const RimWorldModManager()); } class RimWorldModManager extends StatelessWidget { const RimWorldModManager({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'RimWorld Mod Manager', theme: ThemeData.dark().copyWith( primaryColor: const Color(0xFF3D4A59), colorScheme: ColorScheme.fromSeed( seedColor: const Color(0xFF3D4A59), brightness: Brightness.dark, ), scaffoldBackgroundColor: const Color(0xFF1E262F), cardColor: const Color(0xFF2A3440), appBarTheme: const AppBarTheme( backgroundColor: Color(0xFF2A3440), foregroundColor: Colors.white, ), ), home: const ModManagerHomePage(), ); } } class ModManagerHomePage extends StatefulWidget { const ModManagerHomePage({super.key}); @override State createState() => _ModManagerHomePageState(); } class _ModManagerHomePageState extends State { int _selectedIndex = 0; final List _pages = [ const ModListPage(), const LoadOrderPage(), const TroubleshootingPage(), ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('RimWorld Mod Manager')), body: _pages[_selectedIndex], bottomNavigationBar: BottomNavigationBar( currentIndex: _selectedIndex, onTap: (index) { setState(() { _selectedIndex = index; }); }, items: const [ BottomNavigationBarItem(icon: Icon(Icons.extension), label: 'Mods'), BottomNavigationBarItem( icon: Icon(Icons.reorder), label: 'Load Order', ), BottomNavigationBarItem( icon: Icon(Icons.build), label: 'Troubleshoot', ), ], ), ); } } // Page to display all installed mods with enable/disable toggles class ModListPage extends StatefulWidget { const ModListPage({super.key}); @override State createState() => _ModListPageState(); } class _ModListPageState extends State { final List _loadedMods = []; bool _isLoading = false; String _loadingStatus = ''; int _totalModsFound = 0; bool _skipFileCount = true; // Skip file counting by default for faster loading @override Widget build(BuildContext context) { return Scaffold( body: _loadedMods.isEmpty && !_isLoading ? _buildEmptyState() : _buildModList(), ); } Widget _buildEmptyState() { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.extension, size: 64), const SizedBox(height: 16), Text('Mod List', style: Theme.of(context).textTheme.headlineMedium), const SizedBox(height: 16), Text( 'Ready to scan for RimWorld mods.', style: Theme.of(context).textTheme.bodyLarge, ), const SizedBox(height: 12), Row( mainAxisSize: MainAxisSize.min, children: [ Checkbox( value: _skipFileCount, onChanged: (value) { setState(() { _skipFileCount = value ?? true; }); }, ), const Text('Skip file counting (faster loading)'), ], ), const SizedBox(height: 24), ElevatedButton( onPressed: _startLoadingMods, child: const Text('Scan for Mods'), ), ], ), ); } Widget _buildModList() { return Column( children: [ if (_isLoading) Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ LinearProgressIndicator( value: _totalModsFound > 0 ? _loadedMods.length / _totalModsFound : null, ), const SizedBox(height: 8), Text( _loadingStatus, style: Theme.of(context).textTheme.bodyMedium, ), ], ), ), Expanded( child: ListView.builder( itemCount: _loadedMods.length, itemBuilder: (context, index) { final mod = _loadedMods[index]; return Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: ListTile( title: Text(mod.name), subtitle: Text( 'ID: ${mod.id}\nSize: ${mod.size} files', style: Theme.of(context).textTheme.bodySmall, ), trailing: Icon( Icons.circle, color: mod.hardDependencies.isNotEmpty ? Colors.orange : Colors.green, size: 12, ), onTap: () { // TODO: Show mod details }, ), ); }, ), ), if (!_isLoading && _loadedMods.isNotEmpty) Padding( padding: const EdgeInsets.all(16.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('${_loadedMods.length} mods loaded'), ElevatedButton( onPressed: _startLoadingMods, child: const Text('Reload'), ), ], ), ), ], ); } void _startLoadingMods() { setState(() { _loadedMods.clear(); _isLoading = true; _loadingStatus = 'Scanning for mods...'; }); // First get the mod directories to know the total count final directory = Directory(modsRoot); if (directory.existsSync()) { final List entities = directory.listSync(); final List modDirectories = entities.whereType().map((dir) => dir.path).toList(); setState(() { _totalModsFound = modDirectories.length; _loadingStatus = 'Found $_totalModsFound mod directories. Loading...'; }); } // Use the serial loading with our skipFileCount option modManager .load(skipFileCount: _skipFileCount) .listen( (mod) { setState(() { _loadedMods.add(mod); _loadingStatus = 'Loaded ${_loadedMods.length}/$_totalModsFound mods...'; }); }, onError: (error) { setState(() { _isLoading = false; _loadingStatus = 'Error loading mods: $error'; }); }, onDone: () { setState(() { _isLoading = false; _loadingStatus = 'Completed! ${_loadedMods.length} mods loaded.'; // Sort mods by name for better display _loadedMods.sort((a, b) => a.name.compareTo(b.name)); }); }, ); } } // Page to manage mod load order with dependency visualization class LoadOrderPage extends StatelessWidget { const LoadOrderPage({super.key}); @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.reorder, size: 64), const SizedBox(height: 16), Text('Load Order', style: Theme.of(context).textTheme.headlineMedium), const SizedBox(height: 16), Text( 'Manage your mod loading order with dependency resolution.', style: Theme.of(context).textTheme.bodyLarge, textAlign: TextAlign.center, ), const SizedBox(height: 24), ElevatedButton( onPressed: () { // TODO: Implement automatic load order sorting }, child: const Text('Auto-sort Mods'), ), ], ), ); } } // Page for troubleshooting problematic mods class TroubleshootingPage extends StatelessWidget { const TroubleshootingPage({super.key}); @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.build, size: 64), const SizedBox(height: 16), Text( 'Troubleshooting', style: Theme.of(context).textTheme.headlineMedium, ), const SizedBox(height: 16), Padding( padding: const EdgeInsets.symmetric(horizontal: 32.0), child: Text( 'Find problematic mods with smart batch testing.', style: Theme.of(context).textTheme.bodyLarge, textAlign: TextAlign.center, ), ), const SizedBox(height: 24), ElevatedButton( onPressed: () { // TODO: Implement troubleshooting wizard }, child: const Text('Start Troubleshooting'), ), ], ), ); } }