Files
ShowenV2/clients/flutter/lib/providers/model_provider.dart
XiuchengWu 6f7e24db63 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
2026-07-09 12:41:34 +08:00

117 lines
2.9 KiB
Dart

import 'dart:async';
import 'package:flutter/foundation.dart';
import '../models/ai_model.dart';
import '../models/api_response.dart';
import '../services/http_api_service.dart';
import 'debug_provider.dart';
class ModelProvider extends ChangeNotifier {
ModelProvider({
required HttpApiService httpApiService,
required DebugProvider debugProvider,
}) : _http = httpApiService,
_debug = debugProvider;
final HttpApiService _http;
final DebugProvider _debug;
ModelsSnapshot? _snapshot;
bool _loading = false;
bool _acting = false;
String? _error;
String? _status;
Timer? _pollTimer;
ModelsSnapshot? get snapshot => _snapshot;
bool get isLoading => _loading;
bool get isActing => _acting;
String? get error => _error;
String? get status => _status;
Future<void> refresh() async {
_loading = true;
_error = null;
notifyListeners();
try {
_snapshot = await _http.getModels();
_debug.addHttpLog(
'Loaded models',
details: 'count=${_snapshot?.models.length ?? 0}',
);
} catch (error) {
_error = error.toString();
_debug.addHttpLog('Load models failed', details: _error);
} finally {
_loading = false;
notifyListeners();
}
}
Future<void> download(String modelId) async {
await _runAction(() => _http.downloadModel(modelId), '下载已启动: $modelId');
_startPolling();
}
Future<void> switchTo({required String modelId, required String kind}) async {
await _runAction(
() => _http.switchModel(modelId: modelId, kind: kind),
'已切换: $modelId',
);
await refresh();
}
Future<void> delete(String modelId) async {
await _runAction(() => _http.deleteModel(modelId), '已删除: $modelId');
await refresh();
}
Future<void> _runAction(
Future<ApiResponse> Function() action,
String okStatus,
) async {
if (_acting) return;
_acting = true;
_error = null;
notifyListeners();
try {
final result = await action();
_status = result.message.isNotEmpty ? result.message : okStatus;
if (!result.isOk) {
_error = _status;
}
_debug.addHttpLog('Model action ok', details: _status);
} catch (error) {
_error = error.toString();
_status = _error;
_debug.addHttpLog('Model action failed', details: _error);
} finally {
_acting = false;
notifyListeners();
}
}
void _startPolling() {
_pollTimer?.cancel();
var ticks = 0;
_pollTimer = Timer.periodic(const Duration(seconds: 2), (timer) async {
ticks += 1;
await refresh();
final downloading = _snapshot?.models.any(
(m) => m.downloadProgress > 0 && m.downloadProgress < 100 && !m.downloaded,
) ??
false;
if (!downloading || ticks > 90) {
timer.cancel();
}
});
}
@override
void dispose() {
_pollTimer?.cancel();
super.dispose();
}
}