163 lines
3.8 KiB
Dart
163 lines
3.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
// Color resolution now relies on Theme ColorScheme.
|
|
|
|
/// 배경에 따라 자동으로 색상 대비를 조정하는 텍스트 위젯
|
|
class ThemedText extends StatelessWidget {
|
|
final String text;
|
|
final TextStyle? style;
|
|
final TextAlign? textAlign;
|
|
final TextOverflow? overflow;
|
|
final int? maxLines;
|
|
final bool softWrap;
|
|
final bool forceLight;
|
|
final bool forceDark;
|
|
final double? opacity;
|
|
final double? fontSize;
|
|
final FontWeight? fontWeight;
|
|
final double? letterSpacing;
|
|
final Color? color;
|
|
|
|
const ThemedText(
|
|
this.text, {
|
|
super.key,
|
|
this.style,
|
|
this.textAlign,
|
|
this.overflow,
|
|
this.maxLines,
|
|
this.softWrap = true,
|
|
this.forceLight = false,
|
|
this.forceDark = false,
|
|
this.opacity,
|
|
this.fontSize,
|
|
this.fontWeight,
|
|
this.letterSpacing,
|
|
this.color,
|
|
});
|
|
|
|
/// 배경 밝기에 따른 텍스트 색상 결정
|
|
static Color getContrastColor(
|
|
BuildContext context, {
|
|
bool forceLight = false,
|
|
bool forceDark = false,
|
|
}) {
|
|
final scheme = Theme.of(context).colorScheme;
|
|
if (forceLight) return scheme.onPrimary; // typically white
|
|
if (forceDark) return scheme.onSurface; // dark text in light theme
|
|
|
|
// 기본: 스킴의 onSurface 사용(라이트/다크 자동 대비)
|
|
return scheme.onSurface;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final textColor = color ??
|
|
getContrastColor(
|
|
context,
|
|
forceLight: forceLight,
|
|
forceDark: forceDark,
|
|
);
|
|
|
|
final finalColor =
|
|
opacity != null ? textColor.withValues(alpha: opacity!) : textColor;
|
|
|
|
final defaultStyle = DefaultTextStyle.of(context).style;
|
|
|
|
// 개별 스타일 속성들을 병합
|
|
final baseStyle = TextStyle(
|
|
fontSize: fontSize,
|
|
fontWeight: fontWeight,
|
|
letterSpacing: letterSpacing,
|
|
color: finalColor,
|
|
);
|
|
|
|
final effectiveStyle = defaultStyle.merge(baseStyle).merge(style);
|
|
|
|
return Text(
|
|
text,
|
|
style: effectiveStyle,
|
|
textAlign: textAlign,
|
|
overflow: overflow,
|
|
maxLines: maxLines,
|
|
softWrap: softWrap,
|
|
);
|
|
}
|
|
|
|
/// 제목용 스타일 팩토리
|
|
static ThemedText headline({
|
|
required String text,
|
|
TextStyle? style,
|
|
bool forceLight = false,
|
|
bool forceDark = false,
|
|
}) {
|
|
return ThemedText(
|
|
text,
|
|
style: const TextStyle(
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.bold,
|
|
).merge(style),
|
|
forceLight: forceLight,
|
|
forceDark: forceDark,
|
|
);
|
|
}
|
|
|
|
/// 부제목용 스타일 팩토리
|
|
static ThemedText subtitle({
|
|
required String text,
|
|
TextStyle? style,
|
|
bool forceLight = false,
|
|
bool forceDark = false,
|
|
double opacity = 0.8,
|
|
}) {
|
|
return ThemedText(
|
|
text,
|
|
style: const TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w500,
|
|
).merge(style),
|
|
forceLight: forceLight,
|
|
forceDark: forceDark,
|
|
opacity: opacity,
|
|
);
|
|
}
|
|
|
|
/// 본문용 스타일 팩토리
|
|
static ThemedText body({
|
|
required String text,
|
|
TextStyle? style,
|
|
bool forceLight = false,
|
|
bool forceDark = false,
|
|
double opacity = 0.9,
|
|
}) {
|
|
return ThemedText(
|
|
text,
|
|
style: const TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.normal,
|
|
).merge(style),
|
|
forceLight: forceLight,
|
|
forceDark: forceDark,
|
|
opacity: opacity,
|
|
);
|
|
}
|
|
|
|
/// 캡션용 스타일 팩토리
|
|
static ThemedText caption({
|
|
required String text,
|
|
TextStyle? style,
|
|
bool forceLight = false,
|
|
bool forceDark = false,
|
|
double opacity = 0.7,
|
|
}) {
|
|
return ThemedText(
|
|
text,
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.normal,
|
|
).merge(style),
|
|
forceLight: forceLight,
|
|
forceDark: forceDark,
|
|
opacity: opacity,
|
|
);
|
|
}
|
|
}
|