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 'package:animated_theme_switcher/animated_theme_switcher.dart';
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shelfsdk/audiobookshelf_api.dart' as shelfsdk;
@ -51,76 +53,122 @@ class LibraryItemPage extends HookConsumerWidget {
debugPrint('useMaterialThemeOnItemPage is false');
// AsyncValue<ColorScheme?> coverColorScheme = const AsyncValue.loading();
}
return Theme(
data: coverColorScheme.valueOrNull != null && useMaterialThemeOnItemPage
? ThemeData.from(
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
return ThemeProvider(
initTheme: Theme.of(context),
duration: 200.ms,
// 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 {
const _BookCover({
super.key,
@ -128,53 +176,72 @@ class _BookCover extends HookConsumerWidget {
required this.extraMap,
required this.providedCacheImage,
required this.item,
this.coverColorScheme,
});
final String itemId;
final LibraryItemExtras? extraMap;
final Image? providedCacheImage;
final AsyncValue<shelfsdk.LibraryItem> item;
final ColorScheme? coverColorScheme;
@override
Widget build(BuildContext context, WidgetRef ref) {
return Hero(
tag: HeroTagPrefixes.bookCover + itemId + (extraMap?.heroTagSuffix ?? ''),
child: providedCacheImage ??
item.when(
data: (libraryItem) {
final coverImage = ref.watch(coverImageProvider(libraryItem));
return Stack(
children: [
coverImage.when(
data: (image) {
// return const BookCoverSkeleton();
if (image.isEmpty) {
return const Icon(Icons.error);
}
// cover 80% of parent height
return Image.memory(
image,
fit: BoxFit.cover,
// cacheWidth: (height *
// MediaQuery.of(context).devicePixelRatio)
// .round(),
);
},
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()),
),
return ThemeSwitcher(
builder: (context) {
// change theme after 2 seconds
Future.delayed(150.ms, () {
ThemeSwitcher.of(context).changeTheme(
theme: coverColorScheme != null
? ThemeData.from(
colorScheme: coverColorScheme!,
textTheme: Theme.of(context).textTheme,
)
: Theme.of(context),
);
});
return Hero(
tag: HeroTagPrefixes.bookCover +
itemId +
(extraMap?.heroTagSuffix ?? ''),
child: providedCacheImage ??
item.when(
data: (libraryItem) {
final coverImage = ref.watch(coverImageProvider(libraryItem));
return Stack(
children: [
coverImage.when(
data: (image) {
// return const BookCoverSkeleton();
if (image.isEmpty) {
return const Icon(Icons.error);
}
// cover 80% of parent height
return Image.memory(
image,
fit: BoxFit.cover,
// cacheWidth: (height *
// MediaQuery.of(context).devicePixelRatio)
// .round(),
);
},
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({
super.key,
required this.itemBookMetadata,
required this.bookDetailsCached,
this.coverColorScheme,
});
final shelfsdk.BookMetadata? itemBookMetadata;
final shelfsdk.BookMinified? bookDetailsCached;
final ColorScheme? coverColorScheme;
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
String generateAuthorsString() {
final authors = (itemBookMetadata)?.authors ?? [];
if (authors.isEmpty) {
@ -235,6 +300,9 @@ class _BookAuthors extends StatelessWidget {
return authors.map((e) => e.name).join(', ');
}
final useMaterialThemeOnItemPage =
ref.watch(appSettingsProvider).useMaterialThemeOnItemPage;
return Row(
children: [
Container(
@ -242,8 +310,9 @@ class _BookAuthors extends StatelessWidget {
child: FaIcon(
FontAwesomeIcons.penNib,
size: 16,
color: coverColorScheme?.primary ??
Theme.of(context).colorScheme.onBackground,
color: useMaterialThemeOnItemPage
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onBackground.withOpacity(0.75),
),
),
Expanded(

View file

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

View file

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