library; import 'dart:async'; import 'package:flutter/material.dart'; import 'package:wolf_3d_flutter/wolf_3d_flutter.dart'; import 'game_data_picker_manager.dart'; import 'no_game_data_screen.dart'; /// GUI-host application shell that owns setup/import UX. class Wolf3dGuiApp extends StatefulWidget { /// Creates the GUI host shell for a prepared engine facade. const Wolf3dGuiApp({ super.key, required this.engine, this.pickerManager, }); /// Shared initialized facade that owns game data, input, and audio services. final Wolf3dFlutterEngine engine; /// Optional injected picker manager used by tests. final GameDataPickerManager? pickerManager; @override State createState() => _Wolf3dGuiAppState(); } class _Wolf3dGuiAppState extends State with WidgetsBindingObserver { late final GameDataPickerManager _pickerManager; Future? _shutdownFuture; @override void initState() { super.initState(); _pickerManager = widget.pickerManager ?? GameDataPickerManager(engine: widget.engine); WidgetsBinding.instance.addObserver(this); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); unawaited(_ensureAudioShutdown()); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.detached) { unawaited(_ensureAudioShutdown()); } } Future _ensureAudioShutdown() { final Future? existing = _shutdownFuture; if (existing != null) { return existing; } final Future shutdown = widget.engine.shutdownAudio(); _shutdownFuture = shutdown; return shutdown; } Future _pickGameDataDirectory() { return _runPickerAction( () => _pickerManager.pickGameDataDirectory( notifyChanged: () { if (mounted) { setState(() {}); } }, ), ); } Future _pickGameDataFiles() { return _runPickerAction( () => _pickerManager.pickGameDataFiles( notifyChanged: () { if (mounted) { setState(() {}); } }, ), ); } Future _runPickerAction(Future Function() action) async { await action(); final GameDataVersionAnalysis? soleReadyVersion = _pickerManager.scanResult?.soleReadyVersion; if (soleReadyVersion == null || !mounted || widget.engine.availableGames.isNotEmpty) { return; } if (_pickerManager.selectedReadyVersion != soleReadyVersion.version) { setState(() { _pickerManager.selectReadyVersion(soleReadyVersion.version); }); } await _useSelectedData(); } Future _useSelectedData() { return _pickerManager.useSelectedData( notifyChanged: () { if (mounted) { setState(() {}); } }, ); } Future _importSelectedData() { return _pickerManager.importSelectedData( notifyChanged: () { if (mounted) { setState(() {}); } }, ); } @override Widget build(BuildContext context) { return widget.engine.availableGames.isEmpty ? NoGameDataScreen( configuredDataDirectory: widget.engine.configuredDataDirectory, onPickGameDataDirectory: _pickGameDataDirectory, onPickGameDataFiles: _pickGameDataFiles, onUseSelectedData: _useSelectedData, onImportSelectedData: _importSelectedData, onSelectReadyVersion: (version) { if (version == null) { return; } setState(() { _pickerManager.selectReadyVersion(version); }); }, isLoadingGameData: _pickerManager.isLoadingGameData, pickerError: _pickerManager.pickerError, scanResult: _pickerManager.scanResult, selectedReadyVersion: _pickerManager.selectedReadyVersion, ) : GameScreen(wolf3d: widget.engine); } }