theme on item page is much smoother now

This commit is contained in:
Dr-Blank 2024-05-11 05:13:56 -04:00
parent 1609fe9d65
commit 0d54f1cb15
No known key found for this signature in database
GPG key ID: 7452CC63F210A266
3 changed files with 176 additions and 107 deletions

View file

@ -1,6 +1,8 @@
import 'dart:math'; import 'dart:math';
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shelfsdk/audiobookshelf_api.dart' as shelfsdk; import 'package:shelfsdk/audiobookshelf_api.dart' as shelfsdk;
@ -51,76 +53,122 @@ class LibraryItemPage extends HookConsumerWidget {
debugPrint('useMaterialThemeOnItemPage is false'); debugPrint('useMaterialThemeOnItemPage is false');
// AsyncValue<ColorScheme?> coverColorScheme = const AsyncValue.loading(); // AsyncValue<ColorScheme?> coverColorScheme = const AsyncValue.loading();
} }
return Theme( return ThemeProvider(
data: coverColorScheme.valueOrNull != null && useMaterialThemeOnItemPage initTheme: Theme.of(context),
? ThemeData.from( duration: 200.ms,
colorScheme: coverColorScheme.valueOrNull!,
textTheme: Theme.of(context).textTheme,
)
: Theme.of(context),
child: Scaffold(
body: CustomScrollView(
slivers: [
const LibraryItemSliverAppBar(),
SliverPadding(
padding: const EdgeInsets.all(8),
sliver: SliverToBoxAdapter(
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
LayoutBuilder(
builder: (context, constraints) {
return SizedBox(
height: calculateWidth(
context,
constraints,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: _BookCover(
itemId: itemId,
extraMap: extraMap,
providedCacheImage: providedCacheImage,
item: item,
),
),
);
},
),
const SizedBox.square(
dimension: 8,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_BookTitle(
extraMap: extraMap,
itemBookMetadata: itemBookMetadata,
bookDetailsCached: bookDetailsCached,
),
_BookAuthors(
itemBookMetadata: itemBookMetadata,
bookDetailsCached: bookDetailsCached,
coverColorScheme: coverColorScheme.valueOrNull,
),
// series info if available
// narrators info if available // data: coverColorScheme.valueOrNull != null && useMaterialThemeOnItemPage
], // ? ThemeData.from(
), // colorScheme: coverColorScheme.valueOrNull!,
// textTheme: Theme.of(context).textTheme,
// )
// : Theme.of(context),
child: ThemeSwitchingArea(
child: Builder(
builder: (context) {
return Scaffold(
body: CustomScrollView(
slivers: [
const LibraryItemSliverAppBar(),
SliverPadding(
padding: const EdgeInsets.all(8),
sliver: LibraryItemHeroSection(
itemId: itemId,
extraMap: extraMap,
providedCacheImage: providedCacheImage,
item: item,
itemBookMetadata: itemBookMetadata,
bookDetailsCached: bookDetailsCached,
coverColorScheme: coverColorScheme,
useMaterialThemeOnItemPage: useMaterialThemeOnItemPage,
), ),
], ),
), ],
), ),
), );
], },
), ),
), ),
); );
} }
} }
class LibraryItemHeroSection extends StatelessWidget {
const LibraryItemHeroSection({
super.key,
required this.itemId,
required this.extraMap,
required this.providedCacheImage,
required this.item,
required this.itemBookMetadata,
required this.bookDetailsCached,
required this.coverColorScheme,
required this.useMaterialThemeOnItemPage,
});
final bool useMaterialThemeOnItemPage;
final String itemId;
final LibraryItemExtras? extraMap;
final Image? providedCacheImage;
final AsyncValue<shelfsdk.LibraryItem> item;
final shelfsdk.BookMetadata? itemBookMetadata;
final shelfsdk.BookMinified? bookDetailsCached;
final AsyncValue<ColorScheme?> coverColorScheme;
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
LayoutBuilder(
builder: (context, constraints) {
return SizedBox(
height: calculateWidth(
context,
constraints,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: _BookCover(
itemId: itemId,
extraMap: extraMap,
providedCacheImage: providedCacheImage,
coverColorScheme: coverColorScheme.valueOrNull,
item: item,
),
),
);
},
),
const SizedBox.square(
dimension: 8,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_BookTitle(
extraMap: extraMap,
itemBookMetadata: itemBookMetadata,
bookDetailsCached: bookDetailsCached,
),
_BookAuthors(
itemBookMetadata: itemBookMetadata,
bookDetailsCached: bookDetailsCached,
),
// series info if available
// narrators info if available
],
),
),
],
),
);
}
}
class _BookCover extends HookConsumerWidget { class _BookCover extends HookConsumerWidget {
const _BookCover({ const _BookCover({
super.key, super.key,
@ -128,53 +176,72 @@ class _BookCover extends HookConsumerWidget {
required this.extraMap, required this.extraMap,
required this.providedCacheImage, required this.providedCacheImage,
required this.item, required this.item,
this.coverColorScheme,
}); });
final String itemId; final String itemId;
final LibraryItemExtras? extraMap; final LibraryItemExtras? extraMap;
final Image? providedCacheImage; final Image? providedCacheImage;
final AsyncValue<shelfsdk.LibraryItem> item; final AsyncValue<shelfsdk.LibraryItem> item;
final ColorScheme? coverColorScheme;
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
return Hero( return ThemeSwitcher(
tag: HeroTagPrefixes.bookCover + itemId + (extraMap?.heroTagSuffix ?? ''), builder: (context) {
child: providedCacheImage ?? // change theme after 2 seconds
item.when( Future.delayed(150.ms, () {
data: (libraryItem) { ThemeSwitcher.of(context).changeTheme(
final coverImage = ref.watch(coverImageProvider(libraryItem)); theme: coverColorScheme != null
return Stack( ? ThemeData.from(
children: [ colorScheme: coverColorScheme!,
coverImage.when( textTheme: Theme.of(context).textTheme,
data: (image) { )
// return const BookCoverSkeleton(); : Theme.of(context),
if (image.isEmpty) { );
return const Icon(Icons.error); });
} return Hero(
// cover 80% of parent height tag: HeroTagPrefixes.bookCover +
return Image.memory( itemId +
image, (extraMap?.heroTagSuffix ?? ''),
fit: BoxFit.cover, child: providedCacheImage ??
// cacheWidth: (height * item.when(
// MediaQuery.of(context).devicePixelRatio) data: (libraryItem) {
// .round(), final coverImage = ref.watch(coverImageProvider(libraryItem));
); return Stack(
}, children: [
loading: () { coverImage.when(
return const Center( data: (image) {
child: BookCoverSkeleton(), // return const BookCoverSkeleton();
); if (image.isEmpty) {
}, return const Icon(Icons.error);
error: (error, stack) { }
return const Icon(Icons.error); // cover 80% of parent height
}, return Image.memory(
), image,
], fit: BoxFit.cover,
); // cacheWidth: (height *
}, // MediaQuery.of(context).devicePixelRatio)
error: (error, stack) => const Icon(Icons.error), // .round(),
loading: () => const Center(child: BookCoverSkeleton()), );
), },
loading: () {
return const Center(
child: BookCoverSkeleton(),
);
},
error: (error, stack) {
return const Icon(Icons.error);
},
),
],
);
},
error: (error, stack) => const Icon(Icons.error),
loading: () => const Center(child: BookCoverSkeleton()),
),
);
},
); );
} }
} }
@ -211,20 +278,18 @@ class _BookTitle extends StatelessWidget {
} }
} }
class _BookAuthors extends StatelessWidget { class _BookAuthors extends HookConsumerWidget {
const _BookAuthors({ const _BookAuthors({
super.key, super.key,
required this.itemBookMetadata, required this.itemBookMetadata,
required this.bookDetailsCached, required this.bookDetailsCached,
this.coverColorScheme,
}); });
final shelfsdk.BookMetadata? itemBookMetadata; final shelfsdk.BookMetadata? itemBookMetadata;
final shelfsdk.BookMinified? bookDetailsCached; final shelfsdk.BookMinified? bookDetailsCached;
final ColorScheme? coverColorScheme;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context, WidgetRef ref) {
String generateAuthorsString() { String generateAuthorsString() {
final authors = (itemBookMetadata)?.authors ?? []; final authors = (itemBookMetadata)?.authors ?? [];
if (authors.isEmpty) { if (authors.isEmpty) {
@ -235,6 +300,9 @@ class _BookAuthors extends StatelessWidget {
return authors.map((e) => e.name).join(', '); return authors.map((e) => e.name).join(', ');
} }
final useMaterialThemeOnItemPage =
ref.watch(appSettingsProvider).useMaterialThemeOnItemPage;
return Row( return Row(
children: [ children: [
Container( Container(
@ -242,8 +310,9 @@ class _BookAuthors extends StatelessWidget {
child: FaIcon( child: FaIcon(
FontAwesomeIcons.penNib, FontAwesomeIcons.penNib,
size: 16, size: 16,
color: coverColorScheme?.primary ?? color: useMaterialThemeOnItemPage
Theme.of(context).colorScheme.onBackground, ? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onBackground.withOpacity(0.75),
), ),
), ),
Expanded( Expanded(

View file

@ -13,7 +13,7 @@ Future<FutureOr<ColorScheme?>> themeFromCover(
Brightness brightness = Brightness.dark, Brightness brightness = Brightness.dark,
}) async { }) async {
// add deliberate delay to simulate a long running task // add deliberate delay to simulate a long running task
await Future.delayed(200.ms); await Future.delayed(500.ms);
return ColorScheme.fromImageProvider( return ColorScheme.fromImageProvider(
provider: img, provider: img,
brightness: brightness, brightness: brightness,

View file

@ -6,7 +6,7 @@ part of 'theme_from_cover_provider.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$themeFromCoverHash() => r'b1d56a4add77d157a803424d02ef52c4d8c3f1d7'; String _$themeFromCoverHash() => r'7a364393ffff46152db31f0ed0f8f8b9d58c3b5e';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {