From db425e3cbe1db9ca8a92dc892f02375b949ea402 Mon Sep 17 00:00:00 2001 From: Spark <2666652@gmail.com> Date: Tue, 12 Nov 2024 21:26:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/.gradle/config.properties | 2 + android/app/local.properties | 8 + android/gradle.properties | 1 + lib/api/api_client_info.dart | 22 +- lib/api/api_music_list.dart | 41 ++- lib/api/api_music_return.dart | 17 +- lib/common_widget/Song_widegt.dart | 42 ++- lib/view/home_view.dart | 138 +++++++-- lib/view/main_tab_view/main_tab_view.dart | 77 +++-- lib/view/user/user_info.dart | 331 ++++++++++++---------- pubspec.lock | 78 +++-- pubspec.yaml | 2 +- windows/flutter/CMakeLists.txt | 7 +- 13 files changed, 510 insertions(+), 256 deletions(-) create mode 100644 android/app/.gradle/config.properties create mode 100644 android/app/local.properties diff --git a/android/app/.gradle/config.properties b/android/app/.gradle/config.properties new file mode 100644 index 0000000..ba5fd20 --- /dev/null +++ b/android/app/.gradle/config.properties @@ -0,0 +1,2 @@ +#Mon Oct 28 21:45:22 CST 2024 +java.home=C\:\\Program Files\\Android\\Android Studio\\jbr diff --git a/android/app/local.properties b/android/app/local.properties new file mode 100644 index 0000000..3a34857 --- /dev/null +++ b/android/app/local.properties @@ -0,0 +1,8 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Mon Oct 28 21:45:22 CST 2024 +sdk.dir=C\:\\Users\\zxp\\AppData\\Local\\Android\\Sdk diff --git a/android/gradle.properties b/android/gradle.properties index 94adc3a..fb29d12 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +systemProp.org.gradle.wrapper.timeout=300000 diff --git a/lib/api/api_client_info.dart b/lib/api/api_client_info.dart index 24c3f08..7303570 100644 --- a/lib/api/api_client_info.dart +++ b/lib/api/api_client_info.dart @@ -1,7 +1,6 @@ import 'dart:io'; - import 'package:dio/dio.dart'; - +import 'package:flutter/material.dart'; import '../models/universal_bean.dart'; const String _changeNameURL = 'http://flyingpig.fun:10010/users/username'; @@ -9,6 +8,7 @@ const String _changeHeaderURL = 'http://flyingpig.fun:10010/users/avatar'; class ChangeApiClient { final Dio dio = Dio(); + final ValueNotifier avatarUrlNotifier = ValueNotifier(""); ///修改昵称 Future changeName({ required String Authorization, @@ -16,13 +16,19 @@ class ChangeApiClient { }) async { Response response = await dio.put( _changeNameURL, - data: { - 'Authorization': Authorization, - }, + // data: { + // 'Authorization': Authorization, + // }, queryParameters: {'userName':userName}, - options: Options(headers:{'Authorization':Authorization,'Content-Type':'application/json;charset=UTF-8'}) + options: Options( + headers:{ + 'Authorization':Authorization, + 'Content-Type':'application/json;charset=UTF-8' + } + ) ); print(response.data); + return UniversalBean.formMap(response.data); } ///修改头像 @@ -32,7 +38,7 @@ class ChangeApiClient { required File avatar, }) async { FormData formData = FormData.fromMap({ - 'Authorization': Authorization, + // 'Authorization': Authorization, 'avatar': await MultipartFile.fromFile(avatar.path, filename: 'avatar.jpg'), }); @@ -46,8 +52,8 @@ class ChangeApiClient { }, ), ); - print(response.data); + avatarUrlNotifier.value = avatar.path; return UniversalBean.formMap(response.data); } diff --git a/lib/api/api_music_list.dart b/lib/api/api_music_list.dart index 37eb2a9..ab6c26e 100644 --- a/lib/api/api_music_list.dart +++ b/lib/api/api_music_list.dart @@ -1,13 +1,15 @@ import 'package:dio/dio.dart'; +import '../common_widget/Song_widegt.dart'; import '../models/getMusicList_bean.dart'; import '../models/getRank_bean.dart'; const String _getMusic1 = 'http://flyingpig.fun:10010/musics/1'; const String _getMusic2 = 'http://flyingpig.fun:10010/musics/2'; const String _getMusic3 = 'http://flyingpig.fun:10010/musics/3'; +const String _getSongDetail = 'http://flyingpig.fun:10010/musics'; -///排行榜 +/// 精选歌曲 class GetMusic { final Dio dio = Dio(); @@ -51,3 +53,40 @@ class GetMusic { return MusicListBean.formMap(response.data); } } + +// 获取歌曲详细信息 +class GetMusicDetail { + final Dio dio = Dio(); + + // 根据歌曲ID获取歌曲详情 + Future getMusicDetail({required int songId, required String Authorization}) async { + try { + // 更新路径,将 songId 作为路径参数插入 URL + final String url = 'http://flyingpig.fun:10010/musics/$songId'; // 假设这个是你的API路径 + + // 发起 GET 请求 + Response response = await dio.get( + url, + options: Options( + headers: { + 'Authorization': Authorization, + 'Content-Type': 'application/json;charset=UTF-8', + }, + ), + ); + + print("Song detail response: ${response.data}"); + + // 检查响应的状态码和数据 + if (response.statusCode == 200) { + // 将返回的响应数据封装成Song对象 + return Song.fromMap(response.data['data']); + } else { + throw Exception("Failed to load song details"); + } + } catch (e) { + print("Error occurred while fetching song details: $e"); + rethrow; // 抛出异常以供调用方处理 + } + } +} diff --git a/lib/api/api_music_return.dart b/lib/api/api_music_return.dart index ad2ffcf..1905401 100644 --- a/lib/api/api_music_return.dart +++ b/lib/api/api_music_return.dart @@ -9,18 +9,27 @@ import '../models/search_bean.dart'; const String _SearchURL = 'http://flyingpig.fun:10010/musics/search'; const String _postComment = 'http://flyingpig.fun:10010/comments'; ///搜索 -class SearchMusic{ +class SearchMusic { final Dio dio = Dio(); - Future search({required String keyword,}) async { + + Future search({ + required String keyword, + required String Authorization, + }) async { Response response = await dio.get( - _SearchURL, - queryParameters: {'keyword':keyword} + _SearchURL, + queryParameters: {'keyword': keyword}, + options: Options(headers: { + 'Authorization': Authorization, + 'Content-Type': 'application/json;charset=UTF-8', + }), ); print(response.data); return SearchBean.formMap(response.data); } } + ///评论 class commentMusic { final Dio dio = Dio(); diff --git a/lib/common_widget/Song_widegt.dart b/lib/common_widget/Song_widegt.dart index 96109cc..1b3b92e 100644 --- a/lib/common_widget/Song_widegt.dart +++ b/lib/common_widget/Song_widegt.dart @@ -1,12 +1,36 @@ class Song { - final String pic; - final String artistPic; - final String title; - final String artist; - final String musicurl; - final int id; - final bool likes; - final bool collection; + String pic; + String artistPic; + String title; + String artist; + String musicurl; + int id; + bool likes; + bool collection; - Song({required this.pic,required this.artistPic,required this.title, required this.artist, required this.musicurl,required this.id,required this.likes,required this.collection}); + // 构造函数 + Song({ + required this.pic, + required this.artistPic, + required this.title, + required this.artist, + required this.musicurl, + required this.id, + required this.likes, + required this.collection, + }); + + // 使用 Map 数据创建 Song 实例 + factory Song.fromMap(Map map) { + return Song( + pic: map['coverPath'] ?? '', // 封面图,假设是 coverPath + artistPic: map['coverPath'] ?? '', // 如果没有返回值或字段,可以为空字符串(示例中没有提供 artistPic) + title: map['name'] ?? '', // 歌曲名称,假设是 name + artist: map['singerName'] ?? '', // 歌手名称,假设是 singerName + musicurl: map['musicPath'] ?? '', // 歌曲路径,假设是 musicPath + id: map['id'] ?? 0, // 歌曲 ID,假设是 id + likes: map['likeOrNot'] ?? false, // 是否喜欢,假设是 likeOrNot + collection: map['collectOrNot'] ?? false, // 是否收藏,假设是 collectOrNot + ); + } } diff --git a/lib/view/home_view.dart b/lib/view/home_view.dart index 931365c..9872d56 100644 --- a/lib/view/home_view.dart +++ b/lib/view/home_view.dart @@ -20,16 +20,24 @@ class HomeView extends StatefulWidget { } class _HomeViewState extends State { + // 使用 GetX 框架的依赖注入,将 HomeViewModel 实例注册为 homeVM。 + // Get.put() 方法会创建 HomeViewModel 的实例,并将其保存在 GetX 的依赖管理器中,方便后续使用。 final homeVM = Get.put(HomeViewModel()); + + // 创建一个 TextEditingController,用于控制和监听搜索输入框的文本变化。 + // 这个控制器可以用于获取输入框的内容、清空输入框等操作。 final TextEditingController _controller = TextEditingController(); + + // 定义一个布尔变量 _isSearching,用于表示当前是否处于搜索状态。 + // 当用户在搜索框中输入内容时,_isSearching 会变为 true;当输入框为空时,_isSearching 会变为 false。 bool _isSearching = false; + void initState() { super.initState(); _fetchSonglistData(); } - List songs = []; - + List selectedSongs = []; Future _fetchSonglistData() async { MusicListBean bean1 = await GetMusic().getMusic1(Authorization: AppData().currentToken); @@ -39,7 +47,7 @@ class _HomeViewState extends State { await GetMusic().getMusic3(Authorization: AppData().currentToken); setState(() { - songs = [ + selectedSongs = [ Song( artistPic: bean1.coverPath!, title: bean1.name!, @@ -79,28 +87,99 @@ class _HomeViewState extends State { {"image": "assets/img/banner.png"}, ]; - List _filteredData = []; + List _filteredData = []; Future _filterData(String query) async { if (query.isNotEmpty) { - SearchBean bean = await SearchMusic().search(keyword: query); - if (bean.code == 200) { + try { + // 发起搜索请求 + SearchBean bean = await SearchMusic().search( + keyword: query, + Authorization: AppData().currentToken, + ); + + // 如果请求成功且返回的数据不为空 + if (bean.code == 200 && bean.data != null) { + // 创建一个临时列表来存储所有异步请求 + List> songDetailsFutures = []; + + // 循环处理每个搜索结果,通过 id 请求详细信息 + for (var data in bean.data!) { + if (data.id != null) { // 确保 id 不为 null + // 使用每个歌曲的 id 获取详细信息,并返回一个 Future + songDetailsFutures.add( + GetMusicDetail().getMusicDetail( + songId: data.id!, + Authorization: AppData().currentToken, + ).then((details) { + if (details != null) { + // 将详细歌曲信息封装成 Song 对象 + return Song( + artistPic: details.artistPic ?? '', // 歌手封面图 + title: data.name ?? '', // 歌曲名称 + artist: details.artist ?? '', // 歌手名称 + musicurl: details.musicurl ?? '', // 歌曲路径 + pic: details.pic ?? '', // 封面图片路径 + id: details.id, // 歌曲 ID + likes: details.likes, // 是否喜欢 + collection: details.collection, // 是否收藏 + ); + } + return null; // 如果没有详情返回 null + }).catchError((error) { + print("Error occurred while fetching song details: $error"); + return null; // 异常处理,返回 null + }) + ); + } else { + print("Song ID is null for song: ${data.name}"); + } + } + + // 使用 Future.wait 等待所有异步请求完成 + List songDetailsList = await Future.wait(songDetailsFutures); + + // 过滤掉 null 值 + List validSongDetails = songDetailsList.where((song) => song != null).cast().toList(); + + // 最后更新 UI,一次性更新 _filteredData + setState(() { + _filteredData = validSongDetails; // 更新搜索结果 + _isSearching = true; // 设置正在搜索中 + }); + + // 打印最终结果 + print("Filtered Data: $_filteredData"); + } else { + setState(() { + _filteredData = []; + _isSearching = false; + }); + } + } catch (error) { + print("Error occurred during search: $error"); setState(() { - _filteredData = bean.data - ?.map((data) => - "${data.name} ") // Adjust this based on your data structure - .toList() ?? - []; - _isSearching = true; + _filteredData = []; + _isSearching = false; }); } } else { setState(() { + _filteredData = []; _isSearching = false; }); } } + + + + + + + + + @override Widget build(BuildContext context) { ///轮播图 @@ -160,6 +239,14 @@ class _HomeViewState extends State { ), ), + + + + + + + + ///搜索 Container( padding: const EdgeInsets.only(left: 20, right: 20, top: 10), @@ -224,7 +311,21 @@ class _HomeViewState extends State { itemCount: _filteredData.length, itemBuilder: (context, index) { return ListTile( - title: Text(_filteredData[index]), + title: Text(_filteredData[index].title), + onTap: () { + // 用户点击列表项时,执行以下操作: + Navigator.push( + // 使用 Navigator 进行页面跳转 + context, + MaterialPageRoute( + // 创建一个新的页面(MusicView),并将当前歌曲和索引作为参数传递给它 + builder: (context) => MusicView( + song: _filteredData[index], // 传递当前列表项对应的歌曲对象,包含歌曲的详细信息 + initialSongIndex: index, // 传递当前歌曲在歌曲列表中的索引,用于在新页面中显示或操作 + ), + ), + ); + }, ); }, ), @@ -233,6 +334,7 @@ class _HomeViewState extends State { ), ), + ///推荐+轮播图 Container( padding: const EdgeInsets.only(left: 20, right: 20, top: 10), @@ -259,18 +361,18 @@ class _HomeViewState extends State { ), ListView.builder( padding: EdgeInsets.zero, - itemCount: songs.length, + itemCount: selectedSongs.length, physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, itemBuilder: (context, index) { return ListTile( - leading: Image.network(songs[index].pic), + leading: Image.network(selectedSongs[index].pic), title: Text( - songs[index].title, + selectedSongs[index].title, style: const TextStyle(fontSize: 18, color: Colors.black), ), subtitle: Text( - songs[index].artist, + selectedSongs[index].artist, style: const TextStyle(fontSize: 16, color: Colors.black), ), trailing: InkWell( @@ -284,7 +386,7 @@ class _HomeViewState extends State { context, MaterialPageRoute( builder: (context) => MusicView( - song: songs[index], + song: selectedSongs[index], initialSongIndex: index, ), ), diff --git a/lib/view/main_tab_view/main_tab_view.dart b/lib/view/main_tab_view/main_tab_view.dart index ec7d82b..3287148 100644 --- a/lib/view/main_tab_view/main_tab_view.dart +++ b/lib/view/main_tab_view/main_tab_view.dart @@ -1,42 +1,47 @@ import 'package:flutter/material.dart'; import 'package:music_player_miao/view/rank_view.dart'; import 'package:music_player_miao/view/user/user_view.dart'; - +import 'package:get/get.dart'; import '../home_view.dart'; import '../release_view.dart'; - class MainTabView extends StatefulWidget { const MainTabView({super.key}); + + // 新增方法,用于获取 TabController + TabController? getController(BuildContext context) { + final state = context.findAncestorStateOfType<_MainTabViewState>(); + return state?.controller; + } + @override State createState() => _MainTabViewState(); } -class _MainTabViewState extends State with SingleTickerProviderStateMixin{ - TabController? controller; +class _MainTabViewState extends State with SingleTickerProviderStateMixin { + late TabController controller; int selectTab = 0; final GlobalKey scaffoldKey = GlobalKey(); @override void initState() { - // TODO: implement initState super.initState(); + // 初始化时创建 TabController controller = TabController(length: 4, vsync: this); - controller?.addListener(() { - selectTab = controller?.index ?? 0; + controller.addListener(() { setState(() { - + selectTab = controller.index; }); }); } @override void dispose() { - // TODO: implement dispose + controller.dispose(); super.dispose(); - controller?.dispose(); } + @override Widget build(BuildContext context) { return Scaffold( @@ -48,7 +53,7 @@ class _MainTabViewState extends State with SingleTickerProviderStat HomeView(), RankView(), ReleaseView(), - UserView() + UserView(), ], ), bottomNavigationBar: BottomAppBar( @@ -59,43 +64,37 @@ class _MainTabViewState extends State with SingleTickerProviderStat indicatorColor: Colors.transparent, indicatorWeight: 3, labelColor: Colors.black, - labelStyle: const TextStyle( - fontSize: 12 - ), + labelStyle: const TextStyle(fontSize: 12), unselectedLabelColor: const Color(0xffCDCDCD), - unselectedLabelStyle: const TextStyle( - fontSize: 12 - ), + unselectedLabelStyle: const TextStyle(fontSize: 12), tabs: [ Tab( - text: - "首页", - icon:Image.asset( - selectTab == 0?"assets/img/home_tab.png":"assets/img/home_tab_un.png", - width: 45,height: 45,), - + text: "首页", + icon: Image.asset( + selectTab == 0 ? "assets/img/home_tab.png" : "assets/img/home_tab_un.png", + width: 45, height: 45, + ), ), Tab( - text: - "排行榜", - icon:Image.asset( - selectTab == 1?"assets/img/list_tab.png":"assets/img/list_tab_un.png", - width: 45,height: 45,), - + text: "排行榜", + icon: Image.asset( + selectTab == 1 ? "assets/img/list_tab.png" : "assets/img/list_tab_un.png", + width: 45, height: 45, + ), ), Tab( - text: - "发布", - icon:Image.asset(selectTab == 2?"assets/img/music_tab.png":"assets/img/music_tab_un.png", - width: 45,height: 45,), - + text: "发布", + icon: Image.asset( + selectTab == 2 ? "assets/img/music_tab.png" : "assets/img/music_tab_un.png", + width: 45, height: 45, + ), ), Tab( - text: - "个人", - icon:Image.asset(selectTab == 3?"assets/img/user_tab.png":"assets/img/user_tab_un.png", - width: 45,height: 45,), - + text: "个人", + icon: Image.asset( + selectTab == 3 ? "assets/img/user_tab.png" : "assets/img/user_tab_un.png", + width: 45, height: 45, + ), ), ], ), diff --git a/lib/view/user/user_info.dart b/lib/view/user/user_info.dart index 65650fb..431c246 100644 --- a/lib/view/user/user_info.dart +++ b/lib/view/user/user_info.dart @@ -1,17 +1,12 @@ -// ignore_for_file: use_build_context_synchronously - import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:music_player_miao/widget/text_field.dart'; +import 'package:image_picker/image_picker.dart'; import '../../api/api_client.dart'; import '../../api/api_client_info.dart'; import '../../common_widget/app_data.dart'; -import '../../models/getInfo_bean.dart'; -import '../../models/universal_bean.dart'; import '../../view_model/home_view_model.dart'; -import 'package:image_picker/image_picker.dart'; +import '../../view/main_tab_view/main_tab_view.dart'; class UserInfo extends StatefulWidget { const UserInfo({super.key}); @@ -23,7 +18,7 @@ class UserInfo extends StatefulWidget { class _UserInfoState extends State { final listVM = Get.put(HomeViewModel()); final TextEditingController _controller = TextEditingController(); - late File _selectedImage; + File? _selectedImage; @override Widget build(BuildContext context) { @@ -42,7 +37,17 @@ class _UserInfoState extends State { elevation: 0, leading: IconButton( onPressed: () { - Get.back(); + // 返回到 MainTabView 并切换到“个人”标签页 + Get.back(); // 关闭当前页面并返回到主界面 + + // 等待页面关闭后再切换标签 + Future.delayed(Duration(milliseconds: 100), () { + // 查找 MainTabView 并获取 TabController + final mainTabController = Get.find().getController(context); + if (mainTabController != null) { + mainTabController.index = 3; // 切换到“个人”标签页的索引 + } + }); }, icon: Image.asset( "assets/img/back.png", @@ -60,77 +65,8 @@ class _UserInfoState extends State { body: SingleChildScrollView( child: Column( children: [ - Container( - height: 80, - color: Colors.white.withOpacity(0.6), - padding: const EdgeInsets.only(left: 48, right: 25), - child: InkWell( - onTap: () { - _bottomSheet(context); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - "头像", - style: TextStyle(fontSize: 20), - ), - Row( - children: [ - Image.network( - AppData().currentAvatar, - width: 64, - height: 64, - ), - const SizedBox( - width: 20, - ), - Image.asset( - "assets/img/user_next.png", - width: 25, - height: 25, - ) - ], - ) - ], - ), - ), - ), - Container( - height: 80, - color: Colors.white.withOpacity(0.6), - padding: const EdgeInsets.only(left: 48, right: 25), - child: InkWell( - onTap: () { - _showNicknameDialog(); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - "昵称", - style: TextStyle(fontSize: 20), - ), - Row( - children: [ - Text( - AppData().currentUsername, - style: const TextStyle(fontSize: 20), - ), - const SizedBox( - width: 15, - ), - Image.asset( - "assets/img/user_next.png", - width: 25, - height: 25, - ) - ], - ) - ], - ), - ), - ) + _buildAvatarRow(), + _buildNicknameRow(), ], ), ), @@ -138,68 +74,159 @@ class _UserInfoState extends State { ); } + // 构建头像行 + Widget _buildAvatarRow() { + return Container( + height: 80, + color: Colors.white.withOpacity(0.6), + padding: const EdgeInsets.only(left: 48, right: 25), + child: InkWell( + onTap: () { + _bottomSheet(context); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + "头像", + style: TextStyle(fontSize: 20), + ), + Row( + children: [ + _buildAvatarImage(), + const SizedBox(width: 20), + Image.asset( + "assets/img/user_next.png", + width: 25, + height: 25, + ), + ], + ), + ], + ), + ), + ); + } + + // 构建昵称行 + Widget _buildNicknameRow() { + return Container( + height: 80, + color: Colors.white.withOpacity(0.6), + padding: const EdgeInsets.only(left: 48, right: 25), + child: InkWell( + onTap: () { + _showNicknameDialog(); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + "昵称", + style: TextStyle(fontSize: 20), + ), + Row( + children: [ + Text( + AppData().currentUsername, + style: const TextStyle(fontSize: 20), + ), + const SizedBox(width: 15), + Image.asset( + "assets/img/user_next.png", + width: 25, + height: 25, + ), + ], + ), + ], + ), + ), + ); + } + + // 显示头像的 Widget,根据是否是网络 URL 或本地路径来动态加载 + Widget _buildAvatarImage() { + final avatarPath = AppData().currentAvatar; + if (avatarPath.startsWith('http')) { + return Image.network( + avatarPath, + width: 64, + height: 64, + ); + } else { + return Image.file( + File(avatarPath), + width: 64, + height: 64, + ); + } + } + Future _bottomSheet(BuildContext context) async { final picker = ImagePicker(); await showModalBottomSheet( - context: context, - backgroundColor: Colors.white, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(30))), - builder: (context) => Container( - height: 132, - padding: const EdgeInsets.only(top: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ElevatedButton( - onPressed: () async { - Navigator.pop(context); - final pickedFile = - await picker.pickImage(source: ImageSource.gallery); - if (pickedFile != null) { - _selectedImage=File('assets/images/bg.png'); - setState(() { - _selectedImage = File(pickedFile.path); - print(_selectedImage); - }); - UniversalBean bean = await ChangeApiClient().changeHeader( - Authorization: AppData().currentToken, - avatar: _selectedImage); - GetInfoBean bean1 = await GetInfoApiClient().getInfo( - Authorization: AppData().currentToken); - AppData appData = AppData(); - appData.box.write('currentAvatar', AppData().currentAvatar); - } - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.transparent, - elevation: 0, - padding: const EdgeInsets.symmetric(vertical: 12), - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - child: const Text( - "从相册上传头像", - style: TextStyle(color: Colors.black, fontSize: 18), - ), - ), - ElevatedButton( - onPressed: () => Navigator.pop(context), - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xff429482), - padding: const EdgeInsets.symmetric(vertical: 18), - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.zero, - ), - ), - child: const Text( - "取消", - style: TextStyle(color: Colors.black, fontSize: 18), - ), - ), - ], + context: context, + backgroundColor: Colors.white, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(30))), + builder: (context) => Container( + height: 132, + padding: const EdgeInsets.only(top: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ElevatedButton( + onPressed: () async { + Navigator.pop(context); + final pickedFile = + await picker.pickImage(source: ImageSource.gallery); + if (pickedFile != null) { + _selectedImage = File(pickedFile.path); + setState(() {}); // 更新 UI + + // 上传头像 + await ChangeApiClient().changeHeader( + Authorization: AppData().currentToken, + avatar: _selectedImage!); + + // 更新本地存储 + _updatetouxiang(_selectedImage!.path); + // 拉取更新后的用户信息 + await GetInfoApiClient().getInfo( + Authorization: AppData().currentToken); + } + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.transparent, + elevation: 0, + padding: const EdgeInsets.symmetric(vertical: 12), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - )); + child: const Text( + "从相册上传头像", + style: TextStyle(color: Colors.black, fontSize: 18), + ), + ), + ElevatedButton( + onPressed: () => Navigator.pop(context), + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xff429482), + padding: const EdgeInsets.symmetric(vertical: 18), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.zero, + ), + ), + child: const Text( + "取消", + style: TextStyle(color: Colors.black, fontSize: 18), + ), + ), + ], + ), + ), + ); } void _showNicknameDialog() { @@ -209,12 +236,12 @@ class _UserInfoState extends State { return AlertDialog( title: const Center( child: Text( - "修改昵称", - style: TextStyle(fontSize: 20), - )), - content: TextFieldColor( + "修改昵称", + style: TextStyle(fontSize: 20), + )), + content: TextField( controller: _controller, - hintText: '请输入新昵称', + decoration: const InputDecoration(hintText: '请输入新昵称'), ), actions: [ TextButton( @@ -225,8 +252,7 @@ class _UserInfoState extends State { backgroundColor: const Color(0xff429482), minimumSize: const Size(130, 50), shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(5.0), // Adjust the radius as needed + borderRadius: BorderRadius.circular(5.0), ), ), child: const Text( @@ -237,7 +263,7 @@ class _UserInfoState extends State { TextButton( onPressed: () async { _updateNickname(); - UniversalBean bean = await ChangeApiClient().changeName( + await ChangeApiClient().changeName( Authorization: AppData().currentToken, userName: AppData().currentUsername); Navigator.of(context).pop(); @@ -246,8 +272,7 @@ class _UserInfoState extends State { backgroundColor: const Color(0xff429482), minimumSize: const Size(130, 50), shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(5.0), // Adjust the radius as needed + borderRadius: BorderRadius.circular(5.0), ), ), child: const Text( @@ -267,4 +292,14 @@ class _UserInfoState extends State { appData.box.write('currentUsername', _controller.text); }); } + + void _updatetouxiang(String path) { + setState(() { + AppData appData = AppData(); + appData.box.write('currentAvatar', path); // 更新头像路径到本地存储 + }); + } } + + + diff --git a/pubspec.lock b/pubspec.lock index 1902b87..48f1f89 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.flutter-io.cn" source: hosted - version: "1.17.2" + version: "1.18.0" cross_file: dependency: transitive description: @@ -141,10 +141,10 @@ packages: dependency: transitive description: name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" + version: "2.1.3" file: dependency: transitive description: @@ -336,6 +336,30 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.6.7" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + url: "https://pub.flutter-io.cn" + source: hosted + version: "10.0.5" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.5" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.1" lints: dependency: transitive description: @@ -348,26 +372,26 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.flutter-io.cn" source: hosted - version: "0.5.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.flutter-io.cn" source: hosted - version: "1.9.1" + version: "1.15.0" mime: dependency: transitive description: @@ -388,10 +412,10 @@ packages: dependency: "direct main" description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.3" + version: "1.9.0" path_provider: dependency: transitive description: @@ -489,18 +513,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.flutter-io.cn" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -529,10 +553,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.flutter-io.cn" source: hosted - version: "0.6.0" + version: "0.7.2" typed_data: dependency: transitive description: @@ -557,22 +581,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + name: vm_service + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.4-beta" + version: "14.2.5" win32: - dependency: transitive + dependency: "direct main" description: name: win32 - sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f" + sha256: "10169d3934549017f0ae278ccb07f828f9d6ea21573bab0fb77b0e1ef0fce454" url: "https://pub.flutter-io.cn" source: hosted - version: "5.1.0" + version: "5.7.2" xdg_directories: dependency: transitive description: @@ -582,5 +606,5 @@ packages: source: hosted version: "1.0.3" sdks: - dart: ">=3.1.2 <4.0.0" - flutter: ">=3.13.0" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index c825d0c..ae48959 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,7 +30,7 @@ environment: dependencies: flutter: sdk: flutter - + win32: ^5.7.1 flutter_swiper_view: 1.1.8 audioplayers: ^5.2.1 diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt index 930d207..903f489 100644 --- a/windows/flutter/CMakeLists.txt +++ b/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS