import 'dart:io'; import 'package:animations/utils/binary_manager.dart'; import 'package:flutter/material.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; import 'package:thermion_flutter/thermion_flutter.dart'; class LocalModelViewer extends StatefulWidget { @override _LocalModelViewerState createState() => _LocalModelViewerState(); } class _LocalModelViewerState extends State { ThermionViewer? _viewer; ThermionAsset? _asset; bool _isLoading = true; bool _hasError = false; String _statusMessage = "初始化中..."; @override void initState() { super.initState(); _initializeViewerAndLoadModel(); } Future _initializeViewerAndLoadModel() async { try { // 获取应用支持目录并构建模型文件路径 final supportDir = await getApplicationSupportDirectory(); final modelFile = File(p.join(supportDir.path, 'cup', 'model.gltf')); // 检查文件是否存在 if (!await modelFile.exists()) { await BinaryManager.migrate3DModels(); await Future.delayed(const Duration(milliseconds: 3000)); // throw Exception("模型文件不存在于路径: ${modelFile.path}"); } setState(() { _statusMessage = "创建3D查看器..."; }); // 创建ThermionViewer实例 _viewer = await ThermionFlutterPlugin.createViewer(); setState(() { _statusMessage = "获取模型路径..."; }); setState(() { _statusMessage = "加载环境光和天空盒..."; }); // 加载环境光和天空盒(确保在pubspec.yaml中注册) await _viewer!.loadSkybox("assets/default_env_skybox.ktx"); await _viewer!.loadIbl("assets/default_env_ibl.ktx"); setState(() { _statusMessage = "加载3D模型..."; }); // 加载glTF模型 - 使用文件URI格式 _asset = await _viewer!.loadGltf("file://${modelFile.path}"); // 可选:将模型缩放到单位立方体 await _asset!.transformToUnitCube(); // 配置场景 final camera = await _viewer!.getActiveCamera(); await camera.lookAt(Vector3(0, 0, 6)); // 开始渲染 await _viewer!.setRendering(true); setState(() { _isLoading = false; _hasError = false; _statusMessage = "加载完成"; }); } catch (e) { print("加载模型失败: $e"); setState(() { _isLoading = false; _hasError = true; _statusMessage = "加载失败: $e"; }); } } @override Widget build(BuildContext context) { if (_isLoading) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text(_statusMessage), ], ), ); } if (_hasError || _viewer == null) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error_outline, color: Colors.red, size: 48), SizedBox(height: 16), Text("加载3D模型失败"), Text(_statusMessage, style: TextStyle(color: Colors.red)), SizedBox(height: 16), ElevatedButton( onPressed: _initializeViewerAndLoadModel, child: Text("重试"), ), ], ), ); } return Scaffold( appBar: AppBar( title: const Text('本地模型查看'), ), body: ThermionListenerWidget( inputHandler: DelegateInputHandler.fixedOrbit(_viewer!), child: ThermionWidget( viewer: _viewer!, showFpsCounter: true, // 可选:显示FPS计数器 ), ), ); // 使用ThermionWidget显示3D内容 return ThermionListenerWidget( inputHandler: DelegateInputHandler.fixedOrbit(_viewer!), child: ThermionWidget( viewer: _viewer!, showFpsCounter: true, // 可选:显示FPS计数器 ), ); } Future deleteModelFile() async { // 获取应用支持目录 final supportDir = await getApplicationSupportDirectory(); // 构建 model.gltf 的完整路径 final modelFile = File(p.join(supportDir.path, 'cup', 'model.gltf')); // 检查文件是否存在,然后删除 if (await modelFile.exists()) { await modelFile.delete(); print('model.gltf 已删除'); } else { print('model.gltf 文件不存在'); } } @override void dispose() { deleteModelFile(); // 清理资源 _viewer?.dispose(); super.dispose(); } }