mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-21 15:02:21 +02:00
The Material theme now draws its main surfaces with rounded Material Symbols: navigation, player, mini player, queue, settings, sheets, dialogs and shared controls. AppIcon maps legacy Icons to their symbol and keeps outlined glyphs unfilled. Mornye keeps its original icons. Icon buttons, text and filled buttons, dialog actions, sliders, the setup FAB and indeterminate spinners use the Material 3 Expressive components. Each wrapper falls back to the standard Flutter widget for Mornye, reduced motion and custom sizing, so existing layouts and hit areas stay intact.
131 lines
4.2 KiB
Dart
131 lines
4.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:spotiflac_android/l10n/l10n.dart';
|
|
import 'package:spotiflac_android/utils/adaptive_layout.dart';
|
|
import 'package:spotiflac_android/widgets/app_icon_button.dart';
|
|
import 'package:spotiflac_android/widgets/app_sliver_header.dart';
|
|
import 'package:spotiflac_android/widgets/expressive_button.dart';
|
|
|
|
class PrioritySettingsScaffold extends StatelessWidget {
|
|
final bool hasChanges;
|
|
final String title;
|
|
final String description;
|
|
final String infoText;
|
|
final EdgeInsetsGeometry descriptionPadding;
|
|
final List<Widget> slivers;
|
|
final Future<void> Function() onSave;
|
|
final Future<bool> Function(BuildContext context) onConfirmDiscard;
|
|
|
|
const PrioritySettingsScaffold({
|
|
super.key,
|
|
required this.hasChanges,
|
|
required this.title,
|
|
required this.description,
|
|
required this.infoText,
|
|
required this.slivers,
|
|
required this.onSave,
|
|
required this.onConfirmDiscard,
|
|
this.descriptionPadding = const EdgeInsets.fromLTRB(16, 4, 16, 8),
|
|
});
|
|
|
|
Future<void> _handleBack(BuildContext context) async {
|
|
if (!hasChanges) {
|
|
Navigator.pop(context);
|
|
return;
|
|
}
|
|
final shouldPop = await onConfirmDiscard(context);
|
|
if (shouldPop && context.mounted) {
|
|
Navigator.pop(context);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
final wideInset = wideListInset(context);
|
|
|
|
return PopScope(
|
|
canPop: !hasChanges,
|
|
onPopInvokedWithResult: (didPop, result) async {
|
|
if (didPop) return;
|
|
final shouldPop = await onConfirmDiscard(context);
|
|
if (shouldPop && context.mounted) {
|
|
Navigator.pop(context);
|
|
}
|
|
},
|
|
child: Scaffold(
|
|
body: CustomScrollView(
|
|
slivers: [
|
|
AppSliverHeader.page(
|
|
title: title,
|
|
leading: AppIconButton(
|
|
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
|
|
icon: const Icon(Icons.arrow_back),
|
|
onPressed: () => _handleBack(context),
|
|
),
|
|
actions: [
|
|
if (hasChanges)
|
|
ExpressiveButton(
|
|
text: true,
|
|
onPressed: onSave,
|
|
child: Text(context.l10n.dialogSave),
|
|
),
|
|
],
|
|
),
|
|
SliverToBoxAdapter(
|
|
child: Padding(
|
|
padding: descriptionPadding.add(
|
|
EdgeInsets.symmetric(horizontal: wideInset),
|
|
),
|
|
child: Text(
|
|
description,
|
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
color: colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
...slivers,
|
|
SliverToBoxAdapter(
|
|
child: Padding(
|
|
padding: EdgeInsets.fromLTRB(
|
|
16 + wideInset,
|
|
16,
|
|
16 + wideInset,
|
|
16,
|
|
),
|
|
child: Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.tertiaryContainer.withValues(alpha: 0.3),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
Icons.info_outline,
|
|
size: 20,
|
|
color: colorScheme.tertiary,
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Text(
|
|
infoText,
|
|
style: Theme.of(context).textTheme.bodySmall
|
|
?.copyWith(
|
|
color: colorScheme.onTertiaryContainer,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SliverToBoxAdapter(child: SizedBox(height: 32)),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|