refactor(ui): unify expressive actions and modal transitions

This commit is contained in:
zarzet committed 2026-10-15 02:52:15 +07:00
1 parent bd0b4a50c3
commit 1bae2e6b16
17 files changed
+70 -28

No files matched your search

+1
View File
@@ -1001,6 +1001,7 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen>
showModalBottomSheet<void>(
context: context,
sheetAnimationStyle: AppMotion.surfaceEntrance(context),
useRootNavigator: true,
isScrollControlled: true,
backgroundColor: context.isMornye
+1
View File
@@ -430,6 +430,7 @@ extension _HomeTabExploreUI on _HomeTabState {
showModalBottomSheet<void>(
context: context,
sheetAnimationStyle: AppMotion.surfaceEntrance(context),
useRootNavigator: true,
backgroundColor: colorScheme.surface,
builder: (context) => SafeArea(
+8 -6
View File
@@ -177,6 +177,7 @@ extension _HomeTabSearchResultsUI on _HomeTabState {
final mornye = context.isMornye;
showModalBottomSheet<void>(
context: context,
sheetAnimationStyle: AppMotion.surfaceEntrance(context),
useRootNavigator: true,
isScrollControlled: true,
backgroundColor: mornye
@@ -494,7 +495,8 @@ extension _HomeTabSearchResultsUI on _HomeTabState {
if (showSortButton)
SizedBox(
height: 32,
child: TextButton.icon(
child: ExpressiveButton(
text: true,
onPressed: () => _showSortOptions(colorScheme),
icon: mornye
? null
@@ -507,7 +509,11 @@ extension _HomeTabSearchResultsUI on _HomeTabState {
? colorScheme.primary
: colorScheme.onSurfaceVariant,
),
label: Text(
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 8),
visualDensity: VisualDensity.compact,
),
child: Text(
_searchSortOption != HomeSearchSortOption.defaultOrder
? _sortOptionLabel(_searchSortOption)
: context.l10n.libraryFilterSort,
@@ -519,10 +525,6 @@ extension _HomeTabSearchResultsUI on _HomeTabState {
: colorScheme.onSurfaceVariant,
),
),
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 8),
visualDensity: VisualDensity.compact,
),
),
),
],
+5 -5
View File
@@ -309,10 +309,10 @@ extension _QueueTabNavigation on _QueueTabState {
final controller = TextEditingController();
final formKey = GlobalKey<FormState>();
final playlistName = await showDialog<String>(
final playlistName = await showAppDialog<String>(
context: context,
builder: (dialogContext) {
return AlertDialog(
return AppAlertDialog(
title: Text(dialogContext.l10n.collectionCreatePlaylist),
content: Form(
key: formKey,
@@ -336,12 +336,12 @@ extension _QueueTabNavigation on _QueueTabState {
),
),
actions: [
ExpressiveButton(
text: true,
AppDialogAction(
onPressed: () => Navigator.of(dialogContext).pop(),
child: Text(dialogContext.l10n.dialogCancel),
),
ExpressiveButton(
AppDialogAction(
filled: true,
onPressed: () {
if (formKey.currentState?.validate() != true) return;
Navigator.of(dialogContext).pop(controller.text.trim());
+1
View File
@@ -438,6 +438,7 @@ class _SetupScreenState extends ConsumerState<SetupScreen>
final colorScheme = Theme.of(context).colorScheme;
await showModalBottomSheet<void>(
context: context,
sheetAnimationStyle: AppMotion.surfaceEntrance(context),
useRootNavigator: true,
backgroundColor: colorScheme.surfaceContainerHigh,
builder: (ctx) => SafeArea(
+1
View File
@@ -56,6 +56,7 @@ extension _TrackMetadataFileActions on _TrackMetadataScreenState {
final saved = await showModalBottomSheet<bool>(
context: context,
sheetAnimationStyle: AppMotion.surfaceEntrance(context),
useRootNavigator: true,
isScrollControlled: true,
backgroundColor: context.isMornye
+2
View File
@@ -281,6 +281,7 @@ extension _TrackMetadataConvertAndCueSplit on _TrackMetadataScreenState {
showModalBottomSheet<void>(
context: context,
sheetAnimationStyle: AppMotion.surfaceEntrance(context),
useRootNavigator: true,
isScrollControlled: true,
backgroundColor: context.isMornye ? Colors.transparent : null,
@@ -382,6 +383,7 @@ extension _TrackMetadataConvertAndCueSplit on _TrackMetadataScreenState {
showModalBottomSheet<void>(
context: this.context,
sheetAnimationStyle: AppMotion.surfaceEntrance(this.context),
useRootNavigator: true,
isScrollControlled: true,
builder: (sheetContext) {
@@ -9,6 +9,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:path/path.dart' as p;
import 'package:spotiflac_android/controllers/metadata_editor_controller.dart';
import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/theme/app_motion.dart';
import 'package:spotiflac_android/providers/extension_provider.dart';
import 'package:spotiflac_android/providers/settings_provider.dart';
import 'package:spotiflac_android/services/metadata_cover_resources.dart';
@@ -120,6 +121,7 @@ class _EditMetadataSheetState extends State<EditMetadataSheet> {
FocusScope.of(context).unfocus();
final selectedId = await showModalBottomSheet<String>(
context: context,
sheetAnimationStyle: AppMotion.surfaceEntrance(context),
useRootNavigator: true,
isScrollControlled: true,
showDragHandle: true,
@@ -202,6 +204,7 @@ class _EditMetadataSheetState extends State<EditMetadataSheet> {
final currentValue = _coverMaxDimension ?? 0;
final selectedValue = await showModalBottomSheet<int>(
context: context,
sheetAnimationStyle: AppMotion.surfaceEntrance(context),
useRootNavigator: true,
isScrollControlled: true,
showDragHandle: true,
+1
View File
@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io';
import 'dart:ui' show ImageFilter;
import 'package:flutter/material.dart';
import 'package:spotiflac_android/theme/app_motion.dart';
import 'package:spotiflac_android/widgets/app_alert_dialog.dart';
import 'package:flutter/cupertino.dart' show CupertinoButton, CupertinoIcons;
import 'package:spotiflac_android/widgets/app_bottom_sheet.dart';
@@ -268,6 +268,7 @@ extension _TrackMetadataMenu on _TrackMetadataScreenState {
}
showModalBottomSheet<void>(
context: screenContext,
sheetAnimationStyle: AppMotion.surfaceEntrance(screenContext),
useRootNavigator: true,
backgroundColor: colorScheme.surfaceContainerHigh,
isScrollControlled: true,
+2
View File
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:path_provider/path_provider.dart';
import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/theme/app_motion.dart';
import 'package:spotiflac_android/models/unified_library_item.dart';
import 'package:spotiflac_android/providers/download_history_provider.dart';
import 'package:spotiflac_android/providers/local_library_provider.dart';
@@ -97,6 +98,7 @@ Future<void> showBatchConvertSheet(
await showModalBottomSheet<void>(
context: modalContext,
sheetAnimationStyle: AppMotion.surfaceEntrance(modalContext),
useRootNavigator: true,
isScrollControlled: true,
backgroundColor: modalContext.isMornye ? Colors.transparent : null,
+3 -3
View File
@@ -8,6 +8,7 @@ import 'package:spotiflac_android/services/app_navigation_service.dart';
import 'package:spotiflac_android/services/platform_bridge.dart';
import 'package:spotiflac_android/utils/extension_session_callback.dart';
import 'package:spotiflac_android/utils/logger.dart';
import 'package:spotiflac_android/widgets/app_alert_dialog.dart';
import 'package:spotiflac_android/widgets/app_loading_indicator.dart';
import 'package:spotiflac_android/widgets/expressive_button.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -298,15 +299,14 @@ Future<bool> showExtensionVerificationHelpDialog(
});
try {
await showDialog<void>(
await showAppDialog<void>(
context: context,
useRootNavigator: true,
barrierDismissible: false,
builder: (dialogContext) {
activeDialogContext = dialogContext;
final dialogL10n = dialogContext.l10n;
return StatefulBuilder(
builder: (dialogContext, setDialogState) => AlertDialog(
builder: (dialogContext, setDialogState) => AppAlertDialog(
title: Text(title),
content: Column(
mainAxisSize: MainAxisSize.min,
+31 -14
View File
@@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/models/artist_concert.dart';
import 'package:spotiflac_android/screens/artist_concerts_screen.dart';
import 'package:spotiflac_android/theme/mornye_theme.dart';
import 'package:spotiflac_android/widgets/animation_utils.dart';
import 'package:spotiflac_android/widgets/expressive_button.dart';
class ArtistConcertsButton extends StatelessWidget {
const ArtistConcertsButton({
@@ -21,17 +23,36 @@ class ArtistConcertsButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (concerts.isEmpty) return const SizedBox.shrink();
return TextButton.icon(
onPressed: () => Navigator.of(context).push(
slidePageRoute<void>(
page: ArtistConcertsScreen(
artistName: artistName,
concerts: concerts,
coverUrl: coverUrl,
providerId: providerId,
),
void open() => Navigator.of(context).push(
slidePageRoute<void>(
page: ArtistConcertsScreen(
artistName: artistName,
concerts: concerts,
coverUrl: coverUrl,
providerId: providerId,
),
),
);
final label = Text(
context.l10n.artistUpcomingConcerts,
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
if (!context.isMornye) {
// A compact tonal pill with the expressive press morph.
return ExpressiveButton(
tonal: true,
onPressed: open,
icon: const Icon(Icons.confirmation_number_rounded, size: 16),
style: FilledButton.styleFrom(
minimumSize: const Size(0, 36),
padding: const EdgeInsets.symmetric(horizontal: 14),
),
child: label,
);
}
return TextButton.icon(
onPressed: open,
style: TextButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.black.withValues(alpha: 0.35),
@@ -45,11 +66,7 @@ class ArtistConcertsButton extends StatelessWidget {
textStyle: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600),
),
icon: const Icon(Icons.confirmation_number_rounded, size: 16),
label: Text(
context.l10n.artistUpcomingConcerts,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
label: label,
);
}
}
+4
View File
@@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotiflac_android/providers/extension_provider.dart';
import 'package:spotiflac_android/theme/app_motion.dart';
import 'package:spotiflac_android/providers/settings_provider.dart';
import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/theme/mornye_icons.dart';
@@ -44,6 +45,7 @@ class DownloadServicePicker extends ConsumerStatefulWidget {
showModalBottomSheet<void>(
context: context,
sheetAnimationStyle: AppMotion.surfaceEntrance(context),
useRootNavigator: true,
backgroundColor: mornye
? Colors.transparent
@@ -57,7 +59,9 @@ class DownloadServicePicker extends ConsumerStatefulWidget {
: null,
isScrollControlled: true,
showDragHandle: !mornye,
// Explicit constraints replace the theme's, so repeat its tablet width.
constraints: BoxConstraints(
maxWidth: 640,
maxHeight: MediaQuery.sizeOf(context).height * 0.88,
),
builder: (context) {
+2
View File
@@ -2,6 +2,7 @@ import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';
import 'package:spotiflac_android/controllers/player_metadata_controller.dart';
import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/theme/app_motion.dart';
import 'package:spotiflac_android/theme/mornye_theme.dart';
import 'package:spotiflac_android/utils/isrc_utils.dart';
import 'package:spotiflac_android/widgets/app_icon.dart';
@@ -19,6 +20,7 @@ Future<void> showPlayerDetailsSheet(
final metadataFuture = metadata.readDetails(mediaItem);
return showModalBottomSheet<void>(
context: context,
sheetAnimationStyle: AppMotion.surfaceEntrance(context),
isScrollControlled: true,
useRootNavigator: mornye,
showDragHandle: !mornye,
+2
View File
@@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotiflac_android/l10n/l10n.dart';
import 'package:spotiflac_android/theme/app_motion.dart';
import 'package:spotiflac_android/models/track.dart';
import 'package:spotiflac_android/providers/library_collections_provider.dart';
import 'package:spotiflac_android/theme/mornye_theme.dart';
@@ -39,6 +40,7 @@ Future<void> showAddTracksToPlaylistSheet(
await showModalBottomSheet<void>(
context: context,
sheetAnimationStyle: AppMotion.surfaceEntrance(context),
useRootNavigator: true,
showDragHandle: !context.isMornye,
backgroundColor: context.isMornye ? Colors.transparent : null,
@@ -1,6 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:spotiflac_android/theme/app_motion.dart';
import 'package:spotiflac_android/theme/mornye_theme.dart';
import 'package:flutter/services.dart';
import 'package:spotiflac_android/widgets/app_bottom_sheet.dart';
@@ -49,6 +50,7 @@ class TrackCollectionQuickActions extends ConsumerWidget {
}
showModalBottomSheet<void>(
context: context,
sheetAnimationStyle: AppMotion.surfaceEntrance(context),
useRootNavigator: true,
isScrollControlled: true,
backgroundColor: colorScheme.surfaceContainerHigh,