完成本地下载页面,修复离开页面下载终止问题

master
Spark 2 weeks ago
parent 5a0540924d
commit 91386b243f

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

@ -2,6 +2,8 @@ import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:path/path.dart' as path;
class DownloadApi { class DownloadApi {
final Dio dio = Dio(); final Dio dio = Dio();
@ -45,6 +47,14 @@ class DownloadApi {
return status.isGranted; return status.isGranted;
} }
String _getFileExtension(String url) {
// Remove query parameters
final urlWithoutQuery = url.split('?').first;
// Get the extension including the dot
final extension = path.extension(urlWithoutQuery);
return extension.isNotEmpty ? extension : '.mp3'; // Default to .mp3 if no extension found
}
Future<String?> downloadMusic({ Future<String?> downloadMusic({
required String musicUrl, required String musicUrl,
required String name, required String name,
@ -52,35 +62,9 @@ class DownloadApi {
required Function(double) onProgress, required Function(double) onProgress,
}) async { }) async {
try { try {
//
final List<String> audioExtensions = [
'.mp3',
'.wav',
'.m4a',
'.aac',
'.ogg',
'.flac',
'.wma',
'.amr'
];
// URL
String fileExtension = '';
final Uri uri = Uri.parse(musicUrl);
final String pathOnly = uri.path.toLowerCase();
for (var ext in audioExtensions) {
if (pathOnly.contains(ext)) {
fileExtension = ext;
break;
}
}
// String fileExtension = _getFileExtension(musicUrl);
final fileName = fileExtension.isNotEmpty final fileName = '$name$fileExtension';
? name.endsWith(fileExtension) ? name : name + fileExtension
: name;
// //
if (!await _requestStoragePermission(context)) { if (!await _requestStoragePermission(context)) {

@ -0,0 +1,201 @@
import 'dart:convert';
import 'dart:io';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../api/api_download.dart';
import '../common_widget/Song_widegt.dart';
import 'package:path/path.dart' as path;
class DownloadItem {
final Song song;
double progress;
bool isCompleted;
bool isDownloading;
DownloadItem({
required this.song,
this.progress = 0.0,
this.isCompleted = false,
this.isDownloading = false,
});
Map<String, dynamic> toJson() {
return {
'song': {
'pic': song.pic,
'artistPic': song.artistPic,
'title': song.title,
'artist': song.artist,
'musicurl': song.musicurl,
'id': song.id,
'likes': song.likes,
'collection': song.collection
},
'progress': progress,
'isCompleted': isCompleted,
'isDownloading': isDownloading,
};
}
// JSONDownloadItem
factory DownloadItem.fromJson(Map<String, dynamic> json) {
return DownloadItem(
song: Song(
pic: json['song']['pic'],
artistPic: json['song']['artistPic'],
title: json['song']['title'],
artist: json['song']['artist'],
musicurl: json['song']['musicurl'],
id: json['song']['id'],
likes: json['song']['likes'],
collection: json['song']['collection'],
),
progress: json['progress'],
isCompleted: json['isCompleted'],
isDownloading: json['isDownloading'],
);
}
}
class DownloadManager extends GetxController {
static const String PREFS_KEY = 'downloads_data';
final _downloads = <String, DownloadItem>{}.obs;
final downloadApi = DownloadApi();
late SharedPreferences _prefs;
@override
void onInit() async {
super.onInit();
await _initPrefs();
}
Future<void> _initPrefs() async {
_prefs = await SharedPreferences.getInstance();
await _loadDownloadsFromPrefs();
}
// SharedPreferences
Future<void> _loadDownloadsFromPrefs() async {
final String? downloadsJson = _prefs.getString(PREFS_KEY);
if (downloadsJson != null) {
final Map<String, dynamic> downloadsMap = json.decode(downloadsJson);
downloadsMap.forEach((key, value) {
_downloads[key] = DownloadItem.fromJson(value);
});
}
}
// SharedPreferences
Future<void> _saveDownloadsToPrefs() async {
final Map<String, dynamic> downloadsMap = {};
_downloads.forEach((key, value) {
downloadsMap[key] = value.toJson();
});
await _prefs.setString(PREFS_KEY, json.encode(downloadsMap));
}
List<Song> getLocalSongs() {
final localSongs = <Song>[];
_downloads.forEach((key, value) {
if (value.isCompleted) {
localSongs.add(value.song);
}
});
return localSongs;
}
Map<String, DownloadItem> get downloads => _downloads;
bool isDownloading(int id) => _downloads[id.toString()]?.isDownloading ?? false;
bool isCompleted(int id) => _downloads[id.toString()]?.isCompleted ?? false;
double getProgress(int id) => _downloads[id.toString()]?.progress ?? 0.0;
Song? getLocalSong(int id) {
final downloadItem = _downloads[id.toString()];
if (downloadItem?.isCompleted ?? false) {
return downloadItem!.song;
}
return null;
}
bool removeSong(int id) {
if (_downloads[id.toString()]?.isCompleted ?? false) {
File file = File.fromUri(Uri.parse(_downloads[id.toString()]!.song.musicurl));
file.deleteSync();
_downloads.remove(id.toString());
_saveDownloadsToPrefs();
return true;
}
return false;
}
int completedNumber() {
int count = 0;
_downloads.forEach((key, value) {
if (value.isCompleted) {
count++;
}
});
return count;
}
Future<void> startDownload({
required Song song,
required context,
}) async {
if (_downloads[song.id.toString()]?.isDownloading ?? false) return;
final fileName = '${song.id}_${song.title}_${song.artist}';
final downloadItem = DownloadItem(
song: song,
isDownloading: true,
);
_downloads[song.id.toString()] = downloadItem;
try {
final filePath = await downloadApi.downloadMusic(
musicUrl: song.musicurl,
name: fileName,
context: context,
onProgress: (progress) {
downloadItem.progress = progress;
_downloads[song.id.toString()] = downloadItem;
},
);
if (filePath != null) {
downloadItem.isCompleted = true;
downloadItem.isDownloading = false;
downloadItem.progress = 1.0;
song.musicurl = _getLocalAudioPath(fileName, song.musicurl);
print(song.musicurl);
} else {
downloadItem.isDownloading = false;
downloadItem.progress = 0.0;
}
} catch (e) {
print('Download error: $e');
downloadItem.isDownloading = false;
downloadItem.progress = 0.0;
}
_downloads[song.id.toString()] = downloadItem;
await _saveDownloadsToPrefs();
}
String _getFileExtension(String url) {
// Remove query parameters
final urlWithoutQuery = url.split('?').first;
// Get the extension including the dot
final extension = path.extension(urlWithoutQuery);
return extension.isNotEmpty ? extension : '.mp3'; // Default to .mp3 if no extension found
}
String _getLocalAudioPath(String fileName, String url) {
final extension = _getFileExtension(url);
final fullFileName = '$fileName$extension';
return path.join('/storage/emulated/0/MTMusic', fullFileName);
}
}

@ -1,12 +1,20 @@
class Song { class Song {
final String pic; String pic;
final String artistPic; String artistPic;
final String title; String title;
final String artist; String artist;
final String musicurl; String musicurl;
final int id; int id;
final bool likes; bool likes;
final bool collection; 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});
} }

@ -7,6 +7,7 @@ import 'package:music_player_miao/models/search_bean.dart';
import 'package:music_player_miao/view/commend_view.dart'; import 'package:music_player_miao/view/commend_view.dart';
import '../../view_model/home_view_model.dart'; import '../../view_model/home_view_model.dart';
import '../api/api_music_list.dart'; import '../api/api_music_list.dart';
import '../common/download_manager.dart';
import '../common_widget/Song_widegt.dart'; import '../common_widget/Song_widegt.dart';
import '../common_widget/list_cell.dart'; import '../common_widget/list_cell.dart';
import '../models/getMusicList_bean.dart'; import '../models/getMusicList_bean.dart';
@ -23,6 +24,7 @@ class _HomeViewState extends State<HomeView> {
final homeVM = Get.put(HomeViewModel()); final homeVM = Get.put(HomeViewModel());
final TextEditingController _controller = TextEditingController(); final TextEditingController _controller = TextEditingController();
bool _isSearching = false; bool _isSearching = false;
final downloadManager = Get.put(DownloadManager());
void initState() { void initState() {
super.initState(); super.initState();

@ -1,14 +1,12 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:just_audio/just_audio.dart'; import 'package:just_audio/just_audio.dart';
import 'package:music_player_miao/common_widget/app_data.dart'; import 'package:music_player_miao/common_widget/app_data.dart';
import 'package:music_player_miao/models/universal_bean.dart'; import 'package:music_player_miao/models/universal_bean.dart';
import '../../view_model/home_view_model.dart'; import '../../view_model/home_view_model.dart';
import '../api/api_download.dart';
import '../api/api_music_likes.dart'; import '../api/api_music_likes.dart';
import '../common/download_manager.dart';
import '../common_widget/Song_widegt.dart'; import '../common_widget/Song_widegt.dart';
class MusicView extends StatefulWidget { class MusicView extends StatefulWidget {
@ -32,9 +30,7 @@ class _MusicViewState extends State<MusicView> {
late int currentSongIndex; late int currentSongIndex;
late AudioPlayer _audioPlayer; late AudioPlayer _audioPlayer;
double _downloadProgress = 0.0; final downloadManager = Get.put(DownloadManager());
bool _isDownloading = false;
bool _isDownloaded = false;
// Stream values // Stream values
Duration _duration = Duration.zero; Duration _duration = Duration.zero;
@ -55,7 +51,6 @@ class _MusicViewState extends State<MusicView> {
List<bool> collection = []; List<bool> collection = [];
bool _isLoading = false; bool _isLoading = false;
double _bufferProgress = 0.0;
Future<void> _fetchSonglistData() async { Future<void> _fetchSonglistData() async {
setState(() { setState(() {
@ -70,63 +65,6 @@ class _MusicViewState extends State<MusicView> {
}); });
} }
String _getFileExtension(String url) {
// Remove query parameters
final urlWithoutQuery = url.split('?').first;
// Get the extension including the dot
final extension = path.extension(urlWithoutQuery);
return extension.isNotEmpty ? extension : '.mp3'; // Default to .mp3 if no extension found
}
Future<String?> _getLocalAudioPath() async {
if (!_isDownloaded) return null;
final fileName = '${id[currentSongIndex]}_${music[currentSongIndex]}_${artist[currentSongIndex]}';
final extension = _getFileExtension(song2[currentSongIndex]);
final fullFileName = '$fileName$extension';
return path.join('/storage/emulated/0/MTMusic', fullFileName);
}
Future<void> _checkIfDownloaded() async {
try {
final fileName = '${id[currentSongIndex]}_${music[currentSongIndex]}_${artist[currentSongIndex]}';
final directory = Directory('/storage/emulated/0/MTMusic');
if (await directory.exists()) {
final files = directory.listSync();
final isExists = files.any((file) {
final name = path.basename(file.path);
return name.startsWith(fileName);
});
if (mounted) {
setState(() {
_isDownloaded = isExists;
_isDownloading = false;
_downloadProgress = 0.0;
});
}
} else {
if (mounted) {
setState(() {
_isDownloaded = false;
_isDownloading = false;
_downloadProgress = 0.0;
});
}
}
} catch (e) {
print('Error checking downloaded file: $e');
if (mounted) {
setState(() {
_isDownloaded = false;
_isDownloading = false;
_downloadProgress = 0.0;
});
}
}
}
Future<void> _updateCurrentSong() async { Future<void> _updateCurrentSong() async {
// UI // UI
setState(() { setState(() {
@ -139,21 +77,16 @@ class _MusicViewState extends State<MusicView> {
collectionsnot = collection[currentSongIndex]; collectionsnot = collection[currentSongIndex];
}); });
//
await _checkIfDownloaded();
try { try {
// //
unawaited(_audioPlayer.stop()); unawaited(_audioPlayer.stop());
// Check for local file first // Check for local file first
final localPath = await _getLocalAudioPath(); final localSong = downloadManager.getLocalSong(currentSongIndex);
final audioSource = localPath != null final audioSource = localSong != null
? AudioSource.file(localPath) ? AudioSource.file(localSong.musicurl)
: AudioSource.uri(Uri.parse(song2[currentSongIndex])); : AudioSource.uri(Uri.parse(song2[currentSongIndex]));
print("-------------" + audioSource.uri.toString());
// Set the audio source and get duration // Set the audio source and get duration
final duration = await _audioPlayer.setAudioSource( final duration = await _audioPlayer.setAudioSource(
audioSource, audioSource,
@ -181,7 +114,6 @@ class _MusicViewState extends State<MusicView> {
void playerInit() { void playerInit() {
_audioPlayer = AudioPlayer(); _audioPlayer = AudioPlayer();
currentSongIndex = widget.initialSongIndex;
// Initialize with first song // Initialize with first song
artistName = widget.songList[widget.initialSongIndex].artist; artistName = widget.songList[widget.initialSongIndex].artist;
@ -244,20 +176,6 @@ class _MusicViewState extends State<MusicView> {
); );
} }
void _initBufferingListener() {
_audioPlayer.bufferedPositionStream.listen((bufferedPosition) {
if (!_isDisposed) {
final bufferProgress = _duration.inMilliseconds > 0
? bufferedPosition.inMilliseconds / _duration.inMilliseconds
: 0.0;
setState(() {
_bufferProgress = bufferProgress;
});
}
});
}
void playOrPause() async { void playOrPause() async {
if (_audioPlayer.playing) { if (_audioPlayer.playing) {
await _audioPlayer.pause(); await _audioPlayer.pause();
@ -300,8 +218,8 @@ class _MusicViewState extends State<MusicView> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
currentSongIndex = widget.initialSongIndex;
playerInit(); playerInit();
_initBufferingListener();
_initializeAsync(); _initializeAsync();
} }
@ -362,63 +280,45 @@ class _MusicViewState extends State<MusicView> {
), ),
), ),
IconButton( IconButton(
onPressed: _isDownloading || _isDownloaded ? null: () async { onPressed: downloadManager.isDownloading(id[currentSongIndex]) ||
setState(() { downloadManager.isCompleted(id[currentSongIndex])
_isDownloading = true; ? null
_downloadProgress = 0; : () async {
}); await downloadManager.startDownload(
song: widget.songList[currentSongIndex],
final downloadApi = DownloadApi();
final fileName = '${id[currentSongIndex]}_${music[currentSongIndex]}_${artist[currentSongIndex]}';
final filePath = await downloadApi.downloadMusic(
musicUrl: song2[currentSongIndex],
name: fileName,
context: context, context: context,
onProgress: (progress) { //
setState(() {
_downloadProgress = progress;
});
},
); );
setState(() {
_isDownloading = false;
});
if (filePath != null) {
setState(() {
_isDownloaded = true;
});
}
}, },
icon: Obx(() {
icon: _isDownloading if (downloadManager.isDownloading(id[currentSongIndex])) {
? Stack( return Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
SizedBox( SizedBox(
width: 32, width: 32,
height: 32, height: 32,
child: CircularProgressIndicator( child: CircularProgressIndicator(
value: _downloadProgress, value: downloadManager.getProgress(id[currentSongIndex]),
backgroundColor: Colors.grey[200], backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation<Color>(Color(0xff429482)), valueColor: const AlwaysStoppedAnimation<Color>(Color(0xff429482)),
strokeWidth: 3.0, strokeWidth: 3.0,
), ),
), ),
Text( Text(
'${(_downloadProgress * 100).toInt()}', '${(downloadManager.getProgress(id[currentSongIndex]) * 100).toInt()}',
style: TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
), ),
], ],
) );
: Image.asset( }
_isDownloaded return Image.asset(
? "assets/img/music_download_completed.png" // downloadManager.isCompleted(id[currentSongIndex])
: "assets/img/music_download.png", // ? "assets/img/music_download_completed.png"
width: 30, : "assets/img/music_download.png",
height: 30, width: 30,
), height: 30,
);
}),
), ),
], ],
) )
@ -479,11 +379,13 @@ class _MusicViewState extends State<MusicView> {
children: [ children: [
IconButton( IconButton(
onPressed: () async{ onPressed: () async{
UniversalBean bean1 = UniversalBean response =
await LikesApiMusic().likesMusic(musicId: currentSongIndex+1, Authorization: AppData().currentToken); await LikesApiMusic().likesMusic(musicId: id[currentSongIndex], Authorization: AppData().currentToken);
setState(() { if (response.code == 200) {
likesnot = !likesnot; setState(() {
}); likesnot = !likesnot;
});
}
}, },
icon: Image.asset( icon: Image.asset(
likesnot likesnot
@ -646,7 +548,7 @@ class _MusicViewState extends State<MusicView> {
), ),
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
itemCount: 3, itemCount: music.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
bool isCurrentlyPlaying = currentSongIndex == index; bool isCurrentlyPlaying = currentSongIndex == index;
return ListTile( return ListTile(

@ -0,0 +1,508 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:music_player_miao/common_widget/Song_widegt.dart';
import '../../common/download_manager.dart';
import '../../view_model/home_view_model.dart';
import '../music_view.dart';
class MyDownloadView extends StatefulWidget {
const MyDownloadView({super.key});
@override
State<MyDownloadView> createState() => _MyDownloadViewState();
}
class _MyDownloadViewState extends State<MyDownloadView> {
final listVM = Get.put(HomeViewModel());
bool _isSelectMode = false;
final List<bool> _mySongListSelections = List.generate(2, (index) => false);
List<bool> _selectedItems = [];
List<Song> _songs = [];
final downloadManager = Get.put(DownloadManager());
@override
void initState() {
super.initState();
_getSongs();
}
void _toggleSelectMode() {
setState(() {
_isSelectMode = !_isSelectMode;
if (!_isSelectMode) {
_selectedItems = List.generate(_songs.length, (index) => false);
}
});
}
void _getSongs() async {
setState(() {
_songs = downloadManager.getLocalSongs();
});
}
void _selectAll() {
setState(() {
_selectedItems = List.generate(_songs.length, (index) => true);
});
}
void _deleteSongs(List<bool> selectedItems) {
// List<Song> songsToDelete = [];
for (int i = 0; i < selectedItems.length; i++) {
if (selectedItems[i]) {
// songsToDelete.add(_songs[i]);
downloadManager.removeSong(_songs[i].id);
}
}
//
_getSongs();
}
void _deleteSong(int index) {
downloadManager.removeSong(_songs[index].id);
_getSongs();
}
void _showSelectionDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
title: Row(
children: [
const Text(
"添加到",
),
Text(
'(${_selectedItems.where((item) => item).length} 首)',
style: const TextStyle(color: Color(0xff429482), fontSize: 16),
)
],
),
content: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
children: [
for (int i = 0; i < _mySongListSelections.length; i++)
_buildSongListTile(i),
],
),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
style: TextButton.styleFrom(
backgroundColor: const Color(0xff429482),
minimumSize: const Size(130, 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
child: const Text(
"取消",
style: TextStyle(color: Colors.white),
),
),
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
backgroundColor: const Color(0xff429482),
minimumSize: const Size(130, 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
child: const Text(
"保存",
style: TextStyle(color: Colors.white),
),
),
],
);
},
);
}
Widget _buildSongListTile(int index) {
return ListTile(
title: Text("我的歌单 $index"),
trailing: Checkbox(
value: _mySongListSelections[index],
onChanged: (value) {
setState(() {
_mySongListSelections[index] = value ?? false;
});
},
shape: const CircleBorder(),
activeColor: const Color(0xff429482),
),
onTap: () {
setState(() {
_mySongListSelections[index] = !_mySongListSelections[index];
});
},
);
}
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/img/app_bg.png"),
fit: BoxFit.cover,
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
backgroundColor: Colors.transparent,
centerTitle: true,
elevation: 0,
leading: !_isSelectMode
? IconButton(
onPressed: () {
Get.back(result: true);
},
icon: Image.asset(
"assets/img/back.png",
width: 25,
height: 25,
fit: BoxFit.contain,
),
)
: TextButton(
onPressed: _selectAll,
style: TextButton.styleFrom(
foregroundColor: Colors.black,
minimumSize: const Size(50, 40), //
padding:
const EdgeInsets.symmetric(horizontal: 8), //
),
child: const Text(
'全选',
style: TextStyle(fontSize: 18),
),
),
title: _isSelectMode
? Text(
'已选中 ${_selectedItems.where((item) => item).length} 首歌曲',
style: const TextStyle(
color: Colors.black,
),
)
: const Text(
'本地下载',
style: TextStyle(color: Colors.black),
),
actions: [
if (_isSelectMode)
TextButton(
onPressed: () {
setState(() {
_isSelectMode = false;
_selectedItems = List.generate(_songs.length, (index) => false);
});
},
child: const Text(
"完成",
style: TextStyle(color: Colors.black, fontSize: 18),
))
],
),
body: Container(
padding: const EdgeInsets.only(left: 10),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(30)),
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
IconButton(
onPressed: _songs.isEmpty ? null : () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MusicView(
songList: _songs,
initialSongIndex: 0,
),
),
);
},
icon: Image.asset(
"assets/img/button_play.png",
width: 20,
height: 20,
),
),
const Text(
'播放全部',
style: TextStyle(fontSize: 16),
),
const SizedBox(
width: 5,
),
Text(
'${_songs.length}',
style: TextStyle(fontSize: 16),
),
],
),
IconButton(
onPressed: _songs.isEmpty ? null : _toggleSelectMode,
icon: Image.asset(
"assets/img/list_op.png",
width: 20,
height: 20,
),
),
],
),
Expanded(
child: ListView.builder(
itemCount: _songs.length,
itemBuilder: (BuildContext context, int index) {
final song = _songs[index];
return Container(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: ListTile(
leading: _isSelectMode
? Checkbox(
value: _selectedItems[index],
onChanged: (value) {
setState(() {
_selectedItems[index] = value!;
});
},
shape: const CircleBorder(),
activeColor: const Color(0xff429482),
)
: null,
title: Text('${song.title} - ${song.artist}'),
trailing: _isSelectMode
? null
: IconButton(
icon: const Icon(Icons.more_vert),
onPressed: () {
_bottomSheet(context, index);
},
),
//
onTap: _isSelectMode
? () {
//
setState(() {
_selectedItems[index] =
!_selectedItems[index];
});
}
: () {
//
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MusicView(
songList: _songs,
initialSongIndex: index, // 使
),
),
);
},
),
);
},
),
),
],
),
),
bottomNavigationBar: _isSelectMode
? BottomAppBar(
height: 140, // BottomAppBar
child: SingleChildScrollView(
// 使 SingleChildScrollView
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
// "添加到"
Expanded(
child: InkWell(
onTap: () {
_showSelectionDialog();
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 25,
height: 25,
child: Image.asset("assets/img/add.png"),
),
const SizedBox(width: 4),
const Text("添加到"),
],
),
),
),
// 线
Container(
height: 50,
width: 2,
color: const Color(0xff429482),
),
// "删除"
Expanded(
child: InkWell(
onTap: () {
_deleteSongs(_selectedItems);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 22,
height: 22,
child:
Image.asset("assets/img/delete.png"),
),
const SizedBox(width: 4),
const Text("删除"),
],
),
),
),
],
),
),
ElevatedButton(
onPressed: () {
setState(() {
_isSelectMode = false;
_selectedItems =
List.generate(_songs.length, (index) => false);
});
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xff429482),
padding: const EdgeInsets.symmetric(vertical: 14),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
),
child: const Text(
'取消',
style: TextStyle(color: Colors.black, fontSize: 16),
),
),
],
),
),
)
: null,
),
);
}
Future _bottomSheet(BuildContext context, int index) {
return showModalBottomSheet(
context: context,
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(30))),
builder: (context) => Container(
height: 180,
padding: const EdgeInsets.only(top: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
IconButton(
onPressed: () {
_deleteSong(index);
Navigator.pop(context);
},
icon: Image.asset("assets/img/list_remove.png"),
iconSize: 60,
),
const Text("删除")
],
),
Column(
children: [
IconButton(
onPressed: () {},
icon: Image.asset("assets/img/list_collection.png"),
iconSize: 60,
),
const Text("收藏")
],
),
Column(
children: [
IconButton(
onPressed: () {},
icon: Image.asset("assets/img/list_good.png"),
iconSize: 60,
),
const Text("点赞")
],
),
Column(
children: [
IconButton(
onPressed: () {},
icon: Image.asset("assets/img/list_comment.png"),
iconSize: 60,
),
const Text("评论")
],
),
],
),
const SizedBox(
height: 22,
),
ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xff429482),
padding: const EdgeInsets.symmetric(vertical: 8),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
),
child: const Text(
"取消",
style: TextStyle(color: Colors.black, fontSize: 18),
),
),
],
),
));
}
}

