redesign app phase 3
This commit is contained in:
@@ -7,6 +7,7 @@ import 'package:flutter_downloader/flutter_downloader.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
import '../../models/finamp_models.dart';
|
||||
import '../../services/download_support.dart';
|
||||
import '../error_snackbar.dart';
|
||||
import '../album_image.dart';
|
||||
|
||||
@@ -25,6 +26,10 @@ class _CurrentDownloadsListState extends State<CurrentDownloadsList> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (!isDownloaderSupported) {
|
||||
return;
|
||||
}
|
||||
|
||||
IsolateNameServer.registerPortWithName(
|
||||
_port.sendPort, 'downloader_send_port');
|
||||
_port.listen((dynamic data) {
|
||||
@@ -36,7 +41,9 @@ class _CurrentDownloadsListState extends State<CurrentDownloadsList> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
IsolateNameServer.removePortNameMapping('downloader_send_port');
|
||||
if (isDownloaderSupported) {
|
||||
IsolateNameServer.removePortNameMapping('downloader_send_port');
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:finamp/l10n/app_localizations.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
import '../../services/download_update_stream.dart';
|
||||
import '../../services/download_support.dart';
|
||||
import '../error_snackbar.dart';
|
||||
|
||||
const double downloadsOverviewCardLoadingHeight = 120;
|
||||
@@ -39,7 +40,9 @@ class _DownloadsOverviewState extends State<DownloadsOverview> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_downloadsOverviewFuture = FlutterDownloader.loadTasks();
|
||||
_downloadsOverviewFuture = isDownloaderSupported
|
||||
? FlutterDownloader.loadTasks()
|
||||
: Future.value([]);
|
||||
|
||||
// Like in DownloadedIndicator, we use our own listener instead of a
|
||||
// StreamBuilder to ensure that we capture all events.
|
||||
|
||||
@@ -25,14 +25,34 @@ class AlbumItemCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
// In AlbumItem, the OpenContainer handles padding.
|
||||
margin: EdgeInsets.zero,
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(26),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: colorScheme.shadow.withOpacity(0.32),
|
||||
blurRadius: 22,
|
||||
offset: const Offset(0, 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: AlbumImage.borderRadius,
|
||||
borderRadius: BorderRadius.circular(26),
|
||||
child: Stack(
|
||||
children: [
|
||||
AlbumImage(item: item),
|
||||
Positioned.fill(child: AlbumImage(item: item)),
|
||||
Positioned.fill(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Colors.white.withOpacity(0.10),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(26),
|
||||
),
|
||||
),
|
||||
),
|
||||
addSettingsListener
|
||||
? // We need this ValueListenableBuilder to react to changes to
|
||||
// showTextOnGridView. When shown in a MusicScreen, this widget
|
||||
@@ -88,19 +108,21 @@ class _AlbumItemCardText extends StatelessWidget {
|
||||
return Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
colors: [
|
||||
// We fade from half transparent black to transparent so that text is visible on bright images
|
||||
Colors.black.withOpacity(0.5),
|
||||
Colors.black.withOpacity(0.86),
|
||||
Colors.black.withOpacity(0.24),
|
||||
Colors.transparent,
|
||||
],
|
||||
stops: const [0.0, 0.58, 1.0],
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
padding: const EdgeInsets.fromLTRB(14, 46, 14, 14),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomLeft,
|
||||
child: Column(
|
||||
@@ -110,19 +132,22 @@ class _AlbumItemCardText extends StatelessWidget {
|
||||
Text(
|
||||
item.name ?? "Unknown Name",
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 3,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleLarge!
|
||||
.copyWith(color: Colors.white),
|
||||
maxLines: 2,
|
||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w800,
|
||||
height: 1.05,
|
||||
),
|
||||
),
|
||||
if (subtitle != null)
|
||||
Text(
|
||||
subtitle,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(color: Colors.white.withOpacity(0.7)),
|
||||
.copyWith(color: Colors.white.withOpacity(0.72)),
|
||||
)
|
||||
],
|
||||
),
|
||||
@@ -131,4 +156,4 @@ class _AlbumItemCardText extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,24 +25,61 @@ class AlbumItemListTile extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final jellyfinApiHelper = GetIt.instance<JellyfinApiHelper>();
|
||||
final subtitle = generateSubtitle(item, parentType, context);
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return ListTile(
|
||||
// This widget is used on the add to playlist screen, so we allow a custom
|
||||
// onTap to be passed as an argument.
|
||||
onTap: onTap,
|
||||
leading: AlbumImage(item: item),
|
||||
title: Text(
|
||||
item.name ?? AppLocalizations.of(context)!.unknownName,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 5),
|
||||
child: Material(
|
||||
color: colorScheme.surfaceVariant.withOpacity(0.42),
|
||||
borderRadius: BorderRadius.circular(22),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(22),
|
||||
border: Border.all(
|
||||
color: colorScheme.outlineVariant.withOpacity(0.24),
|
||||
),
|
||||
),
|
||||
child: ListTile(
|
||||
// This widget is used on the add to playlist screen, so we allow a
|
||||
// custom onTap to be passed as an argument.
|
||||
onTap: onTap,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 8,
|
||||
),
|
||||
leading: SizedBox.square(
|
||||
dimension: 62,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
child: AlbumImage(item: item),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
item.name ?? AppLocalizations.of(context)!.unknownName,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w800,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
subtitle: subtitle == null
|
||||
? null
|
||||
: Text(
|
||||
subtitle,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
trailing: jellyfinApiHelper.selectedMixAlbumIds.contains(item.id)
|
||||
? Icon(Icons.explore_rounded, color: colorScheme.primary)
|
||||
: Icon(
|
||||
Icons.chevron_right_rounded,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
subtitle: subtitle == null ? null : Text(
|
||||
subtitle,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
trailing: jellyfinApiHelper.selectedMixAlbumIds.contains(item.id)
|
||||
? const Icon(Icons.explore)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,29 +3,35 @@ import 'package:flutter/material.dart';
|
||||
import 'package:finamp/l10n/app_localizations.dart';
|
||||
|
||||
/// Snackbar with error icon for displaying errors
|
||||
ScaffoldFeatureController<SnackBar, SnackBarClosedReason> errorSnackbar(
|
||||
dynamic error, BuildContext context) {
|
||||
return ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.anErrorHasOccured),
|
||||
action: SnackBarAction(
|
||||
label: MaterialLocalizations.of(context).moreButtonTooltip,
|
||||
onPressed: () => showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(AppLocalizations.of(context)!.error),
|
||||
content: Text(_errorText(error, context)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(MaterialLocalizations.of(context).closeButtonLabel),
|
||||
)
|
||||
],
|
||||
void errorSnackbar(dynamic error, BuildContext context) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.anErrorHasOccured),
|
||||
action: SnackBarAction(
|
||||
label: MaterialLocalizations.of(context).moreButtonTooltip,
|
||||
onPressed: () => showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(AppLocalizations.of(context)!.error),
|
||||
content: Text(_errorText(error, context)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child:
|
||||
Text(MaterialLocalizations.of(context).closeButtonLabel),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
String _errorText(dynamic error, BuildContext context) {
|
||||
|
||||
Reference in New Issue
Block a user