📱 Large screen support
This commit is contained in:
parent
249c8fbf80
commit
be44aadc07
@ -3,6 +3,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:rhythm_box/providers/spotify.dart';
|
import 'package:rhythm_box/providers/spotify.dart';
|
||||||
import 'package:rhythm_box/widgets/auto_cache_image.dart';
|
import 'package:rhythm_box/widgets/auto_cache_image.dart';
|
||||||
|
import 'package:rhythm_box/widgets/sized_container.dart';
|
||||||
import 'package:skeletonizer/skeletonizer.dart';
|
import 'package:skeletonizer/skeletonizer.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
|
|
||||||
@ -39,45 +40,48 @@ class _ExploreScreenState extends State<ExploreScreen> {
|
|||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('explore'.tr),
|
title: Text('explore'.tr),
|
||||||
|
centerTitle: MediaQuery.of(context).size.width >= 720,
|
||||||
),
|
),
|
||||||
body: Skeletonizer(
|
body: CenteredContainer(
|
||||||
enabled: _isLoading,
|
child: Skeletonizer(
|
||||||
child: ListView.builder(
|
enabled: _isLoading,
|
||||||
itemCount: _featuredPlaylist?.length ?? 20,
|
child: ListView.builder(
|
||||||
itemBuilder: (context, idx) {
|
itemCount: _featuredPlaylist?.length ?? 20,
|
||||||
final item = _featuredPlaylist?[idx];
|
itemBuilder: (context, idx) {
|
||||||
return ListTile(
|
final item = _featuredPlaylist?[idx];
|
||||||
leading: ClipRRect(
|
return ListTile(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
leading: ClipRRect(
|
||||||
child: item != null
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
? AutoCacheImage(
|
child: item != null
|
||||||
item.images!.first.url!,
|
? AutoCacheImage(
|
||||||
width: 64.0,
|
item.images!.first.url!,
|
||||||
height: 64.0,
|
width: 64.0,
|
||||||
)
|
height: 64.0,
|
||||||
: const SizedBox(
|
)
|
||||||
width: 64,
|
: const SizedBox(
|
||||||
height: 64,
|
width: 64,
|
||||||
child: Center(
|
height: 64,
|
||||||
child: Icon(Icons.image),
|
child: Center(
|
||||||
|
child: Icon(Icons.image),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
title: Text(item?.name ?? 'Loading...'),
|
||||||
title: Text(item?.name ?? 'Loading...'),
|
subtitle: Text(
|
||||||
subtitle: Text(
|
item?.description ?? 'Please stand by...',
|
||||||
item?.description ?? 'Please stand by...',
|
maxLines: 2,
|
||||||
maxLines: 2,
|
overflow: TextOverflow.ellipsis,
|
||||||
overflow: TextOverflow.ellipsis,
|
),
|
||||||
),
|
onTap: () {
|
||||||
onTap: () {
|
if (item == null) return;
|
||||||
if (item == null) return;
|
GoRouter.of(context).pushNamed(
|
||||||
GoRouter.of(context).pushNamed(
|
'playlistView',
|
||||||
'playlistView',
|
pathParameters: {'id': item.id!},
|
||||||
pathParameters: {'id': item.id!},
|
);
|
||||||
);
|
},
|
||||||
},
|
);
|
||||||
);
|
},
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -16,6 +16,7 @@ import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
|||||||
import 'package:rhythm_box/services/duration.dart';
|
import 'package:rhythm_box/services/duration.dart';
|
||||||
import 'package:rhythm_box/widgets/auto_cache_image.dart';
|
import 'package:rhythm_box/widgets/auto_cache_image.dart';
|
||||||
import 'package:rhythm_box/services/audio_services/image.dart';
|
import 'package:rhythm_box/services/audio_services/image.dart';
|
||||||
|
import 'package:rhythm_box/widgets/lyrics/synced.dart';
|
||||||
import 'package:rhythm_box/widgets/tracks/querying_track_info.dart';
|
import 'package:rhythm_box/widgets/tracks/querying_track_info.dart';
|
||||||
|
|
||||||
class PlayerScreen extends StatefulWidget {
|
class PlayerScreen extends StatefulWidget {
|
||||||
@ -50,9 +51,14 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
|
|
||||||
double? _draggingValue;
|
double? _draggingValue;
|
||||||
|
|
||||||
|
static const double maxAlbumSize = 360;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final size = MediaQuery.of(context).size;
|
final size = MediaQuery.of(context).size;
|
||||||
|
final albumSize = max(size.shortestSide, maxAlbumSize).toDouble();
|
||||||
|
|
||||||
|
final isLargeScreen = size.width >= 720;
|
||||||
|
|
||||||
return DismissiblePage(
|
return DismissiblePage(
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
@ -63,230 +69,260 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
child: Material(
|
child: Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: Column(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Expanded(
|
||||||
tag: const Key('current-active-track-album-art'),
|
child: Column(
|
||||||
child: ClipRRect(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
|
||||||
child: AspectRatio(
|
|
||||||
aspectRatio: 1,
|
|
||||||
child: _albumArt != null
|
|
||||||
? AutoCacheImage(
|
|
||||||
_albumArt!,
|
|
||||||
width: size.width,
|
|
||||||
height: size.width,
|
|
||||||
)
|
|
||||||
: Container(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.surfaceContainerHigh,
|
|
||||||
width: 64,
|
|
||||||
height: 64,
|
|
||||||
child: const Center(child: Icon(Icons.image)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
).marginSymmetric(horizontal: 24),
|
|
||||||
),
|
|
||||||
const Gap(24),
|
|
||||||
Text(
|
|
||||||
_playback.state.value.activeTrack?.name ?? 'Not playing',
|
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
_playback.state.value.activeTrack?.artists?.asString() ??
|
|
||||||
'No author',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
const Gap(24),
|
|
||||||
Obx(
|
|
||||||
() => Column(
|
|
||||||
children: [
|
children: [
|
||||||
SliderTheme(
|
LimitedBox(
|
||||||
data: SliderThemeData(
|
maxHeight: maxAlbumSize,
|
||||||
trackHeight: 2,
|
maxWidth: maxAlbumSize,
|
||||||
trackShape: _PlayerProgressTrackShape(),
|
child: Hero(
|
||||||
thumbShape: const RoundSliderThumbShape(
|
tag: const Key('current-active-track-album-art'),
|
||||||
enabledThumbRadius: 8,
|
child: ClipRRect(
|
||||||
),
|
borderRadius:
|
||||||
overlayShape: SliderComponentShape.noOverlay,
|
const BorderRadius.all(Radius.circular(16)),
|
||||||
),
|
child: AspectRatio(
|
||||||
child: Slider(
|
aspectRatio: 1,
|
||||||
secondaryTrackValue: _playback
|
child: _albumArt != null
|
||||||
.durationBuffered.value.inMilliseconds
|
? AutoCacheImage(
|
||||||
.abs()
|
_albumArt!,
|
||||||
.toDouble(),
|
width: albumSize,
|
||||||
value: _draggingValue?.abs() ??
|
height: albumSize,
|
||||||
_playback.durationCurrent.value.inMilliseconds
|
)
|
||||||
.toDouble()
|
: Container(
|
||||||
.abs(),
|
color: Theme.of(context)
|
||||||
min: 0,
|
.colorScheme
|
||||||
max: max(
|
.surfaceContainerHigh,
|
||||||
_playback.durationCurrent.value.inMilliseconds.abs(),
|
width: 64,
|
||||||
_playback.durationTotal.value.inMilliseconds.abs(),
|
height: 64,
|
||||||
).toDouble(),
|
child:
|
||||||
onChanged: (value) {
|
const Center(child: Icon(Icons.image)),
|
||||||
setState(() => _draggingValue = value);
|
),
|
||||||
},
|
),
|
||||||
onChangeEnd: (value) {
|
).marginSymmetric(horizontal: 24),
|
||||||
audioPlayer
|
|
||||||
.seek(Duration(milliseconds: value.toInt()));
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const Gap(24),
|
||||||
|
Text(
|
||||||
|
_playback.state.value.activeTrack?.name ?? 'Not playing',
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
_playback.state.value.activeTrack?.artists?.asString() ??
|
||||||
|
'No author',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
const Gap(24),
|
||||||
|
Obx(
|
||||||
|
() => Column(
|
||||||
|
children: [
|
||||||
|
SliderTheme(
|
||||||
|
data: SliderThemeData(
|
||||||
|
trackHeight: 2,
|
||||||
|
trackShape: _PlayerProgressTrackShape(),
|
||||||
|
thumbShape: const RoundSliderThumbShape(
|
||||||
|
enabledThumbRadius: 8,
|
||||||
|
),
|
||||||
|
overlayShape: SliderComponentShape.noOverlay,
|
||||||
|
),
|
||||||
|
child: Slider(
|
||||||
|
secondaryTrackValue: _playback
|
||||||
|
.durationBuffered.value.inMilliseconds
|
||||||
|
.abs()
|
||||||
|
.toDouble(),
|
||||||
|
value: _draggingValue?.abs() ??
|
||||||
|
_playback.durationCurrent.value.inMilliseconds
|
||||||
|
.toDouble()
|
||||||
|
.abs(),
|
||||||
|
min: 0,
|
||||||
|
max: max(
|
||||||
|
_playback.durationCurrent.value.inMilliseconds
|
||||||
|
.abs(),
|
||||||
|
_playback.durationTotal.value.inMilliseconds
|
||||||
|
.abs(),
|
||||||
|
).toDouble(),
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => _draggingValue = value);
|
||||||
|
},
|
||||||
|
onChangeEnd: (value) {
|
||||||
|
audioPlayer.seek(
|
||||||
|
Duration(milliseconds: value.toInt()));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
_playback.durationCurrent.value
|
||||||
|
.toHumanReadableString(),
|
||||||
|
style: GoogleFonts.robotoMono(fontSize: 12),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
_playback.durationTotal.value
|
||||||
|
.toHumanReadableString(),
|
||||||
|
style: GoogleFonts.robotoMono(fontSize: 12),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetric(horizontal: 8, vertical: 4),
|
||||||
|
],
|
||||||
|
).paddingSymmetric(horizontal: 24),
|
||||||
|
),
|
||||||
|
const Gap(24),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
StreamBuilder<bool>(
|
||||||
_playback.durationCurrent.value
|
stream: audioPlayer.shuffledStream,
|
||||||
.toHumanReadableString(),
|
builder: (context, snapshot) {
|
||||||
style: GoogleFonts.robotoMono(fontSize: 12),
|
final shuffled = snapshot.data ?? false;
|
||||||
|
return IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
shuffled
|
||||||
|
? Icons.shuffle_on_outlined
|
||||||
|
: Icons.shuffle,
|
||||||
|
),
|
||||||
|
onPressed: _isFetchingActiveTrack
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
if (shuffled) {
|
||||||
|
audioPlayer.setShuffle(false);
|
||||||
|
} else {
|
||||||
|
audioPlayer.setShuffle(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
Text(
|
IconButton(
|
||||||
_playback.durationTotal.value.toHumanReadableString(),
|
icon: const Icon(Icons.skip_previous),
|
||||||
style: GoogleFonts.robotoMono(fontSize: 12),
|
onPressed: _isFetchingActiveTrack
|
||||||
|
? null
|
||||||
|
: audioPlayer.skipToPrevious,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
SizedBox(
|
||||||
|
width: 56,
|
||||||
|
height: 56,
|
||||||
|
child: IconButton.filled(
|
||||||
|
icon: _isFetchingActiveTrack
|
||||||
|
? const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
width: 20,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Colors.white,
|
||||||
|
strokeWidth: 2.5,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Icon(
|
||||||
|
!_isPlaying
|
||||||
|
? Icons.play_arrow
|
||||||
|
: Icons.pause,
|
||||||
|
size: 28,
|
||||||
|
),
|
||||||
|
onPressed: _isFetchingActiveTrack
|
||||||
|
? null
|
||||||
|
: _togglePlayState,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.skip_next),
|
||||||
|
onPressed: _isFetchingActiveTrack
|
||||||
|
? null
|
||||||
|
: audioPlayer.skipToNext,
|
||||||
|
),
|
||||||
|
Obx(
|
||||||
|
() => IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
_loopMode == PlaylistMode.none
|
||||||
|
? Icons.repeat
|
||||||
|
: _loopMode == PlaylistMode.loop
|
||||||
|
? Icons.repeat_on_outlined
|
||||||
|
: Icons.repeat_one_on_outlined,
|
||||||
|
),
|
||||||
|
onPressed: _isFetchingActiveTrack
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
await audioPlayer.setLoopMode(
|
||||||
|
switch (_loopMode) {
|
||||||
|
PlaylistMode.loop =>
|
||||||
|
PlaylistMode.single,
|
||||||
|
PlaylistMode.single =>
|
||||||
|
PlaylistMode.none,
|
||||||
|
PlaylistMode.none => PlaylistMode.loop,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).paddingSymmetric(horizontal: 8, vertical: 4),
|
),
|
||||||
],
|
const Gap(20),
|
||||||
).paddingSymmetric(horizontal: 24),
|
Row(
|
||||||
),
|
children: [
|
||||||
const Gap(24),
|
Expanded(
|
||||||
Row(
|
child: TextButton.icon(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
icon: const Icon(Icons.queue_music),
|
||||||
children: [
|
label: const Text('Queue'),
|
||||||
StreamBuilder<bool>(
|
onPressed: () {
|
||||||
stream: audioPlayer.shuffledStream,
|
showModalBottomSheet(
|
||||||
builder: (context, snapshot) {
|
useRootNavigator: true,
|
||||||
final shuffled = snapshot.data ?? false;
|
isScrollControlled: true,
|
||||||
return IconButton(
|
context: context,
|
||||||
icon: Icon(
|
builder: (context) => const PlayerQueuePopup(),
|
||||||
shuffled ? Icons.shuffle_on_outlined : Icons.shuffle,
|
).then((_) {
|
||||||
),
|
if (mounted) {
|
||||||
onPressed: _isFetchingActiveTrack
|
setState(() {});
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
if (shuffled) {
|
|
||||||
audioPlayer.setShuffle(false);
|
|
||||||
} else {
|
|
||||||
audioPlayer.setShuffle(true);
|
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.skip_previous),
|
|
||||||
onPressed: _isFetchingActiveTrack
|
|
||||||
? null
|
|
||||||
: audioPlayer.skipToPrevious,
|
|
||||||
),
|
|
||||||
const Gap(8),
|
|
||||||
SizedBox(
|
|
||||||
width: 56,
|
|
||||||
height: 56,
|
|
||||||
child: IconButton.filled(
|
|
||||||
icon: _isFetchingActiveTrack
|
|
||||||
? const SizedBox(
|
|
||||||
height: 20,
|
|
||||||
width: 20,
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
color: Colors.white,
|
|
||||||
strokeWidth: 2.5,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Icon(
|
|
||||||
!_isPlaying ? Icons.play_arrow : Icons.pause,
|
|
||||||
size: 28,
|
|
||||||
),
|
|
||||||
onPressed:
|
|
||||||
_isFetchingActiveTrack ? null : _togglePlayState,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Gap(8),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.skip_next),
|
|
||||||
onPressed:
|
|
||||||
_isFetchingActiveTrack ? null : audioPlayer.skipToNext,
|
|
||||||
),
|
|
||||||
Obx(
|
|
||||||
() => IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
_loopMode == PlaylistMode.none
|
|
||||||
? Icons.repeat
|
|
||||||
: _loopMode == PlaylistMode.loop
|
|
||||||
? Icons.repeat_on_outlined
|
|
||||||
: Icons.repeat_one_on_outlined,
|
|
||||||
),
|
|
||||||
onPressed: _isFetchingActiveTrack
|
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
await audioPlayer.setLoopMode(
|
|
||||||
switch (_loopMode) {
|
|
||||||
PlaylistMode.loop => PlaylistMode.single,
|
|
||||||
PlaylistMode.single => PlaylistMode.none,
|
|
||||||
PlaylistMode.none => PlaylistMode.loop,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (!isLargeScreen) const Gap(4),
|
||||||
|
if (!isLargeScreen)
|
||||||
|
Expanded(
|
||||||
|
child: TextButton.icon(
|
||||||
|
icon: const Icon(Icons.lyrics),
|
||||||
|
label: const Text('Lyrics'),
|
||||||
|
onPressed: () {
|
||||||
|
GoRouter.of(context).pushNamed('playerLyrics');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Expanded(
|
||||||
|
child: TextButton.icon(
|
||||||
|
icon: const Icon(Icons.merge),
|
||||||
|
label: const Text('Sources'),
|
||||||
|
onPressed: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) =>
|
||||||
|
const SiblingTracksPopup(),
|
||||||
|
).then((_) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
|
||||||
const Gap(20),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: TextButton.icon(
|
|
||||||
icon: const Icon(Icons.queue_music),
|
|
||||||
label: const Text('Queue'),
|
|
||||||
onPressed: () {
|
|
||||||
showModalBottomSheet(
|
|
||||||
useRootNavigator: true,
|
|
||||||
isScrollControlled: true,
|
|
||||||
context: context,
|
|
||||||
builder: (context) => const PlayerQueuePopup(),
|
|
||||||
).then((_) {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Gap(4),
|
|
||||||
Expanded(
|
|
||||||
child: TextButton.icon(
|
|
||||||
icon: const Icon(Icons.lyrics),
|
|
||||||
label: const Text('Lyrics'),
|
|
||||||
onPressed: () {
|
|
||||||
GoRouter.of(context).pushNamed('playerLyrics');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Gap(4),
|
|
||||||
Expanded(
|
|
||||||
child: TextButton.icon(
|
|
||||||
icon: const Icon(Icons.merge),
|
|
||||||
label: const Text('Sources'),
|
|
||||||
onPressed: () {
|
|
||||||
showModalBottomSheet(
|
|
||||||
useRootNavigator: true,
|
|
||||||
isScrollControlled: true,
|
|
||||||
context: context,
|
|
||||||
builder: (context) => const SiblingTracksPopup(),
|
|
||||||
).then((_) {
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
if (isLargeScreen) const Gap(24),
|
||||||
|
if (isLargeScreen)
|
||||||
|
const Expanded(
|
||||||
|
child: SyncedLyrics(defaultTextZoom: 67),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
).marginAll(24),
|
).marginAll(24),
|
||||||
|
@ -10,6 +10,7 @@ import 'package:rhythm_box/providers/history.dart';
|
|||||||
import 'package:rhythm_box/providers/spotify.dart';
|
import 'package:rhythm_box/providers/spotify.dart';
|
||||||
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
import 'package:rhythm_box/widgets/auto_cache_image.dart';
|
import 'package:rhythm_box/widgets/auto_cache_image.dart';
|
||||||
|
import 'package:rhythm_box/widgets/sized_container.dart';
|
||||||
import 'package:rhythm_box/widgets/tracks/playlist_track_list.dart';
|
import 'package:rhythm_box/widgets/tracks/playlist_track_list.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
|
|
||||||
@ -60,6 +61,7 @@ class _PlaylistViewScreenState extends State<PlaylistViewScreen> {
|
|||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Playlist'),
|
title: const Text('Playlist'),
|
||||||
|
centerTitle: MediaQuery.of(context).size.width >= 720,
|
||||||
),
|
),
|
||||||
body: Builder(
|
body: Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
@ -69,86 +71,115 @@ class _PlaylistViewScreenState extends State<PlaylistViewScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CustomScrollView(
|
return CenteredContainer(
|
||||||
slivers: [
|
child: CustomScrollView(
|
||||||
SliverToBoxAdapter(
|
slivers: [
|
||||||
child: Column(
|
SliverToBoxAdapter(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Row(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
Row(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
Material(
|
children: [
|
||||||
borderRadius: radius,
|
Material(
|
||||||
elevation: 2,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: radius,
|
borderRadius: radius,
|
||||||
child: Hero(
|
elevation: 2,
|
||||||
tag: Key('playlist-cover-${_playlist!.id}'),
|
child: ClipRRect(
|
||||||
child: AutoCacheImage(
|
borderRadius: radius,
|
||||||
_playlist!.images!.first.url!,
|
child: Hero(
|
||||||
width: 160.0,
|
tag: Key('playlist-cover-${_playlist!.id}'),
|
||||||
height: 160.0,
|
child: AutoCacheImage(
|
||||||
|
_playlist!.images!.first.url!,
|
||||||
|
width: 160.0,
|
||||||
|
height: 160.0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const Gap(24),
|
||||||
const Gap(24),
|
Expanded(
|
||||||
Expanded(
|
child: Column(
|
||||||
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
children: [
|
Text(
|
||||||
Text(
|
_playlist!.name ?? 'Playlist',
|
||||||
_playlist!.name ?? 'Playlist',
|
style: Theme.of(context)
|
||||||
style:
|
.textTheme
|
||||||
Theme.of(context).textTheme.headlineSmall,
|
.headlineSmall,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
_playlist!.description ?? 'A Playlist',
|
_playlist!.description ?? 'A Playlist',
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Text(
|
Text(
|
||||||
"${NumberFormat.compactCurrency(symbol: '', decimalDigits: 2).format(_playlist!.followers!.total!)} saves",
|
"${NumberFormat.compactCurrency(symbol: '', decimalDigits: 2).format(_playlist!.followers!.total!)} saves",
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'#${_playlist!.id}',
|
'#${_playlist!.id}',
|
||||||
style: GoogleFonts.robotoMono(fontSize: 10),
|
style: GoogleFonts.robotoMono(fontSize: 10),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
).paddingOnly(left: 24, right: 24, top: 24),
|
||||||
).paddingOnly(left: 24, right: 24, top: 24),
|
const Gap(8),
|
||||||
const Gap(8),
|
Wrap(
|
||||||
Wrap(
|
spacing: 8,
|
||||||
spacing: 8,
|
children: [
|
||||||
children: [
|
Obx(
|
||||||
Obx(
|
() => ElevatedButton.icon(
|
||||||
() => ElevatedButton.icon(
|
icon: (_isCurrentPlaylist &&
|
||||||
icon: (_isCurrentPlaylist &&
|
_playback.isPlaying.value)
|
||||||
_playback.isPlaying.value)
|
? const Icon(Icons.pause_outlined)
|
||||||
? const Icon(Icons.pause_outlined)
|
: const Icon(Icons.play_arrow),
|
||||||
: const Icon(Icons.play_arrow),
|
label: const Text('Play'),
|
||||||
label: const Text('Play'),
|
onPressed: _isUpdating
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
if (_isCurrentPlaylist &&
|
||||||
|
_playback.isPlaying.value) {
|
||||||
|
audioPlayer.pause();
|
||||||
|
return;
|
||||||
|
} else if (_isCurrentPlaylist &&
|
||||||
|
!_playback.isPlaying.value) {
|
||||||
|
audioPlayer.resume();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() => _isUpdating = true);
|
||||||
|
|
||||||
|
final tracks = (await _spotify
|
||||||
|
.api.playlists
|
||||||
|
.getTracksByPlaylistId(
|
||||||
|
widget.playlistId)
|
||||||
|
.all())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
await _playback.load(tracks,
|
||||||
|
autoPlay: true);
|
||||||
|
_playback.addCollection(_playlist!.id!);
|
||||||
|
Get.find<PlaybackHistoryProvider>()
|
||||||
|
.addPlaylists([_playlist!]);
|
||||||
|
|
||||||
|
setState(() => _isUpdating = false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton.icon(
|
||||||
|
icon: const Icon(Icons.shuffle),
|
||||||
|
label: const Text('Shuffle'),
|
||||||
onPressed: _isUpdating
|
onPressed: _isUpdating
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
if (_isCurrentPlaylist &&
|
|
||||||
_playback.isPlaying.value) {
|
|
||||||
audioPlayer.pause();
|
|
||||||
return;
|
|
||||||
} else if (_isCurrentPlaylist &&
|
|
||||||
!_playback.isPlaying.value) {
|
|
||||||
audioPlayer.resume();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() => _isUpdating = true);
|
setState(() => _isUpdating = true);
|
||||||
|
|
||||||
|
audioPlayer.setShuffle(true);
|
||||||
|
|
||||||
final tracks = (await _spotify
|
final tracks = (await _spotify
|
||||||
.api.playlists
|
.api.playlists
|
||||||
.getTracksByPlaylistId(
|
.getTracksByPlaylistId(
|
||||||
@ -156,8 +187,12 @@ class _PlaylistViewScreenState extends State<PlaylistViewScreen> {
|
|||||||
.all())
|
.all())
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
await _playback.load(tracks,
|
await _playback.load(
|
||||||
autoPlay: true);
|
tracks,
|
||||||
|
autoPlay: true,
|
||||||
|
initialIndex:
|
||||||
|
Random().nextInt(tracks.length),
|
||||||
|
);
|
||||||
_playback.addCollection(_playlist!.id!);
|
_playback.addCollection(_playlist!.id!);
|
||||||
Get.find<PlaybackHistoryProvider>()
|
Get.find<PlaybackHistoryProvider>()
|
||||||
.addPlaylists([_playlist!]);
|
.addPlaylists([_playlist!]);
|
||||||
@ -165,50 +200,21 @@ class _PlaylistViewScreenState extends State<PlaylistViewScreen> {
|
|||||||
setState(() => _isUpdating = false);
|
setState(() => _isUpdating = false);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
TextButton.icon(
|
).paddingSymmetric(horizontal: 24),
|
||||||
icon: const Icon(Icons.shuffle),
|
const Gap(24),
|
||||||
label: const Text('Shuffle'),
|
],
|
||||||
onPressed: _isUpdating
|
),
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
setState(() => _isUpdating = true);
|
|
||||||
|
|
||||||
audioPlayer.setShuffle(true);
|
|
||||||
|
|
||||||
final tracks = (await _spotify.api.playlists
|
|
||||||
.getTracksByPlaylistId(
|
|
||||||
widget.playlistId)
|
|
||||||
.all())
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
await _playback.load(
|
|
||||||
tracks,
|
|
||||||
autoPlay: true,
|
|
||||||
initialIndex:
|
|
||||||
Random().nextInt(tracks.length),
|
|
||||||
);
|
|
||||||
_playback.addCollection(_playlist!.id!);
|
|
||||||
Get.find<PlaybackHistoryProvider>()
|
|
||||||
.addPlaylists([_playlist!]);
|
|
||||||
|
|
||||||
setState(() => _isUpdating = false);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
).paddingSymmetric(horizontal: 24),
|
|
||||||
const Gap(24),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
SliverToBoxAdapter(
|
||||||
SliverToBoxAdapter(
|
child: Text(
|
||||||
child: Text(
|
'Songs (${_playlist!.tracks!.total})',
|
||||||
'Songs (${_playlist!.tracks!.total})',
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
).paddingOnly(left: 28, right: 28, bottom: 4),
|
||||||
).paddingOnly(left: 28, right: 28, bottom: 4),
|
),
|
||||||
),
|
PlaylistTrackList(playlistId: widget.playlistId),
|
||||||
PlaylistTrackList(playlistId: widget.playlistId),
|
],
|
||||||
],
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:rhythm_box/providers/spotify.dart';
|
import 'package:rhythm_box/providers/spotify.dart';
|
||||||
import 'package:rhythm_box/providers/user_preferences.dart';
|
import 'package:rhythm_box/providers/user_preferences.dart';
|
||||||
|
import 'package:rhythm_box/widgets/sized_container.dart';
|
||||||
import 'package:rhythm_box/widgets/tracks/track_list.dart';
|
import 'package:rhythm_box/widgets/tracks/track_list.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
|
|
||||||
@ -61,11 +62,13 @@ class _SearchScreenState extends State<SearchScreen> {
|
|||||||
FocusManager.instance.primaryFocus?.unfocus(),
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
).paddingSymmetric(horizontal: 24, vertical: 8),
|
).paddingSymmetric(horizontal: 24, vertical: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: CustomScrollView(
|
child: CenteredContainer(
|
||||||
slivers: [
|
child: CustomScrollView(
|
||||||
if (_searchResult != null)
|
slivers: [
|
||||||
TrackSliverList(tracks: List<Track>.from(_searchResult!)),
|
if (_searchResult != null)
|
||||||
],
|
TrackSliverList(tracks: List<Track>.from(_searchResult!)),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -10,6 +10,22 @@ class SettingsScreen extends StatefulWidget {
|
|||||||
class _SettingsScreenState extends State<SettingsScreen> {
|
class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const Placeholder();
|
return Material(
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
child: SafeArea(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
leading: const Icon(Icons.login),
|
||||||
|
title: const Text('Connect with Spotify'),
|
||||||
|
subtitle: const Text('To explore your own library and more'),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ class _NavShellState extends State<NavShell> {
|
|||||||
const BottomPlayer(key: Key('app-wide-bottom-player')),
|
const BottomPlayer(key: Key('app-wide-bottom-player')),
|
||||||
const Divider(height: 0.3, thickness: 0.3),
|
const Divider(height: 0.3, thickness: 0.3),
|
||||||
BottomNavigationBar(
|
BottomNavigationBar(
|
||||||
|
landscapeLayout: BottomNavigationBarLandscapeLayout.centered,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
showUnselectedLabels: false,
|
showUnselectedLabels: false,
|
||||||
currentIndex: _focusDestination,
|
currentIndex: _focusDestination,
|
||||||
|
46
lib/widgets/sized_container.dart
Normal file
46
lib/widgets/sized_container.dart
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SizedContainer extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
final double maxWidth;
|
||||||
|
final double maxHeight;
|
||||||
|
|
||||||
|
const SizedContainer({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
this.maxWidth = 720,
|
||||||
|
this.maxHeight = double.infinity,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Container(
|
||||||
|
constraints: BoxConstraints(maxWidth: maxWidth, maxHeight: maxHeight),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CenteredContainer extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
final double maxWidth;
|
||||||
|
|
||||||
|
const CenteredContainer({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
this.maxWidth = 720,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Container(
|
||||||
|
constraints: BoxConstraints(maxWidth: maxWidth),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <desktop_webview_window/desktop_webview_window_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>
|
||||||
#include <screen_retriever/screen_retriever_plugin.h>
|
#include <screen_retriever/screen_retriever_plugin.h>
|
||||||
@ -13,6 +14,9 @@
|
|||||||
#include <window_manager/window_manager_plugin.h>
|
#include <window_manager/window_manager_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
|
g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin");
|
||||||
|
desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_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
|
||||||
|
desktop_webview_window
|
||||||
flutter_secure_storage_linux
|
flutter_secure_storage_linux
|
||||||
media_kit_libs_linux
|
media_kit_libs_linux
|
||||||
screen_retriever
|
screen_retriever
|
||||||
|
@ -7,7 +7,9 @@ import Foundation
|
|||||||
|
|
||||||
import audio_service
|
import audio_service
|
||||||
import audio_session
|
import audio_session
|
||||||
|
import desktop_webview_window
|
||||||
import device_info_plus
|
import device_info_plus
|
||||||
|
import flutter_inappwebview_macos
|
||||||
import flutter_secure_storage_macos
|
import flutter_secure_storage_macos
|
||||||
import media_kit_libs_macos_audio
|
import media_kit_libs_macos_audio
|
||||||
import package_info_plus
|
import package_info_plus
|
||||||
@ -21,7 +23,9 @@ import window_manager
|
|||||||
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"))
|
||||||
|
DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
|
||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
|
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
||||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
MediaKitLibsMacosAudioPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosAudioPlugin"))
|
MediaKitLibsMacosAudioPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosAudioPlugin"))
|
||||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
|
64
pubspec.lock
64
pubspec.lock
@ -302,6 +302,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.6"
|
version: "2.3.6"
|
||||||
|
desktop_webview_window:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: desktop_webview_window
|
||||||
|
sha256: "57cf20d81689d5cbb1adfd0017e96b669398a669d927906073b0e42fc64111c0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.3"
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -443,6 +451,62 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.1"
|
version: "3.4.1"
|
||||||
|
flutter_inappwebview:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_inappwebview
|
||||||
|
sha256: "3e9a443a18ecef966fb930c3a76ca5ab6a7aafc0c7b5e14a4a850cf107b09959"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.0"
|
||||||
|
flutter_inappwebview_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_inappwebview_android
|
||||||
|
sha256: d247f6ed417f1f8c364612fa05a2ecba7f775c8d0c044c1d3b9ee33a6515c421
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.13"
|
||||||
|
flutter_inappwebview_internal_annotations:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_inappwebview_internal_annotations
|
||||||
|
sha256: "5f80fd30e208ddded7dbbcd0d569e7995f9f63d45ea3f548d8dd4c0b473fb4c8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
flutter_inappwebview_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_inappwebview_ios
|
||||||
|
sha256: f363577208b97b10b319cd0c428555cd8493e88b468019a8c5635a0e4312bd0f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.13"
|
||||||
|
flutter_inappwebview_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_inappwebview_macos
|
||||||
|
sha256: b55b9e506c549ce88e26580351d2c71d54f4825901666bd6cfa4be9415bb2636
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.11"
|
||||||
|
flutter_inappwebview_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_inappwebview_platform_interface
|
||||||
|
sha256: "545fd4c25a07d2775f7d5af05a979b2cac4fbf79393b0a7f5d33ba39ba4f6187"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.10"
|
||||||
|
flutter_inappwebview_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_inappwebview_web
|
||||||
|
sha256: d8c680abfb6fec71609a700199635d38a744df0febd5544c5a020bd73de8ee07
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.8"
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -86,6 +86,8 @@ dependencies:
|
|||||||
animations: ^2.0.11
|
animations: ^2.0.11
|
||||||
flutter_animate: ^4.5.0
|
flutter_animate: ^4.5.0
|
||||||
duration: ^4.0.3
|
duration: ^4.0.3
|
||||||
|
desktop_webview_window: ^0.2.3
|
||||||
|
flutter_inappwebview: ^6.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <desktop_webview_window/desktop_webview_window_plugin.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>
|
||||||
#include <screen_retriever/screen_retriever_plugin.h>
|
#include <screen_retriever/screen_retriever_plugin.h>
|
||||||
@ -13,6 +14,8 @@
|
|||||||
#include <window_manager/window_manager_plugin.h>
|
#include <window_manager/window_manager_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
DesktopWebviewWindowPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("DesktopWebviewWindowPlugin"));
|
||||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
MediaKitLibsWindowsAudioPluginCApiRegisterWithRegistrar(
|
MediaKitLibsWindowsAudioPluginCApiRegisterWithRegistrar(
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
desktop_webview_window
|
||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
media_kit_libs_windows_audio
|
media_kit_libs_windows_audio
|
||||||
screen_retriever
|
screen_retriever
|
||||||
|
Loading…
Reference in New Issue
Block a user