feat: UI 2.0 and other Improvements (#357)

Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
PartyDonut 2025-06-01 10:37:19 +02:00 committed by GitHub
parent 9ca06eaa37
commit e7b5bb40ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
169 changed files with 4584 additions and 3626 deletions

View file

@ -7,7 +7,7 @@ import 'package:iconsax_plus/iconsax_plus.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/screens/shared/flat_button.dart';
import 'package:fladder/screens/shared/media/banner_play_button.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/fladder_image.dart';
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
import 'package:fladder/util/list_padding.dart';
@ -51,16 +51,15 @@ class _CarouselBannerState extends ConsumerState<CarouselBanner> {
final itemExtent = widget.items.length == 1 ? MediaQuery.sizeOf(context).width : maxExtent;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4)
.copyWith(top: AdaptiveLayout.of(context).isDesktop ? 6 : 10),
padding: EdgeInsets.only(top: AdaptiveLayout.of(context).isDesktop ? 6 : 10),
child: Stack(
children: [
CarouselView(
elevation: 3,
shrinkExtent: 0,
controller: carouselController,
shape: RoundedRectangleBorder(borderRadius: border),
padding: const EdgeInsets.symmetric(horizontal: 6),
shape: RoundedRectangleBorder(borderRadius: border),
enableSplash: false,
itemExtent: itemExtent,
children: [
@ -146,8 +145,8 @@ class _CarouselBannerState extends ConsumerState<CarouselBanner> {
? null
: (details) async {
Offset localPosition = details.globalPosition;
RelativeRect position = RelativeRect.fromLTRB(localPosition.dx - 320,
localPosition.dy, localPosition.dx, localPosition.dy);
RelativeRect position = RelativeRect.fromLTRB(
localPosition.dx, localPosition.dy, localPosition.dx, localPosition.dy);
final poster = widget.items[index];
await showMenu(

View file

@ -5,7 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fladder/models/items/chapters_model.dart';
import 'package:fladder/screens/shared/flat_button.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/disable_keypad_focus.dart';
import 'package:fladder/util/humanize_duration.dart';
import 'package:fladder/util/localization_helper.dart';
@ -67,8 +67,8 @@ class ChapterRow extends ConsumerWidget {
FlatButton(
onSecondaryTapDown: (details) async {
Offset localPosition = details.globalPosition;
RelativeRect position = RelativeRect.fromLTRB(
localPosition.dx - 80, localPosition.dy, localPosition.dx, localPosition.dy);
RelativeRect position =
RelativeRect.fromLTRB(localPosition.dx, localPosition.dy, localPosition.dx, localPosition.dy);
await showMenu(
context: context,
position: position,

View file

@ -7,7 +7,7 @@ import 'package:fladder/models/items/episode_model.dart';
import 'package:fladder/providers/sync_provider.dart';
import 'package:fladder/screens/details_screens/components/media_stream_information.dart';
import 'package:fladder/screens/shared/media/episode_posters.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/localization_helper.dart';
import 'package:fladder/util/sticky_header_text.dart';
import 'package:fladder/util/string_extensions.dart';

View file

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:iconsax_plus/iconsax_plus.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:iconsax_plus/iconsax_plus.dart';
import 'package:fladder/models/book_model.dart';
import 'package:fladder/models/item_base_model.dart';
@ -11,7 +11,7 @@ import 'package:fladder/models/items/series_model.dart';
import 'package:fladder/screens/shared/flat_button.dart';
import 'package:fladder/screens/shared/media/components/poster_placeholder.dart';
import 'package:fladder/theme.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/disable_keypad_focus.dart';
import 'package:fladder/util/fladder_image.dart';
import 'package:fladder/util/humanize_duration.dart';
@ -137,7 +137,7 @@ class _PosterImageState extends ConsumerState<PosterImage> {
border: Border.all(width: 3, color: Theme.of(context).colorScheme.primary),
borderRadius: FladderTheme.defaultShape.borderRadius,
),
clipBehavior: Clip.antiAlias,
clipBehavior: Clip.hardEdge,
child: Stack(
alignment: Alignment.topCenter,
children: [
@ -225,7 +225,7 @@ class _PosterImageState extends ConsumerState<PosterImage> {
onSecondaryTapDown: (details) async {
Offset localPosition = details.globalPosition;
RelativeRect position = RelativeRect.fromLTRB(
localPosition.dx - 320, localPosition.dy, localPosition.dx, localPosition.dy);
localPosition.dx, localPosition.dy, localPosition.dx, localPosition.dy);
await showMenu(
context: context,
position: position,

View file

@ -2,7 +2,7 @@ import 'package:iconsax_plus/iconsax_plus.dart';
import 'package:fladder/providers/settings/client_settings_provider.dart';
import 'package:fladder/providers/sync_provider.dart';
import 'package:fladder/screens/shared/media/episode_posters.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
import 'package:fladder/util/list_padding.dart';
import 'package:fladder/util/localization_helper.dart';

View file

@ -9,7 +9,7 @@ import 'package:fladder/providers/sync/sync_provider_helpers.dart';
import 'package:fladder/providers/sync_provider.dart';
import 'package:fladder/screens/shared/flat_button.dart';
import 'package:fladder/screens/syncing/sync_button.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/disable_keypad_focus.dart';
import 'package:fladder/util/fladder_image.dart';
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
@ -241,12 +241,13 @@ class EpisodePoster extends ConsumerWidget {
LayoutBuilder(
builder: (context, constraints) {
return FlatButton(
onSecondaryTapDown: (details) {
onSecondaryTapDown: (details) async {
Offset localPosition = details.globalPosition;
RelativeRect position = RelativeRect.fromLTRB(
localPosition.dx - 260, localPosition.dy, localPosition.dx, localPosition.dy);
localPosition.dx, localPosition.dy, localPosition.dx, localPosition.dy);
showMenu(context: context, position: position, items: actions.popupMenuItems(useIcons: true));
await showMenu(
context: context, position: position, items: actions.popupMenuItems(useIcons: true));
},
onTap: onTap,
onLongPress: onLongPress,

View file

@ -1,9 +1,11 @@
import 'package:iconsax_plus/iconsax_plus.dart';
import 'package:fladder/util/localization_helper.dart';
import 'package:fladder/util/sticky_header_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:iconsax_plus/iconsax_plus.dart';
import 'package:fladder/util/localization_helper.dart';
import 'package:fladder/util/sticky_header_text.dart';
class ExpandingOverview extends ConsumerStatefulWidget {
final String text;
@ -68,11 +70,11 @@ class _ExpandingOverviewState extends ConsumerState<ExpandingOverview> {
child: expanded
? IconButton.filledTonal(
onPressed: toggleState,
icon: const Icon(IconsaxPlusLinear.arrow_up_2),
icon: const Icon(IconsaxPlusLinear.arrow_up_1),
)
: IconButton.filledTonal(
onPressed: toggleState,
icon: const Icon(IconsaxPlusLinear.arrow_down_1),
icon: const Icon(IconsaxPlusLinear.arrow_down),
),
),
),

View file

@ -6,7 +6,7 @@ import 'package:url_launcher/url_launcher.dart' as urilauncher;
import 'package:url_launcher/url_launcher_string.dart';
import 'package:fladder/models/items/item_shared_models.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/localization_helper.dart';
import 'package:fladder/util/sticky_header_text.dart';

View file

@ -25,7 +25,7 @@ class _ItemDetailListWidgetState extends ConsumerState<ItemDetailListWidget> {
return Card(
elevation: widget.elevation,
margin: EdgeInsets.zero,
clipBehavior: Clip.antiAlias,
clipBehavior: Clip.hardEdge,
child: Stack(
children: [
FlatButton(

View file

@ -1,13 +1,13 @@
import 'package:flutter/material.dart';
import 'package:async/async.dart';
import 'package:iconsax_plus/iconsax_plus.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:iconsax_plus/iconsax_plus.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/screens/shared/flat_button.dart';
import 'package:fladder/screens/shared/media/banner_play_button.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/fladder_image.dart';
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
import 'package:fladder/util/list_padding.dart';
@ -87,7 +87,7 @@ class _MediaBannerState extends ConsumerState<MediaBanner> {
final double dragOpacity = (1 - dragOffset.abs()).clamp(0, 1);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
padding: const EdgeInsets.symmetric(vertical: 6),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [

View file

@ -7,7 +7,7 @@ import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
import 'package:fladder/models/items/item_shared_models.dart';
import 'package:fladder/screens/details_screens/person_detail_screen.dart';
import 'package:fladder/screens/shared/flat_button.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/fladder_image.dart';
import 'package:fladder/util/localization_helper.dart';
import 'package:fladder/util/string_extensions.dart';
@ -22,12 +22,14 @@ class PeopleRow extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
Widget placeHolder(String name) {
return Card(
child: FractionallySizedBox(
widthFactor: 0.4,
return Center(
child: SizedBox(
height: 75,
width: 75,
child: Card(
elevation: 5,
shape: const CircleBorder(),
shadowColor: Colors.transparent,
color: Theme.of(context).colorScheme.primaryContainer.withValues(alpha: 0.50),
child: Center(
child: Text(
name.getInitials(),

View file

@ -1,11 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sticky_headers/sticky_headers.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/providers/settings/client_settings_provider.dart';
import 'package:fladder/screens/shared/media/poster_widget.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/sticky_header_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sticky_headers/sticky_headers.dart';
class PosterGrid extends ConsumerWidget {
final String? name;

View file

@ -1,10 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:iconsax_plus/iconsax_plus.dart';
import 'package:fladder/models/book_model.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/item_shared_models.dart';
import 'package:fladder/providers/settings/client_settings_provider.dart';
import 'package:fladder/screens/shared/flat_button.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/fladder_image.dart';
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
import 'package:fladder/util/list_padding.dart';
@ -12,8 +15,6 @@ import 'package:fladder/util/localization_helper.dart';
import 'package:fladder/widgets/shared/clickable_text.dart';
import 'package:fladder/widgets/shared/item_actions.dart';
import 'package:fladder/widgets/shared/modal_bottom_sheet.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class PosterListItem extends ConsumerWidget {
final ItemBaseModel poster;
@ -64,12 +65,12 @@ class PosterListItem extends ConsumerWidget {
color: Theme.of(context).colorScheme.primary.withValues(alpha: selected == true ? 0.25 : 0),
borderRadius: BorderRadius.circular(6),
),
child: FlatButton(
child: InkWell(
onTap: () => pressedWidget(context),
onSecondaryTapDown: (details) async {
Offset localPosition = details.globalPosition;
RelativeRect position =
RelativeRect.fromLTRB(localPosition.dx - 320, localPosition.dy, localPosition.dx, localPosition.dy);
RelativeRect.fromLTRB(localPosition.dx, localPosition.dy, localPosition.dx, localPosition.dy);
await showMenu(
context: context,
position: position,

View file

@ -1,18 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/screens/shared/media/poster_widget.dart';
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
import 'package:fladder/widgets/shared/horizontal_list.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class PosterRow extends ConsumerStatefulWidget {
final List<ItemBaseModel> posters;
final String label;
final double? collectionAspectRatio;
final Function()? onLabelClick;
final EdgeInsets contentPadding;
const PosterRow({
required this.posters,
this.contentPadding = const EdgeInsets.symmetric(horizontal: 16),
required this.label,
this.collectionAspectRatio,
this.onLabelClick,
super.key,
});
@ -32,6 +37,7 @@ class _PosterRowState extends ConsumerState<PosterRow> {
@override
Widget build(BuildContext context) {
final dominantRatio = widget.collectionAspectRatio ?? widget.posters.getMostCommonType.aspectRatio;
return HorizontalList(
contentPadding: widget.contentPadding,
label: widget.label,
@ -41,6 +47,7 @@ class _PosterRowState extends ConsumerState<PosterRow> {
final poster = widget.posters[index];
return PosterWidget(
poster: poster,
aspectRatio: dominantRatio,
key: Key(poster.id),
);
},

View file

@ -4,9 +4,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/item_shared_models.dart';
import 'package:fladder/models/settings/home_settings_model.dart';
import 'package:fladder/screens/shared/media/components/poster_image.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
import 'package:fladder/util/item_base_model/play_item_helpers.dart';
import 'package:fladder/widgets/shared/clickable_text.dart';

View file

@ -4,7 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fladder/models/items/season_model.dart';
import 'package:fladder/screens/shared/flat_button.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
import 'package:fladder/util/disable_keypad_focus.dart';
import 'package:fladder/util/fladder_image.dart';
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
@ -124,11 +124,11 @@ class SeasonPoster extends ConsumerWidget {
LayoutBuilder(
builder: (context, constraints) {
return FlatButton(
onSecondaryTapDown: (details) {
onSecondaryTapDown: (details) async {
Offset localPosition = details.globalPosition;
RelativeRect position = RelativeRect.fromLTRB(
localPosition.dx - 260, localPosition.dy, localPosition.dx, localPosition.dy);
showMenu(
localPosition.dx, localPosition.dy, localPosition.dx, localPosition.dy);
await showMenu(
context: context,
position: position,
items: season.generateActions(context, ref).popupMenuItems(useIcons: true));