fix: loading albums and songs
This commit is contained in:
parent
139844596a
commit
2461c82e1d
16 changed files with 241 additions and 100 deletions
|
@ -5,11 +5,11 @@ meta {
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{baseUrl}}/rest/getMusicDirectory?id=1aee72dc6749f4cb906f7d740ee23b78
|
url: {{baseUrl}}/rest/getMusicDirectory?id=615eb32e4ca36d6ab405852a2842f6b6
|
||||||
body: none
|
body: none
|
||||||
auth: none
|
auth: none
|
||||||
}
|
}
|
||||||
|
|
||||||
query {
|
query {
|
||||||
id: 1aee72dc6749f4cb906f7d740ee23b78
|
id: 615eb32e4ca36d6ab405852a2842f6b6
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
import 'package:fast_cached_network_image/fast_cached_network_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:just_audio/just_audio.dart';
|
import 'package:just_audio/just_audio.dart';
|
||||||
import 'package:just_audio_background/just_audio_background.dart';
|
import 'package:just_audio_background/just_audio_background.dart';
|
||||||
import 'package:ocarina/api/subsonic/song.dart';
|
import 'package:ocarina/api/subsonic/song.dart';
|
||||||
import 'package:ocarina/main.dart';
|
import 'package:ocarina/main.dart';
|
||||||
|
import 'package:ocarina/util/util.dart';
|
||||||
|
|
||||||
/// Service used to control the audio player
|
/// Service used to control the audio player
|
||||||
class AudioPlayerService {
|
class AudioPlayerService {
|
||||||
|
@ -15,19 +18,57 @@ class AudioPlayerService {
|
||||||
AudioPlayerService._internal();
|
AudioPlayerService._internal();
|
||||||
|
|
||||||
/// The [AudioPlayer] instance
|
/// The [AudioPlayer] instance
|
||||||
final player = AudioPlayer();
|
final _player = AudioPlayer();
|
||||||
|
|
||||||
|
/// True if [AudioPlayer] instance is playing
|
||||||
|
bool get isPlaying => _player.playing;
|
||||||
|
|
||||||
/// Currently playing song
|
/// Currently playing song
|
||||||
///
|
///
|
||||||
/// Null if no song is loaded
|
/// Null if no song is loaded
|
||||||
Song? song;
|
Song? get song => _song;
|
||||||
|
|
||||||
|
set song(Song? s) {
|
||||||
|
_song = s;
|
||||||
|
logger.d("CHANGE song");
|
||||||
|
songNotifier.value = s;
|
||||||
|
|
||||||
|
_setColorScheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
Song? _song;
|
||||||
|
|
||||||
|
/// Pauses playback
|
||||||
|
Future<void> pause() async {
|
||||||
|
await _player.pause();
|
||||||
|
logger.d("Paused");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resumes playback
|
||||||
|
void resume() {
|
||||||
|
_player.play();
|
||||||
|
logger.d("Playing");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets color scheme from image
|
||||||
|
Future<void> _setColorScheme() async {
|
||||||
|
if (AudioPlayerService().song == null) {
|
||||||
|
themeNotifier.value = ColorScheme.fromSeed(seedColor: Colors.deepPurple);
|
||||||
|
}
|
||||||
|
themeNotifier.value = await ColorScheme.fromImageProvider(
|
||||||
|
provider: FastCachedImageProvider(
|
||||||
|
AudioPlayerService().song!.coverArtUrl,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
logger.d(AudioPlayerService().song!.coverArtUrl);
|
||||||
|
}
|
||||||
|
|
||||||
/// Plays the passed [Song] as a file
|
/// Plays the passed [Song] as a file
|
||||||
Future<void> playFile({Song? song}) async {
|
Future<void> playFile({Song? song}) async {
|
||||||
final doCache = sp.getBool("doCache") ?? true;
|
final doCache = sp.getBool("doCache") ?? true;
|
||||||
if (song == null && this.song == null) return;
|
if (song == null && this.song == null) return;
|
||||||
song ??= this.song;
|
song ??= this.song;
|
||||||
await player.setAudioSource(
|
await _player.setAudioSource(
|
||||||
doCache
|
doCache
|
||||||
? LockCachingAudioSource(
|
? LockCachingAudioSource(
|
||||||
Uri.parse(song!.streamUrl),
|
Uri.parse(song!.streamUrl),
|
||||||
|
@ -50,7 +91,8 @@ class AudioPlayerService {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await player.seek(Duration.zero);
|
await _player.seek(Duration.zero);
|
||||||
await player.play();
|
playerKey.currentState?.update();
|
||||||
|
resume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ class Album {
|
||||||
Album({
|
Album({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.name,
|
required this.name,
|
||||||
required String coverArtId,
|
required this.coverArtId,
|
||||||
required this.playCount,
|
required this.playCount,
|
||||||
required this.artistName,
|
required this.artistName,
|
||||||
required this.artistId,
|
required this.artistId,
|
||||||
|
@ -15,12 +15,13 @@ class Album {
|
||||||
required this.songCount,
|
required this.songCount,
|
||||||
required this.genres,
|
required this.genres,
|
||||||
required this.duration,
|
required this.duration,
|
||||||
}) : _coverArtId = coverArtId;
|
});
|
||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
final String name;
|
final String name;
|
||||||
@JsonKey(name: "coverArt")
|
@JsonKey(name: "coverArt")
|
||||||
final String _coverArtId;
|
final String coverArtId;
|
||||||
|
@JsonKey(defaultValue: 0)
|
||||||
final int playCount;
|
final int playCount;
|
||||||
@JsonKey(name: "artist")
|
@JsonKey(name: "artist")
|
||||||
final String artistName;
|
final String artistName;
|
||||||
|
@ -30,7 +31,7 @@ class Album {
|
||||||
final List<String> genres;
|
final List<String> genres;
|
||||||
final int duration;
|
final int duration;
|
||||||
|
|
||||||
String get coverArtUrl => SubsonicApiService().getCoverArtUrl(_coverArtId);
|
String get coverArtUrl => SubsonicApiService().getCoverArtUrl(coverArtId);
|
||||||
|
|
||||||
factory Album.fromJson(Map<String, dynamic> json) => _$AlbumFromJson(json);
|
factory Album.fromJson(Map<String, dynamic> json) => _$AlbumFromJson(json);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ Album _$AlbumFromJson(Map<String, dynamic> json) => Album(
|
||||||
id: json['id'] as String,
|
id: json['id'] as String,
|
||||||
name: json['name'] as String,
|
name: json['name'] as String,
|
||||||
coverArtId: json['coverArt'] as String,
|
coverArtId: json['coverArt'] as String,
|
||||||
playCount: (json['playCount'] as num).toInt(),
|
playCount: (json['playCount'] as num?)?.toInt() ?? 0,
|
||||||
artistName: json['artist'] as String,
|
artistName: json['artist'] as String,
|
||||||
artistId: json['artistId'] as String,
|
artistId: json['artistId'] as String,
|
||||||
year: (json['year'] as num).toInt(),
|
year: (json['year'] as num).toInt(),
|
||||||
|
@ -23,7 +23,7 @@ Album _$AlbumFromJson(Map<String, dynamic> json) => Album(
|
||||||
Map<String, dynamic> _$AlbumToJson(Album instance) => <String, dynamic>{
|
Map<String, dynamic> _$AlbumToJson(Album instance) => <String, dynamic>{
|
||||||
'id': instance.id,
|
'id': instance.id,
|
||||||
'name': instance.name,
|
'name': instance.name,
|
||||||
'coverArt': instance._coverArtId,
|
'coverArt': instance.coverArtId,
|
||||||
'playCount': instance.playCount,
|
'playCount': instance.playCount,
|
||||||
'artist': instance.artistName,
|
'artist': instance.artistName,
|
||||||
'artistId': instance.artistId,
|
'artistId': instance.artistId,
|
||||||
|
|
|
@ -15,9 +15,9 @@ class Song {
|
||||||
required this.bitRate,
|
required this.bitRate,
|
||||||
required this.contentType,
|
required this.contentType,
|
||||||
required this.fileType,
|
required this.fileType,
|
||||||
required String coverArtId,
|
required this.coverArtId,
|
||||||
this.trackNumber,
|
this.trackNumber,
|
||||||
}) : _coverArtId = coverArtId;
|
});
|
||||||
|
|
||||||
factory Song.fromJson(Map<String, dynamic> json) => _$SongFromJson(json);
|
factory Song.fromJson(Map<String, dynamic> json) => _$SongFromJson(json);
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class Song {
|
||||||
final String id;
|
final String id;
|
||||||
@JsonKey(name: "artist")
|
@JsonKey(name: "artist")
|
||||||
final String artistName;
|
final String artistName;
|
||||||
final String artistId;
|
final String? artistId;
|
||||||
final String albumId;
|
final String albumId;
|
||||||
final String title;
|
final String title;
|
||||||
@JsonKey(name: "album")
|
@JsonKey(name: "album")
|
||||||
|
@ -38,8 +38,8 @@ class Song {
|
||||||
@JsonKey(name: "suffix")
|
@JsonKey(name: "suffix")
|
||||||
final String fileType;
|
final String fileType;
|
||||||
@JsonKey(name: "coverArt")
|
@JsonKey(name: "coverArt")
|
||||||
final String _coverArtId;
|
final String coverArtId;
|
||||||
|
|
||||||
String get coverArtUrl => SubsonicApiService().getCoverArtUrl(_coverArtId);
|
String get coverArtUrl => SubsonicApiService().getCoverArtUrl(coverArtId);
|
||||||
String get streamUrl => SubsonicApiService().getStreamUrl(id);
|
String get streamUrl => SubsonicApiService().getStreamUrl(id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ Song _$SongFromJson(Map<String, dynamic> json) => Song(
|
||||||
title: json['title'] as String,
|
title: json['title'] as String,
|
||||||
albumName: json['album'] as String,
|
albumName: json['album'] as String,
|
||||||
albumId: json['albumId'] as String,
|
albumId: json['albumId'] as String,
|
||||||
artistId: json['artistId'] as String,
|
artistId: json['artistId'] as String?,
|
||||||
duration: (json['duration'] as num).toInt(),
|
duration: (json['duration'] as num).toInt(),
|
||||||
bitRate: (json['bitRate'] as num).toInt(),
|
bitRate: (json['bitRate'] as num).toInt(),
|
||||||
contentType: json['contentType'] as String,
|
contentType: json['contentType'] as String,
|
||||||
|
@ -33,5 +33,5 @@ Map<String, dynamic> _$SongToJson(Song instance) => <String, dynamic>{
|
||||||
'bitRate': instance.bitRate,
|
'bitRate': instance.bitRate,
|
||||||
'contentType': instance.contentType,
|
'contentType': instance.contentType,
|
||||||
'suffix': instance.fileType,
|
'suffix': instance.fileType,
|
||||||
'coverArt': instance._coverArtId,
|
'coverArt': instance.coverArtId,
|
||||||
};
|
};
|
||||||
|
|
|
@ -105,7 +105,10 @@ class SubsonicApiService {
|
||||||
try {
|
try {
|
||||||
albums.add(Album.fromJson(albumData));
|
albums.add(Album.fromJson(albumData));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e(e);
|
logger
|
||||||
|
..e("Error for $artistId")
|
||||||
|
..e(e)
|
||||||
|
..e(albumData);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,7 +132,10 @@ class SubsonicApiService {
|
||||||
try {
|
try {
|
||||||
songs.add(Song.fromJson(songData));
|
songs.add(Song.fromJson(songData));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e(e);
|
logger
|
||||||
|
..e("Error for $albumId")
|
||||||
|
..e(e)
|
||||||
|
..e(songData);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:fast_cached_network_image/fast_cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:just_audio_background/just_audio_background.dart';
|
import 'package:just_audio_background/just_audio_background.dart';
|
||||||
import 'package:just_audio_media_kit/just_audio_media_kit.dart';
|
import 'package:just_audio_media_kit/just_audio_media_kit.dart';
|
||||||
|
import 'package:ocarina/api/subsonic/song.dart';
|
||||||
import 'package:ocarina/views/home_view.dart';
|
import 'package:ocarina/views/home_view.dart';
|
||||||
import 'package:ocarina/widgets/player.dart';
|
import 'package:ocarina/widgets/player.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
@ -22,6 +23,7 @@ void main() async {
|
||||||
clearCacheAfter: const Duration(days: 31),
|
clearCacheAfter: const Duration(days: 31),
|
||||||
subDir: (await getApplicationCacheDirectory()).path,
|
subDir: (await getApplicationCacheDirectory()).path,
|
||||||
);
|
);
|
||||||
|
|
||||||
sp = await SharedPreferences.getInstance();
|
sp = await SharedPreferences.getInstance();
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
@ -32,29 +34,42 @@ final playerKey = GlobalKey<PlayerState>();
|
||||||
/// Instance of [SharedPreferences] used to get shared preferences
|
/// Instance of [SharedPreferences] used to get shared preferences
|
||||||
late final SharedPreferences sp;
|
late final SharedPreferences sp;
|
||||||
|
|
||||||
|
/// Notifier to change theme from inside the app
|
||||||
|
final ValueNotifier<ColorScheme> themeNotifier =
|
||||||
|
ValueNotifier(ColorScheme.fromSeed(seedColor: Colors.deepPurple));
|
||||||
|
|
||||||
|
/// Notifier to change theme from inside the app
|
||||||
|
final ValueNotifier<Song?> songNotifier = ValueNotifier(null);
|
||||||
|
|
||||||
|
/// Main app class
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
|
/// Main app class
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
// This widget is the root of your application.
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ResponsiveSizer(
|
return ResponsiveSizer(
|
||||||
builder: (context, orientation, screenType) {
|
builder: (context, orientation, screenType) {
|
||||||
return MaterialApp(
|
return ValueListenableBuilder<ColorScheme>(
|
||||||
title: 'Ocarina',
|
valueListenable: themeNotifier,
|
||||||
theme: ThemeData(
|
builder: (BuildContext context, ColorScheme value, Widget? child) {
|
||||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
return MaterialApp(
|
||||||
useMaterial3: true,
|
title: 'Ocarina',
|
||||||
),
|
theme: ThemeData(
|
||||||
home: const HomeView(),
|
colorScheme: value,
|
||||||
builder: (context, child) {
|
useMaterial3: true,
|
||||||
return Stack(
|
),
|
||||||
children: [
|
home: const HomeView(),
|
||||||
child!,
|
builder: (context, child) {
|
||||||
Player(
|
return Stack(
|
||||||
key: playerKey,
|
children: [
|
||||||
),
|
child!,
|
||||||
],
|
Player(
|
||||||
|
key: playerKey,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
import 'package:fast_cached_network_image/fast_cached_network_image.dart';
|
import 'package:fast_cached_network_image/fast_cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:ocarina/api/audio/audioplayer_service.dart';
|
import 'package:ocarina/api/audio/audioplayer_service.dart';
|
||||||
|
import 'package:ocarina/api/subsonic/song.dart';
|
||||||
|
import 'package:ocarina/main.dart';
|
||||||
import 'package:ocarina/util/util.dart';
|
import 'package:ocarina/util/util.dart';
|
||||||
import 'package:responsive_sizer/responsive_sizer.dart';
|
import 'package:responsive_sizer/responsive_sizer.dart';
|
||||||
import 'package:shimmer/shimmer.dart';
|
import 'package:shimmer/shimmer.dart';
|
||||||
|
import 'package:text_scroll/text_scroll.dart';
|
||||||
|
|
||||||
/// The player widget
|
/// The player widget
|
||||||
///
|
///
|
||||||
|
@ -22,7 +28,7 @@ class Player extends StatefulWidget {
|
||||||
/// State of [Player]
|
/// State of [Player]
|
||||||
class PlayerState extends State<Player> {
|
class PlayerState extends State<Player> {
|
||||||
void update() {
|
void update() {
|
||||||
logger.d(AudioPlayerService().song?.title);
|
logger.d(AudioPlayerService().song?.coverArtUrl);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,46 +75,19 @@ class PlayerState extends State<Player> {
|
||||||
width: 100.w,
|
width: 100.w,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: Row(
|
child: ValueListenableBuilder<Song?>(
|
||||||
children: [
|
valueListenable: songNotifier,
|
||||||
SizedBox(
|
builder: (c, t, w) {
|
||||||
height: 10.h,
|
return Row(
|
||||||
width: 10.h,
|
children: [
|
||||||
child: Padding(
|
SizedBox(
|
||||||
padding: const EdgeInsets.all(8),
|
height: 10.h,
|
||||||
child: ClipRRect(
|
width: 10.h,
|
||||||
child: (AudioPlayerService().song == null)
|
child: Padding(
|
||||||
? ColoredBox(
|
padding: const EdgeInsets.all(8),
|
||||||
color: Theme.of(context)
|
child: ClipRRect(
|
||||||
.colorScheme
|
child: (t == null)
|
||||||
.primaryContainer,
|
? ColoredBox(
|
||||||
child: Center(
|
|
||||||
child: Icon(
|
|
||||||
Icons.music_note,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onPrimaryContainer,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: (FastCachedImage(
|
|
||||||
url: AudioPlayerService()
|
|
||||||
.song!
|
|
||||||
.coverArtUrl,
|
|
||||||
loadingBuilder: (c, d) =>
|
|
||||||
Shimmer.fromColors(
|
|
||||||
baseColor: Colors.grey.shade300,
|
|
||||||
highlightColor:
|
|
||||||
Colors.grey.shade100,
|
|
||||||
child: Container(
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
errorBuilder: (c, _, __) {
|
|
||||||
logger
|
|
||||||
..e(_)
|
|
||||||
..e(__);
|
|
||||||
return ColoredBox(
|
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.primaryContainer,
|
.primaryContainer,
|
||||||
|
@ -120,28 +99,106 @@ class PlayerState extends State<Player> {
|
||||||
.onPrimaryContainer,
|
.onPrimaryContainer,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
},
|
: FastCachedImage(
|
||||||
)),
|
key: Key(
|
||||||
|
md5
|
||||||
|
.convert(
|
||||||
|
utf8.encode(
|
||||||
|
t.coverArtUrl,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toString(),
|
||||||
|
),
|
||||||
|
url: t.coverArtUrl,
|
||||||
|
loadingBuilder: (c, d) =>
|
||||||
|
Shimmer.fromColors(
|
||||||
|
baseColor: Colors.grey.shade300,
|
||||||
|
highlightColor:
|
||||||
|
Colors.grey.shade100,
|
||||||
|
child: Container(
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
errorBuilder: (c, _, __) {
|
||||||
|
logger
|
||||||
|
..e(_)
|
||||||
|
..e(__);
|
||||||
|
return ColoredBox(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primaryContainer,
|
||||||
|
child: Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.music_note,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onPrimaryContainer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(
|
||||||
),
|
width: 5,
|
||||||
const SizedBox(
|
),
|
||||||
width: 5,
|
Expanded(
|
||||||
),
|
child: AutoSizeText(
|
||||||
AutoSizeText(
|
AudioPlayerService().song == null
|
||||||
AudioPlayerService().song == null
|
? "Nothing"
|
||||||
? "Nothing"
|
: AudioPlayerService().song!.title,
|
||||||
: AudioPlayerService().song!.title,
|
style: TextStyle(
|
||||||
style: TextStyle(
|
color: Theme.of(context)
|
||||||
color: Theme.of(context)
|
.colorScheme
|
||||||
.colorScheme
|
.onPrimaryContainer,
|
||||||
.onPrimaryContainer,
|
fontSize: 14,
|
||||||
fontSize: 14,
|
decoration: TextDecoration.none,
|
||||||
decoration: TextDecoration.none,
|
),
|
||||||
),
|
overflowReplacement: TextScroll(
|
||||||
),
|
AudioPlayerService().song == null
|
||||||
],
|
? "Nothing"
|
||||||
|
: AudioPlayerService().song!.title,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 30.w,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (AudioPlayerService().song ==
|
||||||
|
null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (AudioPlayerService().isPlaying) {
|
||||||
|
await AudioPlayerService().pause();
|
||||||
|
} else {
|
||||||
|
AudioPlayerService().resume();
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
icon: AnimatedCrossFade(
|
||||||
|
firstChild:
|
||||||
|
const Icon(Icons.play_arrow),
|
||||||
|
secondChild: const Icon(Icons.pause),
|
||||||
|
crossFadeState:
|
||||||
|
(AudioPlayerService().isPlaying)
|
||||||
|
? CrossFadeState.showSecond
|
||||||
|
: CrossFadeState.showFirst,
|
||||||
|
duration:
|
||||||
|
const Duration(milliseconds: 300),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -6,10 +6,14 @@
|
||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <dynamic_color/dynamic_color_plugin.h>
|
||||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
|
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
|
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
|
||||||
|
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||||
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
dynamic_color
|
||||||
flutter_secure_storage_linux
|
flutter_secure_storage_linux
|
||||||
media_kit_libs_linux
|
media_kit_libs_linux
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import Foundation
|
||||||
|
|
||||||
import audio_service
|
import audio_service
|
||||||
import audio_session
|
import audio_session
|
||||||
|
import dynamic_color
|
||||||
import flutter_secure_storage_macos
|
import flutter_secure_storage_macos
|
||||||
import just_audio
|
import just_audio
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
|
@ -16,6 +17,7 @@ import sqflite
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
|
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
|
||||||
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
||||||
|
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
||||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
|
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
|
|
|
@ -233,6 +233,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.4.3+1"
|
version: "5.4.3+1"
|
||||||
|
dynamic_color:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: dynamic_color
|
||||||
|
sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.7.0"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -54,6 +54,7 @@ dependencies:
|
||||||
json_serializable: ^6.8.0
|
json_serializable: ^6.8.0
|
||||||
json_annotation: ^4.9.0
|
json_annotation: ^4.9.0
|
||||||
shared_preferences: ^2.2.3
|
shared_preferences: ^2.2.3
|
||||||
|
dynamic_color: ^1.7.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.4.9
|
build_runner: ^2.4.9
|
||||||
|
|
|
@ -6,10 +6,13 @@
|
||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <dynamic_color/dynamic_color_plugin_c_api.h>
|
||||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
#include <media_kit_libs_windows_audio/media_kit_libs_windows_audio_plugin_c_api.h>
|
#include <media_kit_libs_windows_audio/media_kit_libs_windows_audio_plugin_c_api.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
DynamicColorPluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
|
||||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
MediaKitLibsWindowsAudioPluginCApiRegisterWithRegistrar(
|
MediaKitLibsWindowsAudioPluginCApiRegisterWithRegistrar(
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
dynamic_color
|
||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
media_kit_libs_windows_audio
|
media_kit_libs_windows_audio
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue