1.首页增加下拉刷新
2.修改下载状态监听方式,实现全局同步 3.修复搜索无结果时页面报错 4.歌单页面点击播放全部和随机是播放当前歌单列表
This commit is contained in:
parent
64047ed78a
commit
da21720c3c
@ -10,18 +10,16 @@ class MyMarqueeText extends StatelessWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.text,
|
required this.text,
|
||||||
required this.textStyle,
|
required this.textStyle,
|
||||||
this.enable = true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final String text;
|
final String text;
|
||||||
final TextStyle textStyle;
|
final TextStyle textStyle;
|
||||||
final bool enable;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Marquee(
|
return Marquee(
|
||||||
delay: Duration.zero,
|
delay: Duration.zero,
|
||||||
duration: Duration(seconds: enable ? 16 : 0),
|
duration: const Duration(seconds: 40),
|
||||||
pause: Duration.zero,
|
pause: Duration.zero,
|
||||||
gap: 80,
|
gap: 80,
|
||||||
child: Text(text, style: textStyle),
|
child: Text(text, style: textStyle),
|
||||||
|
|||||||
@ -5,38 +5,41 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
class BaseEasyRefresh extends StatelessWidget {
|
class BaseEasyRefresh extends StatelessWidget {
|
||||||
final EasyRefreshController? controller;
|
final EasyRefreshController? controller;
|
||||||
final bool noMoreRefresh;
|
|
||||||
final bool noMoreLoad;
|
|
||||||
final bool refreshOnStart;
|
final bool refreshOnStart;
|
||||||
|
final bool resetAfterRefresh;
|
||||||
final Header? header;
|
final Header? header;
|
||||||
final Footer? footer;
|
final Footer? footer;
|
||||||
final FutureOr Function()? onRefresh;
|
final FutureOr Function()? onRefresh;
|
||||||
final FutureOr Function()? onLoad;
|
final FutureOr Function()? onLoad;
|
||||||
final Widget child;
|
final ScrollController? scrollController;
|
||||||
|
final Widget Function(BuildContext context, ScrollPhysics physics) childBuilder;
|
||||||
|
|
||||||
const BaseEasyRefresh({
|
const BaseEasyRefresh({
|
||||||
super.key,
|
super.key,
|
||||||
this.controller,
|
this.controller,
|
||||||
this.noMoreRefresh = false,
|
|
||||||
this.noMoreLoad = false,
|
|
||||||
this.refreshOnStart = false,
|
this.refreshOnStart = false,
|
||||||
|
this.resetAfterRefresh = true,
|
||||||
this.header,
|
this.header,
|
||||||
this.footer,
|
this.footer,
|
||||||
required this.child,
|
|
||||||
this.onRefresh,
|
this.onRefresh,
|
||||||
this.onLoad,
|
this.onLoad,
|
||||||
|
this.scrollController,
|
||||||
|
required this.childBuilder,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return EasyRefresh(
|
return EasyRefresh.builder(
|
||||||
refreshOnStart: refreshOnStart,
|
refreshOnStart: refreshOnStart,
|
||||||
|
resetAfterRefresh: resetAfterRefresh,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
header: header,
|
header: header ?? const ClassicHeader(),
|
||||||
footer: footer,
|
footer: footer ?? const ClassicFooter(),
|
||||||
onRefresh: onRefresh,
|
onRefresh: onRefresh,
|
||||||
onLoad: onLoad,
|
onLoad: onLoad,
|
||||||
child: child,
|
scrollController: scrollController,
|
||||||
|
triggerAxis: Axis.vertical,
|
||||||
|
childBuilder: childBuilder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,11 +18,15 @@ class ViewStateWidget extends StatelessWidget {
|
|||||||
required this.viewState,
|
required this.viewState,
|
||||||
required this.child,
|
required this.child,
|
||||||
this.cpiBgColor,
|
this.cpiBgColor,
|
||||||
|
this.showTryAgain = false,
|
||||||
|
this.onTapTryAgain,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ViewState viewState;
|
final ViewState viewState;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final Color? cpiBgColor;
|
final Color? cpiBgColor;
|
||||||
|
final bool showTryAgain;
|
||||||
|
final Function()? onTapTryAgain;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -32,7 +36,10 @@ class ViewStateWidget extends StatelessWidget {
|
|||||||
case ViewState.loading:
|
case ViewState.loading:
|
||||||
return loadingView(backgroundColor: cpiBgColor);
|
return loadingView(backgroundColor: cpiBgColor);
|
||||||
case ViewState.empty:
|
case ViewState.empty:
|
||||||
return AppConfig.appSideEnum == AppSideEnum.sideA ? emptyViewA() : emptyViewB();
|
return AppConfig.appSideEnum == AppSideEnum.sideA ? emptyViewA() : emptyViewB(
|
||||||
|
showTryAgain: showTryAgain,
|
||||||
|
onTapTryAgain: onTapTryAgain,
|
||||||
|
);
|
||||||
case ViewState.error:
|
case ViewState.error:
|
||||||
return errorView();
|
return errorView();
|
||||||
}
|
}
|
||||||
@ -46,14 +53,14 @@ Widget loadingView({
|
|||||||
}) {
|
}) {
|
||||||
return Center(
|
return Center(
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
strokeWidth: 3,
|
strokeWidth: 2.w,
|
||||||
color: AppConfig.appSideEnum == AppSideEnum.sideA ? color : seedColor,
|
color: AppConfig.appSideEnum == AppSideEnum.sideA ? color : seedColor,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 空视图
|
/// 空视图A
|
||||||
Widget emptyViewA({String? msg, Color? textColor}) {
|
Widget emptyViewA({String? msg, Color? textColor}) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -67,13 +74,55 @@ Widget emptyViewA({String? msg, Color? textColor}) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 空视图2
|
/// 空视图B
|
||||||
Widget emptyViewB() {
|
Widget emptyViewB({
|
||||||
|
required bool showTryAgain,
|
||||||
|
Function()? onTapTryAgain,
|
||||||
|
}) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Image.asset(
|
child: Column(
|
||||||
Assets.sideBEmpty,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
width: 180.w,
|
children: [
|
||||||
height: 160.h,
|
Image.asset(
|
||||||
|
Assets.sideBEmpty,
|
||||||
|
width: 180.w,
|
||||||
|
height: 120.h,
|
||||||
|
),
|
||||||
|
if (showTryAgain) ...[
|
||||||
|
Text(
|
||||||
|
'An error occurred\nSorry, please try again later',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: const Color(0x73FFFFFF),
|
||||||
|
fontSize: 14.sp,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10.h),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: onTapTryAgain,
|
||||||
|
child: Container(
|
||||||
|
width: 122.w,
|
||||||
|
height: 35.h,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
width: 1.w,
|
||||||
|
color: seedColor,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(40).r,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Try again',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: seedColor,
|
||||||
|
fontSize: 16.sp,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
6
lib/data/cache/music_cache_manager.dart
vendored
6
lib/data/cache/music_cache_manager.dart
vendored
@ -3,6 +3,7 @@
|
|||||||
// Description: 自定义音乐缓存管理器
|
// Description: 自定义音乐缓存管理器
|
||||||
|
|
||||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
|
import 'package:tone_snap/utils/obj_util.dart';
|
||||||
|
|
||||||
class MusicCacheManager {
|
class MusicCacheManager {
|
||||||
static const key = 'musicCacheKey';
|
static const key = 'musicCacheKey';
|
||||||
@ -19,7 +20,8 @@ class MusicCacheManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 检查是否有缓存
|
/// 检查是否有缓存
|
||||||
static Future<FileInfo?> checkCache(String videoId) async {
|
static Future<FileInfo?> checkCache(String? videoId) async {
|
||||||
return await instance.getFileFromCache(getCacheKey(videoId));
|
if (ObjUtil.isEmpty(videoId)) return null;
|
||||||
|
return await instance.getFileFromCache(getCacheKey(videoId!));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -29,6 +29,8 @@ class PlaylistModel extends HiveObject {
|
|||||||
String? coverUrl;
|
String? coverUrl;
|
||||||
@HiveField(6)
|
@HiveField(6)
|
||||||
String? subtitle;
|
String? subtitle;
|
||||||
|
@HiveField(7)
|
||||||
|
String? musicType;
|
||||||
|
|
||||||
PlaylistModel({
|
PlaylistModel({
|
||||||
required this.id,
|
required this.id,
|
||||||
@ -38,6 +40,7 @@ class PlaylistModel extends HiveObject {
|
|||||||
this.params,
|
this.params,
|
||||||
this.coverUrl,
|
this.coverUrl,
|
||||||
this.subtitle,
|
this.subtitle,
|
||||||
|
this.musicType,
|
||||||
});
|
});
|
||||||
|
|
||||||
PlaylistModel copyWith({
|
PlaylistModel copyWith({
|
||||||
@ -48,6 +51,7 @@ class PlaylistModel extends HiveObject {
|
|||||||
String? params,
|
String? params,
|
||||||
String? coverUrl,
|
String? coverUrl,
|
||||||
String? subtitle,
|
String? subtitle,
|
||||||
|
String? musicType,
|
||||||
}) =>
|
}) =>
|
||||||
PlaylistModel(
|
PlaylistModel(
|
||||||
id: id,
|
id: id,
|
||||||
@ -57,6 +61,7 @@ class PlaylistModel extends HiveObject {
|
|||||||
params: params ?? this.params,
|
params: params ?? this.params,
|
||||||
coverUrl: coverUrl ?? this.coverUrl,
|
coverUrl: coverUrl ?? this.coverUrl,
|
||||||
subtitle: subtitle ?? this.subtitle,
|
subtitle: subtitle ?? this.subtitle,
|
||||||
|
musicType: musicType ?? this.musicType,
|
||||||
);
|
);
|
||||||
|
|
||||||
factory PlaylistModel.fromJson(String str) => PlaylistModel.fromMap(json.decode(str));
|
factory PlaylistModel.fromJson(String str) => PlaylistModel.fromMap(json.decode(str));
|
||||||
@ -71,6 +76,7 @@ class PlaylistModel extends HiveObject {
|
|||||||
params: json["params"],
|
params: json["params"],
|
||||||
coverUrl: json["coverUrl"],
|
coverUrl: json["coverUrl"],
|
||||||
subtitle: json["subtitle"],
|
subtitle: json["subtitle"],
|
||||||
|
musicType: json["musicType"],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toMap() => {
|
Map<String, dynamic> toMap() => {
|
||||||
@ -81,5 +87,6 @@ class PlaylistModel extends HiveObject {
|
|||||||
"params": params,
|
"params": params,
|
||||||
"coverUrl": coverUrl,
|
"coverUrl": coverUrl,
|
||||||
"subtitle": subtitle,
|
"subtitle": subtitle,
|
||||||
|
"musicType": musicType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,8 +61,6 @@ class DioClient {
|
|||||||
/// 请求
|
/// 请求
|
||||||
Future request<T>(
|
Future request<T>(
|
||||||
String path, {
|
String path, {
|
||||||
bool showLoading = false,
|
|
||||||
bool showToast = false,
|
|
||||||
required RequestMethod requestMethod,
|
required RequestMethod requestMethod,
|
||||||
dynamic data,
|
dynamic data,
|
||||||
Map<String, dynamic>? queryParameters,
|
Map<String, dynamic>? queryParameters,
|
||||||
@ -72,6 +70,8 @@ class DioClient {
|
|||||||
T Function(Map<String, dynamic>)? formJson,
|
T Function(Map<String, dynamic>)? formJson,
|
||||||
required Function(T? result) success,
|
required Function(T? result) success,
|
||||||
Function(BaseError baseError)? fail,
|
Function(BaseError baseError)? fail,
|
||||||
|
bool showLoading = false,
|
||||||
|
bool showToast = false,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
BaseEasyLoading.loading(show: showLoading);
|
BaseEasyLoading.loading(show: showLoading);
|
||||||
|
|||||||
@ -31,13 +31,21 @@ class CollectPlaylistsBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 添加数据
|
/// 添加数据
|
||||||
Future<int> add({required String id, required String title, String? params, String? coverUrl, String? subtitle}) async {
|
Future<int> add({
|
||||||
|
required String id,
|
||||||
|
required String title,
|
||||||
|
String? params,
|
||||||
|
String? coverUrl,
|
||||||
|
String? subtitle,
|
||||||
|
String? musicType,
|
||||||
|
}) async {
|
||||||
return await _box.add(PlaylistModel(
|
return await _box.add(PlaylistModel(
|
||||||
id: id,
|
id: id,
|
||||||
title: title,
|
title: title,
|
||||||
params: params,
|
params: params,
|
||||||
coverUrl: coverUrl,
|
coverUrl: coverUrl,
|
||||||
subtitle: subtitle,
|
subtitle: subtitle,
|
||||||
|
musicType: musicType,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -92,16 +92,14 @@ class PlaylistsBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 删除歌曲
|
/// 删除歌曲
|
||||||
Future<bool> removeMusic(String id, String videoId) async {
|
Future<void> removeMusic(String id, String videoId) async {
|
||||||
final playlistModel = _box.get(id);
|
final playlistModel = _box.get(id);
|
||||||
if (playlistModel != null && playlistModel.musicList != null) {
|
if (playlistModel != null && playlistModel.musicList != null) {
|
||||||
if (playlistModel.musicList!.firstWhereOrNull((e) => e.videoId == videoId) != null) {
|
if (playlistModel.musicList!.firstWhereOrNull((e) => e.videoId == videoId) != null) {
|
||||||
playlistModel.musicList!.removeWhere((e) => e.videoId == videoId);
|
playlistModel.musicList!.removeWhere((e) => e.videoId == videoId);
|
||||||
_box.put(id, playlistModel);
|
await _box.put(id, playlistModel);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取当前列表的封面
|
/// 获取当前列表的封面
|
||||||
|
|||||||
@ -17,16 +17,17 @@ import 'package:tone_snap/utils/local_path_util.dart';
|
|||||||
import 'package:tone_snap/utils/log_util.dart';
|
import 'package:tone_snap/utils/log_util.dart';
|
||||||
import 'package:tone_snap/utils/obj_util.dart';
|
import 'package:tone_snap/utils/obj_util.dart';
|
||||||
|
|
||||||
class DownloadManager {
|
class DownloadManager extends GetxController {
|
||||||
static final DownloadManager _instance = DownloadManager._getInstance();
|
static DownloadManager get to => Get.put(DownloadManager(), permanent: true);
|
||||||
|
final downloadStateId = 'download_state_id';
|
||||||
|
|
||||||
factory DownloadManager() => _instance;
|
MemoryTaskQueue? tq;
|
||||||
|
|
||||||
static MemoryTaskQueue? tq;
|
List<MusicModel> downloadList = [];
|
||||||
|
|
||||||
List<Rx<MusicModel>> downloadList = [];
|
@override
|
||||||
|
void onInit() {
|
||||||
DownloadManager._getInstance() {
|
super.onInit();
|
||||||
if (tq == null) {
|
if (tq == null) {
|
||||||
tq ??= MemoryTaskQueue();
|
tq ??= MemoryTaskQueue();
|
||||||
tq!.maxConcurrent = 3; // no more than 5 tasks active at any one time
|
tq!.maxConcurrent = 3; // no more than 5 tasks active at any one time
|
||||||
@ -34,13 +35,13 @@ class DownloadManager {
|
|||||||
tq!.maxConcurrentByGroup = 3; // no more than three tasks from the same group active at the same time
|
tq!.maxConcurrentByGroup = 3; // no more than three tasks from the same group active at the same time
|
||||||
FileDownloader().addTaskQueue(tq!); // 'connects' the TaskQueue to the FileDownloader
|
FileDownloader().addTaskQueue(tq!); // 'connects' the TaskQueue to the FileDownloader
|
||||||
FileDownloader().updates.listen((update) async { // listen to updates as per usual
|
FileDownloader().updates.listen((update) async { // listen to updates as per usual
|
||||||
Rx<MusicModel>? musicModel = downloadList.firstWhereOrNull((e) => e.value.videoId == update.task.taskId);
|
MusicModel? musicModel = downloadList.firstWhereOrNull((e) => e.videoId == update.task.taskId);
|
||||||
if (musicModel == null) return;
|
if (musicModel == null) return;
|
||||||
|
|
||||||
if (update.runtimeType == TaskStatusUpdate) {
|
if (update.runtimeType == TaskStatusUpdate) {
|
||||||
TaskStatus taskStatus = (update as TaskStatusUpdate).status;
|
TaskStatus taskStatus = (update as TaskStatusUpdate).status;
|
||||||
LogUtil.d('${update.task.filename},任务状态: $taskStatus');
|
LogUtil.d('${update.task.filename},任务状态: $taskStatus');
|
||||||
musicModel.update((fn) => fn?.taskStatus = taskStatus);
|
musicModel.taskStatus = taskStatus;
|
||||||
switch (taskStatus) {
|
switch (taskStatus) {
|
||||||
case TaskStatus.enqueued:
|
case TaskStatus.enqueued:
|
||||||
break;
|
break;
|
||||||
@ -48,8 +49,8 @@ class DownloadManager {
|
|||||||
break;
|
break;
|
||||||
case TaskStatus.complete:
|
case TaskStatus.complete:
|
||||||
LogUtil.d('音乐下载路径:${await update.task.filePath()}');
|
LogUtil.d('音乐下载路径:${await update.task.filePath()}');
|
||||||
musicModel.value.localPath = await update.task.filePath();
|
musicModel.localPath = await update.task.filePath();
|
||||||
OfflineBox().add(musicModel.value.copyWith());
|
OfflineBox().add(musicModel);
|
||||||
downloadList.remove(musicModel);
|
downloadList.remove(musicModel);
|
||||||
BaseEasyLoading.toast('Download completed');
|
BaseEasyLoading.toast('Download completed');
|
||||||
if (Get.isRegistered<PersonalMusicLibraryController>()) {
|
if (Get.isRegistered<PersonalMusicLibraryController>()) {
|
||||||
@ -78,77 +79,74 @@ class DownloadManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (update.runtimeType == TaskProgressUpdate) {
|
if (update.runtimeType == TaskProgressUpdate) {
|
||||||
LogUtil.d('${update.task.filename},下载进度: $update');
|
LogUtil.d('${update.task.filename},下载进度: $update');
|
||||||
musicModel.update((fn) => fn?.progress = (update as TaskProgressUpdate).progress);
|
musicModel.progress = (update as TaskProgressUpdate).progress;
|
||||||
}
|
}
|
||||||
|
updateDownloadState();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 下载文件
|
/// 下载文件
|
||||||
void downloadMusic(Rx<MusicModel>? musicModel) {
|
void downloadMusic(MusicModel? m) {
|
||||||
if (musicModel == null) return;
|
if (m == null) return;
|
||||||
musicModel.update((fn) {
|
MusicModel musicModel = m.copyWith();
|
||||||
fn?.taskStatus = TaskStatus.enqueued;
|
musicModel.taskStatus = TaskStatus.enqueued;
|
||||||
fn?.cancelToken = CancelToken();
|
musicModel.cancelToken = CancelToken();
|
||||||
});
|
downloadList.add(musicModel);
|
||||||
|
updateDownloadState();
|
||||||
|
|
||||||
_getMusicUrl(musicModel, (url, mimeType) async {
|
_getMusicUrl(musicModel, (url, mimeType) async {
|
||||||
if (ObjUtil.isEmpty(url)) {
|
if (ObjUtil.isNotEmpty(url)) {
|
||||||
BaseEasyLoading.toast('Resource acquisition failed');
|
String extension = mimeType ?? 'mp4';
|
||||||
musicModel.update((fn) => fn?.taskStatus = TaskStatus.failed);
|
if (ObjUtil.isNotEmpty(mimeType)) {
|
||||||
return;
|
// 从 mimeType 中提取主类型和子类型
|
||||||
|
String type = mimeType!.split(';')[0].trim();
|
||||||
|
// 获取文件扩展名
|
||||||
|
extension = type.split('/')[1];
|
||||||
|
}
|
||||||
|
final filename = '${musicModel.title}_${DateUtil.getNowTimestamp()}.$extension';
|
||||||
|
final task = DownloadTask(
|
||||||
|
taskId: musicModel.videoId,
|
||||||
|
url: url!,
|
||||||
|
filename: filename,
|
||||||
|
directory: LocalPathUtil.getMusicDownloadDir(),
|
||||||
|
updates: Updates.statusAndProgress,
|
||||||
|
requiresWiFi: false,
|
||||||
|
retries: 0,
|
||||||
|
allowPause: false,
|
||||||
|
metaData: '',
|
||||||
|
);
|
||||||
|
tq?.add(task);
|
||||||
}
|
}
|
||||||
String extension = mimeType ?? 'mp4';
|
|
||||||
if (ObjUtil.isNotEmpty(mimeType)) {
|
|
||||||
// 从 mimeType 中提取主类型和子类型
|
|
||||||
String type = mimeType!.split(';')[0].trim();
|
|
||||||
// 获取文件扩展名
|
|
||||||
extension = type.split('/')[1];
|
|
||||||
}
|
|
||||||
final filename = '${musicModel.value.title}_${DateUtil.getNowTimestamp()}.$extension';
|
|
||||||
final task = DownloadTask(
|
|
||||||
taskId: musicModel.value.videoId,
|
|
||||||
url: url!,
|
|
||||||
filename: filename,
|
|
||||||
directory: LocalPathUtil.getMusicDownloadDir(),
|
|
||||||
updates: Updates.statusAndProgress,
|
|
||||||
requiresWiFi: false,
|
|
||||||
retries: 0,
|
|
||||||
allowPause: false,
|
|
||||||
metaData: '',
|
|
||||||
);
|
|
||||||
tq?.add(task);
|
|
||||||
downloadList.add(musicModel);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _getMusicUrl(Rx<MusicModel> musicModel, Function(String? url, String? mimeType) onTap) async {
|
Future<void> _getMusicUrl(MusicModel musicModel, Function(String? url, String? mimeType) onTap) async {
|
||||||
PlayerModel? playerModel = await MusicApi.player(
|
PlayerModel? playerModel = await MusicApi.player(
|
||||||
videoId: musicModel.value.videoId,
|
videoId: musicModel.videoId,
|
||||||
cancelToken: musicModel.value.cancelToken,
|
cancelToken: musicModel.cancelToken,
|
||||||
fail: (baseError) {
|
fail: (baseError) {
|
||||||
if (baseError.code == DioExceptionType.cancel.index) {
|
if (baseError.code == DioExceptionType.cancel.index) {
|
||||||
musicModel.update((fn) {
|
musicModel.taskStatus = TaskStatus.canceled;
|
||||||
fn?.taskStatus = TaskStatus.canceled;
|
musicModel.cancelToken = null;
|
||||||
fn?.cancelToken = null;
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
musicModel.update((fn) {
|
musicModel.taskStatus = TaskStatus.failed;
|
||||||
fn?.taskStatus = TaskStatus.failed;
|
musicModel.cancelToken = null;
|
||||||
fn?.cancelToken = null;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
downloadList.remove(musicModel);
|
||||||
|
updateDownloadState();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (playerModel != null) {
|
if (playerModel != null) {
|
||||||
if (ObjUtil.isEmpty(musicModel.value.coverUrl)) {
|
if (ObjUtil.isEmpty(musicModel.coverUrl)) {
|
||||||
var thumbnails = playerModel.videoDetails?.thumbnail?.thumbnails;
|
var thumbnails = playerModel.videoDetails?.thumbnail?.thumbnails;
|
||||||
if (thumbnails != null && thumbnails.isNotEmpty) {
|
if (thumbnails != null && thumbnails.isNotEmpty) {
|
||||||
musicModel.value.coverUrl = thumbnails.last.url;
|
musicModel.coverUrl = thumbnails.last.url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ObjUtil.isEmpty(musicModel.value.musicType)) {
|
if (ObjUtil.isEmpty(musicModel.musicType)) {
|
||||||
musicModel.value.musicType = playerModel.videoDetails?.musicVideoType;
|
musicModel.musicType = playerModel.videoDetails?.musicVideoType;
|
||||||
}
|
}
|
||||||
var formats = playerModel.streamingData?.formats;
|
var formats = playerModel.streamingData?.formats;
|
||||||
if (formats != null && formats.isNotEmpty) {
|
if (formats != null && formats.isNotEmpty) {
|
||||||
@ -157,9 +155,24 @@ class DownloadManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cancelDownload(Rx<MusicModel>? musicModel) {
|
void cancelDownload(String? videoId) {
|
||||||
if (musicModel == null || musicModel.value.videoId == null) return;
|
if (ObjUtil.isEmpty(videoId)) return;
|
||||||
musicModel.value.cancelToken?.cancel();
|
final m = getMusicModel(videoId);
|
||||||
FileDownloader().cancelTaskWithId(musicModel.value.videoId!);
|
if (m != null) {
|
||||||
|
m.cancelToken?.cancel();
|
||||||
|
if (m.videoId != null) {
|
||||||
|
FileDownloader().cancelTaskWithId(m.videoId!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateDownloadState() {
|
||||||
|
DownloadManager.to.update([downloadStateId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
MusicModel? getMusicModel(String? videoId) {
|
||||||
|
if (ObjUtil.isEmpty(videoId)) return null;
|
||||||
|
final m = downloadList.firstWhereOrNull((e) => e.videoId == videoId);
|
||||||
|
return m;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,7 +79,7 @@ class MyApp extends StatelessWidget {
|
|||||||
MusicBar().hide();
|
MusicBar().hide();
|
||||||
} else {
|
} else {
|
||||||
if (Get.isRegistered<MusicPlayerController>()) {
|
if (Get.isRegistered<MusicPlayerController>()) {
|
||||||
if (MusicPlayerController.to.getMusicModel()?.value.videoId != null) {
|
if (MusicPlayerController.to.getMusicModel()?.videoId != null) {
|
||||||
if (Get.isBottomSheetOpen != null && Get.isBottomSheetOpen!) {
|
if (Get.isBottomSheetOpen != null && Get.isBottomSheetOpen!) {
|
||||||
MusicBar().hide();
|
MusicBar().hide();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
|
import 'dart:ffi';
|
||||||
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:tone_snap/components/base_easyloading.dart';
|
import 'package:tone_snap/components/base_easyloading.dart';
|
||||||
import 'package:tone_snap/components/view_state_widget.dart';
|
import 'package:tone_snap/components/view_state_widget.dart';
|
||||||
import 'package:tone_snap/data/api/music_api.dart';
|
import 'package:tone_snap/data/api/music_api.dart';
|
||||||
import 'package:tone_snap/data/models/browse_model.dart';
|
import 'package:tone_snap/data/models/browse_model.dart';
|
||||||
import 'package:tone_snap/data/models/music_model.dart';
|
import 'package:tone_snap/data/models/music_model.dart';
|
||||||
import 'package:tone_snap/data/models/next_model.dart';
|
|
||||||
import 'package:tone_snap/data/storage/collect_playlists_box.dart';
|
import 'package:tone_snap/data/storage/collect_playlists_box.dart';
|
||||||
import 'package:tone_snap/modules/sideb/collect_playlists/collect_playlists_controller.dart';
|
import 'package:tone_snap/modules/sideb/collect_playlists/collect_playlists_controller.dart';
|
||||||
import 'package:tone_snap/modules/sideb/controllers/music_player_controller.dart';
|
import 'package:tone_snap/modules/sideb/controllers/music_player_controller.dart';
|
||||||
@ -16,6 +17,7 @@ class AlbumSongListController extends GetxController {
|
|||||||
var musicPlayerController = MusicPlayerController.to;
|
var musicPlayerController = MusicPlayerController.to;
|
||||||
var browseId = '';
|
var browseId = '';
|
||||||
var params = '';
|
var params = '';
|
||||||
|
var musicType = '';
|
||||||
|
|
||||||
/// 封面 url、标题 、描述
|
/// 封面 url、标题 、描述
|
||||||
var coverUrl = ''.obs;
|
var coverUrl = ''.obs;
|
||||||
@ -25,7 +27,7 @@ class AlbumSongListController extends GetxController {
|
|||||||
|
|
||||||
var viewState = ViewState.loading.obs;
|
var viewState = ViewState.loading.obs;
|
||||||
/// 专辑/歌单预览列表
|
/// 专辑/歌单预览列表
|
||||||
var musicList = <Rx<MusicModel>>[].obs;
|
var musicList = <MusicModel>[].obs;
|
||||||
|
|
||||||
/// 是否收藏
|
/// 是否收藏
|
||||||
var isCollect = false.obs;
|
var isCollect = false.obs;
|
||||||
@ -34,6 +36,7 @@ class AlbumSongListController extends GetxController {
|
|||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
Map<String, dynamic> arguments = Get.arguments;
|
Map<String, dynamic> arguments = Get.arguments;
|
||||||
|
musicType = arguments['musicType'] ?? '';
|
||||||
browseId = arguments['browseId'] ?? '';
|
browseId = arguments['browseId'] ?? '';
|
||||||
params = arguments['params'] ?? '';
|
params = arguments['params'] ?? '';
|
||||||
coverUrl.value = arguments['coverUrl'] ?? '';
|
coverUrl.value = arguments['coverUrl'] ?? '';
|
||||||
@ -129,7 +132,7 @@ class AlbumSongListController extends GetxController {
|
|||||||
if (playlistItemData != null) {
|
if (playlistItemData != null) {
|
||||||
musicModel.videoId = playlistItemData.videoId;
|
musicModel.videoId = playlistItemData.videoId;
|
||||||
}
|
}
|
||||||
musicList.add(musicModel.obs);
|
musicList.add(musicModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,66 +140,24 @@ class AlbumSongListController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 点击播放全部歌曲
|
/// 点击播放全部歌曲
|
||||||
Future<void> onTapPlayAll(int type) async {
|
Future<void> onTapPlayAll(bool isShuffle) async {
|
||||||
if (musicList.isNotEmpty) {
|
if (musicList.isNotEmpty) {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
if (type == 1) {
|
if (isShuffle && musicList.length > 1) {
|
||||||
index = NumUtil.getRandomNumber(0, musicList.length);
|
final n = musicList.indexWhere((e) => e.videoId == musicPlayerController.getMusicModel()?.videoId);
|
||||||
}
|
if (n != -1) {
|
||||||
List<MusicModel> playList = await _next(musicList[index].value.videoId, playlistId: musicList[index].value.playlistId);
|
index = NumUtil.getRandomNumberExcludingCurrent(0, musicList.length, n);
|
||||||
musicPlayerController.playMusic(playList[index].videoId, playList: playList);
|
} else {
|
||||||
}
|
index = NumUtil.getRandomNumber(0, musicList.length);
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取播放列表
|
|
||||||
Future<List<MusicModel>> _next(String? videoId, {String? playlistId}) async {
|
|
||||||
List<MusicModel> playList = [];
|
|
||||||
NextModel? model = await MusicApi.next(playlistId: playlistId, videoId: videoId, showLoading: true);
|
|
||||||
if (model != null) {
|
|
||||||
var tabs = model.contents?.singleColumnMusicWatchNextResultsRenderer?.tabbedRenderer?.watchNextTabbedResultsRenderer?.tabs;
|
|
||||||
if (tabs != null && tabs.isNotEmpty) {
|
|
||||||
for (var i = 0; i < tabs.length; ++i) {
|
|
||||||
var o = tabs[i];
|
|
||||||
if (i == 0) {
|
|
||||||
var contents = o.tabRenderer?.content?.musicQueueRenderer?.content?.playlistPanelRenderer?.contents;
|
|
||||||
if (contents != null && contents.isNotEmpty) {
|
|
||||||
for (var j = 0; j < contents.length; ++j) {
|
|
||||||
var musicModel = MusicModel();
|
|
||||||
var content = contents[j];
|
|
||||||
if (content.playlistPanelVideoRenderer != null) {
|
|
||||||
// 封面
|
|
||||||
var thumbnails = content.playlistPanelVideoRenderer?.thumbnail?.thumbnails;
|
|
||||||
if (thumbnails != null) {
|
|
||||||
musicModel.coverUrl = thumbnails.last.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 标题
|
|
||||||
var runs = content.playlistPanelVideoRenderer?.title?.runs;
|
|
||||||
if (runs != null && runs.isNotEmpty) {
|
|
||||||
musicModel.title = runs[0].text;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 副标题
|
|
||||||
var subRuns = content.playlistPanelVideoRenderer?.longBylineText?.runs;
|
|
||||||
if (subRuns != null && subRuns.isNotEmpty) {
|
|
||||||
musicModel.subtitle = subRuns.map((e) => e.text).join();
|
|
||||||
}
|
|
||||||
|
|
||||||
// videoId, playlistId
|
|
||||||
var watchEndpoint = content.playlistPanelVideoRenderer?.navigationEndpoint?.watchEndpoint;
|
|
||||||
if (watchEndpoint != null) {
|
|
||||||
musicModel.videoId = watchEndpoint.videoId;
|
|
||||||
musicModel.playlistId = watchEndpoint.playlistId;
|
|
||||||
}
|
|
||||||
playList.add(musicModel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (var o in musicList) {
|
||||||
|
if (ObjUtil.isEmpty(o.coverUrl)) {
|
||||||
|
o.coverUrl = coverUrl.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
musicPlayerController.playMusic(musicList[index].videoId, playList: musicList);
|
||||||
}
|
}
|
||||||
return playList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 点击收藏
|
/// 点击收藏
|
||||||
@ -212,6 +173,7 @@ class AlbumSongListController extends GetxController {
|
|||||||
params: params,
|
params: params,
|
||||||
coverUrl: coverUrl.value,
|
coverUrl: coverUrl.value,
|
||||||
subtitle: subtitle,
|
subtitle: subtitle,
|
||||||
|
musicType: musicType,
|
||||||
);
|
);
|
||||||
BaseEasyLoading.toast('Collected');
|
BaseEasyLoading.toast('Collected');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:tone_snap/components/base_scrollbar.dart';
|
import 'package:tone_snap/components/base_scrollbar.dart';
|
||||||
import 'package:tone_snap/components/network_image_widget.dart';
|
import 'package:tone_snap/components/network_image_widget.dart';
|
||||||
import 'package:tone_snap/components/view_state_widget.dart';
|
import 'package:tone_snap/components/view_state_widget.dart';
|
||||||
|
import 'package:tone_snap/data/enum/music_type.dart';
|
||||||
import 'package:tone_snap/generated/assets.dart';
|
import 'package:tone_snap/generated/assets.dart';
|
||||||
import 'package:tone_snap/modules/sideb/album_song_list/album_song_list_controller.dart';
|
import 'package:tone_snap/modules/sideb/album_song_list/album_song_list_controller.dart';
|
||||||
import 'package:tone_snap/modules/sideb/widgets/music_appbar.dart';
|
import 'package:tone_snap/modules/sideb/widgets/music_appbar.dart';
|
||||||
@ -109,9 +110,9 @@ class AlbumSongListView extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
_buildPlayAll(Assets.sideBPlaylistPlayAll, 'Play all', 0),
|
_buildPlayAll(Assets.sideBPlaylistPlayAll, 'Play all', false),
|
||||||
SizedBox(width: 10.w),
|
SizedBox(width: 10.w),
|
||||||
_buildPlayAll(Assets.sideBPlaylistPlayAllRandom, 'Shuffle', 1),
|
_buildPlayAll(Assets.sideBPlaylistPlayAllRandom, 'Shuffle', true),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -142,13 +143,13 @@ class AlbumSongListView extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildPlayAll(String img, String label, int type) {
|
Widget _buildPlayAll(String img, String label, bool isShuffle) {
|
||||||
return IntrinsicWidth(
|
return IntrinsicWidth(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => controller.onTapPlayAll(type),
|
onTap: () => controller.onTapPlayAll(isShuffle),
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 32.h,
|
height: 32.h,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4).w,
|
padding: const EdgeInsets.only(left: 4, right: 6).w,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(16).r,
|
borderRadius: BorderRadius.circular(16).r,
|
||||||
color: const Color(0x1A80F988),
|
color: const Color(0x1A80F988),
|
||||||
@ -163,7 +164,7 @@ class AlbumSongListView extends StatelessWidget {
|
|||||||
SizedBox(width: 4.w),
|
SizedBox(width: 4.w),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Visibility(
|
child: Visibility(
|
||||||
visible: type == 0,
|
visible: !isShuffle,
|
||||||
replacement: Text(
|
replacement: Text(
|
||||||
label,
|
label,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
@ -205,7 +206,9 @@ class AlbumSongListView extends StatelessWidget {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return MusicItem(
|
return MusicItem(
|
||||||
musicModel: controller.musicList[index],
|
musicModel: controller.musicList[index],
|
||||||
onTapItem: () => controller.onTapItem(controller.musicList[index].value),
|
showNumber: controller.musicType == MusicType.musicPageTypeAlbum.name,
|
||||||
|
number: index + 1,
|
||||||
|
onTapItem: () => controller.onTapItem(controller.musicList[index]),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -5,7 +5,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter_cache_manager/flutter_cache_manager.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:tone_snap/components/base_easyloading.dart';
|
import 'package:tone_snap/components/base_easyloading.dart';
|
||||||
@ -63,7 +62,7 @@ class MusicPlayerController extends GetxController {
|
|||||||
final List<String> _playHistory = [];
|
final List<String> _playHistory = [];
|
||||||
|
|
||||||
/// 播放列表
|
/// 播放列表
|
||||||
var playlist = <Rx<MusicModel>>[].obs;
|
var playlist = <MusicModel>[].obs;
|
||||||
|
|
||||||
/// 当前播放的歌曲索引
|
/// 当前播放的歌曲索引
|
||||||
var currentIndex = 0.obs;
|
var currentIndex = 0.obs;
|
||||||
@ -83,18 +82,18 @@ class MusicPlayerController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void playMusic(String? videoId, {List<MusicModel>? playList, bool isCurrentPlaylist = false}) {
|
void playMusic(String? videoId, {List<MusicModel>? playList, bool isCurrentPlaylist = false}) {
|
||||||
if (videoId == null) return;
|
if (ObjUtil.isEmpty(videoId)) return;
|
||||||
if (!isCurrentPlaylist) {
|
if (!isCurrentPlaylist) {
|
||||||
if (playList == null || playList.isEmpty) return;
|
if (playList == null || playList.isEmpty) return;
|
||||||
_playHistory.clear();
|
_playHistory.clear();
|
||||||
playlist.value = playList.map((e) => e.obs).toList();
|
playlist.value = playList.map((e) => e).toList();
|
||||||
}
|
}
|
||||||
Rx<MusicModel>? model = playlist.firstWhereOrNull((e) => e.value.videoId == videoId);
|
MusicModel? model = playlist.firstWhereOrNull((e) => e.videoId == videoId);
|
||||||
currentIndex.value = model != null ? playlist.indexOf(model) : 0;
|
currentIndex.value = model != null ? playlist.indexOf(model) : 0;
|
||||||
_startPlay();
|
_startPlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
Rx<MusicModel>? getMusicModel() {
|
MusicModel? getMusicModel() {
|
||||||
return playlist.isNotEmpty ? playlist[currentIndex.value] : null;
|
return playlist.isNotEmpty ? playlist[currentIndex.value] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +103,7 @@ class MusicPlayerController extends GetxController {
|
|||||||
resetPlaybackStatus();
|
resetPlaybackStatus();
|
||||||
Get.currentRoute == AppRoutes.playPage ? MusicBar().hide() : MusicBar().show();
|
Get.currentRoute == AppRoutes.playPage ? MusicBar().hide() : MusicBar().show();
|
||||||
try {
|
try {
|
||||||
MusicModel? model = OfflineBox().getList().firstWhereOrNull((e) => e.videoId == getMusicModel()!.value.videoId!);
|
var model = OfflineBox().getList().firstWhereOrNull((e) => e.videoId == getMusicModel()?.videoId);
|
||||||
if (model != null && ObjUtil.isNotEmpty(model.localPath)) {
|
if (model != null && ObjUtil.isNotEmpty(model.localPath)) {
|
||||||
// 有下载
|
// 有下载
|
||||||
LogUtil.d('读取下载路径=${model.localPath}');
|
LogUtil.d('读取下载路径=${model.localPath}');
|
||||||
@ -115,7 +114,7 @@ class MusicPlayerController extends GetxController {
|
|||||||
await _player.setFilePath(model.localPath!);
|
await _player.setFilePath(model.localPath!);
|
||||||
} else {
|
} else {
|
||||||
// 无下载
|
// 无下载
|
||||||
FileInfo? fileInfo = await MusicCacheManager.checkCache(getMusicModel()!.value.videoId!);
|
var fileInfo = await MusicCacheManager.checkCache(getMusicModel()?.videoId);
|
||||||
if (fileInfo != null) {
|
if (fileInfo != null) {
|
||||||
// 有缓存
|
// 有缓存
|
||||||
LogUtil.d('读取缓存路径=${fileInfo.file.path}');
|
LogUtil.d('读取缓存路径=${fileInfo.file.path}');
|
||||||
@ -129,7 +128,7 @@ class MusicPlayerController extends GetxController {
|
|||||||
}
|
}
|
||||||
await _player.setUrl(url!);
|
await _player.setUrl(url!);
|
||||||
// 同时启动缓存下载
|
// 同时启动缓存下载
|
||||||
_cacheManager.downloadFile(url, key: MusicCacheManager.getCacheKey(getMusicModel()!.value.videoId!)).then((fileInfo) {
|
_cacheManager.downloadFile(url, key: MusicCacheManager.getCacheKey(getMusicModel()!.videoId!)).then((fileInfo) {
|
||||||
LogUtil.d('缓存下载路径=${fileInfo.file.path}');
|
LogUtil.d('缓存下载路径=${fileInfo.file.path}');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -146,7 +145,7 @@ class MusicPlayerController extends GetxController {
|
|||||||
|
|
||||||
/// 获取当前歌曲的播放 url
|
/// 获取当前歌曲的播放 url
|
||||||
Future<String?> _getMusicUrl() async {
|
Future<String?> _getMusicUrl() async {
|
||||||
PlayerModel? model = await MusicApi.player(videoId: playlist[currentIndex.value].value.videoId);
|
PlayerModel? model = await MusicApi.player(videoId: playlist[currentIndex.value].videoId);
|
||||||
if (model != null && model.streamingData != null) {
|
if (model != null && model.streamingData != null) {
|
||||||
var formats = model.streamingData?.formats;
|
var formats = model.streamingData?.formats;
|
||||||
if (formats != null && playlist.isNotEmpty) {
|
if (formats != null && playlist.isNotEmpty) {
|
||||||
@ -260,7 +259,7 @@ class MusicPlayerController extends GetxController {
|
|||||||
bool historyExist = false;
|
bool historyExist = false;
|
||||||
for (var i = _playHistory.length - 1; i >= 0; --i) {
|
for (var i = _playHistory.length - 1; i >= 0; --i) {
|
||||||
var history = _playHistory[i];
|
var history = _playHistory[i];
|
||||||
Rx<MusicModel>? model = playlist.firstWhereOrNull((e) => e.value.videoId == history);
|
MusicModel? model = playlist.firstWhereOrNull((e) => e.videoId == history);
|
||||||
if (model != null) {
|
if (model != null) {
|
||||||
currentIndex.value = playlist.indexOf(model);
|
currentIndex.value = playlist.indexOf(model);
|
||||||
_playHistory.remove(history);
|
_playHistory.remove(history);
|
||||||
@ -293,7 +292,7 @@ class MusicPlayerController extends GetxController {
|
|||||||
break;
|
break;
|
||||||
case PlayMode.random:
|
case PlayMode.random:
|
||||||
// 记录当前播放的索引
|
// 记录当前播放的索引
|
||||||
_playHistory.add(getMusicModel()!.value.videoId!);
|
_playHistory.add(getMusicModel()!.videoId!);
|
||||||
currentIndex.value = _getRandomNumber();
|
currentIndex.value = _getRandomNumber();
|
||||||
_startPlay();
|
_startPlay();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -14,11 +14,12 @@ class CustomPlaylistController extends GetxController {
|
|||||||
static CustomPlaylistController get to => Get.find<CustomPlaylistController>();
|
static CustomPlaylistController get to => Get.find<CustomPlaylistController>();
|
||||||
var musicPlayerController = MusicPlayerController.to;
|
var musicPlayerController = MusicPlayerController.to;
|
||||||
late String playlistModelId;
|
late String playlistModelId;
|
||||||
var playlistModel = Rx<PlaylistModel?>(null);
|
late PlaylistModel playlistModel;
|
||||||
var viewState = ViewState.normal.obs;
|
var title = ''.obs;
|
||||||
var musicList = <Rx<MusicModel>>[].obs;
|
|
||||||
var showSearch = false.obs;
|
var showSearch = false.obs;
|
||||||
var textEditingController = TextEditingController();
|
var textEditingController = TextEditingController();
|
||||||
|
var viewState = ViewState.normal.obs;
|
||||||
|
var musicList = <MusicModel>[].obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -34,21 +35,21 @@ class CustomPlaylistController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void getPlaylistMode() {
|
void getPlaylistMode() {
|
||||||
playlistModel.value = PlaylistsBox().getPlaylistModel(playlistModelId);
|
if (PlaylistsBox().getPlaylistModel(playlistModelId) != null) {
|
||||||
playlistModel.update((fn) {});
|
playlistModel = PlaylistsBox().getPlaylistModel(playlistModelId)!;
|
||||||
_getList();
|
title.value = playlistModel.title;
|
||||||
|
_getList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _getList() {
|
void _getList() {
|
||||||
if (playlistModel.value != null && playlistModel.value!.musicList != null) {
|
musicList.value = playlistModel.musicList?.toList() ?? <MusicModel>[];
|
||||||
musicList.value = playlistModel.value!.musicList!.map((e) => e.obs).toList();
|
|
||||||
}
|
|
||||||
viewState.value = musicList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
viewState.value = musicList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
String? getFirstCoverUrl() {
|
String? getFirstCoverUrl() {
|
||||||
if (musicList.isNotEmpty) {
|
if (musicList.isNotEmpty) {
|
||||||
return musicList.first.value.coverUrl;
|
return musicList.first.coverUrl;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -56,18 +57,23 @@ class CustomPlaylistController extends GetxController {
|
|||||||
void onTapMore() {
|
void onTapMore() {
|
||||||
Get.bottomSheet(
|
Get.bottomSheet(
|
||||||
MorePlaylistBottomSheetView(
|
MorePlaylistBottomSheetView(
|
||||||
playlistModel: playlistModel.value!,
|
playlistModel: playlistModel,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onTapPlayAll(int type) async {
|
Future<void> onTapPlayAll(bool isShuffle) async {
|
||||||
if (musicList.isNotEmpty) {
|
if (musicList.isNotEmpty) {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
if (type == 1) {
|
if (isShuffle && musicList.length > 1) {
|
||||||
index = NumUtil.getRandomNumber(0, musicList.length);
|
final n = musicList.indexWhere((e) => e.videoId == musicPlayerController.getMusicModel()?.videoId);
|
||||||
|
if (n != -1) {
|
||||||
|
index = NumUtil.getRandomNumberExcludingCurrent(0, musicList.length, n);
|
||||||
|
} else {
|
||||||
|
index = NumUtil.getRandomNumber(0, musicList.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
musicPlayerController.playMusic(musicList[index].value.videoId, playList: musicList.map((e) => e.value).toList());
|
musicPlayerController.playMusic(musicList[index].videoId, playList: musicList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,10 +90,10 @@ class CustomPlaylistController extends GetxController {
|
|||||||
|
|
||||||
void onTapSearch(String keywords) {
|
void onTapSearch(String keywords) {
|
||||||
if (ObjUtil.isNotEmpty(keywords)) {
|
if (ObjUtil.isNotEmpty(keywords)) {
|
||||||
final result = <Rx<MusicModel>>[].obs;
|
final result = <MusicModel>[];
|
||||||
playlistModel.value!.musicList!.map((e) {
|
playlistModel.musicList!.map((e) {
|
||||||
if (e.title!.toLowerCase().contains(keywords.trim().toLowerCase())) {
|
if (e.title!.toLowerCase().contains(keywords.trim().toLowerCase())) {
|
||||||
result.add(e.obs);
|
result.add(e);
|
||||||
}
|
}
|
||||||
}).toList();
|
}).toList();
|
||||||
musicList.value = result;
|
musicList.value = result;
|
||||||
@ -99,7 +105,7 @@ class CustomPlaylistController extends GetxController {
|
|||||||
void onTapItem(MusicModel musicModel) {
|
void onTapItem(MusicModel musicModel) {
|
||||||
Get.toNamed(AppRoutes.playPage, arguments: {
|
Get.toNamed(AppRoutes.playPage, arguments: {
|
||||||
'videoId': musicModel.videoId,
|
'videoId': musicModel.videoId,
|
||||||
'playList': musicList.map((e) => e.value).toList(),
|
'playList': musicList.toList(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -101,7 +101,7 @@ class CustomPlaylistView extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.only(top: 20.0).h,
|
padding: const EdgeInsets.only(top: 20.0).h,
|
||||||
child: Obx(() {
|
child: Obx(() {
|
||||||
return MyMarqueeText(
|
return MyMarqueeText(
|
||||||
text: ObjUtil.getStr(controller.playlistModel.value!.title),
|
text: ObjUtil.getStr(controller.title.value),
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 18.sp,
|
fontSize: 18.sp,
|
||||||
@ -115,7 +115,7 @@ class CustomPlaylistView extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(top: 2).h,
|
padding: const EdgeInsets.only(top: 2).h,
|
||||||
child: Text(
|
child: Text(
|
||||||
'Created in: ${controller.playlistModel.value!.milliseconds != null ? DateUtil.formatDateMs(controller.playlistModel.value!.milliseconds!) : ''}',
|
'Created in: ${controller.playlistModel.milliseconds != null ? DateUtil.formatDateMs(controller.playlistModel.milliseconds!) : ''}',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -138,9 +138,9 @@ class CustomPlaylistView extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
_buildPlayAll(Assets.sideBPlaylistPlayAll, 'Play all', 0),
|
_buildPlayAll(Assets.sideBPlaylistPlayAll, 'Play all', false),
|
||||||
SizedBox(width: 10.w),
|
SizedBox(width: 10.w),
|
||||||
_buildPlayAll(Assets.sideBPlaylistPlayAllRandom, 'Shuffle', 1),
|
_buildPlayAll(Assets.sideBPlaylistPlayAllRandom, 'Shuffle', true),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -215,13 +215,13 @@ class CustomPlaylistView extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildPlayAll(String img, String label, int type) {
|
Widget _buildPlayAll(String img, String label, bool isShuffle) {
|
||||||
return IntrinsicWidth(
|
return IntrinsicWidth(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => controller.onTapPlayAll(type),
|
onTap: () => controller.onTapPlayAll(isShuffle),
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 32.h,
|
height: 32.h,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4).w,
|
padding: const EdgeInsets.only(left: 4, right: 6).w,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(16).r,
|
borderRadius: BorderRadius.circular(16).r,
|
||||||
color: const Color(0x1A80F988),
|
color: const Color(0x1A80F988),
|
||||||
@ -236,7 +236,7 @@ class CustomPlaylistView extends StatelessWidget {
|
|||||||
SizedBox(width: 4.w),
|
SizedBox(width: 4.w),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Visibility(
|
child: Visibility(
|
||||||
visible: type == 0,
|
visible: !isShuffle,
|
||||||
replacement: Text(
|
replacement: Text(
|
||||||
label,
|
label,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
@ -273,17 +273,15 @@ class CustomPlaylistView extends StatelessWidget {
|
|||||||
viewState: controller.viewState.value,
|
viewState: controller.viewState.value,
|
||||||
child: BaseScrollbar(
|
child: BaseScrollbar(
|
||||||
child: Obx(() {
|
child: Obx(() {
|
||||||
return Visibility(
|
return ListView.builder(
|
||||||
child: ListView.builder(
|
itemCount: controller.musicList.length,
|
||||||
itemCount: controller.musicList.length,
|
itemBuilder: (context, index) {
|
||||||
itemBuilder: (context, index) {
|
return MusicItem(
|
||||||
return MusicItem(
|
musicModel: controller.musicList[index],
|
||||||
musicModel: controller.musicList[index],
|
playlistModelId: controller.playlistModelId,
|
||||||
playlistModelId: controller.playlistModel.value!.id,
|
onTapItem: () => controller.onTapItem(controller.musicList[index]),
|
||||||
onTapItem: () => controller.onTapItem(controller.musicList[index].value),
|
);
|
||||||
);
|
},
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@ -291,41 +289,4 @@ class CustomPlaylistView extends StatelessWidget {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widget _buildEmpty() {
|
|
||||||
// return Column(
|
|
||||||
// children: [
|
|
||||||
// SizedBox(height: 40.h),
|
|
||||||
// Text(
|
|
||||||
// 'Nothing Yet',
|
|
||||||
// style: TextStyle(
|
|
||||||
// color: const Color(0x99FFFFFF),
|
|
||||||
// fontSize: 14.sp,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 16.h),
|
|
||||||
// GestureDetector(
|
|
||||||
// onTap: controller.onTapAddSongs,
|
|
||||||
// child: Container(
|
|
||||||
// width: 122.w,
|
|
||||||
// height: 35.h,
|
|
||||||
// alignment: Alignment.center,
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// borderRadius: BorderRadius.circular(40).r,
|
|
||||||
// border: Border.all(
|
|
||||||
// color: seedColor,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// child: Text(
|
|
||||||
// 'Add Songs',
|
|
||||||
// style: TextStyle(
|
|
||||||
// color: seedColor,
|
|
||||||
// fontSize: 16.sp,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import 'package:easy_refresh/easy_refresh.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:tone_snap/components/view_state_widget.dart';
|
import 'package:tone_snap/components/view_state_widget.dart';
|
||||||
import 'package:tone_snap/data/api/music_api.dart';
|
import 'package:tone_snap/data/api/music_api.dart';
|
||||||
@ -12,6 +14,10 @@ class HomeController extends GetxController {
|
|||||||
var viewState = ViewState.loading.obs;
|
var viewState = ViewState.loading.obs;
|
||||||
var groupList = <BrowseGroupModel>[].obs;
|
var groupList = <BrowseGroupModel>[].obs;
|
||||||
String? visitorData;
|
String? visitorData;
|
||||||
|
var refreshController = EasyRefreshController(
|
||||||
|
controlFinishRefresh: true,
|
||||||
|
);
|
||||||
|
var isRefresh = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onReady() {
|
void onReady() {
|
||||||
@ -19,6 +25,23 @@ class HomeController extends GetxController {
|
|||||||
firstBrowse();
|
firstBrowse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
refreshController.dispose();
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onRefresh() {
|
||||||
|
isRefresh = true;
|
||||||
|
refreshController.callRefresh();
|
||||||
|
firstBrowse();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onTapTryAgain() {
|
||||||
|
viewState.value = ViewState.loading;
|
||||||
|
firstBrowse();
|
||||||
|
}
|
||||||
|
|
||||||
/// 首次请求
|
/// 首次请求
|
||||||
Future<void> firstBrowse() async {
|
Future<void> firstBrowse() async {
|
||||||
Map<String, dynamic> queryParameters = {
|
Map<String, dynamic> queryParameters = {
|
||||||
@ -27,8 +50,9 @@ class HomeController extends GetxController {
|
|||||||
};
|
};
|
||||||
BrowseModel? browseModel = await MusicApi.browse<BrowseModel>(queryParameters: queryParameters, formJson: BrowseModel.fromMap);
|
BrowseModel? browseModel = await MusicApi.browse<BrowseModel>(queryParameters: queryParameters, formJson: BrowseModel.fromMap);
|
||||||
if (browseModel != null) {
|
if (browseModel != null) {
|
||||||
_extractAssemblyData(browseModel);
|
refreshController.finishRefresh();
|
||||||
|
|
||||||
|
_extractAssemblyData(browseModel);
|
||||||
// 获取 visitorData
|
// 获取 visitorData
|
||||||
visitorData = browseModel.responseContext?.visitorData;
|
visitorData = browseModel.responseContext?.visitorData;
|
||||||
|
|
||||||
@ -41,6 +65,8 @@ class HomeController extends GetxController {
|
|||||||
clickTrackingParams = continuations[0].nextContinuationData?.clickTrackingParams;
|
clickTrackingParams = continuations[0].nextContinuationData?.clickTrackingParams;
|
||||||
}
|
}
|
||||||
_reBrowse(continuation: continuation, clickTrackingParams: clickTrackingParams);
|
_reBrowse(continuation: continuation, clickTrackingParams: clickTrackingParams);
|
||||||
|
} else {
|
||||||
|
refreshController.finishRefresh(IndicatorResult.fail);
|
||||||
}
|
}
|
||||||
viewState.value = groupList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
viewState.value = groupList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
||||||
}
|
}
|
||||||
@ -119,7 +145,7 @@ class HomeController extends GetxController {
|
|||||||
} else {
|
} else {
|
||||||
// 获取副标题
|
// 获取副标题
|
||||||
if (runs != null && runs.isNotEmpty) {
|
if (runs != null && runs.isNotEmpty) {
|
||||||
musicModel.subtitle = runs.map((e) => e.text).join(' • ');
|
musicModel.subtitle = runs.map((e) => e.text).join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,6 +180,10 @@ class HomeController extends GetxController {
|
|||||||
}
|
}
|
||||||
// 根据类型判断是否添加
|
// 根据类型判断是否添加
|
||||||
if (MusicTypeExtension.isThereAny(browseGroupModel.musicType)) {
|
if (MusicTypeExtension.isThereAny(browseGroupModel.musicType)) {
|
||||||
|
if (isRefresh) {
|
||||||
|
groupList.clear();
|
||||||
|
isRefresh = false;
|
||||||
|
}
|
||||||
groupList.add(browseGroupModel);
|
groupList.add(browseGroupModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,8 +205,9 @@ class HomeController extends GetxController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void openAlbumSong(MusicModel musicModel) {
|
void openAlbumSong(BrowseGroupModel browseGroupModel, MusicModel musicModel) {
|
||||||
Get.toNamed(AppRoutes.albumSongList, arguments: {
|
Get.toNamed(AppRoutes.albumSongList, arguments: {
|
||||||
|
'musicType': browseGroupModel.musicType,
|
||||||
'browseId': musicModel.browseId,
|
'browseId': musicModel.browseId,
|
||||||
'params': musicModel.params,
|
'params': musicModel.params,
|
||||||
'coverUrl': musicModel.coverUrl,
|
'coverUrl': musicModel.coverUrl,
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:tone_snap/components/base_scrollbar.dart';
|
import 'package:tone_snap/components/base_scrollbar.dart';
|
||||||
|
import 'package:tone_snap/components/refresh/base_easyrefresh.dart';
|
||||||
import 'package:tone_snap/components/view_state_widget.dart';
|
import 'package:tone_snap/components/view_state_widget.dart';
|
||||||
import 'package:tone_snap/data/enum/music_type.dart';
|
import 'package:tone_snap/data/enum/music_type.dart';
|
||||||
import 'package:tone_snap/data/models/browse_group_model.dart';
|
import 'package:tone_snap/data/models/browse_group_model.dart';
|
||||||
@ -97,30 +98,39 @@ class HomeView extends GetView<HomeController> {
|
|||||||
child: Obx(() {
|
child: Obx(() {
|
||||||
return ViewStateWidget(
|
return ViewStateWidget(
|
||||||
viewState: controller.viewState.value,
|
viewState: controller.viewState.value,
|
||||||
child: BaseScrollbar(
|
showTryAgain: true,
|
||||||
child: Obx(() {
|
onTapTryAgain: controller.onTapTryAgain,
|
||||||
return ListView.separated(
|
child: BaseEasyRefresh(
|
||||||
padding: const EdgeInsets.only(bottom: 16).h,
|
controller: controller.refreshController,
|
||||||
itemCount: controller.groupList.length,
|
onRefresh: controller.onRefresh,
|
||||||
separatorBuilder: (context, index) => SizedBox(height: 16.h),
|
childBuilder: (context, physics) {
|
||||||
itemBuilder: (context, index) {
|
return BaseScrollbar(
|
||||||
final browseGroupModel = controller.groupList[index];
|
child: Obx(() {
|
||||||
if (!MusicTypeExtension.isThereAny(browseGroupModel.musicType)) {
|
return ListView.separated(
|
||||||
return Container();
|
physics: physics,
|
||||||
}
|
padding: const EdgeInsets.only(bottom: 16).h,
|
||||||
return Column(
|
itemCount: controller.groupList.length,
|
||||||
children: [
|
separatorBuilder: (context, index) => SizedBox(height: 16.h),
|
||||||
_buildGroupTitle(ObjUtil.getStr(browseGroupModel.groupTitle)),
|
itemBuilder: (context, index) {
|
||||||
if (browseGroupModel.musicType == MusicType.musicVideoTypeAtv.name) ...[
|
final browseGroupModel = controller.groupList[index];
|
||||||
_buildGroupAtv(browseGroupModel),
|
if (!MusicTypeExtension.isThereAny(browseGroupModel.musicType)) {
|
||||||
] else ...[
|
return Container();
|
||||||
_buildGroupPlaylist(browseGroupModel),
|
}
|
||||||
],
|
return Column(
|
||||||
],
|
children: [
|
||||||
|
_buildGroupTitle(ObjUtil.getStr(browseGroupModel.groupTitle)),
|
||||||
|
if (browseGroupModel.musicType == MusicType.musicVideoTypeAtv.name) ...[
|
||||||
|
_buildGroupAtv(browseGroupModel),
|
||||||
|
] else ...[
|
||||||
|
_buildGroupPlaylist(browseGroupModel),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
}),
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -136,9 +146,9 @@ class HomeView extends GetView<HomeController> {
|
|||||||
itemCount: browseGroupModel.browseList != null ? browseGroupModel.browseList!.length : 0,
|
itemCount: browseGroupModel.browseList != null ? browseGroupModel.browseList!.length : 0,
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
mainAxisSpacing: 0,
|
mainAxisSpacing: 12.w,
|
||||||
crossAxisSpacing: 12.w,
|
crossAxisSpacing: 12.w,
|
||||||
childAspectRatio: 60.w / (1.sw - 48.w),
|
childAspectRatio: 60.w / (1.sw - 50.w),
|
||||||
),
|
),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final musicModel = browseGroupModel.browseList![index];
|
final musicModel = browseGroupModel.browseList![index];
|
||||||
@ -163,7 +173,7 @@ class HomeView extends GetView<HomeController> {
|
|||||||
final musicModel = browseGroupModel.browseList![index];
|
final musicModel = browseGroupModel.browseList![index];
|
||||||
return BrowseItemAlbumSongList(
|
return BrowseItemAlbumSongList(
|
||||||
musicModel: musicModel,
|
musicModel: musicModel,
|
||||||
onTap: () => controller.openAlbumSong(musicModel),
|
onTap: () => controller.openAlbumSong(browseGroupModel, musicModel),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import 'package:tone_snap/routes/app_routes.dart';
|
|||||||
class LoveSongsController extends GetxController {
|
class LoveSongsController extends GetxController {
|
||||||
static LoveSongsController get to => Get.find<LoveSongsController>();
|
static LoveSongsController get to => Get.find<LoveSongsController>();
|
||||||
var viewState = ViewState.loading.obs;
|
var viewState = ViewState.loading.obs;
|
||||||
var loveList = <Rx<MusicModel>>[].obs;
|
var loveList = <MusicModel>[].obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onReady() {
|
void onReady() {
|
||||||
@ -16,7 +16,7 @@ class LoveSongsController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void getLoveList() {
|
void getLoveList() {
|
||||||
loveList.value = LoveSongsBox().getReversedList().map((e) => e.obs).toList();
|
loveList.value = LoveSongsBox().getReversedList();
|
||||||
viewState.value = loveList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
viewState.value = loveList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -75,7 +75,7 @@ class LoveSongsView extends StatelessWidget {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return MusicItem(
|
return MusicItem(
|
||||||
musicModel: controller.loveList[index],
|
musicModel: controller.loveList[index],
|
||||||
onTapItem: () => controller.onTapItem(controller.loveList[index].value),
|
onTapItem: () => controller.onTapItem(controller.loveList[index]),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -13,29 +13,30 @@ import 'package:tone_snap/modules/sideb/love_songs/love_songs_controller.dart';
|
|||||||
import 'package:tone_snap/modules/sideb/offline/offline_controller.dart';
|
import 'package:tone_snap/modules/sideb/offline/offline_controller.dart';
|
||||||
import 'package:tone_snap/modules/sideb/personal_music_library/personal_music_library_controller.dart';
|
import 'package:tone_snap/modules/sideb/personal_music_library/personal_music_library_controller.dart';
|
||||||
import 'package:tone_snap/modules/sideb/playlists/playlists_controller.dart';
|
import 'package:tone_snap/modules/sideb/playlists/playlists_controller.dart';
|
||||||
|
import 'package:tone_snap/utils/obj_util.dart';
|
||||||
|
|
||||||
class MoreBottomSheetController extends GetxController {
|
class MoreBottomSheetController extends GetxController {
|
||||||
late Rx<MusicModel> musicModel;
|
late MusicModel musicModel;
|
||||||
String? playlistModelId;
|
String? playlistModelId;
|
||||||
var isLove = false.obs;
|
var isLove = false.obs;
|
||||||
|
|
||||||
void setMusicModel(Rx<MusicModel> musicModel, {String? playlistModelId}) {
|
void setMusicModel(MusicModel musicModel, {String? playlistModelId}) {
|
||||||
this.musicModel = musicModel;
|
this.musicModel = musicModel;
|
||||||
this.playlistModelId = playlistModelId;
|
this.playlistModelId = playlistModelId;
|
||||||
_checkIsLove();
|
_checkIsLove();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _checkIsLove() {
|
void _checkIsLove() {
|
||||||
isLove.value = LoveSongsBox().checkLove(musicModel.value.videoId);
|
isLove.value = LoveSongsBox().checkLove(musicModel.videoId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onTapLove() async {
|
void onTapLove() async {
|
||||||
if (musicModel.value.videoId == null) return;
|
if (ObjUtil.isEmpty(musicModel.videoId)) return;
|
||||||
if (isLove.value) {
|
if (isLove.value) {
|
||||||
await LoveSongsBox().delete(musicModel.value.videoId!);
|
await LoveSongsBox().delete(musicModel.videoId!);
|
||||||
BaseEasyLoading.toast('Removed');
|
BaseEasyLoading.toast('Removed');
|
||||||
} else {
|
} else {
|
||||||
await LoveSongsBox().add(musicModel.value.copyWith());
|
await LoveSongsBox().add(musicModel.copyWith());
|
||||||
BaseEasyLoading.toast('Collected');
|
BaseEasyLoading.toast('Collected');
|
||||||
}
|
}
|
||||||
_checkIsLove();
|
_checkIsLove();
|
||||||
@ -48,14 +49,14 @@ class MoreBottomSheetController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onTapDownload() {
|
void onTapDownload() {
|
||||||
if (OfflineBox().checkDownloaded(musicModel.value.videoId) || musicModel.value.taskStatus == TaskStatus.complete) {
|
if (OfflineBox().checkDownloaded(musicModel.videoId)) {
|
||||||
Get.dialog(
|
Get.dialog(
|
||||||
RemindDialog(
|
RemindDialog(
|
||||||
content: 'Confirm to remove this song?',
|
content: 'Confirm to remove this song?',
|
||||||
confirmOnTap: () async {
|
confirmOnTap: () async {
|
||||||
Get.back();
|
Get.back();
|
||||||
await OfflineBox().delete(musicModel.value.videoId!);
|
await OfflineBox().delete(musicModel.videoId!);
|
||||||
musicModel.update((fn) => fn?.taskStatus = null);
|
DownloadManager.to.updateDownloadState();
|
||||||
BaseEasyLoading.toast('Removed');
|
BaseEasyLoading.toast('Removed');
|
||||||
if (Get.isRegistered<PersonalMusicLibraryController>()) {
|
if (Get.isRegistered<PersonalMusicLibraryController>()) {
|
||||||
PersonalMusicLibraryController.to.refreshOffline();
|
PersonalMusicLibraryController.to.refreshOffline();
|
||||||
@ -67,11 +68,11 @@ class MoreBottomSheetController extends GetxController {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (musicModel.value.taskStatus == TaskStatus.enqueued
|
if (DownloadManager.to.getMusicModel(musicModel.videoId)?.taskStatus == TaskStatus.enqueued
|
||||||
|| musicModel.value.taskStatus == TaskStatus.running) {
|
|| DownloadManager.to.getMusicModel(musicModel.videoId)?.taskStatus == TaskStatus.running) {
|
||||||
DownloadManager().cancelDownload(musicModel);
|
DownloadManager.to.cancelDownload(musicModel.videoId);
|
||||||
} else {
|
} else {
|
||||||
DownloadManager().downloadMusic(musicModel);
|
DownloadManager.to.downloadMusic(musicModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,23 +81,21 @@ class MoreBottomSheetController extends GetxController {
|
|||||||
Get.back();
|
Get.back();
|
||||||
Get.bottomSheet(
|
Get.bottomSheet(
|
||||||
AddToPlaylistBottomSheetView(
|
AddToPlaylistBottomSheetView(
|
||||||
musicModel: musicModel.value,
|
musicModel: musicModel,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onTapRemove() async {
|
Future<void> onTapRemove() async {
|
||||||
if (playlistModelId != null && musicModel.value.videoId != null) {
|
if (ObjUtil.isNotEmpty(playlistModelId) && ObjUtil.isNotEmpty(musicModel.videoId)) {
|
||||||
bool result = await PlaylistsBox().removeMusic(playlistModelId!, musicModel.value.videoId!);
|
await PlaylistsBox().removeMusic(playlistModelId!, musicModel.videoId!);
|
||||||
if (result) {
|
BaseEasyLoading.toast('Removed');
|
||||||
BaseEasyLoading.toast('Removed');
|
Get.back();
|
||||||
Get.back();
|
if (Get.isRegistered<PlaylistsController>()) {
|
||||||
if (Get.isRegistered<PlaylistsController>()) {
|
PlaylistsController.to.getList();
|
||||||
PlaylistsController.to.getList();
|
}
|
||||||
}
|
if (Get.isRegistered<CustomPlaylistController>()) {
|
||||||
if (Get.isRegistered<CustomPlaylistController>()) {
|
CustomPlaylistController.to.getPlaylistMode();
|
||||||
CustomPlaylistController.to.getPlaylistMode();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import 'package:tone_snap/components/network_image_widget.dart';
|
|||||||
import 'package:tone_snap/data/models/music_model.dart';
|
import 'package:tone_snap/data/models/music_model.dart';
|
||||||
import 'package:tone_snap/data/storage/offline_box.dart';
|
import 'package:tone_snap/data/storage/offline_box.dart';
|
||||||
import 'package:tone_snap/generated/assets.dart';
|
import 'package:tone_snap/generated/assets.dart';
|
||||||
|
import 'package:tone_snap/global/download_manager.dart';
|
||||||
import 'package:tone_snap/res/themes/app_colors.dart';
|
import 'package:tone_snap/res/themes/app_colors.dart';
|
||||||
import 'package:tone_snap/utils/obj_util.dart';
|
import 'package:tone_snap/utils/obj_util.dart';
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ class MoreBottomSheetView extends StatelessWidget {
|
|||||||
MoreBottomSheetView({super.key, required this.musicModel, this.playlistModelId});
|
MoreBottomSheetView({super.key, required this.musicModel, this.playlistModelId});
|
||||||
|
|
||||||
final controller = Get.put(MoreBottomSheetController());
|
final controller = Get.put(MoreBottomSheetController());
|
||||||
final Rx<MusicModel> musicModel;
|
final MusicModel musicModel;
|
||||||
final String? playlistModelId;
|
final String? playlistModelId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -42,7 +43,7 @@ class MoreBottomSheetView extends StatelessWidget {
|
|||||||
_buildDownload(),
|
_buildDownload(),
|
||||||
_buildItem('Add to playlist', Assets.sideBAddToPlaylist, controller.onTapAddToPlaylist),
|
_buildItem('Add to playlist', Assets.sideBAddToPlaylist, controller.onTapAddToPlaylist),
|
||||||
// _buildItem('Add to queue', Assets.sideBAddToQueue, () {}),
|
// _buildItem('Add to queue', Assets.sideBAddToQueue, () {}),
|
||||||
if (playlistModelId != null) ...[
|
if (ObjUtil.isNotEmpty(playlistModelId)) ...[
|
||||||
_buildItem('Remove from list', Assets.sideBMoreRemove, controller.onTapRemove),
|
_buildItem('Remove from list', Assets.sideBMoreRemove, controller.onTapRemove),
|
||||||
],
|
],
|
||||||
_buildItem('Report', Assets.sideBReport, controller.onTapReport),
|
_buildItem('Report', Assets.sideBReport, controller.onTapReport),
|
||||||
@ -73,21 +74,19 @@ class MoreBottomSheetView extends StatelessWidget {
|
|||||||
SizedBox(height: 20.h),
|
SizedBox(height: 20.h),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Obx(() {
|
NetworkImageWidget(
|
||||||
return NetworkImageWidget(
|
url: musicModel.coverUrl,
|
||||||
url: musicModel.value.coverUrl,
|
width: 50.w,
|
||||||
width: 50.w,
|
height: 50.w,
|
||||||
height: 50.w,
|
radius: 10.r,
|
||||||
radius: 10.r,
|
),
|
||||||
);
|
|
||||||
}),
|
|
||||||
SizedBox(width: 12.w),
|
SizedBox(width: 12.w),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
ObjUtil.getStr(musicModel.value.title),
|
ObjUtil.getStr(musicModel.title),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -96,7 +95,7 @@ class MoreBottomSheetView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
ObjUtil.getStr(musicModel.value.subtitle),
|
ObjUtil.getStr(musicModel.subtitle),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -148,52 +147,58 @@ class MoreBottomSheetView extends StatelessWidget {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: 24.w,
|
width: 24.w,
|
||||||
height: 24.w,
|
height: 24.w,
|
||||||
child: Obx(() {
|
child: GetBuilder<DownloadManager>(
|
||||||
if (OfflineBox().checkDownloaded(musicModel.value.videoId) || musicModel.value.taskStatus == TaskStatus.complete) {
|
id: DownloadManager.to.downloadStateId,
|
||||||
return Image.asset(Assets.sideBDownloaded);
|
builder: (_) {
|
||||||
} else {
|
if (OfflineBox().checkDownloaded(musicModel.videoId)) {
|
||||||
if (musicModel.value.taskStatus == TaskStatus.enqueued) {
|
return Image.asset(Assets.sideBDownloaded);
|
||||||
return CircularProgressIndicator(
|
|
||||||
color: seedColor,
|
|
||||||
strokeWidth: 2.w,
|
|
||||||
);
|
|
||||||
} else if (musicModel.value.taskStatus == TaskStatus.running) {
|
|
||||||
return CircularProgressIndicator(
|
|
||||||
value: musicModel.value.progress,
|
|
||||||
backgroundColor: seedColor.withOpacity(0.2),
|
|
||||||
valueColor: const AlwaysStoppedAnimation<Color>(seedColor),
|
|
||||||
strokeWidth: 2.w,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return Image.asset(Assets.sideBNotDownload1);
|
if (DownloadManager.to.getMusicModel(musicModel.videoId)?.taskStatus == TaskStatus.enqueued) {
|
||||||
|
return CircularProgressIndicator(
|
||||||
|
color: seedColor,
|
||||||
|
strokeWidth: 2.w,
|
||||||
|
);
|
||||||
|
} else if (DownloadManager.to.getMusicModel(musicModel.videoId)?.taskStatus == TaskStatus.running) {
|
||||||
|
return CircularProgressIndicator(
|
||||||
|
value: DownloadManager.to.getMusicModel(musicModel.videoId)?.progress,
|
||||||
|
backgroundColor: seedColor.withOpacity(0.2),
|
||||||
|
valueColor: const AlwaysStoppedAnimation<Color>(seedColor),
|
||||||
|
strokeWidth: 2.w,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Image.asset(Assets.sideBNotDownload1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 12.w),
|
SizedBox(width: 12.w),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Obx(() {
|
child: GetBuilder<DownloadManager>(
|
||||||
var text = '';
|
id: DownloadManager.to.downloadStateId,
|
||||||
if (OfflineBox().checkDownloaded(musicModel.value.videoId) || musicModel.value.taskStatus == TaskStatus.complete) {
|
builder: (_) {
|
||||||
text = ' Removed';
|
var text = '';
|
||||||
} else {
|
if (OfflineBox().checkDownloaded(musicModel.videoId)) {
|
||||||
if (musicModel.value.taskStatus == TaskStatus.enqueued
|
text = 'Removed';
|
||||||
|| musicModel.value.taskStatus == TaskStatus.running) {
|
|
||||||
text = 'Cancel download';
|
|
||||||
} else {
|
} else {
|
||||||
text = 'Download';
|
if (DownloadManager.to.getMusicModel(musicModel.videoId)?.taskStatus == TaskStatus.enqueued
|
||||||
|
|| DownloadManager.to.getMusicModel(musicModel.videoId)?.taskStatus == TaskStatus.running) {
|
||||||
|
text = 'Cancel download';
|
||||||
|
} else {
|
||||||
|
text = 'Download';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return Text(
|
||||||
return Text(
|
text,
|
||||||
text,
|
maxLines: 1,
|
||||||
maxLines: 1,
|
overflow: TextOverflow.ellipsis,
|
||||||
overflow: TextOverflow.ellipsis,
|
style: TextStyle(
|
||||||
style: TextStyle(
|
color: Colors.white,
|
||||||
color: Colors.white,
|
fontSize: 14.sp,
|
||||||
fontSize: 14.sp,
|
),
|
||||||
),
|
);
|
||||||
);
|
},
|
||||||
}),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -44,15 +44,15 @@ class MusicBarView extends StatelessWidget {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 52.w,
|
width: 51.w,
|
||||||
height: 52.w,
|
height: 51.w,
|
||||||
child: Obx(() {
|
child: Obx(() {
|
||||||
int comparison = musicPlayerController.positionDuration.value.compareTo(musicPlayerController.totalDuration.value);
|
int comparison = musicPlayerController.positionDuration.value.compareTo(musicPlayerController.totalDuration.value);
|
||||||
double value = comparison < 0 ? (musicPlayerController.positionDuration.value.inSeconds / musicPlayerController.totalDuration.value.inSeconds).toDouble() : 0;
|
double value = comparison < 0 ? (musicPlayerController.positionDuration.value.inSeconds / musicPlayerController.totalDuration.value.inSeconds).toDouble() : 0;
|
||||||
return CircularProgressIndicator(
|
return CircularProgressIndicator(
|
||||||
value: value,
|
value: value,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
strokeWidth: 4.w,
|
strokeWidth: 3.w,
|
||||||
valueColor: const AlwaysStoppedAnimation<Color>(Colors.white),
|
valueColor: const AlwaysStoppedAnimation<Color>(Colors.white),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -62,7 +62,7 @@ class MusicBarView extends StatelessWidget {
|
|||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
fit: BoxFit.none,
|
fit: BoxFit.none,
|
||||||
child: NetworkImageWidget(
|
child: NetworkImageWidget(
|
||||||
url: musicPlayerController.getMusicModel()?.value.coverUrl,
|
url: musicPlayerController.getMusicModel()?.coverUrl,
|
||||||
width: 48.w,
|
width: 48.w,
|
||||||
height: 48.w,
|
height: 48.w,
|
||||||
),
|
),
|
||||||
@ -79,7 +79,7 @@ class MusicBarView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Obx(() {
|
Obx(() {
|
||||||
return MyMarqueeText(
|
return MyMarqueeText(
|
||||||
text: ObjUtil.getStr(musicPlayerController.getMusicModel()?.value.title),
|
text: ObjUtil.getStr(musicPlayerController.getMusicModel()?.title),
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontSize: 16.sp,
|
fontSize: 16.sp,
|
||||||
@ -90,7 +90,7 @@ class MusicBarView extends StatelessWidget {
|
|||||||
SizedBox(height: 4.h),
|
SizedBox(height: 4.h),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
return MyMarqueeText(
|
return MyMarqueeText(
|
||||||
text: ObjUtil.getStr(musicPlayerController.getMusicModel()?.value.subtitle),
|
text: ObjUtil.getStr(musicPlayerController.getMusicModel()?.subtitle),
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontSize: 12.sp,
|
fontSize: 12.sp,
|
||||||
@ -100,7 +100,7 @@ class MusicBarView extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 13),
|
SizedBox(width: 13.w),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: musicPlayerController.playPause,
|
onTap: musicPlayerController.playPause,
|
||||||
@ -111,7 +111,7 @@ class MusicBarView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
const SizedBox(width: 26),
|
SizedBox(width: 26.w),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: musicPlayerController.nextTrack,
|
onTap: musicPlayerController.nextTrack,
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import 'package:tone_snap/routes/app_routes.dart';
|
|||||||
class OfflineController extends GetxController {
|
class OfflineController extends GetxController {
|
||||||
static OfflineController get to => Get.find<OfflineController>();
|
static OfflineController get to => Get.find<OfflineController>();
|
||||||
var viewState = ViewState.loading.obs;
|
var viewState = ViewState.loading.obs;
|
||||||
var offlineList = <Rx<MusicModel>>[].obs;
|
var offlineList = <MusicModel>[].obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onReady() {
|
void onReady() {
|
||||||
@ -16,7 +16,7 @@ class OfflineController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void getOfflineList() {
|
void getOfflineList() {
|
||||||
offlineList.value = OfflineBox().getReversedList().map((e) => e.obs).toList();
|
offlineList.value = OfflineBox().getReversedList();
|
||||||
viewState.value = offlineList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
viewState.value = offlineList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -76,7 +76,7 @@ class OfflineView extends StatelessWidget {
|
|||||||
return MusicItem(
|
return MusicItem(
|
||||||
musicModel: controller.offlineList[index],
|
musicModel: controller.offlineList[index],
|
||||||
showDownload: false,
|
showDownload: false,
|
||||||
onTapItem: () => controller.onTapItem(controller.offlineList[index].value),
|
onTapItem: () => controller.onTapItem(controller.offlineList[index]),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -10,9 +10,12 @@ import 'package:tone_snap/data/storage/offline_box.dart';
|
|||||||
import 'package:tone_snap/global/download_manager.dart';
|
import 'package:tone_snap/global/download_manager.dart';
|
||||||
import 'package:tone_snap/modules/sideb/controllers/music_player_controller.dart';
|
import 'package:tone_snap/modules/sideb/controllers/music_player_controller.dart';
|
||||||
import 'package:tone_snap/modules/sideb/personal_music_library/personal_music_library_controller.dart';
|
import 'package:tone_snap/modules/sideb/personal_music_library/personal_music_library_controller.dart';
|
||||||
|
import 'package:tone_snap/utils/obj_util.dart';
|
||||||
|
|
||||||
class PlayPageController extends GetxController {
|
class PlayPageController extends GetxController {
|
||||||
|
static PlayPageController get to => Get.find<PlayPageController>();
|
||||||
var musicPlayerController = MusicPlayerController.to;
|
var musicPlayerController = MusicPlayerController.to;
|
||||||
|
final loveStateId = 'loveStateId';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onReady() async {
|
void onReady() async {
|
||||||
@ -85,30 +88,29 @@ class PlayPageController extends GetxController {
|
|||||||
|
|
||||||
/// 加入/取消收藏
|
/// 加入/取消收藏
|
||||||
Future<void> onTapLove() async {
|
Future<void> onTapLove() async {
|
||||||
if (musicPlayerController.getMusicModel()?.value.videoId == null) return;
|
if (ObjUtil.isEmpty(musicPlayerController.getMusicModel()?.videoId)) return;
|
||||||
final isLove = LoveSongsBox().checkLove(musicPlayerController.getMusicModel()!.value.videoId);
|
final isLove = LoveSongsBox().checkLove(musicPlayerController.getMusicModel()!.videoId);
|
||||||
if (isLove) {
|
if (isLove) {
|
||||||
await LoveSongsBox().delete(musicPlayerController.getMusicModel()!.value.videoId!);
|
await LoveSongsBox().delete(musicPlayerController.getMusicModel()!.videoId!);
|
||||||
BaseEasyLoading.toast('Removed');
|
BaseEasyLoading.toast('Removed');
|
||||||
} else {
|
} else {
|
||||||
await LoveSongsBox().add(musicPlayerController.getMusicModel()!.value.copyWith());
|
await LoveSongsBox().add(musicPlayerController.getMusicModel()!.copyWith());
|
||||||
BaseEasyLoading.toast('Collected');
|
BaseEasyLoading.toast('Collected');
|
||||||
}
|
}
|
||||||
if (Get.isRegistered<PersonalMusicLibraryController>()) {
|
if (Get.isRegistered<PersonalMusicLibraryController>()) {
|
||||||
PersonalMusicLibraryController.to.refreshLoveSongs();
|
PersonalMusicLibraryController.to.refreshLoveSongs();
|
||||||
}
|
}
|
||||||
musicPlayerController.getMusicModel()!.update((fn) => fn?.isLove = LoveSongsBox().checkLove(musicPlayerController.getMusicModel()!.value.videoId));
|
update([loveStateId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onTapDownload() {
|
void onTapDownload() {
|
||||||
if (OfflineBox().checkDownloaded(musicPlayerController.getMusicModel()?.value.videoId)
|
if (OfflineBox().checkDownloaded(musicPlayerController.getMusicModel()?.videoId)) {
|
||||||
|| musicPlayerController.getMusicModel()?.value.taskStatus == TaskStatus.complete) {
|
|
||||||
Get.dialog(
|
Get.dialog(
|
||||||
RemindDialog(
|
RemindDialog(
|
||||||
content: 'Confirm to remove this song?',
|
content: 'Confirm to remove this song?',
|
||||||
confirmOnTap: () async {
|
confirmOnTap: () async {
|
||||||
await OfflineBox().delete(musicPlayerController.getMusicModel()!.value.videoId!);
|
await OfflineBox().delete(musicPlayerController.getMusicModel()!.videoId!);
|
||||||
musicPlayerController.getMusicModel()!.update((fn) => fn?.taskStatus = null);
|
DownloadManager.to.updateDownloadState();
|
||||||
BaseEasyLoading.toast('Removed');
|
BaseEasyLoading.toast('Removed');
|
||||||
if (Get.isRegistered<PersonalMusicLibraryController>()) {
|
if (Get.isRegistered<PersonalMusicLibraryController>()) {
|
||||||
PersonalMusicLibraryController.to.refreshOffline();
|
PersonalMusicLibraryController.to.refreshOffline();
|
||||||
@ -117,11 +119,11 @@ class PlayPageController extends GetxController {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (musicPlayerController.getMusicModel()?.value.taskStatus == TaskStatus.enqueued
|
if (DownloadManager.to.getMusicModel(musicPlayerController.getMusicModel()?.videoId)?.taskStatus == TaskStatus.enqueued
|
||||||
|| musicPlayerController.getMusicModel()?.value.taskStatus == TaskStatus.running) {
|
|| DownloadManager.to.getMusicModel(musicPlayerController.getMusicModel()?.videoId)?.taskStatus == TaskStatus.running) {
|
||||||
DownloadManager().cancelDownload(musicPlayerController.getMusicModel());
|
DownloadManager.to.cancelDownload(musicPlayerController.getMusicModel()?.videoId);
|
||||||
} else {
|
} else {
|
||||||
DownloadManager().downloadMusic(musicPlayerController.getMusicModel());
|
DownloadManager.to.downloadMusic(musicPlayerController.getMusicModel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import 'package:tone_snap/data/enum/play_mode.dart';
|
|||||||
import 'package:tone_snap/data/storage/love_songs_box.dart';
|
import 'package:tone_snap/data/storage/love_songs_box.dart';
|
||||||
import 'package:tone_snap/data/storage/offline_box.dart';
|
import 'package:tone_snap/data/storage/offline_box.dart';
|
||||||
import 'package:tone_snap/generated/assets.dart';
|
import 'package:tone_snap/generated/assets.dart';
|
||||||
|
import 'package:tone_snap/global/download_manager.dart';
|
||||||
import 'package:tone_snap/modules/sideb/controllers/music_player_controller.dart';
|
import 'package:tone_snap/modules/sideb/controllers/music_player_controller.dart';
|
||||||
import 'package:tone_snap/modules/sideb/play_page/play_page_controller.dart';
|
import 'package:tone_snap/modules/sideb/play_page/play_page_controller.dart';
|
||||||
import 'package:tone_snap/modules/sideb/widgets/music_appbar.dart';
|
import 'package:tone_snap/modules/sideb/widgets/music_appbar.dart';
|
||||||
@ -67,7 +68,7 @@ class PlayPageView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Obx(() {
|
Obx(() {
|
||||||
return NetworkImageWidget(
|
return NetworkImageWidget(
|
||||||
url: musicPlayerController.getMusicModel()?.value.coverUrl,
|
url: musicPlayerController.getMusicModel()?.coverUrl,
|
||||||
width: 1.sw,
|
width: 1.sw,
|
||||||
height: 1.sh,
|
height: 1.sh,
|
||||||
noPlaceholder: true,
|
noPlaceholder: true,
|
||||||
@ -130,7 +131,7 @@ class PlayPageView extends StatelessWidget {
|
|||||||
Widget _buildCover() {
|
Widget _buildCover() {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
return NetworkImageWidget(
|
return NetworkImageWidget(
|
||||||
url: musicPlayerController.getMusicModel()?.value.coverUrl,
|
url: musicPlayerController.getMusicModel()?.coverUrl,
|
||||||
width: 1.sw,
|
width: 1.sw,
|
||||||
height: 330.h,
|
height: 330.h,
|
||||||
radius: 16.r,
|
radius: 16.r,
|
||||||
@ -150,7 +151,7 @@ class PlayPageView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Obx(() {
|
Obx(() {
|
||||||
return MyMarqueeText(
|
return MyMarqueeText(
|
||||||
text: ObjUtil.getStr(musicPlayerController.getMusicModel()?.value.title),
|
text: ObjUtil.getStr(musicPlayerController.getMusicModel()?.title),
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
color: const Color(0xD9FFFFFF),
|
color: const Color(0xD9FFFFFF),
|
||||||
fontSize: 22.sp,
|
fontSize: 22.sp,
|
||||||
@ -160,7 +161,7 @@ class PlayPageView extends StatelessWidget {
|
|||||||
SizedBox(height: 6.h),
|
SizedBox(height: 6.h),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
return MyMarqueeText(
|
return MyMarqueeText(
|
||||||
text: ObjUtil.getStr(musicPlayerController.getMusicModel()?.value.subtitle),
|
text: ObjUtil.getStr(musicPlayerController.getMusicModel()?.subtitle),
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
color: const Color(0x99EEEEEE),
|
color: const Color(0x99EEEEEE),
|
||||||
fontSize: 12.sp,
|
fontSize: 12.sp,
|
||||||
@ -181,13 +182,16 @@ class PlayPageView extends StatelessWidget {
|
|||||||
onTap: controller.onTapLove,
|
onTap: controller.onTapLove,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(6).w,
|
padding: const EdgeInsets.all(6).w,
|
||||||
child: Obx(() {
|
child: GetBuilder<PlayPageController>(
|
||||||
return Image.asset(
|
id: PlayPageController.to.loveStateId,
|
||||||
LoveSongsBox().checkLove(musicPlayerController.getMusicModel()?.value.videoId) ? Assets.sideBLoveSolid : Assets.sideBLove,
|
builder: (_) {
|
||||||
width: 24.w,
|
return Image.asset(
|
||||||
height: 24.w,
|
LoveSongsBox().checkLove(musicPlayerController.getMusicModel()?.videoId) ? Assets.sideBLoveSolid : Assets.sideBLove,
|
||||||
);
|
width: 24.w,
|
||||||
}),
|
height: 24.w,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -208,29 +212,31 @@ class PlayPageView extends StatelessWidget {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 24.w,
|
width: 24.w,
|
||||||
height: 24.w,
|
height: 24.w,
|
||||||
child: Obx(() {
|
child: GetBuilder<DownloadManager>(
|
||||||
if (OfflineBox().checkDownloaded(musicPlayerController.getMusicModel()?.value.videoId)
|
id: DownloadManager.to.downloadStateId,
|
||||||
|| musicPlayerController.getMusicModel()?.value.taskStatus == TaskStatus.complete) {
|
builder: (_) {
|
||||||
return Image.asset(Assets.sideBDownloaded);
|
if (OfflineBox().checkDownloaded(musicPlayerController.getMusicModel()?.videoId)) {
|
||||||
} else {
|
return Image.asset(Assets.sideBDownloaded);
|
||||||
if (musicPlayerController.getMusicModel()?.value.taskStatus == TaskStatus.enqueued) {
|
|
||||||
return CircularProgressIndicator(
|
|
||||||
color: seedColor,
|
|
||||||
strokeWidth: 2.w,
|
|
||||||
);
|
|
||||||
} else if (musicPlayerController.getMusicModel()?.value.taskStatus == TaskStatus.running) {
|
|
||||||
return CircularProgressIndicator(
|
|
||||||
value: musicPlayerController.getMusicModel()?.value.progress,
|
|
||||||
backgroundColor: seedColor.withOpacity(0.2),
|
|
||||||
valueColor:
|
|
||||||
const AlwaysStoppedAnimation<Color>(seedColor),
|
|
||||||
strokeWidth: 2.w,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return Image.asset(Assets.sideBNotDownload1);
|
if (DownloadManager.to.getMusicModel(musicPlayerController.getMusicModel()?.videoId)?.taskStatus == TaskStatus.enqueued) {
|
||||||
|
return CircularProgressIndicator(
|
||||||
|
color: seedColor,
|
||||||
|
strokeWidth: 2.w,
|
||||||
|
);
|
||||||
|
} else if (DownloadManager.to.getMusicModel(musicPlayerController.getMusicModel()?.videoId)?.taskStatus == TaskStatus.running) {
|
||||||
|
return CircularProgressIndicator(
|
||||||
|
value: DownloadManager.to.getMusicModel(musicPlayerController.getMusicModel()?.videoId)?.progress,
|
||||||
|
backgroundColor: seedColor.withOpacity(0.2),
|
||||||
|
valueColor:
|
||||||
|
const AlwaysStoppedAnimation<Color>(seedColor),
|
||||||
|
strokeWidth: 2.w,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Image.asset(Assets.sideBNotDownload1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -250,15 +256,17 @@ class PlayPageView extends StatelessWidget {
|
|||||||
data: SliderTheme.of(context).copyWith(
|
data: SliderTheme.of(context).copyWith(
|
||||||
activeTrackColor: seedColor,
|
activeTrackColor: seedColor,
|
||||||
inactiveTrackColor: const Color(0x4DFFFFFF),
|
inactiveTrackColor: const Color(0x4DFFFFFF),
|
||||||
|
disabledInactiveTrackColor: const Color(0x4DFFFFFF),
|
||||||
secondaryActiveTrackColor: seedColor.withOpacity(0.3),
|
secondaryActiveTrackColor: seedColor.withOpacity(0.3),
|
||||||
trackHeight: 4.h,
|
trackHeight: 4.h,
|
||||||
thumbColor: Colors.white,
|
thumbColor: Colors.white,
|
||||||
|
disabledThumbColor: Colors.white,
|
||||||
thumbShape: RoundSliderThumbShape(
|
thumbShape: RoundSliderThumbShape(
|
||||||
disabledThumbRadius: 8.w,
|
disabledThumbRadius: 8.w,
|
||||||
enabledThumbRadius: 8.w,
|
enabledThumbRadius: 8.w,
|
||||||
),
|
),
|
||||||
trackShape: FullWidthTrackShape(),
|
trackShape: FullWidthTrackShape(),
|
||||||
overlayShape: RoundSliderOverlayShape(overlayRadius: 10.w), // 扩大覆盖层半径
|
overlayShape: RoundSliderOverlayShape(overlayRadius: 10.w),
|
||||||
),
|
),
|
||||||
child: Obx(() {
|
child: Obx(() {
|
||||||
return Slider(
|
return Slider(
|
||||||
@ -272,6 +280,7 @@ class PlayPageView extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
SizedBox(height: 4.h),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@ -380,9 +389,9 @@ class PlayPageView extends StatelessWidget {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 20.w,
|
width: 20.w,
|
||||||
height: 20.w,
|
height: 20.w,
|
||||||
child: const CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
color: seedColor,
|
color: seedColor,
|
||||||
strokeWidth: 3,
|
strokeWidth: 2.w,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -480,7 +489,7 @@ class PlayPageView extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildPlayListItem(int index) {
|
Widget _buildPlayListItem(int index) {
|
||||||
final musicModel = musicPlayerController.playlist[index].value;
|
final musicModel = musicPlayerController.playlist[index];
|
||||||
return Material(
|
return Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
@ -503,13 +512,13 @@ class PlayPageView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Obx(() {
|
Obx(() {
|
||||||
return Visibility(
|
return Visibility(
|
||||||
visible: musicPlayerController.getMusicModel()?.value.videoId == musicModel.videoId,
|
visible: musicPlayerController.getMusicModel()?.videoId == musicModel.videoId,
|
||||||
replacement: Text(
|
replacement: Text(
|
||||||
ObjUtil.getStr(musicModel.title),
|
ObjUtil.getStr(musicModel.title),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: musicPlayerController.getMusicModel()?.value.videoId == musicModel.videoId
|
color: musicPlayerController.getMusicModel()?.videoId == musicModel.videoId
|
||||||
? seedColor
|
? seedColor
|
||||||
: const Color(0xD9FFFFFF),
|
: const Color(0xD9FFFFFF),
|
||||||
fontSize: 14.sp,
|
fontSize: 14.sp,
|
||||||
@ -518,7 +527,7 @@ class PlayPageView extends StatelessWidget {
|
|||||||
child: MyMarqueeText(
|
child: MyMarqueeText(
|
||||||
text: ObjUtil.getStr(musicModel.title),
|
text: ObjUtil.getStr(musicModel.title),
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
color: musicPlayerController.getMusicModel()?.value.videoId == musicModel.videoId
|
color: musicPlayerController.getMusicModel()?.videoId == musicModel.videoId
|
||||||
? seedColor
|
? seedColor
|
||||||
: const Color(0xD9FFFFFF),
|
: const Color(0xD9FFFFFF),
|
||||||
fontSize: 14.sp,
|
fontSize: 14.sp,
|
||||||
@ -529,13 +538,13 @@ class PlayPageView extends StatelessWidget {
|
|||||||
SizedBox(height: 8.h),
|
SizedBox(height: 8.h),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
return Visibility(
|
return Visibility(
|
||||||
visible: musicPlayerController.getMusicModel()?.value.videoId == musicModel.videoId,
|
visible: musicPlayerController.getMusicModel()?.videoId == musicModel.videoId,
|
||||||
replacement: Text(
|
replacement: Text(
|
||||||
ObjUtil.getStr(musicModel.subtitle),
|
ObjUtil.getStr(musicModel.subtitle),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: musicPlayerController.getMusicModel()?.value.videoId == musicModel.videoId
|
color: musicPlayerController.getMusicModel()?.videoId == musicModel.videoId
|
||||||
? seedColor
|
? seedColor
|
||||||
: const Color(0x99FFFFFF),
|
: const Color(0x99FFFFFF),
|
||||||
fontSize: 12.sp,
|
fontSize: 12.sp,
|
||||||
@ -544,7 +553,7 @@ class PlayPageView extends StatelessWidget {
|
|||||||
child: MyMarqueeText(
|
child: MyMarqueeText(
|
||||||
text: ObjUtil.getStr(musicModel.subtitle),
|
text: ObjUtil.getStr(musicModel.subtitle),
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
color: musicPlayerController.getMusicModel()?.value.videoId == musicModel.videoId
|
color: musicPlayerController.getMusicModel()?.videoId == musicModel.videoId
|
||||||
? seedColor
|
? seedColor
|
||||||
: const Color(0x99FFFFFF),
|
: const Color(0x99FFFFFF),
|
||||||
fontSize: 12.sp,
|
fontSize: 12.sp,
|
||||||
@ -557,7 +566,7 @@ class PlayPageView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
return Visibility(
|
return Visibility(
|
||||||
visible: musicPlayerController.getMusicModel()?.value.videoId != musicModel.videoId,
|
visible: musicPlayerController.getMusicModel()?.videoId != musicModel.videoId,
|
||||||
child: ClipOval(
|
child: ClipOval(
|
||||||
child: Material(
|
child: Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
@ -577,7 +586,7 @@ class PlayPageView extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
return SizedBox(width: musicPlayerController.getMusicModel()?.value.videoId != musicModel.videoId ? 12.w : 16.w);
|
return SizedBox(width: musicPlayerController.getMusicModel()?.videoId != musicModel.videoId ? 12.w : 16.w);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -124,7 +124,7 @@ class SearchResultController extends GetxController with GetTickerProviderStateM
|
|||||||
await _cleanTab();
|
await _cleanTab();
|
||||||
searchResultViewState.value = ViewState.loading;
|
searchResultViewState.value = ViewState.loading;
|
||||||
await _searchPreviewResult(value);
|
await _searchPreviewResult(value);
|
||||||
searchResultViewState.value = tabs.isNotEmpty ? ViewState.normal : ViewState.empty;
|
searchResultViewState.value = tabs.length > 1 ? ViewState.normal : ViewState.empty;
|
||||||
tabController.value = TabController(length: tabs.length, vsync: this);
|
tabController.value = TabController(length: tabs.length, vsync: this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,12 +257,12 @@ class SearchResultController extends GetxController with GetTickerProviderStateM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (var i = 0; i < this.tabs.length; ++i) {
|
}
|
||||||
if (i == 0) {
|
for (var i = 0; i < this.tabs.length; ++i) {
|
||||||
pages.add(const SearchResultChildOptimumView());
|
if (i == 0) {
|
||||||
} else {
|
pages.add(const SearchResultChildOptimumView());
|
||||||
pages.add(SearchResultChildView(searchResultTabBarModel: this.tabs[i]));
|
} else {
|
||||||
}
|
pages.add(SearchResultChildView(searchResultTabBarModel: this.tabs[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import 'package:tone_snap/utils/obj_util.dart';
|
|||||||
class SearchResultChildController extends GetxController {
|
class SearchResultChildController extends GetxController {
|
||||||
var viewState = ViewState.loading.obs;
|
var viewState = ViewState.loading.obs;
|
||||||
var scrollController = ScrollController();
|
var scrollController = ScrollController();
|
||||||
var musicList = <Rx<MusicModel>>[].obs;
|
var musicList = <MusicModel>[].obs;
|
||||||
String? params;
|
String? params;
|
||||||
String? continuation;
|
String? continuation;
|
||||||
String? clickTrackingParams;
|
String? clickTrackingParams;
|
||||||
@ -107,7 +107,7 @@ class SearchResultChildController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
musicList.value = list.map((e) => e.obs).toList();
|
musicList.value = list;
|
||||||
viewState.value = musicList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
viewState.value = musicList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +124,6 @@ class SearchResultChildController extends GetxController {
|
|||||||
};
|
};
|
||||||
refreshController.callLoad();
|
refreshController.callLoad();
|
||||||
SearchResultMoreModel? searchResultMoreModel = await MusicApi.search<SearchResultMoreModel>(queryParameters: queryParameters, formJson: SearchResultMoreModel.fromMap);
|
SearchResultMoreModel? searchResultMoreModel = await MusicApi.search<SearchResultMoreModel>(queryParameters: queryParameters, formJson: SearchResultMoreModel.fromMap);
|
||||||
final list = <MusicModel>[];
|
|
||||||
if (searchResultMoreModel != null) {
|
if (searchResultMoreModel != null) {
|
||||||
var continuations = searchResultMoreModel.continuationContents?.musicShelfContinuation?.continuations;
|
var continuations = searchResultMoreModel.continuationContents?.musicShelfContinuation?.continuations;
|
||||||
if (continuations != null && continuations.isNotEmpty) {
|
if (continuations != null && continuations.isNotEmpty) {
|
||||||
@ -166,11 +165,10 @@ class SearchResultChildController extends GetxController {
|
|||||||
musicModel.browseId = o.musicResponsiveListItemRenderer?.navigationEndpoint?.browseEndpoint?.browseId;
|
musicModel.browseId = o.musicResponsiveListItemRenderer?.navigationEndpoint?.browseEndpoint?.browseId;
|
||||||
musicModel.musicType = o.musicResponsiveListItemRenderer?.navigationEndpoint?.browseEndpoint?.browseEndpointContextSupportedConfigs?.browseEndpointContextMusicConfig?.pageType;
|
musicModel.musicType = o.musicResponsiveListItemRenderer?.navigationEndpoint?.browseEndpoint?.browseEndpointContextSupportedConfigs?.browseEndpointContextMusicConfig?.pageType;
|
||||||
}
|
}
|
||||||
list.add(musicModel);
|
musicList.add(musicModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
musicList.addAll(list.map((e) => e.obs).toList());
|
|
||||||
if (searchResultMoreModel != null) {
|
if (searchResultMoreModel != null) {
|
||||||
if (ObjUtil.isNotEmpty(continuation)) {
|
if (ObjUtil.isNotEmpty(continuation)) {
|
||||||
refreshController.finishLoad(IndicatorResult.success);
|
refreshController.finishLoad(IndicatorResult.success);
|
||||||
@ -197,6 +195,7 @@ class SearchResultChildController extends GetxController {
|
|||||||
'coverUrl': musicModel.coverUrl,
|
'coverUrl': musicModel.coverUrl,
|
||||||
'title': musicModel.title,
|
'title': musicModel.title,
|
||||||
'subtitle': musicModel.subtitle,
|
'subtitle': musicModel.subtitle,
|
||||||
|
'musicType': musicModel.musicType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,9 +4,9 @@ import 'package:tone_snap/components/base_scrollbar.dart';
|
|||||||
import 'package:tone_snap/components/refresh/base_easyrefresh.dart';
|
import 'package:tone_snap/components/refresh/base_easyrefresh.dart';
|
||||||
import 'package:tone_snap/components/view_state_widget.dart';
|
import 'package:tone_snap/components/view_state_widget.dart';
|
||||||
import 'package:tone_snap/data/enum/music_type.dart';
|
import 'package:tone_snap/data/enum/music_type.dart';
|
||||||
|
import 'package:tone_snap/data/models/search_result_tabbar_model.dart';
|
||||||
import 'package:tone_snap/modules/sideb/widgets/music_item.dart';
|
import 'package:tone_snap/modules/sideb/widgets/music_item.dart';
|
||||||
|
|
||||||
import '../../../data/models/search_result_tabbar_model.dart';
|
|
||||||
import 'search_result_child_controller.dart';
|
import 'search_result_child_controller.dart';
|
||||||
|
|
||||||
class SearchResultChildView extends GetView<SearchResultChildController> {
|
class SearchResultChildView extends GetView<SearchResultChildController> {
|
||||||
@ -28,23 +28,26 @@ class SearchResultChildView extends GetView<SearchResultChildController> {
|
|||||||
child: BaseEasyRefresh(
|
child: BaseEasyRefresh(
|
||||||
controller: controller.refreshController,
|
controller: controller.refreshController,
|
||||||
onLoad: controller.onLoad,
|
onLoad: controller.onLoad,
|
||||||
child: BaseScrollbar(
|
childBuilder: (context, physics) {
|
||||||
scrollController: controller.scrollController,
|
return BaseScrollbar(
|
||||||
child: Obx(() {
|
scrollController: controller.scrollController,
|
||||||
return ListView.builder(
|
child: Obx(() {
|
||||||
controller: controller.scrollController,
|
return ListView.builder(
|
||||||
itemCount: controller.musicList.length,
|
physics: physics,
|
||||||
itemBuilder: (context, index) {
|
controller: controller.scrollController,
|
||||||
return MusicItem(
|
itemCount: controller.musicList.length,
|
||||||
musicModel: controller.musicList[index],
|
itemBuilder: (context, index) {
|
||||||
showDownload: controller.musicList[index].value.musicType == MusicType.musicVideoTypeAtv.name,
|
return MusicItem(
|
||||||
showMore: controller.musicList[index].value.musicType == MusicType.musicVideoTypeAtv.name,
|
musicModel: controller.musicList[index],
|
||||||
onTapItem: () => controller.onTapItem(controller.musicList[index].value),
|
showDownload: controller.musicList[index].musicType == MusicType.musicVideoTypeAtv.name,
|
||||||
);
|
showMore: controller.musicList[index].musicType == MusicType.musicVideoTypeAtv.name,
|
||||||
},
|
onTapItem: () => controller.onTapItem(controller.musicList[index]),
|
||||||
);
|
);
|
||||||
}),
|
},
|
||||||
),
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -18,6 +18,7 @@ class SearchResultChildOptimumController extends GetxController {
|
|||||||
'coverUrl': musicModel.coverUrl,
|
'coverUrl': musicModel.coverUrl,
|
||||||
'title': musicModel.title,
|
'title': musicModel.title,
|
||||||
'subtitle': musicModel.subtitle,
|
'subtitle': musicModel.subtitle,
|
||||||
|
'musicType': musicModel.musicType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ class SearchResultChildOptimumView extends GetView<SearchResultChildOptimumContr
|
|||||||
itemCount: SearchResultController.to.tabs.length,
|
itemCount: SearchResultController.to.tabs.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final searchResultTabBarModel = SearchResultController.to.tabs[index];
|
final searchResultTabBarModel = SearchResultController.to.tabs[index];
|
||||||
final musicList = searchResultTabBarModel.musicList?.map((e) => e.obs).toList();
|
final musicList = searchResultTabBarModel.musicList;
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
@ -51,9 +51,9 @@ class SearchResultChildOptimumView extends GetView<SearchResultChildOptimumContr
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return MusicItem(
|
return MusicItem(
|
||||||
musicModel: musicList[index],
|
musicModel: musicList[index],
|
||||||
showDownload: musicList[index].value.musicType == MusicType.musicVideoTypeAtv.name,
|
showDownload: musicList[index].musicType == MusicType.musicVideoTypeAtv.name,
|
||||||
showMore: musicList[index].value.musicType == MusicType.musicVideoTypeAtv.name,
|
showMore: musicList[index].musicType == MusicType.musicVideoTypeAtv.name,
|
||||||
onTapItem: () => controller.onTapItem(musicList[index].value),
|
onTapItem: () => controller.onTapItem(musicList[index]),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@ -17,6 +17,7 @@ class BrowseItemAlbumSongList extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 109.w,
|
width: 109.w,
|
||||||
|
|||||||
@ -19,18 +19,21 @@ class BrowseItemAtv extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Material(
|
return ClipRRect(
|
||||||
color: Colors.transparent,
|
borderRadius: BorderRadius.circular(8).r,
|
||||||
child: InkWell(
|
child: Material(
|
||||||
onTap: onTap,
|
color: Colors.transparent,
|
||||||
child: SizedBox(
|
child: InkWell(
|
||||||
height: 60.w,
|
onTap: onTap,
|
||||||
child: Row(
|
child: SizedBox(
|
||||||
children: [
|
height: 60.w,
|
||||||
_buildCover(),
|
child: Row(
|
||||||
_buildContent(),
|
children: [
|
||||||
_buildMore(),
|
_buildCover(),
|
||||||
],
|
_buildContent(),
|
||||||
|
_buildMore(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -80,20 +83,17 @@ class BrowseItemAtv extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMore() {
|
Widget _buildMore() {
|
||||||
return Padding(
|
return ClipOval(
|
||||||
padding: const EdgeInsets.only(right: 12).w,
|
child: Material(
|
||||||
child: ClipOval(
|
color: Colors.transparent,
|
||||||
child: Material(
|
child: InkWell(
|
||||||
color: Colors.transparent,
|
onTap: onTapMore,
|
||||||
child: InkWell(
|
child: Padding(
|
||||||
onTap: onTapMore,
|
padding: const EdgeInsets.all(4).w,
|
||||||
child: Padding(
|
child: Image.asset(
|
||||||
padding: const EdgeInsets.all(4).w,
|
Assets.sideBMore,
|
||||||
child: Image.asset(
|
width: 24.w,
|
||||||
Assets.sideBMore,
|
height: 24.w,
|
||||||
width: 24.w,
|
|
||||||
height: 24.w,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -104,7 +104,7 @@ class BrowseItemAtv extends StatelessWidget {
|
|||||||
void onTapMore() {
|
void onTapMore() {
|
||||||
Get.bottomSheet(
|
Get.bottomSheet(
|
||||||
MoreBottomSheetView(
|
MoreBottomSheetView(
|
||||||
musicModel: musicModel.obs,
|
musicModel: musicModel,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:tone_snap/components/base_easyloading.dart';
|
import 'package:tone_snap/components/base_easyloading.dart';
|
||||||
import 'package:tone_snap/components/dialog/remind_dialog.dart';
|
import 'package:tone_snap/components/dialog/remind_dialog.dart';
|
||||||
import 'package:tone_snap/components/my_marquee_text.dart';
|
|
||||||
import 'package:tone_snap/components/network_image_widget.dart';
|
import 'package:tone_snap/components/network_image_widget.dart';
|
||||||
import 'package:tone_snap/data/models/music_model.dart';
|
import 'package:tone_snap/data/models/music_model.dart';
|
||||||
import 'package:tone_snap/data/storage/offline_box.dart';
|
import 'package:tone_snap/data/storage/offline_box.dart';
|
||||||
@ -14,6 +13,7 @@ import 'package:tone_snap/modules/sideb/controllers/music_player_controller.dart
|
|||||||
import 'package:tone_snap/modules/sideb/more_bottom_sheet/more_bottom_sheet_view.dart';
|
import 'package:tone_snap/modules/sideb/more_bottom_sheet/more_bottom_sheet_view.dart';
|
||||||
import 'package:tone_snap/modules/sideb/offline/offline_controller.dart';
|
import 'package:tone_snap/modules/sideb/offline/offline_controller.dart';
|
||||||
import 'package:tone_snap/modules/sideb/personal_music_library/personal_music_library_controller.dart';
|
import 'package:tone_snap/modules/sideb/personal_music_library/personal_music_library_controller.dart';
|
||||||
|
import 'package:tone_snap/modules/sideb/widgets/music_item_marquee_text.dart';
|
||||||
import 'package:tone_snap/res/themes/app_colors.dart';
|
import 'package:tone_snap/res/themes/app_colors.dart';
|
||||||
import 'package:tone_snap/utils/obj_util.dart';
|
import 'package:tone_snap/utils/obj_util.dart';
|
||||||
|
|
||||||
@ -24,15 +24,19 @@ class MusicItem extends StatelessWidget {
|
|||||||
required this.onTapItem,
|
required this.onTapItem,
|
||||||
this.showDownload = true,
|
this.showDownload = true,
|
||||||
this.showMore = true,
|
this.showMore = true,
|
||||||
|
this.showNumber = false,
|
||||||
|
this.number = 0,
|
||||||
this.playlistModelId,
|
this.playlistModelId,
|
||||||
});
|
});
|
||||||
|
|
||||||
final musicPlayerController = MusicPlayerController.to;
|
final musicPlayerController = MusicPlayerController.to;
|
||||||
|
|
||||||
final Rx<MusicModel> musicModel;
|
final MusicModel musicModel;
|
||||||
final Function() onTapItem;
|
final Function() onTapItem;
|
||||||
final bool showDownload;
|
final bool showDownload;
|
||||||
final bool showMore;
|
final bool showMore;
|
||||||
|
final bool showNumber;
|
||||||
|
final int number;
|
||||||
final String? playlistModelId;
|
final String? playlistModelId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -46,7 +50,11 @@ class MusicItem extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(width: 18.w),
|
SizedBox(width: 18.w),
|
||||||
_buildCover(),
|
if (showNumber) ...[
|
||||||
|
_buildNumber(),
|
||||||
|
] else ...[
|
||||||
|
_buildCover(),
|
||||||
|
],
|
||||||
SizedBox(width: 12.w),
|
SizedBox(width: 12.w),
|
||||||
_buildContent(),
|
_buildContent(),
|
||||||
SizedBox(width: 8.w),
|
SizedBox(width: 8.w),
|
||||||
@ -66,15 +74,34 @@ class MusicItem extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildNumber() {
|
||||||
|
return Container(
|
||||||
|
width: 60.w,
|
||||||
|
height: 60.w,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10).w,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFF242529),
|
||||||
|
borderRadius: BorderRadius.circular(8).r,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
number.toString(),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 14.sp,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildCover() {
|
Widget _buildCover() {
|
||||||
return Obx(() {
|
return NetworkImageWidget(
|
||||||
return NetworkImageWidget(
|
url: musicModel.coverUrl,
|
||||||
url: musicModel.value.coverUrl,
|
width: 60.w,
|
||||||
width: 60.w,
|
height: 60.w,
|
||||||
height: 60.w,
|
radius: 8.r,
|
||||||
radius: 8.r,
|
);
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildContent() {
|
Widget _buildContent() {
|
||||||
@ -83,48 +110,17 @@ class MusicItem extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Obx(() {
|
Obx(() {
|
||||||
bool isCurrentPlayModel = ObjUtil.isNotEmpty(musicModel.value.videoId) && musicPlayerController.getMusicModel()?.value.videoId == musicModel.value.videoId;
|
return MusicItemMarqueeText(
|
||||||
return Visibility(
|
text: musicModel.title,
|
||||||
visible: isCurrentPlayModel,
|
showMarquee: musicPlayerController.getMusicModel()?.videoId == musicModel.videoId && ObjUtil.isNotEmpty(musicModel.videoId),
|
||||||
replacement: Text(
|
|
||||||
ObjUtil.getStr(musicModel.value.title),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 14.sp,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: MyMarqueeText(
|
|
||||||
text: ObjUtil.getStr(musicModel.value.title),
|
|
||||||
textStyle: TextStyle(
|
|
||||||
color: seedColor,
|
|
||||||
fontSize: 14.sp,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
SizedBox(height: 4.h),
|
SizedBox(height: 4.h),
|
||||||
Obx(() {
|
Obx(() {
|
||||||
bool isCurrentPlayModel = ObjUtil.isNotEmpty(musicModel.value.videoId) && musicPlayerController.getMusicModel()?.value.videoId == musicModel.value.videoId;
|
return MusicItemMarqueeText(
|
||||||
return Visibility(
|
text: musicModel.subtitle,
|
||||||
visible: isCurrentPlayModel,
|
isTitle: false,
|
||||||
replacement: Text(
|
showMarquee: musicPlayerController.getMusicModel()?.videoId == musicModel.videoId && ObjUtil.isNotEmpty(musicModel.videoId),
|
||||||
ObjUtil.getStr(musicModel.value.subtitle),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(
|
|
||||||
color: const Color(0x99FFFFFF),
|
|
||||||
fontSize: 12.sp,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: MyMarqueeText(
|
|
||||||
text: ObjUtil.getStr(musicModel.value.subtitle),
|
|
||||||
textStyle: TextStyle(
|
|
||||||
color: seedColor,
|
|
||||||
fontSize: 12.sp,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@ -143,27 +139,30 @@ class MusicItem extends StatelessWidget {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 24.w,
|
width: 24.w,
|
||||||
height: 24.w,
|
height: 24.w,
|
||||||
child: Obx(() {
|
child: GetBuilder<DownloadManager>(
|
||||||
if (OfflineBox().checkDownloaded(musicModel.value.videoId) || musicModel.value.taskStatus == TaskStatus.complete) {
|
id: DownloadManager.to.downloadStateId,
|
||||||
return Image.asset(Assets.sideBDownloaded);
|
builder: (_) {
|
||||||
} else {
|
if (OfflineBox().checkDownloaded(musicModel.videoId)) {
|
||||||
if (musicModel.value.taskStatus == TaskStatus.enqueued) {
|
return Image.asset(Assets.sideBDownloaded);
|
||||||
return CircularProgressIndicator(
|
|
||||||
color: seedColor,
|
|
||||||
strokeWidth: 2.w,
|
|
||||||
);
|
|
||||||
} else if (musicModel.value.taskStatus == TaskStatus.running) {
|
|
||||||
return CircularProgressIndicator(
|
|
||||||
value: musicModel.value.progress,
|
|
||||||
backgroundColor: seedColor.withOpacity(0.2),
|
|
||||||
valueColor: const AlwaysStoppedAnimation<Color>(seedColor),
|
|
||||||
strokeWidth: 2.w,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return Image.asset(Assets.sideBNotDownload2);
|
if (DownloadManager.to.getMusicModel(musicModel.videoId)?.taskStatus == TaskStatus.enqueued) {
|
||||||
|
return CircularProgressIndicator(
|
||||||
|
color: seedColor,
|
||||||
|
strokeWidth: 2.w,
|
||||||
|
);
|
||||||
|
} else if (DownloadManager.to.getMusicModel(musicModel.videoId)?.taskStatus == TaskStatus.running) {
|
||||||
|
return CircularProgressIndicator(
|
||||||
|
value: DownloadManager.to.getMusicModel(musicModel.videoId)?.progress,
|
||||||
|
backgroundColor: seedColor.withOpacity(0.2),
|
||||||
|
valueColor: const AlwaysStoppedAnimation<Color>(seedColor),
|
||||||
|
strokeWidth: 2.w,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Image.asset(Assets.sideBNotDownload2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -191,13 +190,13 @@ class MusicItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onTapDownload() {
|
void onTapDownload() {
|
||||||
if (OfflineBox().checkDownloaded(musicModel.value.videoId) || musicModel.value.taskStatus == TaskStatus.complete) {
|
if (OfflineBox().checkDownloaded(musicModel.videoId)) {
|
||||||
Get.dialog(
|
Get.dialog(
|
||||||
RemindDialog(
|
RemindDialog(
|
||||||
content: 'Confirm to remove this song?',
|
content: 'Confirm to remove this song?',
|
||||||
confirmOnTap: () async {
|
confirmOnTap: () async {
|
||||||
await OfflineBox().delete(musicModel.value.videoId!);
|
await OfflineBox().delete(musicModel.videoId!);
|
||||||
musicModel.update((fn) => fn?.taskStatus = null);
|
DownloadManager.to.updateDownloadState();
|
||||||
BaseEasyLoading.toast('Removed');
|
BaseEasyLoading.toast('Removed');
|
||||||
if (Get.isRegistered<PersonalMusicLibraryController>()) {
|
if (Get.isRegistered<PersonalMusicLibraryController>()) {
|
||||||
PersonalMusicLibraryController.to.refreshOffline();
|
PersonalMusicLibraryController.to.refreshOffline();
|
||||||
@ -209,11 +208,11 @@ class MusicItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (musicModel.value.taskStatus == TaskStatus.enqueued
|
if (DownloadManager.to.getMusicModel(musicModel.videoId)?.taskStatus == TaskStatus.enqueued
|
||||||
|| musicModel.value.taskStatus == TaskStatus.running) {
|
|| DownloadManager.to.getMusicModel(musicModel.videoId)?.taskStatus == TaskStatus.running) {
|
||||||
DownloadManager().cancelDownload(musicModel);
|
DownloadManager.to.cancelDownload(musicModel.videoId);
|
||||||
} else {
|
} else {
|
||||||
DownloadManager().downloadMusic(musicModel);
|
DownloadManager.to.downloadMusic(musicModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
51
lib/modules/sideb/widgets/music_item_marquee_text.dart
Normal file
51
lib/modules/sideb/widgets/music_item_marquee_text.dart
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/6/5
|
||||||
|
// Description: 跑马灯
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:tone_snap/res/themes/app_colors.dart';
|
||||||
|
import 'package:tone_snap/utils/obj_util.dart';
|
||||||
|
import 'package:widget_marquee/widget_marquee.dart';
|
||||||
|
|
||||||
|
class MusicItemMarqueeText extends StatelessWidget {
|
||||||
|
const MusicItemMarqueeText({
|
||||||
|
super.key,
|
||||||
|
required this.text,
|
||||||
|
this.isTitle = true,
|
||||||
|
this.showMarquee = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? text;
|
||||||
|
final bool isTitle;
|
||||||
|
final bool showMarquee;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Visibility(
|
||||||
|
visible: showMarquee,
|
||||||
|
replacement: Text(
|
||||||
|
ObjUtil.getStr(text),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color: isTitle ? Colors.white : const Color(0x99FFFFFF),
|
||||||
|
fontSize: isTitle ? 14.sp : 12.sp,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Marquee(
|
||||||
|
delay: Duration.zero,
|
||||||
|
duration: const Duration(seconds: 40),
|
||||||
|
pause: Duration.zero,
|
||||||
|
gap: 80,
|
||||||
|
child: Text(
|
||||||
|
ObjUtil.getStr(text),
|
||||||
|
style: TextStyle(
|
||||||
|
color: seedColor,
|
||||||
|
fontSize: isTitle ? 14.sp : 12.sp,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -78,6 +78,7 @@ class PlaylistItem extends StatelessWidget {
|
|||||||
'coverUrl': playlistModel.coverUrl,
|
'coverUrl': playlistModel.coverUrl,
|
||||||
'title': playlistModel.title,
|
'title': playlistModel.title,
|
||||||
'subtitle': playlistModel.subtitle,
|
'subtitle': playlistModel.subtitle,
|
||||||
|
'musicType': playlistModel.musicType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user