cover art color scheme extraction (in background)

refactor text styles to use theme
port over part of album screen
This commit is contained in:
austinried
2025-12-03 13:22:14 +09:00
parent b9a094c1c4
commit 6609671ae2
13 changed files with 682 additions and 101 deletions

View File

@@ -0,0 +1,56 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../state/source.dart';
import '../util/color_scheme.dart';
import 'theme.dart';
class CoverArtTheme extends HookConsumerWidget {
const CoverArtTheme({
super.key,
required this.coverArt,
required this.child,
});
final String? coverArt;
final Widget child;
@override
Widget build(BuildContext context, WidgetRef ref) {
final source = ref.watch(sourceProvider);
final sourceId = ref.watch(sourceIdProvider);
final getColorScheme = useMemoized(
() async {
try {
return await colorSchemefromImageProvider(
brightness: Brightness.dark,
provider: CachedNetworkImageProvider(
coverArt != null
? source.coverArtUri(coverArt!, thumbnail: true).toString()
: 'https://placehold.net/400x400.png',
cacheKey: coverArt != null
? '$sourceId$coverArt${true}'
: 'https://placehold.net/400x400.png',
),
);
} catch (err) {
print(err);
return null;
}
},
[source, sourceId, coverArt],
);
final colorScheme = useFuture(getColorScheme).data;
return colorScheme != null
? Theme(
data: subtracksTheme(colorScheme),
child: child,
)
: child;
}
}

45
lib/app/ui/images.dart Normal file
View File

@@ -0,0 +1,45 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../state/source.dart';
class CoverArtImage extends HookConsumerWidget {
const CoverArtImage({
super.key,
this.coverArt,
this.thumbnail = true,
this.fit,
this.height,
this.width,
});
final String? coverArt;
final bool thumbnail;
final BoxFit? fit;
final double? height;
final double? width;
@override
Widget build(BuildContext context, WidgetRef ref) {
final source = ref.watch(sourceProvider);
final sourceId = ref.watch(sourceIdProvider);
final imageUrl = coverArt != null
? source.coverArtUri(coverArt!, thumbnail: thumbnail).toString()
: 'https://placehold.net/400x400.png';
return CachedNetworkImage(
height: height,
width: width,
imageUrl: imageUrl,
cacheKey: '$sourceId$coverArt$thumbnail',
placeholder: (context, url) => Icon(Symbols.cached_rounded),
errorWidget: (context, url, error) => Icon(Icons.error),
fit: BoxFit.cover,
fadeOutDuration: Duration(milliseconds: 100),
fadeInDuration: Duration(milliseconds: 200),
);
}
}

View File

@@ -1,43 +0,0 @@
import 'package:flutter/material.dart';
class TextH1 extends StatelessWidget {
const TextH1(
this.data, {
super.key,
});
final String data;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Text(
data,
style: theme.textTheme.headlineLarge?.copyWith(
fontWeight: FontWeight.w800,
),
);
}
}
class TextH2 extends StatelessWidget {
const TextH2(
this.data, {
super.key,
});
final String data;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Text(
data,
style: theme.textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.w700,
),
);
}
}

28
lib/app/ui/theme.dart Normal file
View File

@@ -0,0 +1,28 @@
import 'package:flutter/material.dart';
ThemeData subtracksTheme([ColorScheme? colorScheme]) {
final theme = ThemeData.from(
colorScheme:
colorScheme ??
ColorScheme.fromSeed(
seedColor: Colors.purple.shade800,
brightness: Brightness.dark,
),
useMaterial3: true,
);
final text = theme.textTheme;
return theme.copyWith(
textTheme: text.copyWith(
headlineLarge: text.headlineLarge?.copyWith(
fontWeight: FontWeight.w800,
),
headlineMedium: text.headlineMedium?.copyWith(
fontWeight: FontWeight.w700,
),
headlineSmall: text.headlineSmall?.copyWith(
fontWeight: FontWeight.w600,
),
),
);
}