feat(client): 新增 AppTokens ThemeExtension 类型
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@immutable
|
||||
class AppTokens extends ThemeExtension<AppTokens> {
|
||||
final Color page, bg, surface, border, borderSubtle, text, heading, title, muted, faint;
|
||||
final Color primary, primaryDark, onPrimary, brand400, brand50, accent;
|
||||
final Color success, danger, warn, infoSoft, okSoft, accentSoft, successBg, dangerBg, warnBg;
|
||||
final Color thBg, rowHover, shadow;
|
||||
final Color topBg, topFg, topFaint, topBorder, topControl, topMarkBg, topMarkFg;
|
||||
final Color sideBg, sideFg, sideMuted, sideActiveBg, sideActiveFg, sideBorder, sideHover;
|
||||
final Color menuUserBg, menuUserFg, menuUserHover;
|
||||
final bool isDark;
|
||||
|
||||
const AppTokens({
|
||||
required this.page, required this.bg, required this.surface, required this.border,
|
||||
required this.borderSubtle, required this.text, required this.heading, required this.title,
|
||||
required this.muted, required this.faint, required this.primary, required this.primaryDark,
|
||||
required this.onPrimary, required this.brand400, required this.brand50, required this.accent,
|
||||
required this.success, required this.danger, required this.warn, required this.infoSoft,
|
||||
required this.okSoft, required this.accentSoft, required this.successBg, required this.dangerBg,
|
||||
required this.warnBg, required this.thBg, required this.rowHover, required this.shadow,
|
||||
required this.topBg, required this.topFg, required this.topFaint, required this.topBorder,
|
||||
required this.topControl, required this.topMarkBg, required this.topMarkFg, required this.sideBg,
|
||||
required this.sideFg, required this.sideMuted, required this.sideActiveBg, required this.sideActiveFg,
|
||||
required this.sideBorder, required this.sideHover, required this.menuUserBg, required this.menuUserFg,
|
||||
required this.menuUserHover, required this.isDark,
|
||||
});
|
||||
|
||||
@override
|
||||
AppTokens copyWith({
|
||||
Color? page, Color? bg, Color? surface, Color? border, Color? borderSubtle, Color? text,
|
||||
Color? heading, Color? title, Color? muted, Color? faint, Color? primary, Color? primaryDark,
|
||||
Color? onPrimary, Color? brand400, Color? brand50, Color? accent, Color? success, Color? danger,
|
||||
Color? warn, Color? infoSoft, Color? okSoft, Color? accentSoft, Color? successBg, Color? dangerBg,
|
||||
Color? warnBg, Color? thBg, Color? rowHover, Color? shadow, Color? topBg, Color? topFg,
|
||||
Color? topFaint, Color? topBorder, Color? topControl, Color? topMarkBg, Color? topMarkFg,
|
||||
Color? sideBg, Color? sideFg, Color? sideMuted, Color? sideActiveBg, Color? sideActiveFg,
|
||||
Color? sideBorder, Color? sideHover, Color? menuUserBg, Color? menuUserFg, Color? menuUserHover,
|
||||
bool? isDark,
|
||||
}) {
|
||||
return AppTokens(
|
||||
page: page ?? this.page, bg: bg ?? this.bg, surface: surface ?? this.surface,
|
||||
border: border ?? this.border, borderSubtle: borderSubtle ?? this.borderSubtle,
|
||||
text: text ?? this.text, heading: heading ?? this.heading, title: title ?? this.title,
|
||||
muted: muted ?? this.muted, faint: faint ?? this.faint, primary: primary ?? this.primary,
|
||||
primaryDark: primaryDark ?? this.primaryDark, onPrimary: onPrimary ?? this.onPrimary,
|
||||
brand400: brand400 ?? this.brand400, brand50: brand50 ?? this.brand50, accent: accent ?? this.accent,
|
||||
success: success ?? this.success, danger: danger ?? this.danger, warn: warn ?? this.warn,
|
||||
infoSoft: infoSoft ?? this.infoSoft, okSoft: okSoft ?? this.okSoft, accentSoft: accentSoft ?? this.accentSoft,
|
||||
successBg: successBg ?? this.successBg, dangerBg: dangerBg ?? this.dangerBg, warnBg: warnBg ?? this.warnBg,
|
||||
thBg: thBg ?? this.thBg, rowHover: rowHover ?? this.rowHover, shadow: shadow ?? this.shadow,
|
||||
topBg: topBg ?? this.topBg, topFg: topFg ?? this.topFg, topFaint: topFaint ?? this.topFaint,
|
||||
topBorder: topBorder ?? this.topBorder, topControl: topControl ?? this.topControl,
|
||||
topMarkBg: topMarkBg ?? this.topMarkBg, topMarkFg: topMarkFg ?? this.topMarkFg,
|
||||
sideBg: sideBg ?? this.sideBg, sideFg: sideFg ?? this.sideFg, sideMuted: sideMuted ?? this.sideMuted,
|
||||
sideActiveBg: sideActiveBg ?? this.sideActiveBg, sideActiveFg: sideActiveFg ?? this.sideActiveFg,
|
||||
sideBorder: sideBorder ?? this.sideBorder, sideHover: sideHover ?? this.sideHover,
|
||||
menuUserBg: menuUserBg ?? this.menuUserBg, menuUserFg: menuUserFg ?? this.menuUserFg,
|
||||
menuUserHover: menuUserHover ?? this.menuUserHover, isDark: isDark ?? this.isDark,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AppTokens lerp(ThemeExtension<AppTokens>? other, double t) {
|
||||
if (other is! AppTokens) return this;
|
||||
Color c(Color a, Color b) => Color.lerp(a, b, t)!;
|
||||
return AppTokens(
|
||||
page: c(page, other.page), bg: c(bg, other.bg), surface: c(surface, other.surface),
|
||||
border: c(border, other.border), borderSubtle: c(borderSubtle, other.borderSubtle),
|
||||
text: c(text, other.text), heading: c(heading, other.heading), title: c(title, other.title),
|
||||
muted: c(muted, other.muted), faint: c(faint, other.faint), primary: c(primary, other.primary),
|
||||
primaryDark: c(primaryDark, other.primaryDark), onPrimary: c(onPrimary, other.onPrimary),
|
||||
brand400: c(brand400, other.brand400), brand50: c(brand50, other.brand50), accent: c(accent, other.accent),
|
||||
success: c(success, other.success), danger: c(danger, other.danger), warn: c(warn, other.warn),
|
||||
infoSoft: c(infoSoft, other.infoSoft), okSoft: c(okSoft, other.okSoft), accentSoft: c(accentSoft, other.accentSoft),
|
||||
successBg: c(successBg, other.successBg), dangerBg: c(dangerBg, other.dangerBg), warnBg: c(warnBg, other.warnBg),
|
||||
thBg: c(thBg, other.thBg), rowHover: c(rowHover, other.rowHover), shadow: c(shadow, other.shadow),
|
||||
topBg: c(topBg, other.topBg), topFg: c(topFg, other.topFg), topFaint: c(topFaint, other.topFaint),
|
||||
topBorder: c(topBorder, other.topBorder), topControl: c(topControl, other.topControl),
|
||||
topMarkBg: c(topMarkBg, other.topMarkBg), topMarkFg: c(topMarkFg, other.topMarkFg),
|
||||
sideBg: c(sideBg, other.sideBg), sideFg: c(sideFg, other.sideFg), sideMuted: c(sideMuted, other.sideMuted),
|
||||
sideActiveBg: c(sideActiveBg, other.sideActiveBg), sideActiveFg: c(sideActiveFg, other.sideActiveFg),
|
||||
sideBorder: c(sideBorder, other.sideBorder), sideHover: c(sideHover, other.sideHover),
|
||||
menuUserBg: c(menuUserBg, other.menuUserBg), menuUserFg: c(menuUserFg, other.menuUserFg),
|
||||
menuUserHover: c(menuUserHover, other.menuUserHover), isDark: t < 0.5 ? isDark : other.isDark,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:jiu_client/core/theme/app_tokens.dart';
|
||||
|
||||
AppTokens _sample() => const AppTokens(
|
||||
page: Color(0xFF111111), bg: Color(0xFF222222), surface: Color(0xFF333333),
|
||||
border: Color(0xFF444444), borderSubtle: Color(0xFF555555), text: Color(0xFF666666),
|
||||
heading: Color(0xFF777777), title: Color(0xFF888888), muted: Color(0xFF999999),
|
||||
faint: Color(0xFFAAAAAA), primary: Color(0xFF2563AC), primaryDark: Color(0xFF154072),
|
||||
onPrimary: Color(0xFFFFFFFF), brand400: Color(0xFF4F86C6), brand50: Color(0xFFEEF4FB),
|
||||
accent: Color(0xFF8B2331), success: Color(0xFF2E8B57), danger: Color(0xFFD14343),
|
||||
warn: Color(0xFFE08E00), infoSoft: Color(0xFFEEF4FB), okSoft: Color(0xFFE8F5EE),
|
||||
accentSoft: Color(0xFFFAEEF0), successBg: Color(0xFFE8F5EE), dangerBg: Color(0xFFFDECEC),
|
||||
warnBg: Color(0xFFFFF4DB), thBg: Color(0xFFF7F9FC), rowHover: Color(0xFFEEF4FB),
|
||||
shadow: Color(0x33142846), topBg: Color(0xFF2563AC), topFg: Color(0xFFFFFFFF),
|
||||
topFaint: Color(0xB8FFFFFF), topBorder: Color(0xFF1E5594), topControl: Color(0x29FFFFFF),
|
||||
topMarkBg: Color(0x38FFFFFF), topMarkFg: Color(0xFFFFFFFF), sideBg: Color(0xFF163A6C),
|
||||
sideFg: Color(0xFFC4D6ED), sideMuted: Color(0xFF7C9FCB), sideActiveBg: Color(0xFF2C6BB3),
|
||||
sideActiveFg: Color(0xFFFFFFFF), sideBorder: Color(0xFF1E4A86), sideHover: Color(0xFF1C4680),
|
||||
menuUserBg: Color(0xFF3C79C0), menuUserFg: Color(0xFFFFFFFF), menuUserHover: Color(0xFF4D88CC),
|
||||
isDark: false,
|
||||
);
|
||||
|
||||
void main() {
|
||||
test('copyWith overrides one field, keeps others', () {
|
||||
final t = _sample().copyWith(primary: const Color(0xFF000000));
|
||||
expect(t.primary, const Color(0xFF000000));
|
||||
expect(t.surface, const Color(0xFF333333));
|
||||
expect(t.isDark, false);
|
||||
});
|
||||
|
||||
test('lerp at t=0 returns first, t=1 returns second', () {
|
||||
final a = _sample();
|
||||
final b = _sample().copyWith(primary: const Color(0xFFFFFFFF));
|
||||
expect(a.lerp(b, 0).primary, a.primary);
|
||||
expect(a.lerp(b, 1).primary, b.primary);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user