diff --git a/flutter/lib/core/icons/app_icons.dart b/flutter/lib/core/icons/app_icons.dart
index df1e30a..240c329 100644
--- a/flutter/lib/core/icons/app_icons.dart
+++ b/flutter/lib/core/icons/app_icons.dart
@@ -25,6 +25,7 @@ enum AppIconData {
'',
),
chevronDown(''),
+ chevronUp(''),
chevronRight(''),
chevronLeft(''),
arrowRight(''),
diff --git a/flutter/lib/core/theme/app_colors.dart b/flutter/lib/core/theme/app_colors.dart
index 4ffbd8e..c02e5d4 100644
--- a/flutter/lib/core/theme/app_colors.dart
+++ b/flutter/lib/core/theme/app_colors.dart
@@ -54,6 +54,9 @@ abstract final class AppColors {
/// 흰 바탕 대비가 3.2:1 이라 읽어야 하는 글자에는 [textHint] 를 쓴다
static const Color iconMuted = Color(0xFF8A90A2);
+ /// 검색어 지우기 원
+ static const Color clearFill = Color(0xFFA3A8B6);
+
/// 진행 막대 바탕
static const Color progressTrack = Color(0xFFF1F2F6);
diff --git a/flutter/lib/core/theme/app_theme.dart b/flutter/lib/core/theme/app_theme.dart
index 7bd278d..e44ef82 100644
--- a/flutter/lib/core/theme/app_theme.dart
+++ b/flutter/lib/core/theme/app_theme.dart
@@ -70,9 +70,11 @@ abstract final class AppTheme {
elevation: 0,
scrolledUnderElevation: 0,
centerTitle: false,
+ // 화면 제목 17/24 w700(시안 머리)
titleTextStyle: text.titleSmall?.copyWith(
fontSize: 17,
height: 24 / 17,
+ fontWeight: FontWeight.w700,
),
),
filledButtonTheme: FilledButtonThemeData(
diff --git a/flutter/lib/features/event/presentation/pages/event_detail_page.dart b/flutter/lib/features/event/presentation/pages/event_detail_page.dart
index 0266936..5e0368d 100644
--- a/flutter/lib/features/event/presentation/pages/event_detail_page.dart
+++ b/flutter/lib/features/event/presentation/pages/event_detail_page.dart
@@ -28,12 +28,13 @@ class EventDetailPage extends ConsumerWidget {
appBar: AppBar(),
body: switch (detail) {
AsyncData(:final value) => _DetailBody(detail: value),
- AsyncError(:final error) => Padding(
- padding: const EdgeInsets.all(kPagePadding),
- child: LoadError(
- error: error,
- onRetry: () => ref.invalidate(eventDetailProvider(id)),
- ),
+ AsyncError(:final error) => ListView(
+ children: [
+ LoadErrorBlock(
+ error: error,
+ onRetry: () => ref.invalidate(eventDetailProvider(id)),
+ ),
+ ],
),
_ => const Center(child: CircularProgressIndicator()),
},
diff --git a/flutter/lib/features/event/presentation/pages/event_list_page.dart b/flutter/lib/features/event/presentation/pages/event_list_page.dart
index c058c2e..d72651d 100644
--- a/flutter/lib/features/event/presentation/pages/event_list_page.dart
+++ b/flutter/lib/features/event/presentation/pages/event_list_page.dart
@@ -92,7 +92,14 @@ class _EventListPageState extends ConsumerState {
}
Widget _body(BuildContext context) {
- if (_items.isEmpty && _error == null && !_hasNext) {
+ final error = _error;
+ if (_items.isEmpty && error != null) {
+ // 목록 전체를 못 불러왔다 — 공통 오류 화면(시안 상태 2)
+ return ListView(
+ children: [LoadErrorBlock(error: error, onRetry: _loadMore)],
+ );
+ }
+ if (_items.isEmpty && error == null && !_hasNext) {
return Center(
child: Text(
context.l10n.homeEventsEmpty,
diff --git a/flutter/lib/features/event/presentation/widgets/event_card.dart b/flutter/lib/features/event/presentation/widgets/event_card.dart
index 75f5936..3ef455d 100644
--- a/flutter/lib/features/event/presentation/widgets/event_card.dart
+++ b/flutter/lib/features/event/presentation/widgets/event_card.dart
@@ -1,8 +1,10 @@
import 'package:flutter/material.dart';
+import 'package:wandergo/core/icons/app_icons.dart';
import 'package:wandergo/core/l10n/l10n.dart';
import 'package:wandergo/core/theme/app_colors.dart';
import 'package:wandergo/core/utils/format.dart';
+import 'package:wandergo/core/utils/keep_all.dart';
import 'package:wandergo/features/event/data/models/event_models.dart';
import 'package:wandergo/features/event/presentation/widgets/event_visuals.dart';
@@ -117,8 +119,8 @@ class EventRow extends StatelessWidget {
),
const SizedBox(height: 4),
Text(
- item.name,
- style: textTheme.titleSmall,
+ keepAll(item.name),
+ style: textTheme.titleSmall?.copyWith(height: 20 / 15),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
@@ -127,6 +129,13 @@ class EventRow extends StatelessWidget {
],
),
),
+ const SizedBox(width: 8),
+ const AppIcon(
+ AppIconData.chevronRight,
+ size: 18,
+ strokeWidth: 2,
+ color: AppColors.iconMuted,
+ ),
],
),
),
diff --git a/flutter/lib/features/home/presentation/pages/home_page.dart b/flutter/lib/features/home/presentation/pages/home_page.dart
index 9d2588c..7ec1e92 100644
--- a/flutter/lib/features/home/presentation/pages/home_page.dart
+++ b/flutter/lib/features/home/presentation/pages/home_page.dart
@@ -315,8 +315,8 @@ class _Footer extends StatelessWidget {
children: [
const Divider(),
const SizedBox(height: 12),
+ // 출처와 방문 전 확인 안내는 한 문단(시안
)
Text(keepAll(l10n.footerSources), style: style),
- const SizedBox(height: 10),
Text(keepAll(l10n.footerNotice), style: style),
const SizedBox(height: 10),
Text('© ${l10n.appTitle}', style: style),
diff --git a/flutter/lib/features/home/presentation/widgets/category_grid.dart b/flutter/lib/features/home/presentation/widgets/category_grid.dart
index 9e18c46..61937f1 100644
--- a/flutter/lib/features/home/presentation/widgets/category_grid.dart
+++ b/flutter/lib/features/home/presentation/widgets/category_grid.dart
@@ -86,7 +86,9 @@ class CategoryGrid extends ConsumerWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- for (var c = 0; c < _kColumns; c++)
+ for (var c = 0; c < _kColumns; c++) ...[
+ // 열 사이 8(시안 gap 20px 8px)
+ if (c > 0) const SizedBox(width: 8),
Expanded(
child: c < rows[r].length
? _CategoryCell(
@@ -97,6 +99,7 @@ class CategoryGrid extends ConsumerWidget {
)
: const SizedBox.shrink(),
),
+ ],
],
),
],
diff --git a/flutter/lib/features/onboarding/presentation/pages/onboarding_page.dart b/flutter/lib/features/onboarding/presentation/pages/onboarding_page.dart
index e7304be..b8ebb65 100644
--- a/flutter/lib/features/onboarding/presentation/pages/onboarding_page.dart
+++ b/flutter/lib/features/onboarding/presentation/pages/onboarding_page.dart
@@ -13,6 +13,7 @@ import 'package:wandergo/features/location/domain/base_location.dart';
import 'package:wandergo/features/location/presentation/providers/base_location_provider.dart';
import 'package:wandergo/features/location/presentation/widgets/region_sheet.dart';
import 'package:wandergo/features/onboarding/presentation/providers/onboarding_provider.dart';
+import 'package:wandergo/shared/widgets/character_image.dart';
import 'package:wandergo/shared/widgets/language_button.dart';
/// 2 첫 실행 · 위치 권한 — 현재 위치를 쓸지, 동네를 직접 고를지 정한다
@@ -71,31 +72,36 @@ class _OnboardingPageState extends ConsumerState {
child: LanguageButton(compact: false),
),
),
+ // 그림 · 제목 · 설명은 위 줄과 아래 묶음 사이의 세로 가운데(짧은 화면에서는 넘긴다)
Expanded(
- child: SingleChildScrollView(
- padding: const EdgeInsets.symmetric(horizontal: 28),
- child: Column(
- children: [
- const SizedBox(height: 40),
- Image.asset(
- 'assets/images/character.png',
- width: 176,
- height: 128,
- excludeFromSemantics: true,
+ child: LayoutBuilder(
+ builder: (context, constraints) => SingleChildScrollView(
+ padding: const EdgeInsets.symmetric(horizontal: 28),
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ minHeight: constraints.maxHeight,
),
- const SizedBox(height: 24),
- Text(
- keepAll(l10n.onboardingTitle),
- style: textTheme.headlineSmall,
- textAlign: TextAlign.center,
+ child: Center(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const CharacterImage(width: 176, height: 128),
+ const SizedBox(height: 24),
+ Text(
+ keepAll(l10n.onboardingTitle),
+ style: textTheme.headlineSmall,
+ textAlign: TextAlign.center,
+ ),
+ const SizedBox(height: 12),
+ Text(
+ keepAll(l10n.onboardingBody),
+ style: textTheme.bodyLarge,
+ textAlign: TextAlign.center,
+ ),
+ ],
+ ),
),
- const SizedBox(height: 12),
- Text(
- keepAll(l10n.onboardingBody),
- style: textTheme.bodyLarge,
- textAlign: TextAlign.center,
- ),
- ],
+ ),
),
),
),
@@ -117,6 +123,10 @@ class _OnboardingPageState extends ConsumerState {
const SizedBox(height: 4),
TextButton(
onPressed: _busy ? null : _pickRegion,
+ // 누르는 칸은 44(Material 기본 48 탭 영역은 쓰지 않는다)
+ style: TextButton.styleFrom(
+ tapTargetSize: MaterialTapTargetSize.shrinkWrap,
+ ),
child: Text(l10n.onboardingPickRegion),
),
],
diff --git a/flutter/lib/features/place/presentation/pages/place_detail_page.dart b/flutter/lib/features/place/presentation/pages/place_detail_page.dart
index 11010bf..85edd5b 100644
--- a/flutter/lib/features/place/presentation/pages/place_detail_page.dart
+++ b/flutter/lib/features/place/presentation/pages/place_detail_page.dart
@@ -48,12 +48,9 @@ class PlaceDetailPage extends ConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(padding: EdgeInsets.all(12), child: _BackButton()),
- Padding(
- padding: const EdgeInsets.all(kPagePadding),
- child: LoadError(
- error: error,
- onRetry: () => ref.invalidate(placeDetailProvider(id)),
- ),
+ LoadErrorBlock(
+ error: error,
+ onRetry: () => ref.invalidate(placeDetailProvider(id)),
),
],
),
@@ -251,22 +248,23 @@ class _TopBar extends StatelessWidget {
}
}
-/// 흰 원 안의 뒤로 버튼 — 사진 위에서도 보이게 옅은 그림자
+/// 흰 원 안의 뒤로 버튼 — 지름 44, 그림자 없음(시안)
class _BackButton extends StatelessWidget {
const _BackButton();
@override
Widget build(BuildContext context) {
- return Material(
- color: AppColors.background,
- shape: const CircleBorder(),
- elevation: 1,
- shadowColor: AppColors.text.withValues(alpha: 0.4),
- child: IconButton(
- tooltip: context.l10n.back,
- onPressed: () => context.pop(),
- icon: const AppIcon(AppIconData.chevronLeft, color: AppColors.text),
+ return IconButton(
+ tooltip: context.l10n.back,
+ onPressed: () => context.pop(),
+ style: IconButton.styleFrom(
+ backgroundColor: AppColors.background,
+ fixedSize: const Size.square(44),
+ minimumSize: const Size.square(44),
+ padding: EdgeInsets.zero,
+ tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
+ icon: const AppIcon(AppIconData.chevronLeft, color: AppColors.text),
);
}
}
@@ -332,7 +330,7 @@ class _Hero extends StatelessWidget {
Positioned(
left: kPagePadding,
right: kPagePadding,
- bottom: _kSheetOverlap + 12,
+ bottom: _kSheetOverlap + 14,
child: Text(
l10n.photoCredit(
detail.attribution.provider,
@@ -371,7 +369,8 @@ class _TitleBlock extends ConsumerWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- if (meta.isNotEmpty) Text(meta, style: textTheme.bodyMedium),
+ if (meta.isNotEmpty)
+ Text(meta, style: textTheme.bodyMedium?.copyWith(height: 18 / 13)),
const SizedBox(height: 6),
Text(
keepAll(item.displayName),
@@ -383,7 +382,10 @@ class _TitleBlock extends ConsumerWidget {
],
if (distance != null) ...[
const SizedBox(height: 6),
- Text(distance, style: textTheme.bodyMedium),
+ Text(
+ distance,
+ style: textTheme.bodyMedium?.copyWith(height: 18 / 13),
+ ),
],
],
);
diff --git a/flutter/lib/features/place/presentation/pages/place_search_page.dart b/flutter/lib/features/place/presentation/pages/place_search_page.dart
index 14dedda..63850c7 100644
--- a/flutter/lib/features/place/presentation/pages/place_search_page.dart
+++ b/flutter/lib/features/place/presentation/pages/place_search_page.dart
@@ -16,6 +16,7 @@ import 'package:wandergo/features/place/data/repositories/place_repository.dart'
import 'package:wandergo/features/place/domain/place_search.dart';
import 'package:wandergo/features/place/presentation/widgets/place_tiles.dart';
import 'package:wandergo/features/place/presentation/widgets/place_visuals.dart';
+import 'package:wandergo/shared/widgets/app_back_button.dart';
import 'package:wandergo/shared/widgets/load_error.dart';
/// 한 번에 받는 수
@@ -179,12 +180,9 @@ class _PlaceSearchPageState extends ConsumerState {
final error = _error;
if (_items.isEmpty) {
if (error != null) {
- return Padding(
- padding: const EdgeInsets.all(kPagePadding),
- child: Align(
- alignment: Alignment.topCenter,
- child: LoadError(error: error, onRetry: _loadMore),
- ),
+ // 결과 전체를 못 불러왔다 — 공통 오류 화면(시안 상태 2)
+ return ListView(
+ children: [LoadErrorBlock(error: error, onRetry: _loadMore)],
);
}
if (!_hasNext && !_loading) {
@@ -261,11 +259,7 @@ class _SearchHeader extends StatelessWidget {
padding: const EdgeInsets.fromLTRB(8, 8, 16, 8),
child: Row(
children: [
- IconButton(
- tooltip: l10n.back,
- onPressed: () => context.pop(),
- icon: const AppIcon(AppIconData.chevronLeft, color: AppColors.text),
- ),
+ AppBackButton(onPressed: () => context.pop()),
const SizedBox(width: 4),
Expanded(
child: SizedBox(
@@ -293,24 +287,35 @@ class _SearchHeader extends StatelessWidget {
),
),
prefixIconConstraints: const BoxConstraints(),
+ // 지우기 — 누르는 칸 36, 오른쪽 여백 4(시안 입력창 padding 0 4 0 12)
+ suffixIconConstraints: const BoxConstraints(),
suffixIcon: controller.text.isEmpty
? null
- : IconButton(
- tooltip: l10n.searchClear,
- onPressed: onClear,
- icon: Container(
- width: 18,
- height: 18,
- decoration: const BoxDecoration(
- color: AppColors.textHint,
- shape: BoxShape.circle,
+ : Padding(
+ padding: const EdgeInsets.only(right: 4),
+ child: IconButton(
+ tooltip: l10n.searchClear,
+ onPressed: onClear,
+ style: IconButton.styleFrom(
+ fixedSize: const Size.square(36),
+ minimumSize: const Size.square(36),
+ padding: EdgeInsets.zero,
+ tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
- alignment: Alignment.center,
- child: const AppIcon(
- AppIconData.close,
- size: 10,
- color: Colors.white,
- strokeWidth: 3.5,
+ icon: Container(
+ width: 18,
+ height: 18,
+ decoration: const BoxDecoration(
+ color: AppColors.clearFill,
+ shape: BoxShape.circle,
+ ),
+ alignment: Alignment.center,
+ child: const AppIcon(
+ AppIconData.close,
+ size: 10,
+ color: Colors.white,
+ strokeWidth: 3.5,
+ ),
),
),
),
@@ -523,7 +528,7 @@ class _EmptyResult extends StatelessWidget {
child: const AppIcon(
AppIconData.search,
size: 30,
- color: AppColors.textHint,
+ color: AppColors.iconMuted,
),
),
),
diff --git a/flutter/lib/features/place/presentation/widgets/place_detail_sections.dart b/flutter/lib/features/place/presentation/widgets/place_detail_sections.dart
index 651beec..10a46dd 100644
--- a/flutter/lib/features/place/presentation/widgets/place_detail_sections.dart
+++ b/flutter/lib/features/place/presentation/widgets/place_detail_sections.dart
@@ -20,8 +20,9 @@ import 'package:wandergo/shared/utils/open_link.dart';
/// 관광통역안내 전화(영어·일본어·중국어 24시간) — 영어 화면에만 보인다(2026-10-29 합의)
const String kTravelHotline = '1330';
-/// 이용 안내 왼쪽 이름 칸 너비
-const double _kInfoLabelWidth = 72;
+/// 이용 안내 왼쪽 이름 칸 너비 — 한국어 56, 영어 72(시안)
+double _infoLabelWidth(BuildContext context) =>
+ context.l10n.localeName == 'ko' ? 56 : 72;
/// 이용 안내 오른쪽 동작 칸 너비 — 글자(복사·전화) · 아이콘(외부 링크)
const double _kTextActionWidth = 56;
@@ -113,7 +114,7 @@ class PlaceGlance extends StatelessWidget {
),
const SizedBox(height: 8),
Text(
- fact.label,
+ keepAll(fact.label),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
@@ -411,8 +412,11 @@ class _InfoRow extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
- width: _kInfoLabelWidth,
- child: Text(label, style: textTheme.bodyMedium),
+ width: _infoLabelWidth(context),
+ child: Text(
+ keepAll(label),
+ style: textTheme.bodyMedium?.copyWith(height: 20 / 13),
+ ),
),
const SizedBox(width: 12),
Expanded(
@@ -431,7 +435,10 @@ class _InfoRow extends StatelessWidget {
),
if (secondary != null) ...[
const SizedBox(height: 2),
- Text(keepAll(secondary!), style: textTheme.bodySmall),
+ Text(
+ keepAll(secondary!),
+ style: textTheme.bodySmall?.copyWith(height: 17 / 12),
+ ),
],
],
),
@@ -483,6 +490,8 @@ class _TextAction extends StatelessWidget {
foregroundColor: AppColors.brand,
padding: const EdgeInsets.only(left: 8),
minimumSize: const Size(44, 44),
+ // 글자를 오른쪽 끝에 붙인다(시안 padding-left 8)
+ alignment: Alignment.centerRight,
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: Text(label),
@@ -505,6 +514,12 @@ class _LinkAction extends StatelessWidget {
child: IconButton(
tooltip: tooltip,
onPressed: onPressed,
+ // 아이콘을 오른쪽 끝에 붙인다
+ style: IconButton.styleFrom(
+ fixedSize: const Size.square(44),
+ padding: EdgeInsets.zero,
+ alignment: Alignment.centerRight,
+ ),
icon: const AppIcon(
AppIconData.externalLink,
size: 16,
@@ -576,7 +591,7 @@ class _PlaceAboutSectionState extends State {
Widget build(BuildContext context) {
final l10n = context.l10n;
final textTheme = Theme.of(context).textTheme;
- final style = textTheme.bodyLarge?.copyWith(fontSize: 14, height: 22 / 14);
+ final style = textTheme.bodyLarge?.copyWith(fontSize: 14, height: 23 / 14);
final text = keepAll(widget.text);
return _Section(
@@ -607,7 +622,7 @@ class _PlaceAboutSectionState extends State {
Expanded(
child: Text(
l10n.sourceLabel(widget.provider),
- style: textTheme.bodySmall,
+ style: textTheme.bodySmall?.copyWith(height: 17 / 12),
),
),
if (overflows || _expanded)
@@ -616,9 +631,23 @@ class _PlaceAboutSectionState extends State {
style: TextButton.styleFrom(
foregroundColor: AppColors.textSecondary,
padding: EdgeInsets.zero,
+ tapTargetSize: MaterialTapTargetSize.shrinkWrap,
textStyle: textTheme.labelLarge,
),
- child: Text(_expanded ? l10n.showLess : l10n.showMore),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(_expanded ? l10n.showLess : l10n.showMore),
+ const SizedBox(width: 2),
+ AppIcon(
+ _expanded
+ ? AppIconData.chevronUp
+ : AppIconData.chevronDown,
+ size: 14,
+ color: AppColors.textSecondary,
+ ),
+ ],
+ ),
),
],
),
diff --git a/flutter/lib/features/place/presentation/widgets/place_tiles.dart b/flutter/lib/features/place/presentation/widgets/place_tiles.dart
index 176f3d0..37b32b5 100644
--- a/flutter/lib/features/place/presentation/widgets/place_tiles.dart
+++ b/flutter/lib/features/place/presentation/widgets/place_tiles.dart
@@ -73,6 +73,7 @@ class PlaceRow extends StatelessWidget {
],
),
style: textTheme.bodyMedium?.copyWith(
+ height: 18 / 13,
color: AppColors.textSecondary,
),
maxLines: 1,
@@ -128,7 +129,7 @@ class PetPlaceRow extends StatelessWidget {
children: [
Text(
item.displayName,
- style: textTheme.titleSmall,
+ style: textTheme.titleSmall?.copyWith(height: 20 / 15),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
@@ -137,6 +138,7 @@ class PetPlaceRow extends StatelessWidget {
Text(
meta,
style: textTheme.bodyMedium?.copyWith(
+ height: 18 / 13,
color: AppColors.textSecondary,
),
),
@@ -146,7 +148,7 @@ class PetPlaceRow extends StatelessWidget {
Text.rich(
TextSpan(
children: [
- petSpan(pet, fontSize: 12),
+ petSpan(pet, fontSize: 12, iconSize: 13),
if (verifiedAt != null)
TextSpan(
text: ' · ${l10n.placeVerifiedOn(verifiedAt)}',
@@ -202,7 +204,7 @@ class PlaceCard extends StatelessWidget {
const SizedBox(height: 10),
Text(
item.displayName,
- style: textTheme.titleSmall,
+ style: textTheme.titleSmall?.copyWith(height: 20 / 15),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
@@ -215,7 +217,7 @@ class PlaceCard extends StatelessWidget {
),
if (pet != null) ...[
const SizedBox(height: 4),
- PetLabel(text: pet, fontSize: 12),
+ PetLabel(text: pet, fontSize: 12, iconSize: 13),
],
],
),
diff --git a/flutter/lib/features/place/presentation/widgets/place_visuals.dart b/flutter/lib/features/place/presentation/widgets/place_visuals.dart
index 6a55eba..d754946 100644
--- a/flutter/lib/features/place/presentation/widgets/place_visuals.dart
+++ b/flutter/lib/features/place/presentation/widgets/place_visuals.dart
@@ -169,42 +169,51 @@ class PlaceImage extends StatelessWidget {
/// 반려동물 동반 글자 — 꽉 찬 발바닥 + 초록 굵은 글자(배지 없이 글자에 녹인다)
class PetLabel extends StatelessWidget {
- const PetLabel({super.key, required this.text, this.fontSize = 13});
+ const PetLabel({
+ super.key,
+ required this.text,
+ this.fontSize = 13,
+ this.iconSize,
+ });
final String text;
final double fontSize;
+ /// 없으면 글자 크기
+ final double? iconSize;
+
@override
Widget build(BuildContext context) {
return Text.rich(
- petSpan(text, fontSize: fontSize),
+ petSpan(text, fontSize: fontSize, iconSize: iconSize),
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
}
}
-/// 다른 글자 사이에 넣는 반려동물 동반 글자
-InlineSpan petSpan(String text, {double fontSize = 13}) => TextSpan(
- children: [
- WidgetSpan(
- alignment: PlaceholderAlignment.middle,
- child: Padding(
- padding: const EdgeInsets.only(right: 4),
- child: AppIcon(
- AppIconData.pawSolid,
- size: fontSize,
- color: AppColors.pet,
- ),
- ),
- ),
+/// 다른 글자 사이에 넣는 반려동물 동반 글자 — 발바닥은 [iconSize](없으면 글자 크기)
+InlineSpan petSpan(String text, {double fontSize = 13, double? iconSize}) =>
TextSpan(
- text: text,
- style: TextStyle(
- fontSize: fontSize,
- fontWeight: FontWeight.w600,
- color: AppColors.pet,
- ),
- ),
- ],
-);
+ children: [
+ WidgetSpan(
+ alignment: PlaceholderAlignment.middle,
+ child: Padding(
+ padding: const EdgeInsets.only(right: 4),
+ child: AppIcon(
+ AppIconData.pawSolid,
+ size: iconSize ?? fontSize,
+ color: AppColors.pet,
+ ),
+ ),
+ ),
+ TextSpan(
+ text: text,
+ style: TextStyle(
+ fontSize: fontSize,
+ fontWeight: FontWeight.w600,
+ color: AppColors.pet,
+ ),
+ ),
+ ],
+ );
diff --git a/flutter/lib/shared/widgets/language_button.dart b/flutter/lib/shared/widgets/language_button.dart
index 18f0a69..4227df0 100644
--- a/flutter/lib/shared/widgets/language_button.dart
+++ b/flutter/lib/shared/widgets/language_button.dart
@@ -28,6 +28,8 @@ class LanguageButton extends ConsumerWidget {
style: OutlinedButton.styleFrom(
minimumSize: Size(44, compact ? 44 : 40),
padding: EdgeInsets.only(left: 12, right: compact ? 12 : 10),
+ // 보이는 크기 그대로 자리를 잡는다(Material 기본 48 탭 영역 없이, 시안 머리 높이)
+ tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: Row(
mainAxisSize: MainAxisSize.min,
diff --git a/flutter/lib/shared/widgets/section_header.dart b/flutter/lib/shared/widgets/section_header.dart
index 3f7ce77..5b294a5 100644
--- a/flutter/lib/shared/widgets/section_header.dart
+++ b/flutter/lib/shared/widgets/section_header.dart
@@ -25,6 +25,8 @@ class SectionHeader extends StatelessWidget {
onPressed: onSeeAll,
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
+ // 누르는 칸은 44 를 지키되 Material 기본 48 탭 영역은 쓰지 않는다(시안 머리 높이에 가깝게)
+ tapTargetSize: MaterialTapTargetSize.shrinkWrap,
textStyle: textTheme.bodyMedium,
),
child: Row(
diff --git a/flutter/test/widget_test.dart b/flutter/test/widget_test.dart
index 01a6dcc..791d787 100644
--- a/flutter/test/widget_test.dart
+++ b/flutter/test/widget_test.dart
@@ -362,8 +362,8 @@ void main() {
expect(_text('목줄 착용'), findsOneWidget);
expect(_text('실내 시설은 동반 불가'), findsOneWidget);
// 상시 개방은 한눈에 보기에 오르고, 주차 원문이 불가능이면 오르지 않는다
- expect(find.text('상시 개방'), findsWidgets);
- expect(find.text('주차 가능'), findsNothing);
+ expect(_text('상시 개방'), findsWidgets);
+ expect(_text('주차 가능'), findsNothing);
expect(find.text('출처: 한국관광공사 · 2026-06-26 기준'), findsOneWidget);
});