[Bugfix] Wrap body in SafeArea for improved layout (#9)

Wrapped the `body` widget in a `SafeArea` widget to prevent content from
being obscured by notches, status bars, or other screen intrusions. #6

---------

Co-authored-by: PartyDonut <42371342+PartyDonut@users.noreply.github.com>
This commit is contained in:
Rafael 2024-10-14 17:08:29 +02:00 committed by GitHub
parent dddf72241d
commit 4b846f4a51
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -55,200 +55,202 @@ class _SyncItemDetailsState extends ConsumerState<SyncItemDetails> {
return SyncMarkedForDelete( return SyncMarkedForDelete(
syncedItem: syncedItem, syncedItem: syncedItem,
child: Padding( child: SafeArea(
padding: const EdgeInsets.all(16.0), child: Padding(
child: Column( padding: const EdgeInsets.all(16.0),
mainAxisSize: MainAxisSize.min, child: Column(
crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min,
children: [ crossAxisAlignment: CrossAxisAlignment.center,
Row( children: [
mainAxisAlignment: MainAxisAlignment.spaceBetween, Row(
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
Card( children: [
elevation: 1, Card(
child: Padding( elevation: 1,
padding: const EdgeInsets.all(8.0), child: Padding(
child: Text(baseItem?.type.label(context) ?? ""), padding: const EdgeInsets.all(8.0),
)), child: Text(baseItem?.type.label(context) ?? ""),
Text( )),
context.localized.navigationSync, Text(
style: Theme.of(context).textTheme.titleMedium, context.localized.navigationSync,
), style: Theme.of(context).textTheme.titleMedium,
IconButton( ),
onPressed: () => Navigator.pop(context), IconButton(
icon: const Icon(IconsaxBold.close_circle), onPressed: () => Navigator.pop(context),
) icon: const Icon(IconsaxBold.close_circle),
], )
), ],
if (baseItem != null) ...{ ),
const Divider(), if (baseItem != null) ...{
Padding( const Divider(),
padding: const EdgeInsets.symmetric(vertical: 8), Padding(
child: Row( padding: const EdgeInsets.symmetric(vertical: 8),
mainAxisSize: MainAxisSize.min, child: Row(
crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min,
children: [ crossAxisAlignment: CrossAxisAlignment.center,
SizedBox( children: [
height: (AdaptiveLayout.poster(context).size * SizedBox(
ref.watch(clientSettingsProvider.select((value) => value.posterSize))) * height: (AdaptiveLayout.poster(context).size *
0.6, ref.watch(clientSettingsProvider.select((value) => value.posterSize))) *
child: IgnorePointer( 0.6,
child: PosterWidget( child: IgnorePointer(
aspectRatio: 0.7, child: PosterWidget(
poster: baseItem, aspectRatio: 0.7,
inlineTitle: true, poster: baseItem,
inlineTitle: true,
),
), ),
), ),
), Expanded(
Expanded( child: SyncProgressBuilder(
child: SyncProgressBuilder( item: syncedItem,
item: syncedItem, builder: (context, combinedStream) {
builder: (context, combinedStream) { return Row(
return Row( children: [
children: [ Expanded(
Expanded( child: Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ Text(
Text( baseItem.detailedName(context) ?? "",
baseItem.detailedName(context) ?? "", style: Theme.of(context).textTheme.titleMedium,
style: Theme.of(context).textTheme.titleMedium, ),
), SyncSubtitle(syncItem: syncedItem),
SyncSubtitle(syncItem: syncedItem), SyncLabel(
SyncLabel( label: context.localized
label: context.localized .totalSize(ref.watch(syncSizeProvider(syncedItem)).byteFormat ?? '--'),
.totalSize(ref.watch(syncSizeProvider(syncedItem)).byteFormat ?? '--'), status: ref.watch(syncStatusesProvider(syncedItem)).value ?? SyncStatus.partially,
status: ref.watch(syncStatusesProvider(syncedItem)).value ?? SyncStatus.partially, ),
), ].addInBetween(const SizedBox(height: 8)),
].addInBetween(const SizedBox(height: 8)), ),
), ),
), if (combinedStream?.task != null) ...{
if (combinedStream?.task != null) ...{ if (combinedStream?.status != TaskStatus.paused)
if (combinedStream?.status != TaskStatus.paused) IconButton(
IconButton( onPressed: () =>
onPressed: () => ref.read(backgroundDownloaderProvider).pause(combinedStream!.task!),
ref.read(backgroundDownloaderProvider).pause(combinedStream!.task!), icon: const Icon(IconsaxBold.pause),
icon: const Icon(IconsaxBold.pause), ),
), if (combinedStream?.status == TaskStatus.paused) ...[
if (combinedStream?.status == TaskStatus.paused) ...[ IconButton(
IconButton( onPressed: () =>
onPressed: () => ref.read(backgroundDownloaderProvider).resume(combinedStream!.task!),
ref.read(backgroundDownloaderProvider).resume(combinedStream!.task!), icon: const Icon(IconsaxBold.play),
icon: const Icon(IconsaxBold.play), ),
), IconButton(
IconButton( onPressed: () => ref.read(syncProvider.notifier).deleteFullSyncFiles(syncedItem),
onPressed: () => ref.read(syncProvider.notifier).deleteFullSyncFiles(syncedItem), icon: const Icon(IconsaxBold.stop),
icon: const Icon(IconsaxBold.stop), ),
), ],
], const SizedBox(width: 16)
const SizedBox(width: 16) },
}, if (combinedStream != null && combinedStream.hasDownload)
if (combinedStream != null && combinedStream.hasDownload) SizedBox.fromSize(
SizedBox.fromSize( size: const Size.fromRadius(35),
size: const Size.fromRadius(35), child: Stack(
child: Stack( fit: StackFit.expand,
fit: StackFit.expand, alignment: Alignment.center,
alignment: Alignment.center, children: [
children: [ CircularProgressIndicator(
CircularProgressIndicator( value: combinedStream.progress,
value: combinedStream.progress, strokeWidth: 8,
strokeWidth: 8, backgroundColor: Theme.of(context).colorScheme.surface.withOpacity(0.5),
backgroundColor: Theme.of(context).colorScheme.surface.withOpacity(0.5), strokeCap: StrokeCap.round,
strokeCap: StrokeCap.round, color: combinedStream.status.color(context),
color: combinedStream.status.color(context), ),
), Center(child: Text("${((combinedStream.progress) * 100).toStringAsFixed(0)}%"))
Center(child: Text("${((combinedStream.progress) * 100).toStringAsFixed(0)}%")) ],
], )),
)), ],
], );
); },
}, ),
), ),
), if (!hasFile && !downloadTask.hasDownload && syncedItem.hasVideoFile)
if (!hasFile && !downloadTask.hasDownload && syncedItem.hasVideoFile) IconButtonAwait(
IconButtonAwait( onPressed: () async => await ref.read(syncProvider.notifier).syncVideoFile(syncedItem, false),
onPressed: () async => await ref.read(syncProvider.notifier).syncVideoFile(syncedItem, false), icon: const Icon(IconsaxOutline.cloud_change),
icon: const Icon(IconsaxOutline.cloud_change), )
) else if (hasFile)
else if (hasFile) IconButtonAwait(
IconButtonAwait( color: Theme.of(context).colorScheme.error,
color: Theme.of(context).colorScheme.error, onPressed: () {
showDefaultAlertDialog(
context,
context.localized.syncRemoveDataTitle,
context.localized.syncRemoveDataDesc,
(context) {
ref.read(syncProvider.notifier).deleteFullSyncFiles(syncedItem);
Navigator.of(context).pop();
},
context.localized.delete,
(context) => Navigator.of(context).pop(),
context.localized.cancel,
);
},
icon: const Icon(IconsaxOutline.trash),
),
].addInBetween(const SizedBox(width: 16)),
),
),
},
const Divider(),
if (syncChildren.isNotEmpty == true)
Flexible(
child: ListView(
shrinkWrap: true,
children: [
...syncChildren.map(
(e) => ChildSyncWidget(syncedChild: e),
),
],
),
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (baseItem is! EpisodeModel)
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.errorContainer,
foregroundColor: Theme.of(context).colorScheme.onErrorContainer,
),
onPressed: () { onPressed: () {
showDefaultAlertDialog( showDefaultAlertDialog(
context, context,
context.localized.syncRemoveDataTitle, context.localized.syncDeleteItemTitle,
context.localized.syncRemoveDataDesc, context.localized.syncDeleteItemDesc(baseItem?.detailedName(context) ?? ""),
(context) { (context) async {
ref.read(syncProvider.notifier).deleteFullSyncFiles(syncedItem); await ref.read(syncProvider.notifier).removeSync(syncedItem);
Navigator.of(context).pop(); Navigator.pop(context);
Navigator.pop(context);
}, },
context.localized.delete, context.localized.delete,
(context) => Navigator.of(context).pop(), (context) => Navigator.pop(context),
context.localized.cancel, context.localized.cancel,
); );
}, },
icon: const Icon(IconsaxOutline.trash), child: Text(context.localized.delete),
), )
].addInBetween(const SizedBox(width: 16)), else if (syncedItem.parentId != null)
), ElevatedButton(
), onPressed: () {
}, final parentItem = ref.read(syncProvider.notifier).getParentItem(syncedItem.parentId!);
const Divider(), setState(() {
if (syncChildren.isNotEmpty == true) if (parentItem != null) {
Flexible( syncedItem = parentItem;
child: ListView( }
shrinkWrap: true, });
children: [ },
...syncChildren.map( child: Text(context.localized.syncOpenParent),
(e) => ChildSyncWidget(syncedChild: e), )
),
], ],
), ),
), )
Padding( ],
padding: const EdgeInsets.only(top: 16), ),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (baseItem is! EpisodeModel)
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.errorContainer,
foregroundColor: Theme.of(context).colorScheme.onErrorContainer,
),
onPressed: () {
showDefaultAlertDialog(
context,
context.localized.syncDeleteItemTitle,
context.localized.syncDeleteItemDesc(baseItem?.detailedName(context) ?? ""),
(context) async {
await ref.read(syncProvider.notifier).removeSync(syncedItem);
Navigator.pop(context);
Navigator.pop(context);
},
context.localized.delete,
(context) => Navigator.pop(context),
context.localized.cancel,
);
},
child: Text(context.localized.delete),
)
else if (syncedItem.parentId != null)
ElevatedButton(
onPressed: () {
final parentItem = ref.read(syncProvider.notifier).getParentItem(syncedItem.parentId!);
setState(() {
if (parentItem != null) {
syncedItem = parentItem;
}
});
},
child: Text(context.localized.syncOpenParent),
)
],
),
)
],
), ),
), ),
); );