This commit is contained in:
rang 2026-01-08 17:49:30 +08:00
parent edd5a01482
commit eef72c6aa6
13 changed files with 1341 additions and 1012 deletions

View file

@ -1,6 +1,5 @@
import 'dart:math';
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
@ -8,9 +7,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vaani/api/library_item_provider.dart';
import 'package:vaani/features/item_viewer/view/library_item_sliver_app_bar.dart';
import 'package:vaani/features/player/view/mini_player_bottom_padding.dart';
import 'package:vaani/features/settings/app_settings_provider.dart';
import 'package:vaani/generated/l10n.dart';
import 'package:vaani/router/models/library_item_extras.dart';
import 'package:vaani/shared/widgets/expandable_description.dart';
import 'package:vaani/theme/providers/system_theme_provider.dart';
import 'library_item_actions.dart';
import 'library_item_hero_section.dart';
@ -32,6 +33,23 @@ class LibraryItemPage extends HookConsumerWidget {
extra is LibraryItemExtras ? extra as LibraryItemExtras : null;
final scrollController = useScrollController();
final showFab = useState(false);
final themeSettings =
ref.watch(appSettingsProvider.select((v) => v.themeSettings));
var currentTheme = Theme.of(context);
if (themeSettings.useMaterialThemeOnItemPage) {
final theme = ref.watch(
CurrentThemeProvider(
highContrast: MediaQuery.of(context).highContrast,
id: itemId,
),
);
if (currentTheme.brightness == Brightness.dark) {
currentTheme = theme.$2;
} else {
currentTheme = theme.$1;
}
}
// Effect to listen to scroll changes and update FAB visibility
useEffect(
@ -72,65 +90,62 @@ class LibraryItemPage extends HookConsumerWidget {
}
}
return ThemeProvider(
initTheme: Theme.of(context),
duration: 200.ms,
child: ThemeSwitchingArea(
child: Scaffold(
floatingActionButton: AnimatedSwitcher(
duration: 250.ms,
// A common transition for FABs (fade + scale)
transitionBuilder: (Widget child, Animation<double> animation) {
return ScaleTransition(
scale: animation,
child: FadeTransition(
opacity: animation,
child: child,
return Theme(
data: currentTheme,
child: Scaffold(
floatingActionButton: AnimatedSwitcher(
duration: 250.ms,
// A common transition for FABs (fade + scale)
transitionBuilder: (Widget child, Animation<double> animation) {
return ScaleTransition(
scale: animation,
child: FadeTransition(
opacity: animation,
child: child,
),
);
},
child: showFab.value
? FloatingActionButton(
// Key is important for AnimatedSwitcher to differentiate
key: const ValueKey('fab-scroll-top'),
onPressed: scrollToTop,
tooltip: 'Scroll to top',
child: const Icon(Icons.arrow_upward),
)
: const SizedBox.shrink(
key: ValueKey('fab-empty'),
),
);
},
child: showFab.value
? FloatingActionButton(
// Key is important for AnimatedSwitcher to differentiate
key: const ValueKey('fab-scroll-top'),
onPressed: scrollToTop,
tooltip: 'Scroll to top',
child: const Icon(Icons.arrow_upward),
)
: const SizedBox.shrink(
key: ValueKey('fab-empty'),
),
),
body: CustomScrollView(
controller: scrollController,
slivers: [
LibraryItemSliverAppBar(
id: itemId,
scrollController: scrollController,
),
body: CustomScrollView(
controller: scrollController,
slivers: [
LibraryItemSliverAppBar(
id: itemId,
scrollController: scrollController,
),
SliverPadding(
padding: const EdgeInsets.all(8),
sliver: LibraryItemHeroSection(
itemId: itemId,
extraMap: additionalItemData,
),
SliverPadding(
padding: const EdgeInsets.all(8),
sliver: LibraryItemHeroSection(
itemId: itemId,
extraMap: additionalItemData,
),
),
// a horizontal display with dividers of metadata
SliverToBoxAdapter(
child: LibraryItemMetadata(id: itemId),
),
// a row of actions like play, download, share, etc
SliverToBoxAdapter(
child: LibraryItemActions(id: itemId),
),
// a expandable section for book description
SliverToBoxAdapter(
child: LibraryItemDescription(id: itemId),
),
// a padding at the bottom to make sure the last item is not hidden by mini player
const SliverToBoxAdapter(child: MiniPlayerBottomPadding()),
],
),
),
// a horizontal display with dividers of metadata
SliverToBoxAdapter(
child: LibraryItemMetadata(id: itemId),
),
// a row of actions like play, download, share, etc
SliverToBoxAdapter(
child: LibraryItemActions(id: itemId),
),
// a expandable section for book description
SliverToBoxAdapter(
child: LibraryItemDescription(id: itemId),
),
// a padding at the bottom to make sure the last item is not hidden by mini player
const SliverToBoxAdapter(child: MiniPlayerBottomPadding()),
],
),
),
);

View file

@ -2,6 +2,7 @@ import 'dart:math';
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shelfsdk/audiobookshelf_api.dart';
import 'package:vaani/constants/sizes.dart';
@ -29,6 +30,7 @@ class PlayerExpandedDesktop extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// final textTheme = Theme.of(context).textTheme;
final book = ref.watch(currentBookProvider);
if (book == null) {
return SizedBox.shrink();
@ -116,7 +118,16 @@ class PlayerExpandedDesktop extends HookConsumerWidget {
timeLabelLocation: TimeLabelLocation.sides,
),
),
Container(child: _buildBottom()),
MouseRegion(
cursor: SystemMouseCursors.click, //
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
context.pop();
},
child: _buildBottom(),
),
),
],
),
],
@ -125,11 +136,15 @@ class PlayerExpandedDesktop extends HookConsumerWidget {
Widget _buildBottom() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
// mainAxisAlignment: MainAxisAlignment.center,
children: [
Spacer(),
Expanded(
flex: 1,
child: Row(),
),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const AudiobookPlayerSeekChapterButton(isForward: false),
const AudiobookPlayerSeekButton(isForward: false),
@ -141,6 +156,7 @@ class PlayerExpandedDesktop extends HookConsumerWidget {
),
),
Expanded(
flex: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [

View file

@ -6,12 +6,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_settings_ui/flutter_settings_ui.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vaani/generated/l10n.dart';
import 'package:vaani/features/settings/app_settings_provider.dart';
import 'package:vaani/features/settings/view/buttons.dart';
import 'package:vaani/features/settings/view/simple_settings_page.dart';
import 'package:vaani/generated/l10n.dart';
import 'package:vaani/globals.dart';
import 'package:vaani/shared/extensions/duration_format.dart';
class DownloadSettingsPage extends HookConsumerWidget {
const DownloadSettingsPage({
@ -22,7 +21,7 @@ class DownloadSettingsPage extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final appSettings = ref.watch(appSettingsProvider);
final downloadSettings = appSettings.downloadSettings;
final primaryColor = Theme.of(context).colorScheme.primary;
// final primaryColor = Theme.of(context).colorScheme.primary;
return SimpleSettingsPage(
title: Text(S.of(context).playerSettings),

View file

@ -91,7 +91,7 @@ class PlayerSettingsPage extends HookConsumerWidget {
if (newSpeedOptions != null) {
ref.read(appSettingsProvider.notifier).update(
appSettings.copyWith.playerSettings(
speedOptions: newSpeedOptions..sort(),
speedOptions: [...newSpeedOptions]..sort(),
),
);
}

View file

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_settings_ui/flutter_settings_ui.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vaani/features/player/view/mini_player_bottom_padding.dart';
class SimpleSettingsPage extends HookConsumerWidget {
const SimpleSettingsPage({
@ -15,30 +14,43 @@ class SimpleSettingsPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final colorScheme = Theme.of(context).colorScheme;
return Scaffold(
// appBar: AppBar(
// title: title,
// ),
appBar: AppBar(
title: title,
),
// body: body,
// an app bar which is bigger than the default app bar but on scroll shrinks to the default app bar with the title being animated
body: CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 200.0,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: title,
// background: Theme.of(context).primaryColor,
),
),
// SliverAppBar(
// expandedHeight: 100.0,
// floating: false,
// pinned: true,
// flexibleSpace: FlexibleSpaceBar(
// title: title,
// // background: Theme.of(context).primaryColor,
// ),
// ),
if (sections != null)
SliverList(
delegate: SliverChildListDelegate(
[
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(20)),
// borderRadius: const BorderRadius.all(Radius.circular(20)),
child: SettingsList(
lightTheme: SettingsThemeData(
settingsListBackground: colorScheme.surface,
settingsSectionBackground: colorScheme.surfaceContainer,
// inactiveTitleColor:
// trailingTextColor: colorScheme.primary,
// settingsTileTextColor:
),
darkTheme: SettingsThemeData(
settingsListBackground: colorScheme.surface,
settingsSectionBackground: colorScheme.surfaceContainer,
),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
sections: sections!,
@ -48,7 +60,7 @@ class SimpleSettingsPage extends HookConsumerWidget {
),
),
// some padding at the bottom
const SliverPadding(padding: EdgeInsets.only(bottom: 20)),
// const SliverPadding(padding: EdgeInsets.only(bottom: 20)),
// SliverToBoxAdapter(child: MiniPlayerBottomPadding()),
],
),