feat: M2.1 客户端骨架、BLE 修复、仓库整理与现状板
- BLE: 修复 BlueZ LocalName 与 Includes 冲突,串行注册与退避 - HTTP: 语音对话补 Live2D talking 标志与 session;Web 补 X-Session-Id - Flutter: 角色/对话/模型页 + API,Gradle/依赖升级,麦克风权限 - 文档: 新增 docs/STATUS.md,校准 CLAUDE/PROGRESS/README - 清理: 移除根目录截图与临时模型,运维脚本迁入 scripts/device
This commit is contained in:
83
clients/flutter/lib/providers/character_provider.dart
Normal file
83
clients/flutter/lib/providers/character_provider.dart
Normal file
@@ -0,0 +1,83 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../models/character_pack.dart';
|
||||
import '../services/http_api_service.dart';
|
||||
import 'debug_provider.dart';
|
||||
|
||||
class CharacterProvider extends ChangeNotifier {
|
||||
CharacterProvider({
|
||||
required HttpApiService httpApiService,
|
||||
required DebugProvider debugProvider,
|
||||
}) : _http = httpApiService,
|
||||
_debug = debugProvider;
|
||||
|
||||
final HttpApiService _http;
|
||||
final DebugProvider _debug;
|
||||
|
||||
List<CharacterPack> _packs = const [];
|
||||
String _active = '';
|
||||
bool _loading = false;
|
||||
bool _switching = false;
|
||||
String? _error;
|
||||
|
||||
List<CharacterPack> get packs => _packs;
|
||||
String get activeFilename => _active;
|
||||
bool get isLoading => _loading;
|
||||
bool get isSwitching => _switching;
|
||||
String? get error => _error;
|
||||
|
||||
CharacterPack? get activePack {
|
||||
for (final pack in _packs) {
|
||||
if (pack.filename == _active) return pack;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
_loading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
try {
|
||||
final snapshot = await _http.getAvailableCharacterConfigs();
|
||||
_packs = snapshot.configs;
|
||||
_active = snapshot.active;
|
||||
_debug.addHttpLog(
|
||||
'Loaded characters',
|
||||
details: 'count=${_packs.length} active=$_active',
|
||||
);
|
||||
} catch (error) {
|
||||
_error = error.toString();
|
||||
_debug.addHttpLog('Load characters failed', details: _error);
|
||||
} finally {
|
||||
_loading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> switchTo(String filename) async {
|
||||
if (_switching || filename == _active) return false;
|
||||
_switching = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
try {
|
||||
final result = await _http.switchConfig(filename);
|
||||
if (!result.isOk) {
|
||||
_error = result.message;
|
||||
_debug.addHttpLog('Switch character failed', details: result.message);
|
||||
return false;
|
||||
}
|
||||
_active = filename;
|
||||
_debug.addHttpLog('Switched character', details: filename);
|
||||
// 热重载后刷新列表,确保 active 与服务端一致
|
||||
await refresh();
|
||||
return true;
|
||||
} catch (error) {
|
||||
_error = error.toString();
|
||||
_debug.addHttpLog('Switch character failed', details: _error);
|
||||
return false;
|
||||
} finally {
|
||||
_switching = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user