mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2025-12-06 11:09:28 +00:00
theme on item page is much smoother now
This commit is contained in:
parent
1609fe9d65
commit
0d54f1cb15
3 changed files with 176 additions and 107 deletions
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue