import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import '../controllers/opus_test_controller.dart'; class OpusTestView extends GetView { const OpusTestView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { // 获取主题模式 final isDarkMode = Theme.of(context).brightness == Brightness.dark; return DefaultTabController( length: 2, child: Scaffold( backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], appBar: AppBar( backgroundColor: isDarkMode ? Colors.black : Colors.white, elevation: 0.5, title: Text( 'OPUS解码', style: TextStyle( fontSize: 17.sp, fontWeight: FontWeight.w600, color: isDarkMode ? Colors.white : Colors.black, ), ), centerTitle: true, bottom: TabBar( indicatorColor: Colors.blue, indicatorWeight: 3, labelColor: isDarkMode ? Colors.white : Colors.black, unselectedLabelColor: isDarkMode ? Colors.grey : Colors.grey, tabs: const [ Tab(text: "解码"), Tab(text: "输出"), ], ), ), body: TabBarView( children: [ _buildDecodeTab(isDarkMode), _buildEncodeTab(isDarkMode), ], ), ), ); } // 解码标签页 Widget _buildDecodeTab(bool isDarkMode) { return ListView( padding: EdgeInsets.all(16.w), children: [ // 文件选择部分 _buildSection( title: '选择文件', isDarkMode: isDarkMode, child: Column( children: [ // 当前选中的文件 Obx(() => controller.selectedFiles.isEmpty ? const SizedBox.shrink() : ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: controller.selectedFiles.length, itemBuilder: (context, index) { final file = controller.selectedFiles[index]; return ListTile( title: Text( file.name, style: TextStyle( color: isDarkMode ? Colors.white70 : Colors.black87, fontSize: 14.sp, ), ), subtitle: Text( '${(file.size / 1024).toStringAsFixed(2)} Kb', style: TextStyle( color: isDarkMode ? Colors.white38 : Colors.black54, fontSize: 12.sp, ), ), trailing: IconButton( icon: Icon(Icons.clear, color: Colors.red), onPressed: () { controller.selectedFiles.removeAt(index); }, ), ); }, )), // 外部文件列表标题和刷新按钮 Padding( padding: EdgeInsets.symmetric(vertical: 8.h), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '外部存储文件', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.bold, color: isDarkMode ? Colors.white : Colors.black, ), ), Row( mainAxisSize: MainAxisSize.min, children: [ // 一键清理:仅在列表非空时显示 Obx(() => controller.externalFiles.isEmpty ? const SizedBox.shrink() : IconButton( icon: const Icon( Icons.delete_sweep, color: Colors.red, ), tooltip: '一键清理', onPressed: () => controller.confirmClearAll(Get.context!), )), IconButton( icon: Icon( Icons.refresh, color: isDarkMode ? Colors.blue[300] : Colors.blue, ), tooltip: '刷新', onPressed: controller.loadExternalFiles, ), ], ), ], ), ), // 外部文件列表 Obx(() => controller.externalFiles.isEmpty ? Center( child: Padding( padding: EdgeInsets.all(16.h), child: Text( '未找到Opus文件', style: TextStyle( color: isDarkMode ? Colors.white54 : Colors.black54, ), ), ), ) : Container( height: 200.h, decoration: BoxDecoration( border: Border.all( color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!, ), borderRadius: BorderRadius.circular(8.r), ), child: ListView.builder( itemCount: controller.externalFiles.length, itemBuilder: (context, index) { final file = controller.externalFiles[index]; return ListTile( title: Text( file.name, style: TextStyle( color: isDarkMode ? Colors.white70 : Colors.black87, fontSize: 14.sp, ), overflow: TextOverflow.ellipsis, ), subtitle: Text( '${(file.size / 1024).toStringAsFixed(2)} Kb', style: TextStyle( color: isDarkMode ? Colors.white38 : Colors.black54, fontSize: 12.sp, ), ), onTap: () { controller.selectExternalFile(file); }, onLongPress: () { // 长按显示操作菜单 controller.showFileActionSheet(context, file); }, trailing: Icon( Icons.arrow_forward, color: isDarkMode ? Colors.white54 : Colors.black54, ), ); }, ), )), ], ), ), SizedBox(height: 16.h), // 解码参数部分 _buildSection( title: '解码参数', isDarkMode: isDarkMode, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Obx(() => Checkbox( value: controller.hasHeader.value, onChanged: (value) => controller.toggleHeader(value!), activeColor: Colors.blue, )), Text( '是否携带协议头', style: TextStyle( color: isDarkMode ? Colors.white70 : Colors.black87, fontSize: 14.sp, ), ), ], ), // 数据包长度 Row( children: [ Text( '数据包长度', style: TextStyle( color: isDarkMode ? Colors.white70 : Colors.black87, fontSize: 14.sp, ), ), SizedBox(width: 16.w), Expanded( child: Obx(() => TextField( controller: TextEditingController( text: controller.packetLength.value), onChanged: (value) => controller.setPacketLength(value), keyboardType: TextInputType.number, style: TextStyle( color: isDarkMode ? Colors.white : Colors.black, ), decoration: InputDecoration( isDense: true, suffix: Text( 'Byte', style: TextStyle( color: isDarkMode ? Colors.white60 : Colors.black54, fontSize: 12.sp, ), ), enabledBorder: UnderlineInputBorder( borderSide: BorderSide( color: isDarkMode ? Colors.white30 : Colors.black26, ), ), focusedBorder: UnderlineInputBorder( borderSide: BorderSide( color: Colors.blue, ), ), ), )), ), ], ), SizedBox(height: 20.h), // 采样率 Row( children: [ Text( '采样率', style: TextStyle( color: isDarkMode ? Colors.white70 : Colors.black87, fontSize: 14.sp, ), ), SizedBox(width: 16.w), Expanded( child: Obx(() => DropdownButton( value: controller.selectedSampleRate.value, isExpanded: true, dropdownColor: isDarkMode ? Colors.grey[800] : Colors.white, items: controller.sampleRateOptions.map((rate) { return DropdownMenuItem( value: rate, child: Text( rate.toString(), style: TextStyle( color: isDarkMode ? Colors.white : Colors.black87, ), ), ); }).toList(), onChanged: (value) => controller.setSampleRate(value!), style: TextStyle( color: isDarkMode ? Colors.white : Colors.black, ), )), ), ], ), SizedBox(height: 20.h), // 通道数量 Text( '通道数量', style: TextStyle( color: isDarkMode ? Colors.white70 : Colors.black87, fontSize: 14.sp, ), ), Row( children: [ Expanded( child: Row( children: [ Obx(() => Radio( value: true, groupValue: controller.isMono.value, onChanged: (value) => controller.toggleChannel(value!), activeColor: Colors.blue, )), Text( '单通道', style: TextStyle( color: isDarkMode ? Colors.white70 : Colors.black87, fontSize: 14.sp, ), ), ], ), ), Expanded( child: Row( children: [ Obx(() => Radio( value: false, groupValue: controller.isMono.value, onChanged: (value) => controller.toggleChannel(value!), activeColor: Colors.blue, )), Text( '双通道', style: TextStyle( color: isDarkMode ? Colors.white70 : Colors.black87, fontSize: 14.sp, ), ), ], ), ), ], ), ], ), ), SizedBox(height: 16.h), // 状态消息 Obx(() => controller.statusMessage.value.isNotEmpty ? Container( padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 16.w), margin: EdgeInsets.only(bottom: 16.h), decoration: BoxDecoration( color: isDarkMode ? Colors.blue.withOpacity(0.2) : Colors.blue.withOpacity(0.1), borderRadius: BorderRadius.circular(4.r), border: Border.all( color: isDarkMode ? Colors.blue.withOpacity(0.3) : Colors.blue.withOpacity(0.2), width: 1, ), ), child: Text( controller.statusMessage.value, style: TextStyle( color: isDarkMode ? Colors.blue[300] : Colors.blue[700], fontSize: 12.sp, ), ), ) : const SizedBox.shrink()), // 播放按钮 Container( margin: EdgeInsets.symmetric(vertical: 8.h), child: ElevatedButton.icon( icon: Obx(() => Icon( controller.isPlaying.value ? Icons.stop : Icons.play_arrow, color: Colors.white, )), label: Obx(() => Text( controller.isPlaying.value ? '停止播放' : '播放音频', style: TextStyle( color: Colors.white, fontSize: 16.sp, ), )), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, padding: EdgeInsets.symmetric(vertical: 16.h), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.r), ), ), onPressed: controller.isDecoding.value ? null : () { controller.playAudio(); }, ), ), // 解码按钮 Container( margin: EdgeInsets.symmetric(vertical: 8.h), child: Obx(() => ElevatedButton( onPressed: controller.selectedFiles.isEmpty || controller.isDecoding.value ? null : () { controller.pickFiles(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, padding: EdgeInsets.symmetric(vertical: 16.h), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.r), ), ), child: Text( '开始解码', style: TextStyle( color: Colors.white, fontSize: 16.sp, ), ), )), ), ], ); } // 编码标签页(暂未实现) Widget _buildEncodeTab(bool isDarkMode) { return Center( child: Text( '编码功能尚未实现', style: TextStyle( color: isDarkMode ? Colors.white54 : Colors.black54, fontSize: 16.sp, ), ), ); } // 构建带标题的区段 Widget _buildSection({ required String title, required Widget child, required bool isDarkMode, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( title, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.bold, color: isDarkMode ? Colors.white : Colors.black87, ), ), SizedBox(width: 8.w), Expanded( child: Container( height: 1, color: isDarkMode ? Colors.white24 : Colors.black12, ), ), if (title == '选择文件') IconButton( icon: Icon( Icons.add_circle_outline, color: isDarkMode ? Colors.blue[300] : Colors.blue, ), onPressed: () { controller.pickFiles(); }, ), ], ), Container( padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white, borderRadius: BorderRadius.circular(8.r), boxShadow: [ BoxShadow( color: isDarkMode ? Colors.black.withOpacity(0.2) : Colors.black.withOpacity(0.03), blurRadius: 4, offset: Offset(0, 2), ), ], ), child: child, ), ], ); } }