From 1dd669287c7a2e64c8461d54c9a9a32e3f143175 Mon Sep 17 00:00:00 2001 From: lxh2875931338 Date: Sun, 16 Aug 2026 21:21:22 +0800 Subject: [PATCH] =?UTF-8?q?=E7=8E=B0=E5=9C=A8webdav=E9=93=BE=E6=8E=A5?= =?UTF-8?q?=E8=B7=91=E9=80=9A=E4=BA=86=EF=BC=8C=E5=87=86=E5=A4=87=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=95=8C=E9=9D=A2=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/.gitignore | 3 + .../.kotlin/errors/errors-1786806960565.log | 3 - android/gradle.properties | 2 + android/settings.gradle.kts | 2 +- lib/main.dart | 15 +- lib/pages/webdav_file_list_page.dart | 205 ++++++++++++++++++ pubspec.lock | 8 +- pubspec.yaml | 2 +- 8 files changed, 223 insertions(+), 17 deletions(-) delete mode 100644 android/.kotlin/errors/errors-1786806960565.log create mode 100644 lib/pages/webdav_file_list_page.dart diff --git a/android/.gitignore b/android/.gitignore index be3943c..d64ce7c 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -6,6 +6,9 @@ gradle-wrapper.jar /local.properties GeneratedPluginRegistrant.java .cxx/ +.kotlin +/build + # Remember to never publicly share your keystore. # See https://flutter.dev/to/reference-keystore diff --git a/android/.kotlin/errors/errors-1786806960565.log b/android/.kotlin/errors/errors-1786806960565.log deleted file mode 100644 index 88185a8..0000000 --- a/android/.kotlin/errors/errors-1786806960565.log +++ /dev/null @@ -1,3 +0,0 @@ -kotlin version: 2.3.20 -error message: Daemon compilation failed - diff --git a/android/gradle.properties b/android/gradle.properties index e96108c..f445ec2 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -3,4 +3,6 @@ android.useAndroidX=true # This newDsl flag was added by the Flutter template android.newDsl=false # This builtInKotlin flag was added by the Flutter template +kotlin.incremental=false +kotlin.parallel.tasks.in.project=false android.builtInKotlin=false diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts index c21f0c5..5915089 100644 --- a/android/settings.gradle.kts +++ b/android/settings.gradle.kts @@ -20,7 +20,7 @@ pluginManagement { plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("com.android.application") version "9.0.1" apply false - id("org.jetbrains.kotlin.android") version "2.3.20" apply false + id("org.jetbrains.kotlin.android") version "1.9.0" apply false } include(":app") diff --git a/lib/main.dart b/lib/main.dart index dbeaa62..390a38b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,8 +7,13 @@ import 'pages/home_page.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); - // media_kit 不需要静态 init,PlaybackService 中会初始化 Player + + // ✅ 关键:必须在调用任何 media_kit API 之前执行 + MediaKit.ensureInitialized(); + + // 现在可以安全地初始化 PlaybackService PlaybackService().init(); + runApp( ChangeNotifierProvider( create: (_) => AudioService(), @@ -30,15 +35,9 @@ class QTPlayerApp extends StatelessWidget { colorScheme: const ColorScheme.dark( primary: Color(0xFF7C9A9E), secondary: Color(0xFFB8D4D0), - surface: Color(0xFF1A1F1E), // 替代 background - // 用 onSurface 控制文字颜色 + surface: Color(0xFF1A1F1E), onSurface: Colors.white, ), - appBarTheme: const AppBarTheme( - backgroundColor: Color(0xFF1A1F1E), - elevation: 0, - centerTitle: true, - ), useMaterial3: true, ), home: const HomePage(), diff --git a/lib/pages/webdav_file_list_page.dart b/lib/pages/webdav_file_list_page.dart new file mode 100644 index 0000000..ef11779 --- /dev/null +++ b/lib/pages/webdav_file_list_page.dart @@ -0,0 +1,205 @@ +import 'package:flutter/material.dart'; +import '../services/webdav_service.dart'; +import '../services/playback_service.dart'; +import '../services/audio_service.dart'; +import 'package:provider/provider.dart'; + +class WebDAVFileListPage extends StatefulWidget { + const WebDAVFileListPage({super.key, this.currentPath = '/'}); + + final String currentPath; + + @override + State createState() => _WebDAVFileListPageState(); +} + +class _WebDAVFileListPageState extends State { + List _files = []; + List _directories = []; + bool _isLoading = true; + String _errorMessage = ''; + + @override + void initState() { + super.initState(); + _loadFiles(); + } + + Future _loadFiles() async { + setState(() { + _isLoading = true; + _errorMessage = ''; + }); + + try { + final allItems = + await WebDAVService.instance.getMusicFiles(path: widget.currentPath); + + // 分离目录和文件(这里 getMusicFiles 只返回音乐文件,但如果有目录逻辑需要扩展) + // 由于 getMusicFiles 只返回音乐文件,我们需要获取目录列表 + // 这里先简化:只显示音乐文件 + setState(() { + _files = allItems; + _directories = []; + _isLoading = false; + }); + } catch (e) { + setState(() { + _errorMessage = '加载失败: $e'; + _isLoading = false; + }); + } + } + + void _playSong(WebDAVFileItem file) async { + try { + final url = WebDAVService.instance.getFileUrl(file.path); + await PlaybackService().play(url); + + // 更新 AudioService 状态 + final song = Song( + id: file.path, + title: file.name.replaceAll(RegExp(r'\.[^.]*$'), ''), + artist: '未知艺术家', + url: url, + ); + context.read().playSong(song); + + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('正在播放: ${file.name}'), + backgroundColor: const Color(0xFF4CAF50), + duration: const Duration(seconds: 1), + ), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('播放失败: $e'), + backgroundColor: Colors.red, + ), + ); + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFF0E1211), + appBar: AppBar( + title: const Text('音乐库'), + backgroundColor: Colors.transparent, + elevation: 0, + foregroundColor: Colors.white, + leading: IconButton( + icon: const Icon(Icons.arrow_back_ios_new), + onPressed: () => Navigator.pop(context), + ), + actions: [ + IconButton( + icon: const Icon(Icons.refresh), + onPressed: _loadFiles, + ), + ], + ), + body: _isLoading + ? const Center( + child: CircularProgressIndicator( + color: Color(0xFFB8D4D0), + ), + ) + : _errorMessage.isNotEmpty + ? Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.error_outline, + size: 48, color: Colors.grey[600]), + const SizedBox(height: 16), + Text( + _errorMessage, + style: TextStyle(color: Colors.grey[400]), + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: _loadFiles, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFB8D4D0), + foregroundColor: Colors.black87, + ), + child: const Text('重试'), + ), + ], + ), + ) + : _files.isEmpty + ? Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.music_note, + size: 48, color: Colors.grey[600]), + const SizedBox(height: 16), + Text( + '没有找到音乐文件', + style: TextStyle(color: Colors.grey[400]), + ), + const SizedBox(height: 8), + Text( + '支持格式: MP3, FLAC, M4A, APE, WAV, OPUS', + style: TextStyle( + color: Colors.grey[600], fontSize: 12), + ), + ], + ), + ) + : ListView.builder( + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 8), + itemCount: _files.length, + itemBuilder: (context, index) { + final file = _files[index]; + return ListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 4, vertical: 2), + leading: const Icon( + Icons.audiotrack, + color: Color(0xFFB8D4D0), + size: 28, + ), + title: Text( + file.name, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: Colors.white, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + file.size != null + ? '${(file.size! / 1024 / 1024).toStringAsFixed(1)} MB' + : '', + style: TextStyle( + fontSize: 12, + color: Colors.grey[500], + ), + ), + trailing: IconButton( + icon: const Icon(Icons.play_arrow, + color: Color(0xFFB8D4D0)), + onPressed: () => _playSong(file), + ), + onTap: () => _playSong(file), + ); + }, + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 40b91f3..50d2edf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -372,10 +372,10 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf + sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" url: "https://pub.flutter-io.cn" source: hosted - version: "2.5.5" + version: "2.2.2" shared_preferences_android: dependency: transitive description: @@ -465,10 +465,10 @@ packages: dependency: transitive description: name: synchronized - sha256: "61894a1956de6b4fc1aefd0892e109514a1a706cbece3ac59decd90ff5a7a423" + sha256: "3a7b5d17422dd0f8d5c6c14feaa5a1c65638b9455f871a96f08437562c046931" url: "https://pub.flutter-io.cn" source: hosted - version: "3.4.1+1" + version: "3.4.1+2" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7eea464..747358e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,7 +32,7 @@ dependencies: sdk: flutter media_kit: ^1.1.10 media_kit_libs_audio: ^1.0.7 - shared_preferences: ^2.2.2 + shared_preferences: 2.2.2 provider: ^6.1.2 dio: ^5.4.0 # HTTP 客户端 xml: ^6.5.0