@ -139,7 +139,7 @@ class _MyMusicViewState extends State<MyMusicView> {
leading: !_isSelectMode leading: !_isSelectMode
?IconButton( ?IconButton(
onPressed: () { onPressed: () {
Get.back(); Get.back(result: true);
}, },
icon: Image.asset( icon: Image.asset(
"assets/img/back.png", "assets/img/back.png",

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:music_player_miao/api/api_songlist.dart'; import 'package:music_player_miao/api/api_songlist.dart';
@ -5,11 +7,13 @@ import 'package:music_player_miao/common_widget/app_data.dart';
import 'package:music_player_miao/models/songlist_bean.dart'; import 'package:music_player_miao/models/songlist_bean.dart';
import 'package:music_player_miao/models/universal_bean.dart'; import 'package:music_player_miao/models/universal_bean.dart';
import 'package:music_player_miao/view/begin/begin_view.dart'; import 'package:music_player_miao/view/begin/begin_view.dart';
import 'package:music_player_miao/view/user/my_download_view.dart';
import 'package:music_player_miao/view/user/my_music_view.dart'; import 'package:music_player_miao/view/user/my_music_view.dart';
import 'package:music_player_miao/view/user/user_info.dart'; import 'package:music_player_miao/view/user/user_info.dart';
import 'package:music_player_miao/widget/text_field.dart'; import 'package:music_player_miao/widget/text_field.dart';
import '../../../view_model/home_view_model.dart'; import '../../../view_model/home_view_model.dart';
import '../../api/api_client.dart'; import '../../api/api_client.dart';
import '../../common/download_manager.dart';
import '../../models/search_bean.dart'; import '../../models/search_bean.dart';
import 'my_work_view.dart'; import 'my_work_view.dart';
@ -26,14 +30,17 @@ class _UserViewState extends State<UserView> {
int playlistCount = 2; int playlistCount = 2;
List playlistNames = []; List playlistNames = [];
List playlistid = []; List playlistid = [];
int downloadCount = 0;
final downloadManager = Get.put(DownloadManager());
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_fetchSonglistData(); _fetchSonglistData();
downloadCount = downloadManager.completedNumber();
} }
///getSonglist
Future<void> _fetchSonglistData() async { Future<void> _fetchSonglistData() async {
try { try {
SearchBean bean2 = await SonglistApi().getSonglist( SearchBean bean2 = await SonglistApi().getSonglist(
@ -144,22 +151,29 @@ class _UserViewState extends State<UserView> {
height: 10, height: 10,
), ),
InkWell( InkWell(
onTap: () { onTap: () async {
Get.to(const MyMusicView()); final result = await Get.to(const MyDownloadView());
if (result == true) {
setState(() {
downloadCount = downloadManager.completedNumber();
});
}
}, },
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Image.asset("assets/img/artist_pic.png"), Image.asset("assets/img/artist_pic.png"),
const Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( const Text(
"本地下载", "本地下载",
style: TextStyle(fontSize: 20), style: TextStyle(fontSize: 20),
), ),
Text( Text(
"19首", '${downloadCount}',
style: TextStyle(fontSize: 16), style: TextStyle(fontSize: 16),
), ),
], ],

@ -10,6 +10,7 @@ import audioplayers_darwin
import file_selector_macos import file_selector_macos
import just_audio import just_audio
import path_provider_foundation import path_provider_foundation
import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
@ -17,4 +18,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin")) JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
} }

@ -592,6 +592,62 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.28.0" version: "0.28.0"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: "95f9997ca1fb9799d494d0cb2a780fd7be075818d59f00c43832ed112b158a82"
url: "https://pub.dev"
source: hosted
version: "2.3.3"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab"
url: "https://pub.dev"
source: hosted
version: "2.3.3"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d"
url: "https://pub.dev"
source: hosted
version: "2.5.3"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
url: "https://pub.dev"
source: hosted
version: "2.4.2"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter

@ -35,6 +35,7 @@ dependencies:
audioplayers: ^5.2.1 audioplayers: ^5.2.1
permission_handler: ^11.0.1 permission_handler: ^11.0.1
just_audio: ^0.9.34 just_audio: ^0.9.34
shared_preferences: ^2.2.0
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.

Loading…
Cancel
Save