mirror of
https://github.com/gokadzev/Musify.git
synced 2025-05-17 22:46:05 +08:00
feat: add local image picking functionality (Beta) (closes: #548)
This commit is contained in:
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "Offline songs",
|
||||
"originalRecommendations": "Original algorithm for recommendations",
|
||||
"others": "آخرون",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "قائمة تشغيل",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "Offline-Songs",
|
||||
"originalRecommendations": "Originaler Algorithmus für Vorschläge",
|
||||
"others": "Andere",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "Playlist",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "Τραγούδια εκτός σύνδεσης",
|
||||
"originalRecommendations": "Πρωτότυπος αλγόριθμος για συστάσεις",
|
||||
"others": "Άλλα",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "Λίστα αναπαραγωγής",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "Offline songs",
|
||||
"originalRecommendations": "Original recommendation algorithm",
|
||||
"others": "Others",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "Playlist",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "Canciones sin conexión",
|
||||
"originalRecommendations": "Algoritmo de recomendaciones original",
|
||||
"others": "Otros",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "Lista de reproducción",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "Titres hors ligne",
|
||||
"originalRecommendations": "Algorithme original pour les recommandations",
|
||||
"others": "Autres",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "Playlist",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "ऑफ़लाइन गाने",
|
||||
"originalRecommendations": "मूल अनुशंसा एल्गोरिथ्म",
|
||||
"others": "अन्य",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "प्लेलिस्ट",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "Lagu offline",
|
||||
"originalRecommendations": "Algoritma rekomendasi asli",
|
||||
"others": "Lainnya",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Putar berikutnya",
|
||||
"playlist": "Playlist",
|
||||
"playlistAlreadyDownloaded": "Playlist sudah diunduh",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "Brani offline",
|
||||
"originalRecommendations": "Algoritmo originale per le raccomandazioni",
|
||||
"others": "Altri",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "Playlist",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "オフライン用の曲",
|
||||
"originalRecommendations": "おすすめに独自アルゴリズムを使用",
|
||||
"others": "ほか",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "再生リスト",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "오프라인 노래",
|
||||
"originalRecommendations": "추천을 위한 오리지널 알고리즘",
|
||||
"others": "기타",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "다음 재생",
|
||||
"playlist": "재생목록",
|
||||
"playlistAlreadyDownloaded": "재생목록이 이미 다운로드되었음",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "Utwory offline",
|
||||
"originalRecommendations": "Oryginalny algorytm rekomendacji",
|
||||
"others": "Inne",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "Playlista",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "Músicas offline",
|
||||
"originalRecommendations": "Recomendações originais",
|
||||
"others": "Outros",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "Playlist",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "Треки без интернета",
|
||||
"originalRecommendations": "Оригинальный алгоритм рекомендаций",
|
||||
"others": "Ещё",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "Плейлист",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "Çevrimdışı Parçalar",
|
||||
"originalRecommendations": "Asıl Öneri Algoritması",
|
||||
"others": "Diğerleri",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Bundan Sonra Oynat",
|
||||
"playlist": "Çalma Listeleri",
|
||||
"playlistAlreadyDownloaded": "Çalma listesi hali hazırda indirildi",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "Offline songs",
|
||||
"originalRecommendations": "Original algorithm for recommendations",
|
||||
"others": "Інше",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "Плейлист",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "已離線歌曲",
|
||||
"originalRecommendations": "原始推薦算法",
|
||||
"others": "其他",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "播放列表",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"offlineSongs": "已离线歌曲",
|
||||
"originalRecommendations": "原始推荐算法",
|
||||
"others": "其他",
|
||||
"pickImageFromDevice": "Pick image from device",
|
||||
"playNext": "Play next",
|
||||
"playlist": "播放列表",
|
||||
"playlistAlreadyDownloaded": "Playlist already downloaded",
|
||||
|
@ -19,6 +19,9 @@
|
||||
* please visit: https://github.com/gokadzev/Musify
|
||||
*/
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:musify/API/musify.dart';
|
||||
@ -232,6 +235,7 @@ class _LibraryPageState extends State<LibraryPage> {
|
||||
var customPlaylistName = '';
|
||||
var isYouTubeMode = true;
|
||||
String? imageUrl;
|
||||
String? imageBase64;
|
||||
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
@ -240,6 +244,76 @@ class _LibraryPageState extends State<LibraryPage> {
|
||||
final inactiveButtonBackground = theme.colorScheme.secondaryContainer;
|
||||
final dialogBackgroundColor = theme.dialogTheme.backgroundColor;
|
||||
|
||||
Future<void> _pickImage() async {
|
||||
final result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.image,
|
||||
withData: true,
|
||||
);
|
||||
if (result != null && result.files.single.bytes != null) {
|
||||
final file = result.files.single;
|
||||
String? mimeType;
|
||||
if (file.extension != null) {
|
||||
switch (file.extension!.toLowerCase()) {
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
mimeType = 'image/jpeg';
|
||||
break;
|
||||
case 'png':
|
||||
mimeType = 'image/png';
|
||||
break;
|
||||
case 'gif':
|
||||
mimeType = 'image/gif';
|
||||
break;
|
||||
case 'bmp':
|
||||
mimeType = 'image/bmp';
|
||||
break;
|
||||
case 'webp':
|
||||
mimeType = 'image/webp';
|
||||
break;
|
||||
default:
|
||||
mimeType = 'application/octet-stream';
|
||||
}
|
||||
} else {
|
||||
mimeType = 'application/octet-stream';
|
||||
}
|
||||
setState(() {
|
||||
imageBase64 =
|
||||
'data:$mimeType;base64,${base64Encode(file.bytes!)}';
|
||||
imageUrl = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _imagePreview() {
|
||||
if (imageBase64 != null) {
|
||||
final base64Data =
|
||||
imageBase64!.contains(',')
|
||||
? imageBase64!.split(',').last
|
||||
: imageBase64!;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Image.memory(
|
||||
base64Decode(base64Data),
|
||||
width: 80,
|
||||
height: 80,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
);
|
||||
} else if (imageUrl != null && imageUrl!.isNotEmpty) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Image.network(
|
||||
imageUrl!,
|
||||
width: 80,
|
||||
height: 80,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => const Icon(Icons.broken_image),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return AlertDialog(
|
||||
backgroundColor: dialogBackgroundColor,
|
||||
content: SingleChildScrollView(
|
||||
@ -256,6 +330,7 @@ class _LibraryPageState extends State<LibraryPage> {
|
||||
id = '';
|
||||
customPlaylistName = '';
|
||||
imageUrl = null;
|
||||
imageBase64 = null;
|
||||
});
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
@ -274,6 +349,7 @@ class _LibraryPageState extends State<LibraryPage> {
|
||||
id = '';
|
||||
customPlaylistName = '';
|
||||
imageUrl = null;
|
||||
imageBase64 = null;
|
||||
});
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
@ -305,6 +381,7 @@ class _LibraryPageState extends State<LibraryPage> {
|
||||
customPlaylistName = value;
|
||||
},
|
||||
),
|
||||
if (imageBase64 == null) ...[
|
||||
const SizedBox(height: 7),
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
@ -312,9 +389,33 @@ class _LibraryPageState extends State<LibraryPage> {
|
||||
),
|
||||
onChanged: (value) {
|
||||
imageUrl = value;
|
||||
imageBase64 = null;
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 7),
|
||||
if (imageUrl == null) ...[
|
||||
Row(
|
||||
children: [
|
||||
ElevatedButton.icon(
|
||||
onPressed: _pickImage,
|
||||
icon: const Icon(Icons.image),
|
||||
label: Text(context.l10n!.pickImageFromDevice),
|
||||
),
|
||||
if (imageBase64 != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: Icon(
|
||||
Icons.check_circle,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
_imagePreview(),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -329,7 +430,7 @@ class _LibraryPageState extends State<LibraryPage> {
|
||||
context,
|
||||
createCustomPlaylist(
|
||||
customPlaylistName,
|
||||
imageUrl,
|
||||
imageBase64 ?? imageUrl,
|
||||
context,
|
||||
),
|
||||
);
|
||||
|
@ -19,8 +19,10 @@
|
||||
* please visit: https://github.com/gokadzev/Musify
|
||||
*/
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@ -291,8 +293,86 @@ class _PlaylistPageState extends State<PlaylistPage> {
|
||||
() => showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
var customPlaylistName = _playlist['title'];
|
||||
var imageUrl = _playlist['image'];
|
||||
String customPlaylistName = _playlist['title'];
|
||||
String? imageUrl = _playlist['image'];
|
||||
var imageBase64 =
|
||||
(imageUrl != null && imageUrl.startsWith('data:'))
|
||||
? imageUrl
|
||||
: null;
|
||||
if (imageBase64 != null) imageUrl = null;
|
||||
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
Future<void> _pickImage() async {
|
||||
final result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.image,
|
||||
withData: true,
|
||||
);
|
||||
if (result != null && result.files.single.bytes != null) {
|
||||
final file = result.files.single;
|
||||
String? mimeType;
|
||||
if (file.extension != null) {
|
||||
switch (file.extension!.toLowerCase()) {
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
mimeType = 'image/jpeg';
|
||||
break;
|
||||
case 'png':
|
||||
mimeType = 'image/png';
|
||||
break;
|
||||
case 'gif':
|
||||
mimeType = 'image/gif';
|
||||
break;
|
||||
case 'bmp':
|
||||
mimeType = 'image/bmp';
|
||||
break;
|
||||
case 'webp':
|
||||
mimeType = 'image/webp';
|
||||
break;
|
||||
default:
|
||||
mimeType = 'application/octet-stream';
|
||||
}
|
||||
} else {
|
||||
mimeType = 'application/octet-stream';
|
||||
}
|
||||
setState(() {
|
||||
imageBase64 =
|
||||
'data:$mimeType;base64,${base64Encode(file.bytes!)}';
|
||||
imageUrl = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _imagePreview() {
|
||||
if (imageBase64 != null) {
|
||||
final base64Data =
|
||||
imageBase64!.contains(',')
|
||||
? imageBase64!.split(',').last
|
||||
: imageBase64!;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Image.memory(
|
||||
base64Decode(base64Data),
|
||||
width: 80,
|
||||
height: 80,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
);
|
||||
} else if (imageUrl != null && imageUrl!.isNotEmpty) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Image.network(
|
||||
imageUrl!,
|
||||
width: 80,
|
||||
height: 80,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder:
|
||||
(_, __, ___) => const Icon(Icons.broken_image),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return AlertDialog(
|
||||
content: SingleChildScrollView(
|
||||
@ -310,6 +390,7 @@ class _PlaylistPageState extends State<PlaylistPage> {
|
||||
customPlaylistName = value;
|
||||
},
|
||||
),
|
||||
if (imageBase64 == null) ...[
|
||||
const SizedBox(height: 7),
|
||||
TextField(
|
||||
controller: TextEditingController(text: imageUrl),
|
||||
@ -318,9 +399,36 @@ class _PlaylistPageState extends State<PlaylistPage> {
|
||||
),
|
||||
onChanged: (value) {
|
||||
imageUrl = value;
|
||||
imageBase64 = null;
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 7),
|
||||
if (imageUrl == null) ...[
|
||||
Row(
|
||||
children: [
|
||||
ElevatedButton.icon(
|
||||
onPressed: _pickImage,
|
||||
icon: const Icon(Icons.image),
|
||||
label: Text(
|
||||
context.l10n!.pickImageFromDevice,
|
||||
),
|
||||
),
|
||||
if (imageBase64 != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: Icon(
|
||||
Icons.check_circle,
|
||||
color:
|
||||
Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
_imagePreview(),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
@ -336,7 +444,10 @@ class _PlaylistPageState extends State<PlaylistPage> {
|
||||
final newPlaylist = {
|
||||
'title': customPlaylistName,
|
||||
'source': 'user-created',
|
||||
if (imageUrl != null) 'image': imageUrl,
|
||||
if (imageBase64 != null)
|
||||
'image': imageBase64
|
||||
else if (imageUrl != null)
|
||||
'image': imageUrl,
|
||||
'list': widget.playlistData['list'],
|
||||
};
|
||||
final updatedPlaylists = List<Map>.from(
|
||||
@ -347,7 +458,7 @@ class _PlaylistPageState extends State<PlaylistPage> {
|
||||
addOrUpdateData(
|
||||
'user',
|
||||
'customPlaylists',
|
||||
userCustomPlaylists,
|
||||
userCustomPlaylists.value,
|
||||
);
|
||||
_playlist = newPlaylist;
|
||||
showToast(context, context.l10n!.playlistUpdated);
|
||||
@ -360,6 +471,8 @@ class _PlaylistPageState extends State<PlaylistPage> {
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
104
lib/widgets/playlist_artwork.dart
Normal file
104
lib/widgets/playlist_artwork.dart
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Valeri Gokadze
|
||||
*
|
||||
* Musify is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Musify is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* For more information about Musify, including how to contribute,
|
||||
* please visit: https://github.com/gokadzev/Musify
|
||||
*/
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:musify/utilities/common_variables.dart';
|
||||
import 'package:musify/widgets/no_artwork_cube.dart';
|
||||
|
||||
class PlaylistArtwork extends StatelessWidget {
|
||||
const PlaylistArtwork({
|
||||
super.key,
|
||||
required this.playlistArtwork,
|
||||
this.playlistTitle,
|
||||
this.cubeIcon = FluentIcons.music_note_1_24_regular,
|
||||
this.iconSize = 30,
|
||||
this.size = 220,
|
||||
});
|
||||
|
||||
final String? playlistArtwork;
|
||||
final String? playlistTitle;
|
||||
final IconData cubeIcon;
|
||||
final double iconSize;
|
||||
final double size;
|
||||
|
||||
Widget _nullArtwork() => NullArtworkWidget(
|
||||
icon: cubeIcon,
|
||||
iconSize: iconSize,
|
||||
size: size,
|
||||
title: playlistTitle,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final image = playlistArtwork;
|
||||
if (image == null) return _nullArtwork();
|
||||
|
||||
if (image.startsWith('data:image')) {
|
||||
final commaIdx = image.indexOf(',');
|
||||
if (commaIdx == -1) return _nullArtwork();
|
||||
try {
|
||||
final bytes = base64Decode(image.substring(commaIdx + 1));
|
||||
return SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: ClipRRect(
|
||||
borderRadius: commonBarRadius,
|
||||
child: Image.memory(
|
||||
bytes,
|
||||
height: size,
|
||||
width: size,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => _nullArtwork(),
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (_) {
|
||||
return _nullArtwork();
|
||||
}
|
||||
}
|
||||
|
||||
if (image.startsWith('http')) {
|
||||
return CachedNetworkImage(
|
||||
key: Key(image),
|
||||
height: size,
|
||||
width: size,
|
||||
imageUrl: image,
|
||||
fit: BoxFit.cover,
|
||||
imageBuilder:
|
||||
(_, imageProvider) => SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: ClipRRect(
|
||||
borderRadius: commonBarRadius,
|
||||
child: Image(image: imageProvider),
|
||||
),
|
||||
),
|
||||
errorWidget: (_, __, ___) => _nullArtwork(),
|
||||
);
|
||||
}
|
||||
|
||||
return _nullArtwork();
|
||||
}
|
||||
}
|
@ -19,14 +19,13 @@
|
||||
* please visit: https://github.com/gokadzev/Musify
|
||||
*/
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:musify/API/musify.dart';
|
||||
import 'package:musify/extensions/l10n.dart';
|
||||
import 'package:musify/screens/playlist_page.dart';
|
||||
import 'package:musify/utilities/common_variables.dart';
|
||||
import 'package:musify/widgets/no_artwork_cube.dart';
|
||||
import 'package:musify/widgets/playlist_artwork.dart';
|
||||
|
||||
class PlaylistBar extends StatelessWidget {
|
||||
PlaylistBar(
|
||||
@ -100,7 +99,12 @@ class PlaylistBar extends StatelessWidget {
|
||||
padding: commonBarContentPadding,
|
||||
child: Row(
|
||||
children: [
|
||||
_buildAlbumArt(),
|
||||
PlaylistArtwork(
|
||||
playlistArtwork: playlistArtwork,
|
||||
size: artworkSize,
|
||||
iconSize: iconSize,
|
||||
cubeIcon: cubeIcon,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
@ -126,37 +130,6 @@ class PlaylistBar extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAlbumArt() {
|
||||
return playlistArtwork != null
|
||||
? CachedNetworkImage(
|
||||
key: Key(playlistArtwork.toString()),
|
||||
height: artworkSize,
|
||||
width: artworkSize,
|
||||
imageUrl: playlistArtwork.toString(),
|
||||
fit: BoxFit.cover,
|
||||
imageBuilder:
|
||||
(context, imageProvider) => SizedBox(
|
||||
width: artworkSize,
|
||||
height: artworkSize,
|
||||
child: ClipRRect(
|
||||
borderRadius: commonBarRadius,
|
||||
child: Image(image: imageProvider),
|
||||
),
|
||||
),
|
||||
errorWidget:
|
||||
(context, url, error) => NullArtworkWidget(
|
||||
icon: cubeIcon,
|
||||
iconSize: iconSize,
|
||||
size: artworkSize,
|
||||
),
|
||||
)
|
||||
: NullArtworkWidget(
|
||||
icon: cubeIcon,
|
||||
iconSize: iconSize,
|
||||
size: artworkSize,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButtons(BuildContext context, Color primaryColor) {
|
||||
return PopupMenuButton<String>(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
|
@ -19,12 +19,11 @@
|
||||
* please visit: https://github.com/gokadzev/Musify
|
||||
*/
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:musify/API/musify.dart';
|
||||
import 'package:musify/extensions/l10n.dart';
|
||||
import 'package:musify/widgets/no_artwork_cube.dart';
|
||||
import 'package:musify/widgets/playlist_artwork.dart';
|
||||
|
||||
class PlaylistCube extends StatelessWidget {
|
||||
PlaylistCube(
|
||||
@ -46,7 +45,6 @@ class PlaylistCube extends StatelessWidget {
|
||||
|
||||
static const double paddingValue = 4;
|
||||
static const double typeLabelOffset = 10;
|
||||
static const double iconSize = 30;
|
||||
|
||||
final ValueNotifier<bool> playlistLikeStatus;
|
||||
|
||||
@ -63,7 +61,11 @@ class PlaylistCube extends StatelessWidget {
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: Stack(
|
||||
children: [
|
||||
_buildImage(context),
|
||||
PlaylistArtwork(
|
||||
playlistArtwork: playlist['image'],
|
||||
size: size,
|
||||
cubeIcon: cubeIcon,
|
||||
),
|
||||
if (borderRadius == 13 && playlist['image'] != null)
|
||||
Positioned(
|
||||
top: typeLabelOffset,
|
||||
@ -75,30 +77,6 @@ class PlaylistCube extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildImage(BuildContext context) {
|
||||
return playlist['image'] != null
|
||||
? CachedNetworkImage(
|
||||
key: ValueKey(playlist['image'].toString()),
|
||||
imageUrl: playlist['image'].toString(),
|
||||
height: size,
|
||||
width: size,
|
||||
fit: BoxFit.cover,
|
||||
errorWidget:
|
||||
(context, url, error) => NullArtworkWidget(
|
||||
icon: cubeIcon,
|
||||
iconSize: iconSize,
|
||||
size: size,
|
||||
title: playlist['title'],
|
||||
),
|
||||
)
|
||||
: NullArtworkWidget(
|
||||
icon: cubeIcon,
|
||||
iconSize: iconSize,
|
||||
size: size,
|
||||
title: playlist['title'],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLabel(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return Container(
|
||||
|
Reference in New Issue
Block a user