feat(flutter): 공통 위젯 시안 맞춤 — 모달 배경 흐림·누름 효과·휠 직접 입력·제거 확인창

시안 전체 점검에서 여러 화면에 걸쳐 나온 차이를 공통 위젯에서 한 번에 고쳤다.

- 시트·확인창 뒤 화면을 시안(backdrop-filter blur)처럼 흐린다. showModalBottomSheet·showDialog
  대신 BlurredSheetRoute·showBlurredDialog 로 띄우고, 흐림은 등장 애니메이션을 따라간다.
- 탭바 배경 흐림에 채도(saturate 1.6)를 더하고, 탭 라벨은 본문 서체 12/800 으로 맞췄다.
- 누름 효과: Pressable 에 누름 밝기를 두어 인트로 흰 버튼은 살짝 줄고 어두워진다(.99·밝기 .94).
  하트 토글은 누르는 동안 손잡이가 움직이고 안쪽 그림자가 생긴다.
- 시간·날짜 휠의 가운데 숫자를 누르면 직접 입력할 수 있다 (범위를 넘으면 가장 가까운 값).
- 일정·다이어리 제거용 확인창(showRupRemoveConfirm)과 추억 보관함 토스트 색(ToastTone.memory) 추가.
- 버튼 줄 높이를 제목 서체 normal(1.10)로, 빈 상태 여백을 .rup-empty(위아래 40·좌우 20)로 맞추고,
  화면별로 필요한 선택지(작은 섹션 링크, 라벨 간격, 아이콘 달린 선택 버튼, 흐림 불투명도)를 열었다.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-29 01:24:24 +09:00
parent 03cb0224d6
commit d9a4eba9a3
15 changed files with 541 additions and 98 deletions
@@ -2,7 +2,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:tma_app/features/pet/domain/pet_archive.dart';
import 'package:tma_app/features/pet/presentation/providers/pet_providers.dart';
import 'package:tma_app/features/schedule/presentation/providers/schedule_providers.dart';
import 'package:tma_app/shared/widgets/icons/rup_icon.dart';
import 'package:tma_app/shared/widgets/rup_toast.dart';
/// 추억 보관함 — 옮기기 / 다시 불러오기
@@ -16,7 +15,7 @@ class ArchiveActions {
Future<void> archive(String petId, PetArchive archive) async {
await _ref.read(petListProvider.notifier).archivePet(petId, archive);
await _ref.read(scheduleProvider.notifier).dropTarget(petId);
_ref.read(toastProvider.notifier).show('추억보관함에 보관했어요.', icon: RupIconName.memory);
_ref.read(toastProvider.notifier).show('추억보관함에 보관했어요.', tone: ToastTone.memory);
}
/// 보관함에서 다시 불러온다 — 지운 일정은 돌아오지 않는다
@@ -3,12 +3,13 @@ import 'dart:ui' show ImageFilter;
import 'package:flutter/material.dart';
import 'package:tma_app/core/constants/app_colors.dart';
import 'package:tma_app/core/constants/app_sizes.dart';
import 'package:tma_app/core/theme/app_typography.dart';
import 'package:tma_app/features/shell/domain/app_tab.dart';
import 'package:tma_app/shared/widgets/grayscale.dart';
import 'package:tma_app/shared/widgets/icons/rup_icon.dart';
/// 시안 .rup-tabbar 의 뒤 배경 흐림 정도 (blur 18px → 표준편차 9)
const double _kBlurSigma = 9;
/// 시안 .rup-tabbar — backdrop-filter: blur(18px) saturate(160%). CSS blur 값이 곧 표준편차다
const double _kBlurSigma = 18;
const double _kSaturate = 1.6;
const double _kActiveStroke = 2.2;
const double _kInactiveStroke = 1.9;
@@ -27,7 +28,10 @@ class RupTabBar extends StatelessWidget {
Widget build(BuildContext context) {
return ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: _kBlurSigma, sigmaY: _kBlurSigma),
filter: ImageFilter.compose(
outer: saturateFilter(_kSaturate),
inner: ImageFilter.blur(sigmaX: _kBlurSigma, sigmaY: _kBlurSigma),
),
child: DecoratedBox(
decoration: const BoxDecoration(
color: AppColors.tabBarBg,
@@ -64,21 +68,6 @@ class RupTabBar extends StatelessWidget {
}
}
/// 제목 서체의 가운뎃점(·)은 폭이 넓고 왼쪽에 붙어 있다.
/// 브라우저는 문장부호 간격을 자동으로 줄이지만 Flutter 는 그대로 그리므로
/// 가운뎃점만 본문 서체로 바꿔 "일정·기록" 처럼 붙여 보이게 한다.
TextSpan _narrowDots(String label) {
final parts = label.split('·');
return TextSpan(
children: [
for (final (i, part) in parts.indexed) ...[
if (i > 0) const TextSpan(text: '·', style: TextStyle(fontFamily: AppFonts.ui)),
TextSpan(text: part),
],
],
);
}
class _TabItem extends StatelessWidget {
const _TabItem({required this.tab, required this.active, required this.onTap});
@@ -124,7 +113,7 @@ class _TabItem extends StatelessWidget {
color: color,
letterSpacing: 0,
),
child: Text.rich(_narrowDots(tab.label), maxLines: 1, softWrap: false),
child: Text(tab.label, maxLines: 1, softWrap: false),
),
],
),