generated from ttipo/project-base
fix(flutter): 함께 가기 좋은 곳 · 화면 읽기 버튼 · 사진 메모리 수정 #5
@@ -62,4 +62,6 @@ lib/
|
||||
- 공공누리 Type3 사진은 자르지 않는다(`PublicImage` 가 contain 으로 보인다 — 행사·장소 사진 모두 이 위젯을 거친다).
|
||||
- 영업시간을 모르는 장소는 "영업시간 미확인 · 방문 전 확인"으로 보이고, 확인 안 된 상가정보 매장은 "함께 가기 좋은 곳"에 고르지 않는다
|
||||
(서버가 `trusted=true` 로 거른다 — 앱에서 거르면 상가가 빽빽한 도심에서 칸이 비었다).
|
||||
- `Semantics(excludeSemantics: true)` 로 버튼을 감싸면 안쪽 InkWell 의 누르기 동작까지 지워진다. Semantics 에도 `onTap` 을 같이 준다
|
||||
(빠뜨리면 TalkBack·VoiceOver 로 누를 수 없다 — `widget_test.dart` 의 화면 읽기 테스트가 잡는다).
|
||||
- 실패 문구는 에러 사전(`error_lexicon.g.dart`)에서만 가져온다 — `errorMessageOf()`.
|
||||
|
||||
@@ -70,11 +70,13 @@ class _TabItem extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final color = selected ? AppColors.brand : AppColors.textTertiary;
|
||||
// excludeSemantics 는 아래 InkResponse 의 누르기 동작까지 지운다 — 화면 읽기 프로그램이 누를 수 있게 onTap 을 함께 준다
|
||||
return Semantics(
|
||||
button: true,
|
||||
selected: selected,
|
||||
label: label,
|
||||
excludeSemantics: true,
|
||||
onTap: onTap,
|
||||
child: InkResponse(
|
||||
onTap: onTap,
|
||||
radius: 36,
|
||||
|
||||
@@ -132,17 +132,21 @@ class _BaseLocationButton extends ConsumerWidget {
|
||||
final label = fellBack
|
||||
? kFallbackNeighborhood.label(l10n)
|
||||
: choice.label(l10n);
|
||||
Future<void> pickBase() async {
|
||||
final picked = await showRegionSheet(context, selected: choice);
|
||||
if (picked != null && context.mounted) {
|
||||
await applyBaseLocation(context, ref, picked);
|
||||
}
|
||||
}
|
||||
|
||||
// excludeSemantics 는 InkWell 의 누르기 동작까지 지운다 — 화면 읽기 프로그램이 누를 수 있게 onTap 을 함께 준다
|
||||
return Semantics(
|
||||
button: true,
|
||||
label: l10n.baseLocationButtonLabel(label),
|
||||
excludeSemantics: true,
|
||||
onTap: pickBase,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
final picked = await showRegionSheet(context, selected: choice);
|
||||
if (picked != null && context.mounted) {
|
||||
await applyBaseLocation(context, ref, picked);
|
||||
}
|
||||
},
|
||||
onTap: pickBase,
|
||||
child: SizedBox(
|
||||
height: 32,
|
||||
child: Row(
|
||||
|
||||
@@ -115,10 +115,12 @@ class _CategoryCell extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final label = shortcut.label(context.l10n);
|
||||
// excludeSemantics 는 InkWell 의 누르기 동작까지 지운다 — 화면 읽기 프로그램이 누를 수 있게 onTap 을 함께 준다
|
||||
return Semantics(
|
||||
button: true,
|
||||
label: label,
|
||||
excludeSemantics: true,
|
||||
onTap: onTap,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
|
||||
@@ -14,16 +14,19 @@ class HomeSearchBar extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final hint = context.l10n.searchHint;
|
||||
void openSearch() => context.push(AppRoutes.placeSearch(focus: true));
|
||||
// excludeSemantics 는 InkWell 의 누르기 동작까지 지운다 — 화면 읽기 프로그램이 누를 수 있게 onTap 을 함께 준다
|
||||
return Semantics(
|
||||
button: true,
|
||||
label: hint,
|
||||
excludeSemantics: true,
|
||||
onTap: openSearch,
|
||||
child: Material(
|
||||
color: AppColors.inputFill,
|
||||
borderRadius: BorderRadius.circular(kRadius),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(kRadius),
|
||||
onTap: () => context.push(AppRoutes.placeSearch(focus: true)),
|
||||
onTap: openSearch,
|
||||
child: SizedBox(
|
||||
height: 48,
|
||||
child: Row(
|
||||
|
||||
@@ -427,17 +427,20 @@ class FilterToggleChip extends StatelessWidget {
|
||||
color: selected ? AppColors.chipBorderSelected : AppColors.chipBorder,
|
||||
),
|
||||
);
|
||||
void toggle() => onChanged(!selected);
|
||||
// excludeSemantics 는 InkWell 의 누르기 동작까지 지운다 — 화면 읽기 프로그램이 누를 수 있게 onTap 을 함께 준다
|
||||
return Semantics(
|
||||
toggled: selected,
|
||||
button: true,
|
||||
excludeSemantics: true,
|
||||
label: label,
|
||||
onTap: toggle,
|
||||
child: Material(
|
||||
color: selected ? AppColors.brandSoft : AppColors.background,
|
||||
shape: shape,
|
||||
child: InkWell(
|
||||
customBorder: shape,
|
||||
onTap: () => onChanged(!selected),
|
||||
onTap: toggle,
|
||||
child: Container(
|
||||
height: 36,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
|
||||
@@ -16,12 +16,15 @@ class LanguageButton extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final l10n = context.l10n;
|
||||
void openSheet() => _openSheet(context, ref);
|
||||
// excludeSemantics 는 버튼의 누르기 동작까지 지운다 — 화면 읽기 프로그램이 누를 수 있게 onTap 을 함께 준다
|
||||
return Semantics(
|
||||
button: true,
|
||||
label: l10n.languageButtonLabel(l10n.languageName),
|
||||
excludeSemantics: true,
|
||||
onTap: openSheet,
|
||||
child: OutlinedButton(
|
||||
onPressed: () => _openSheet(context, ref),
|
||||
onPressed: openSheet,
|
||||
style: OutlinedButton.styleFrom(
|
||||
minimumSize: Size(44, compact ? 44 : 40),
|
||||
padding: EdgeInsets.only(left: 12, right: compact ? 12 : 10),
|
||||
|
||||
@@ -443,4 +443,36 @@ void main() {
|
||||
expect(find.text('대구근대골목'), findsOneWidget);
|
||||
expect(find.text('마실커피 동성로점'), findsNothing);
|
||||
});
|
||||
|
||||
// 2026-11-04 회귀 점검: excludeSemantics 가 누르기 동작까지 지워 TalkBack 으로 누를 수 없었다
|
||||
testWidgets('홈·검색의 버튼은 화면 읽기 프로그램으로도 누를 수 있다', (tester) async {
|
||||
final semantics = tester.ensureSemantics();
|
||||
await _pumpApp(
|
||||
tester,
|
||||
prefs: {'onboarding_done': true},
|
||||
places: _FakePlaceRepository(places: [_storeCafe]),
|
||||
);
|
||||
|
||||
for (final label in <Pattern>[
|
||||
'홈',
|
||||
'관광지',
|
||||
'장소 이름으로 검색',
|
||||
RegExp('^언어 변경'),
|
||||
RegExp('^기준 위치 변경'),
|
||||
]) {
|
||||
expect(
|
||||
tester.getSemantics(find.bySemanticsLabel(label).first),
|
||||
isSemantics(isButton: true, hasTapAction: true),
|
||||
reason: '$label',
|
||||
);
|
||||
}
|
||||
|
||||
await tester.tap(find.text('관광지'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
tester.getSemantics(find.bySemanticsLabel('반려견 동반')),
|
||||
isSemantics(isButton: true, hasTapAction: true),
|
||||
);
|
||||
semantics.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user