INSIDE · STATION · 참여형 번역 · 알림 · MY 화면
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../core/theme/tokens.dart';
|
||||
import '../../core/ui/app_icons.dart';
|
||||
import '../../core/ui/relative_time.dart';
|
||||
import '../home/home_screen.dart' show formatPop;
|
||||
import 'inside_data.dart';
|
||||
|
||||
/// INSIDE 화면군이 같이 쓰는 시안 조각 — 아이콘 · 글자 · 작성자 줄 · 현상금 칩 · 반응 줄.
|
||||
///
|
||||
/// 아이콘은 Figma Iconset(Lucide) 을 3배 PNG 로 둔 것이다. 공용 [AppIcons] 로 올릴 때까지
|
||||
/// 여기 둔다(디자인 일치 2단계 「공유 변경 요청」).
|
||||
/// 시안 INSIDE 의 회색 단계.
|
||||
abstract final class InsideColors {
|
||||
static const stat = Color(0xFF4F4F4F);
|
||||
static const avatar = Color(0xFFD9D9D9);
|
||||
static const rule = Color(0xFF1A1A1A);
|
||||
static const hint = Color(0xFF6B6B6B);
|
||||
static const chipLabel = Color(0xFFD5D7D9);
|
||||
}
|
||||
|
||||
/// 42dot Sans · 자간 -2.5% · 행간 130%(시안 INSIDE 본문 기본값).
|
||||
TextStyle insideText(
|
||||
double size, {
|
||||
FontWeight weight = FontWeight.w500,
|
||||
Color color = AppColors.onCosmic,
|
||||
double height = 1.3,
|
||||
}) => TextStyle(
|
||||
fontFamily: AppFonts.body,
|
||||
fontSize: size,
|
||||
fontWeight: weight,
|
||||
height: height,
|
||||
letterSpacing: -0.025 * size,
|
||||
color: color,
|
||||
);
|
||||
|
||||
/// 작성자 닉네임 — ABeeZee 12(한글은 42dot 대체).
|
||||
const insideNameStyle = TextStyle(
|
||||
fontFamily: AppFonts.display,
|
||||
fontFamilyFallback: [AppFonts.body],
|
||||
fontSize: 12,
|
||||
height: 1.3,
|
||||
letterSpacing: -0.3,
|
||||
color: AppColors.onCosmic,
|
||||
);
|
||||
|
||||
/// 첨부 이미지. `assets/` 로 시작하면 번들 표본이다.
|
||||
ImageProvider insideImage(String url) =>
|
||||
url.startsWith('assets/') ? AssetImage(url) : NetworkImage(url);
|
||||
|
||||
/// 아바타 26 · 이름 · (있으면) 시각 줄.
|
||||
class InsideAuthor extends StatelessWidget {
|
||||
const InsideAuthor({super.key, required this.name, this.meta});
|
||||
|
||||
final String name;
|
||||
final String? meta;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 26,
|
||||
height: 26,
|
||||
decoration: const BoxDecoration(
|
||||
color: InsideColors.avatar,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 9),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(name, style: insideNameStyle),
|
||||
if (meta != null)
|
||||
Text(meta!, style: insideText(10, color: AppColors.onCosmicMuted)),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 현상금 칩 — 시안 58×24 · 모서리 2 · 번개 + 「500 P」. 채택이 끝난 질문은 회색이다.
|
||||
class InsideBountyChip extends StatelessWidget {
|
||||
const InsideBountyChip({super.key, required this.question});
|
||||
|
||||
final InsideQuestion question;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final c = question.isSolved ? InsideColors.stat : AppColors.cosmicBlue;
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: c.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ImageIcon(AppIcons.bolt, size: 10, color: c),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'${formatPop(question.bountyPop)} P',
|
||||
style: insideText(12, color: c),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 좋아요 · 댓글 · 조회 줄. 0 이면 숫자를 빼고 아이콘만 둔다(시안 「아미보이」 카드).
|
||||
class InsideStats extends StatelessWidget {
|
||||
const InsideStats({
|
||||
super.key,
|
||||
required this.question,
|
||||
this.size = 14,
|
||||
this.textSize = 12,
|
||||
this.color = InsideColors.stat,
|
||||
});
|
||||
|
||||
final InsideQuestion question;
|
||||
final double size;
|
||||
final double textSize;
|
||||
final Color color;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget stat(ImageProvider icon, int n) => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ImageIcon(icon, size: size, color: color),
|
||||
if (n > 0) ...[
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
formatReaction(n),
|
||||
style: insideText(textSize, color: color, height: 1),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
return Row(
|
||||
children: [
|
||||
stat(AppIcons.heart, question.likeCount),
|
||||
const SizedBox(width: 12),
|
||||
stat(AppIcons.messageCircle, question.answerCount),
|
||||
const SizedBox(width: 12),
|
||||
stat(AppIcons.eye, question.viewCount),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user