하단 내비게이션 바 여백 · 인증 칩 · 위치 선택 지도 · 퀘스트 상세 값 정렬
This commit is contained in:
+14
-2
@@ -12,6 +12,7 @@ import 'core/push/push_messaging.dart';
|
||||
import 'core/router/app_router.dart';
|
||||
import 'features/home/home_screen.dart' show homeRefreshSignal;
|
||||
import 'core/theme/app_theme.dart';
|
||||
import 'core/theme/tokens.dart';
|
||||
import 'features/auth/auth_repository.dart';
|
||||
import 'features/campaign/campaign_detail_screen.dart';
|
||||
import 'features/notification/notification_screen.dart';
|
||||
@@ -241,8 +242,19 @@ class _FancakeAppState extends State<FancakeApp> with WidgetsBindingObserver {
|
||||
screenFor: _notificationScreen,
|
||||
child: MaterialApp.router(
|
||||
title: 'FANUZU',
|
||||
builder: (context, child) =>
|
||||
AdaptivePlanetEffects(child: child ?? const SizedBox()),
|
||||
// 하단 시스템 내비게이션 바(Android 15 edge-to-edge · iOS 홈 표시줄) 위로 모든 화면을 올린다.
|
||||
// 본문이 `ListView(padding: …)` 처럼 하단 여백을 직접 정하면 안전 영역이 빠져 마지막 칸 ·
|
||||
// 하단 버튼이 바에 가렸다(10/6 실기기 — 약관 「이전 · 완료」 · 퀘스트 생성 「총 예치 · 수수료」 ·
|
||||
// 설정 「서비스 탈퇴」). 화면마다 고치지 않고 여기서 한 번에. 띠는 앱 바탕색이다.
|
||||
builder: (context, child) => ColoredBox(
|
||||
color: AppColors.cosmicVoid,
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
left: false,
|
||||
right: false,
|
||||
child: AdaptivePlanetEffects(child: child ?? const SizedBox()),
|
||||
),
|
||||
),
|
||||
debugShowCheckedModeBanner: false,
|
||||
scaffoldMessengerKey: _messengerKey,
|
||||
locale: _locale.locale,
|
||||
|
||||
@@ -114,7 +114,15 @@ ThemeData buildAppTheme() {
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||||
),
|
||||
labelStyle: _style(AppType.caption, const Color(0xFFD5D7D9)),
|
||||
// 선택(흰 바탕)이면 검은 글자 — 상태 색을 주지 않으면 선택 칩도 연회색 글자라 읽히지 않는다(10/6 실기기).
|
||||
labelStyle: _style(
|
||||
AppType.caption,
|
||||
WidgetStateColor.resolveWith(
|
||||
(s) => s.contains(WidgetState.selected)
|
||||
? Colors.black
|
||||
: const Color(0xFFD5D7D9),
|
||||
),
|
||||
),
|
||||
secondaryLabelStyle: _style(AppType.caption, AppColors.onCosmicAccent),
|
||||
checkmarkColor: AppColors.onCosmicAccent,
|
||||
showCheckmark: false,
|
||||
|
||||
@@ -73,10 +73,57 @@ class _TermsScreenState extends State<TermsScreen> {
|
||||
void _toggle(_Term t) =>
|
||||
setState(() => _agreed.contains(t) ? _agreed.remove(t) : _agreed.add(t));
|
||||
|
||||
Widget _termsButtons(BuildContext context) {
|
||||
final shape = RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||||
);
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: FilledButton(
|
||||
onPressed: widget.onBack ?? () => Navigator.of(context).pop(),
|
||||
style: FilledButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(56),
|
||||
shape: shape,
|
||||
side: const BorderSide(color: _stroke),
|
||||
backgroundColor: Colors.black,
|
||||
foregroundColor: AppColors.onCosmic,
|
||||
),
|
||||
child: Text(context.l10n.commonPrev),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: FilledButton(
|
||||
onPressed: _ready ? _done : null,
|
||||
style: FilledButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(56),
|
||||
shape: shape,
|
||||
side: const BorderSide(color: _stroke),
|
||||
backgroundColor: AppColors.cosmicAccent,
|
||||
foregroundColor: AppColors.onCosmicAccent,
|
||||
disabledBackgroundColor: const Color(0xFF333333),
|
||||
disabledForegroundColor: const Color(0xFF777777),
|
||||
),
|
||||
child: Text(context.l10n.commonDone),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(context.l10n.termsTitle)),
|
||||
// Figma `179:24230` — 「이전 · 완료」는 목록 끝이 아니라 화면 하단 고정(56 높이 · 간격 10 · 아래 16).
|
||||
// 시스템 내비게이션 바 위로 올린다(SafeArea) — 10/6 실기기에서 첫 탭이 바에 먹혔다.
|
||||
bottomNavigationBar: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(AppSpace.s4),
|
||||
child: _termsButtons(context),
|
||||
),
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(AppSpace.s4),
|
||||
children: [
|
||||
@@ -137,34 +184,6 @@ class _TermsScreenState extends State<TermsScreen> {
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: AppSpace.s6),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: FilledButton(
|
||||
onPressed: widget.onBack ?? () => Navigator.of(context).pop(),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: Colors.black,
|
||||
foregroundColor: AppColors.onCosmic,
|
||||
),
|
||||
child: Text(context.l10n.commonPrev),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: FilledButton(
|
||||
onPressed: _ready ? _done : null,
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: AppColors.cosmicAccent,
|
||||
foregroundColor: AppColors.onCosmicAccent,
|
||||
disabledBackgroundColor: const Color(0xFF333333),
|
||||
disabledForegroundColor: const Color(0xFF777777),
|
||||
),
|
||||
child: Text(context.l10n.commonDone),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -420,6 +420,9 @@ class _EclipseDefendScreenState extends State<EclipseDefendScreen>
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
// 사본 `373:445267` 「…행성 운석 충돌을 막으세요!」와 다르게 「이클립스를 막으세요!」다 —
|
||||
// 발주사 상세정의 v1.6.1 3장 · 1주차 공식 검토 회신서 17항 「운석 충돌은 Figma 에 남은 임시
|
||||
// 카피, Eclipse 와 별도 이벤트로 구현하지 않고 Eclipse 문구로 일원화」(해석확인 영역A N-A05).
|
||||
TextSpan(text: context.l10n.eclipseShakeTimed),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:geocoding/geocoding.dart';
|
||||
@@ -62,11 +64,20 @@ class _PlacePickerScreenState extends State<PlacePickerScreen> {
|
||||
Future<void> _drop(LatLng point) async {
|
||||
final coords =
|
||||
'${point.latitude.toStringAsFixed(5)}, ${point.longitude.toStringAsFixed(5)}';
|
||||
setState(() => _selected = QuestPlace(
|
||||
id: '', name: coords, lat: point.latitude, lng: point.longitude));
|
||||
setState(
|
||||
() => _selected = QuestPlace(
|
||||
id: '',
|
||||
name: coords,
|
||||
lat: point.latitude,
|
||||
lng: point.longitude,
|
||||
),
|
||||
);
|
||||
var address = coords;
|
||||
try {
|
||||
final marks = await Geocoding().placemarkFromCoordinates(point.latitude, point.longitude);
|
||||
final marks = await Geocoding().placemarkFromCoordinates(
|
||||
point.latitude,
|
||||
point.longitude,
|
||||
);
|
||||
if (marks.isNotEmpty) {
|
||||
final line = placeAddress(marks.first);
|
||||
if (line.isNotEmpty) address = line;
|
||||
@@ -75,8 +86,14 @@ class _PlacePickerScreenState extends State<PlacePickerScreen> {
|
||||
// 지오코더가 없거나(에뮬레이터 · 테스트 — 플랫폼 구현 없음) 실패 — 좌표 표시로 둔다.
|
||||
}
|
||||
if (!mounted || _selected?.lat != point.latitude) return;
|
||||
setState(() => _selected = QuestPlace(
|
||||
id: '', name: address, lat: point.latitude, lng: point.longitude));
|
||||
setState(
|
||||
() => _selected = QuestPlace(
|
||||
id: '',
|
||||
name: address,
|
||||
lat: point.latitude,
|
||||
lng: point.longitude,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -129,6 +146,8 @@ class _PlacePickerScreenState extends State<PlacePickerScreen> {
|
||||
TileLayer(
|
||||
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
userAgentPackageName: 'kr.co.fanuzu.app',
|
||||
// Figma `371:356705` 은 어두운 지도다 — 같은 OSM 타일을 어둡게 칠한다(flutter_map 내장).
|
||||
tileBuilder: darkModeTileBuilder,
|
||||
),
|
||||
MarkerLayer(
|
||||
markers: [
|
||||
@@ -138,14 +157,21 @@ class _PlacePickerScreenState extends State<PlacePickerScreen> {
|
||||
point: LatLng(p.lat!, p.lng!),
|
||||
child: GestureDetector(
|
||||
onTap: () => _choose(p),
|
||||
child: const Icon(Icons.place, color: AppColors.cosmicAccent),
|
||||
child: const Icon(
|
||||
Icons.place,
|
||||
color: AppColors.cosmicAccent,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (pinned != null && pinned.lat != null && pinned.lng != null)
|
||||
if (pinned != null &&
|
||||
pinned.lat != null &&
|
||||
pinned.lng != null)
|
||||
Marker(
|
||||
point: LatLng(pinned.lat!, pinned.lng!),
|
||||
width: 38,
|
||||
height: 44,
|
||||
alignment: Alignment.topCenter,
|
||||
child: const Icon(Icons.location_pin, size: 40, color: Colors.redAccent),
|
||||
child: const CustomPaint(painter: _PinPainter()),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -154,7 +180,7 @@ class _PlacePickerScreenState extends State<PlacePickerScreen> {
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Text(
|
||||
'© OpenStreetMap',
|
||||
style: TextStyle(fontSize: 10, color: Colors.black54),
|
||||
style: TextStyle(fontSize: 10, color: Colors.white54),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -181,7 +207,8 @@ class _PlacePickerScreenState extends State<PlacePickerScreen> {
|
||||
// 디폴트 미선택 · 단수 선택.
|
||||
: RadioGroup<String>(
|
||||
groupValue: pinned?.id,
|
||||
onChanged: (v) => _choose(widget.places.firstWhere((p) => p.id == v)),
|
||||
onChanged: (v) =>
|
||||
_choose(widget.places.firstWhere((p) => p.id == v)),
|
||||
child: ListView.builder(
|
||||
itemCount: _visible.length,
|
||||
itemBuilder: (context, i) {
|
||||
@@ -243,9 +270,46 @@ String placeAddress(Placemark m) {
|
||||
}
|
||||
final road = street.isNotEmpty
|
||||
? street
|
||||
: [t(m.thoroughfare), t(m.subThoroughfare)].where((s) => s.isNotEmpty).join(' ');
|
||||
return [area, t(m.locality), t(m.subLocality), road]
|
||||
.where((s) => s.isNotEmpty)
|
||||
.toSet()
|
||||
.join(' ');
|
||||
: [
|
||||
t(m.thoroughfare),
|
||||
t(m.subThoroughfare),
|
||||
].where((s) => s.isNotEmpty).join(' ');
|
||||
return [
|
||||
area,
|
||||
t(m.locality),
|
||||
t(m.subLocality),
|
||||
road,
|
||||
].where((s) => s.isNotEmpty).toSet().join(' ');
|
||||
}
|
||||
|
||||
/// Figma `372:356854` 핀 — 38×44 분홍(#FF7DDD) 물방울 · 흰 테두리 · 가운데 흰 점 12.
|
||||
class _PinPainter extends CustomPainter {
|
||||
const _PinPainter();
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final r = size.width / 2;
|
||||
final drop = ui.Path.combine(
|
||||
PathOperation.union,
|
||||
ui.Path()..addOval(Rect.fromCircle(center: Offset(r, r), radius: r)),
|
||||
ui.Path()
|
||||
..moveTo(r * 0.16, r * 1.5)
|
||||
..lineTo(size.width - r * 0.16, r * 1.5)
|
||||
..lineTo(r, size.height)
|
||||
..close(),
|
||||
);
|
||||
canvas
|
||||
..drawPath(drop, Paint()..color = AppColors.cosmicAccent)
|
||||
..drawPath(
|
||||
drop,
|
||||
Paint()
|
||||
..color = Colors.white
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 1.5,
|
||||
)
|
||||
..drawCircle(Offset(r, r), 6, Paint()..color = Colors.white);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
||||
}
|
||||
|
||||
@@ -193,6 +193,8 @@ class _QuestCreateScreenState extends State<QuestCreateScreen> {
|
||||
context.l10n.questNoCreatable,
|
||||
style: const TextStyle(color: AppColors.onCosmicVariant),
|
||||
),
|
||||
// Figma `254:164081` 은 375 폭에서 3+1 줄(칩 88 · 114 · 111 · 간격 8 = 329)이다. 360 폭 기기(본문
|
||||
// 328)에서는 1 이 모자라 2+2 로 접힌다 — 칩 치수는 사본 그대로 두고 기기 폭대로 줄을 바꾼다.
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
@@ -309,17 +311,23 @@ class _QuestCreateScreenState extends State<QuestCreateScreen> {
|
||||
Text(context.l10n.questExtraVerify, style: Theme.of(context).textTheme.bodySmall),
|
||||
const SizedBox(height: AppSpace.s2),
|
||||
Wrap(
|
||||
spacing: AppSpace.s2,
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
for (final (key, label) in [
|
||||
('photo', context.l10n.questVerifyPhoto),
|
||||
('qr', context.l10n.questVerifyQr),
|
||||
])
|
||||
FilterChip(
|
||||
label: Text(label),
|
||||
// 칩 모양은 Figma 카테고리 칩(`259:46062` 선택 = 흰 바탕 검은 글자) — 기본 FilterChip 은
|
||||
// 선택 시 흰 바탕에 연회색 글자라 읽히지 않았다(10/6 실기기).
|
||||
_CategoryChip(
|
||||
label: label,
|
||||
icon: null,
|
||||
selected: _extraVerify.contains(key),
|
||||
onSelected: (on) => setState(
|
||||
() => on ? _extraVerify.add(key) : _extraVerify.remove(key),
|
||||
onTap: () => setState(
|
||||
() => _extraVerify.contains(key)
|
||||
? _extraVerify.remove(key)
|
||||
: _extraVerify.add(key),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -327,7 +327,10 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withValues(alpha: 0.3),
|
||||
borderRadius: BorderRadius.circular(9999),
|
||||
@@ -335,7 +338,11 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.music_note_rounded, size: 18, color: AppColors.onCosmic),
|
||||
const Icon(
|
||||
Icons.music_note_rounded,
|
||||
size: 18,
|
||||
color: AppColors.onCosmic,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Flexible(
|
||||
child: Text(
|
||||
@@ -790,14 +797,15 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
}
|
||||
if (!mounted) return;
|
||||
setState(() => _stage = QuestStage.inProgress);
|
||||
final run = await Navigator.of(context, rootNavigator: true).push<ShakeJudge>(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => MusicShakePlayScreen(
|
||||
setup: setup,
|
||||
shakes: widget.repository.shakeEvents(),
|
||||
),
|
||||
),
|
||||
);
|
||||
final run = await Navigator.of(context, rootNavigator: true)
|
||||
.push<ShakeJudge>(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => MusicShakePlayScreen(
|
||||
setup: setup,
|
||||
shakes: widget.repository.shakeEvents(),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (!mounted) return;
|
||||
if (run != null) widget.repository.recordShake(run);
|
||||
setState(() {
|
||||
@@ -864,9 +872,10 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
Future<PlaceProof?> _placeProof(List<String> methods) async {
|
||||
String? qr;
|
||||
if (methods.contains('qr')) {
|
||||
qr = await Navigator.of(context, rootNavigator: true).push<String>(
|
||||
MaterialPageRoute(builder: (_) => const QrScanScreen()),
|
||||
);
|
||||
qr = await Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).push<String>(MaterialPageRoute(builder: (_) => const QrScanScreen()));
|
||||
if (qr == null) return null;
|
||||
}
|
||||
Uint8List? photo;
|
||||
@@ -913,7 +922,8 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
setState(() {
|
||||
_busy = false;
|
||||
// Music Shake 판정 미달(B-24 — 보상 0) — 판정표는 소진됐으니 다시 하게 시작 전으로.
|
||||
if (detail.card.type == QuestType.musicShake) _stage = QuestStage.notStarted;
|
||||
if (detail.card.type == QuestType.musicShake)
|
||||
_stage = QuestStage.notStarted;
|
||||
});
|
||||
_fail(error);
|
||||
// 타임어택 창 밖(E-QST-01) — 서버가 본 상태로 다시 읽어 마감 · 완료를 반영한다.
|
||||
@@ -996,12 +1006,14 @@ class _QuestDetailScreenState extends State<QuestDetailScreen> {
|
||||
Future<String?> _watchAd(QuestReward reward) async {
|
||||
RewardedAdHandle? ad;
|
||||
BackendException? failure;
|
||||
final loading = widget.repository.loadRewardedAd(reward).then<void>(
|
||||
(handle) => ad = handle,
|
||||
onError: (Object error) => failure = error is BackendException
|
||||
? error
|
||||
: BackendException('E-AD-01', context.l10n.adLoadFailed),
|
||||
);
|
||||
final loading = widget.repository
|
||||
.loadRewardedAd(reward)
|
||||
.then<void>(
|
||||
(handle) => ad = handle,
|
||||
onError: (Object error) => failure = error is BackendException
|
||||
? error
|
||||
: BackendException('E-AD-01', context.l10n.adLoadFailed),
|
||||
);
|
||||
await _warp(QuestAdWarpScreen.before(until: loading));
|
||||
final loaded = ad;
|
||||
if (!mounted) {
|
||||
@@ -1095,35 +1107,47 @@ class _Row extends StatelessWidget {
|
||||
color: AppColors.cosmicSurfaceContainer,
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
// Figma `264:48835` — 라벨은 왼쪽, 값(번개 포함)은 오른쪽 끝. 예전에는 라벨 Expanded 와 값 Flexible 이
|
||||
// 폭을 반씩 나눠 값이 가운데 놓였다(10/6 실기기).
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 20 / 14,
|
||||
color: AppColors.onCosmicVariant,
|
||||
),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 20 / 14,
|
||||
color: AppColors.onCosmicVariant,
|
||||
),
|
||||
),
|
||||
if (bolt) ...[
|
||||
Image.asset('assets/figma/icon-bolt.png', width: 16, height: 16.7),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
// 마감 줄처럼 값이 길면 줄을 바꾼다 — 좁은 화면에서 넘치지 않게.
|
||||
Flexible(
|
||||
child: Text(
|
||||
value,
|
||||
textAlign: TextAlign.end,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.4,
|
||||
letterSpacing: -0.4,
|
||||
color: AppColors.onCosmic,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
if (bolt) ...[
|
||||
Image.asset(
|
||||
'assets/figma/icon-bolt.png',
|
||||
width: 16,
|
||||
height: 16.7,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
// 마감 줄처럼 값이 길면 줄을 바꾼다 — 좁은 화면에서 넘치지 않게.
|
||||
Flexible(
|
||||
child: Text(
|
||||
value,
|
||||
textAlign: TextAlign.end,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.4,
|
||||
letterSpacing: -0.4,
|
||||
color: AppColors.onCosmic,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:fancake/core/theme/app_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
/// 10/6 실기기 — 선택한 칩(흰 바탕)의 글자가 연회색이라 읽히지 않았다. 선택이면 검은 글자.
|
||||
void main() {
|
||||
Color? labelColor(WidgetTester t) => t
|
||||
.widget<DefaultTextStyle>(
|
||||
find.ancestor(of: find.text('A'), matching: find.byType(DefaultTextStyle)).first,
|
||||
)
|
||||
.style
|
||||
.color;
|
||||
|
||||
Future<void> pump(WidgetTester t, bool selected) => t.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: buildAppTheme(),
|
||||
home: Scaffold(
|
||||
body: FilterChip(label: const Text('A'), selected: selected, onSelected: (_) {}),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
testWidgets('선택 칩은 검은 글자 · 미선택은 연회색', (t) async {
|
||||
await pump(t, true);
|
||||
expect(labelColor(t), Colors.black);
|
||||
await pump(t, false);
|
||||
expect(labelColor(t), const Color(0xFFD5D7D9));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user