Skip to content

Commit

Permalink
feat: better mobile video full screen top controls, queue color and a…
Browse files Browse the repository at this point in the history
…udio bottom sheet (#1093)
  • Loading branch information
Feichtmeier authored Dec 14, 2024
1 parent eeca186 commit ba858d6
Show file tree
Hide file tree
Showing 14 changed files with 269 additions and 133 deletions.
59 changes: 56 additions & 3 deletions lib/common/view/audio_tile_bottom_sheet.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:watch_it/watch_it.dart';

import 'ui_constants.dart';
import '../../constants.dart';
import '../../extensions/build_context_x.dart';
import '../../l10n/l10n.dart';
import '../../library/library_model.dart';
import '../../local_audio/local_audio_model.dart';
import '../../local_audio/view/album_page.dart';
import '../../local_audio/view/artist_page.dart';
import '../../player/player_model.dart';
import '../../playlists/view/add_to_playlist_dialog.dart';
import '../data/audio.dart';
Expand All @@ -14,12 +18,11 @@ import 'icons.dart';
import 'like_all_icon.dart';
import 'like_icon.dart';
import 'meta_data_dialog.dart';
import 'package:flutter/material.dart';
import 'package:watch_it/watch_it.dart';
import 'snackbars.dart';
import 'spaced_divider.dart';
import 'stream_provider_share_button.dart';
import 'theme.dart';
import 'ui_constants.dart';

class AudioTileBottomSheet extends StatelessWidget {
const AudioTileBottomSheet({
Expand Down Expand Up @@ -179,6 +182,56 @@ class AudioTileBottomSheet extends StatelessWidget {
child: ListView(
shrinkWrap: true,
children: [
ListTile(
contentPadding: const EdgeInsets.symmetric(
vertical: kSmallestSpace,
horizontal: kLargestSpace,
),
leading: Icon(Iconz.artist),
minLeadingWidth: 2 * kLargestSpace,
title: Text(l10n.showArtistPage),
onTap: () {
final artistId = audios.firstOrNull?.artist;
if (artistId != null) {
final artistAudios = di<LocalAudioModel>()
.findTitlesOfArtist(artistId);
if (artistAudios != null) {
libraryModel.push(
pageId: artistId,
builder: (c) => ArtistPage(
artistAudios: artistAudios,
),
);
}
}
},
),
ListTile(
contentPadding: const EdgeInsets.symmetric(
horizontal: kLargestSpace,
vertical: kSmallestSpace,
),
leading: Icon(Iconz.album),
minLeadingWidth: 2 * kLargestSpace,
title: Text(l10n.showAlbumPage),
onTap: () {
final albumId = audios.firstOrNull?.albumId;
final albumName = audios.firstOrNull?.album;
if (albumName != null) {
final albumAudios =
di<LocalAudioModel>().findAlbum(albumName);
if (albumId != null && albumAudios != null) {
libraryModel.push(
pageId: albumId,
builder: (context) => AlbumPage(
id: albumId,
album: albumAudios,
),
);
}
}
},
),
StreamProviderShareButton(
streamProvider: StreamProvider.youTubeMusic,
text: searchTerm,
Expand Down
10 changes: 10 additions & 0 deletions lib/common/view/icons.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ class Iconz {
: appleStyled
? CupertinoIcons.double_music_note
: Icons.music_note_rounded;
static IconData get artist => yaruStyled
? YaruIcons.music_artist
: appleStyled
? CupertinoIcons.music_albums
: Icons.interpreter_mode_outlined;
static IconData get album => yaruStyled
? YaruIcons.disk
: appleStyled
? CupertinoIcons.music_albums
: Icons.album_rounded;
static IconData get external => yaruStyled
? YaruIcons.external_link
: appleStyled
Expand Down
6 changes: 4 additions & 2 deletions lib/common/view/stream_provider_share_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:watch_it/watch_it.dart';
import '../../app/app_model.dart';
import '../../l10n/l10n.dart';
import 'icons.dart';
import 'ui_constants.dart';

class StreamProviderShareButton extends StatelessWidget {
const StreamProviderShareButton({
Expand Down Expand Up @@ -49,9 +50,10 @@ class StreamProviderShareButton extends StatelessWidget {
if (tile) {
return ListTile(
contentPadding: const EdgeInsets.symmetric(
horizontal: 15,
vertical: 5,
horizontal: kLargestSpace,
vertical: kSmallestSpace,
),
minLeadingWidth: 2 * kLargestSpace,
leading: Icon(iconData),
title: Text('$tooltip ${context.l10n.search}'),
onTap: () => launchUrl(
Expand Down
2 changes: 2 additions & 0 deletions lib/l10n/app_de.arb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
"titles": "Titel",
"artist": "Künstler",
"artists": "Künstler",
"showArtistPage": "Zeige Künstlerseite",
"showAlbumPage": "Zeige Albumseite",
"album": "Album",
"albums": "Alben",
"genres": "Genres",
Expand Down
2 changes: 2 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
"titles": "Titles",
"artist": "Artist",
"artists": "Artists",
"showArtistPage": "Show artist page",
"showAlbumPage": "Show album page",
"album": "Album",
"albums": "Albums",
"genres": "Genres",
Expand Down
11 changes: 7 additions & 4 deletions lib/library/library_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -201,15 +201,18 @@ class LibraryModel extends SafeChangeNotifier implements NavigatorObserver {
await _masterNavigatorKey.currentState?.pushNamed(pageId);
}
} else if (builder != null) {
final materialPageRoute = MaterialPageRoute(
builder: (context) => isMobilePlatform
? MobilePage(page: builder(context))
: BackGesture(child: builder(context)),
final materialPageRoute = PageRouteBuilder(
maintainState: maintainState,
settings: RouteSettings(
name: pageId,
),
pageBuilder: (context, __, ___) => isMobilePlatform
? MobilePage(page: builder(context))
: BackGesture(child: builder(context)),
transitionsBuilder: (_, a, __, c) =>
FadeTransition(opacity: a, child: c),
);

if (replace) {
await _masterNavigatorKey.currentState?.pushReplacement(
materialPageRoute,
Expand Down
2 changes: 1 addition & 1 deletion lib/player/view/bottom_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import 'player_main_controls.dart';
import 'player_title_and_artist.dart';
import 'player_track.dart';
import 'player_view.dart';
import 'queue_button.dart';
import 'queue/queue_button.dart';
import 'volume_popup.dart';

class BottomPlayer extends StatelessWidget with WatchItMixin {
Expand Down
3 changes: 2 additions & 1 deletion lib/player/view/full_height_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import 'player_main_controls.dart';
import 'player_title_and_artist.dart';
import 'player_track.dart';
import 'player_view.dart';
import 'queue_button.dart';
import 'queue/queue_body.dart';
import 'queue/queue_button.dart';

class FullHeightPlayer extends StatelessWidget with WatchItMixin {
const FullHeightPlayer({
Expand Down
2 changes: 1 addition & 1 deletion lib/player/view/full_height_player_top_controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import '../../player/player_model.dart';
import '../../search/search_model.dart';
import 'playback_rate_button.dart';
import 'player_view.dart';
import 'queue_button.dart';
import 'queue/queue_button.dart';
import 'volume_popup.dart';

class FullHeightPlayerTopControls extends StatelessWidget with WatchItMixin {
Expand Down
5 changes: 4 additions & 1 deletion lib/player/view/full_height_video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ class FullHeightVideoPlayer extends StatelessWidget with WatchItMixin {
),
],
seekBarMargin: const EdgeInsets.all(kLargestSpace),
topButtonBarMargin: const EdgeInsets.only(right: kLargestSpace),
topButtonBarMargin: EdgeInsets.only(
right: kLargestSpace,
top: isMobilePlatform ? 2 * kLargestSpace : 0,
),
topButtonBar: [
const Spacer(),
controls,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,124 +2,10 @@ import 'package:flutter/material.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:watch_it/watch_it.dart';

import '../../app/app_model.dart';
import '../../app_config.dart';
import '../../common/data/audio.dart';
import '../../common/data/audio_type.dart';
import '../../common/view/icons.dart';
import '../../common/view/modals.dart';
import '../../common/view/ui_constants.dart';
import '../../extensions/build_context_x.dart';
import '../../l10n/l10n.dart';
import '../../library/library_model.dart';
import '../player_model.dart';
import 'player_main_controls.dart';

enum _Mode {
icon,
text;
}

class QueueButton extends StatelessWidget with WatchItMixin {
const QueueButton({super.key, this.color, this.isSelected})
: _mode = _Mode.icon;

const QueueButton.text({super.key, this.color, this.isSelected})
: _mode = _Mode.text;

final Color? color;
final bool? isSelected;

final _Mode _mode;

@override
Widget build(BuildContext context) {
final theme = context.theme;
final playerToTheRight = context.mediaQuerySize.width > kSideBarThreshHold;
final isFullScreen = watchPropertyValue((AppModel m) => m.fullWindowMode);
final radio = watchPropertyValue(
(PlayerModel m) => m.audio?.audioType == AudioType.radio,
);

return switch (_mode) {
_Mode.icon => IconButton(
isSelected: isSelected ??
watchPropertyValue((AppModel m) => m.showQueueOverlay),
color: color ?? theme.colorScheme.onSurface,
padding: EdgeInsets.zero,
tooltip: radio ? context.l10n.hearingHistory : context.l10n.queue,
icon: Icon(
Iconz.playlist,
color: color ?? theme.colorScheme.onSurface,
),
onPressed: () => onPressed(playerToTheRight, isFullScreen, context),
),
_Mode.text => TextButton(
onPressed: () => onPressed(playerToTheRight, isFullScreen, context),
child: Text(
context.l10n.queue,
style: context.textTheme.bodyLarge?.copyWith(
color: context.colorScheme.onSurface,
),
),
)
};
}

void onPressed(
bool playerToTheRight,
bool? isFullScreen,
BuildContext context,
) {
if ((playerToTheRight || isFullScreen == true) && !isMobilePlatform) {
di<AppModel>().setOrToggleQueueOverlay();
} else {
showModal(
context: context,
isScrollControlled: true,
showDragHandle: true,
content: ModalMode.platformModalMode == ModalMode.bottomSheet
? const QueueBody()
: const QueueDialog(),
mode: ModalMode.platformModalMode,
);
}
}
}

class QueueDialog extends StatelessWidget with WatchItMixin {
const QueueDialog({super.key});

@override
Widget build(BuildContext context) {
final queue = watchPropertyValue((PlayerModel m) => m.queue);

return AlertDialog(
titlePadding: const EdgeInsets.only(
left: 10,
right: 10,
top: kLargestSpace,
bottom: 10,
),
contentPadding: const EdgeInsets.only(bottom: kLargestSpace, top: 10),
title: const PlayerMainControls(active: true),
actionsAlignment: MainAxisAlignment.center,
actions: [
OutlinedButton(
onPressed: () {
di<LibraryModel>().addPlaylist(
'${context.l10n.queue} ${DateTime.now()}',
List.from(queue),
);
Navigator.of(context).pop();
},
child: Text(context.l10n.createNewPlaylist),
),
],
content: const QueueBody(),
);
}
}
import '../../../common/data/audio.dart';
import '../../../common/view/icons.dart';
import '../../../extensions/build_context_x.dart';
import '../../player_model.dart';

class QueueBody extends StatefulWidget with WatchItStatefulWidgetMixin {
const QueueBody({
Expand Down Expand Up @@ -200,7 +86,7 @@ class _QueueBodyState extends State<QueueBody> {
key: ValueKey(index),
index: index,
controller: _controller,
selectedColor: context.colorScheme.primary,
selectedColor: context.colorScheme.onSurface,
queueName: queueName,
queue: queue,
audio: audio,
Expand Down
Loading

0 comments on commit ba858d6

Please sign in to comment.