- 좌우 슬라이드 대신 맥북 런치패드처럼 확대+페이드로 나타나고 전환 중 뒤 화면이 블러 처리되는 LaunchpadPageRoute 추가, 대시보드의 모든 기능 타일 이동에 적용 - 오른쪽 위 설정 아이콘 + 로그아웃 아이콘 2개를 ">" 버튼 1개로 통합하고, 누르면 로그아웃/설정 메뉴가 런치패드 스타일 오버레이(배경 블러 유지)로 뜨게 변경 - 기능 타일을 누를 때 뜨는 안내 메시지를 스낵바 대신 알약(pill) 모양 알림으로 교체 (AppNotice) - 웹에서는 왼쪽 위, 앱에서는 기존처럼 아래쪽에 표시. 푸시 알림과는 무관. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
116 lines
4.5 KiB
Dart
116 lines
4.5 KiB
Dart
// 🚀 맥북 런치패드처럼 "앞에서 확대되며 나타나고, 뒤는 블러 처리되는" 전환 효과.
|
|
// 기존의 좌우 슬라이드 전환을 대체한다. 두 가지 쓰임새가 있다:
|
|
// 1) launchpadPageRoute — 전체 화면 이동(다른 기능 타일 진입)용. 전환 중에만 블러가 보이고,
|
|
// 화면이 다 뜨면 원래 화면(대시보드)은 완전히 가려진다.
|
|
// 2) showLaunchpadMenu — 설정/로그아웃 같은 작은 메뉴 오버레이용. 메뉴가 떠 있는 동안
|
|
// 뒤에 있는 대시보드가 계속 뿌옇게 보인다. 바깥을 탭하면 닫힌다.
|
|
import 'dart:ui';
|
|
import 'package:flutter/material.dart';
|
|
|
|
class LaunchpadPageRoute<T> extends PageRouteBuilder<T> {
|
|
LaunchpadPageRoute({required WidgetBuilder builder})
|
|
: super(
|
|
transitionDuration: const Duration(milliseconds: 320),
|
|
reverseTransitionDuration: const Duration(milliseconds: 240),
|
|
pageBuilder: (context, animation, secondaryAnimation) =>
|
|
builder(context),
|
|
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
|
final curved = CurvedAnimation(
|
|
parent: animation,
|
|
curve: Curves.easeOutCubic,
|
|
reverseCurve: Curves.easeInCubic,
|
|
);
|
|
return AnimatedBuilder(
|
|
animation: curved,
|
|
child: child,
|
|
builder: (context, child) {
|
|
final t = curved.value;
|
|
return Stack(
|
|
children: [
|
|
// 뒤에 있던 화면(대시보드)을 전환하는 동안만 뿌옇게 보여준다.
|
|
Positioned.fill(
|
|
child: IgnorePointer(
|
|
child: BackdropFilter(
|
|
filter: ImageFilter.blur(
|
|
sigmaX: 22 * t,
|
|
sigmaY: 22 * t,
|
|
),
|
|
child: Container(
|
|
color: Colors.black.withValues(alpha: 0.12 * t),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
Opacity(
|
|
opacity: t,
|
|
child: Transform.scale(
|
|
scale: 0.94 + 0.06 * t,
|
|
child: child,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
/// 화면 전체 이동에 런치패드 전환 효과를 적용한다. MaterialPageRoute 대신 이걸 쓰면 됨.
|
|
Future<T?> pushLaunchpad<T>(BuildContext context, WidgetBuilder builder) {
|
|
return Navigator.push<T>(context, LaunchpadPageRoute<T>(builder: builder));
|
|
}
|
|
|
|
/// 설정/로그아웃 같은 작은 메뉴를 런치패드 스타일(확대+페이드, 배경은 계속 블러 유지)로 띄운다.
|
|
/// 바깥을 탭하면 닫힌다. builder는 화면 전체를 채우는 Align 등으로 메뉴 위치를 직접 잡아야 한다.
|
|
Future<T?> showLaunchpadMenu<T>({
|
|
required BuildContext context,
|
|
required WidgetBuilder builder,
|
|
}) {
|
|
return showGeneralDialog<T>(
|
|
context: context,
|
|
barrierDismissible: true,
|
|
barrierLabel: 'menu',
|
|
barrierColor: Colors.transparent,
|
|
transitionDuration: const Duration(milliseconds: 260),
|
|
pageBuilder: (context, animation, secondaryAnimation) => builder(context),
|
|
transitionBuilder: (context, animation, secondaryAnimation, child) {
|
|
final curved = CurvedAnimation(
|
|
parent: animation,
|
|
curve: Curves.easeOutCubic,
|
|
reverseCurve: Curves.easeInCubic,
|
|
);
|
|
return AnimatedBuilder(
|
|
animation: curved,
|
|
child: child,
|
|
builder: (context, child) {
|
|
final t = curved.value;
|
|
return Stack(
|
|
children: [
|
|
// 메뉴가 떠 있는 동안 계속 뿌옇게 — 기능 화면이랑 헷갈리지 않게 진하게.
|
|
Positioned.fill(
|
|
child: IgnorePointer(
|
|
child: BackdropFilter(
|
|
filter: ImageFilter.blur(sigmaX: 32 * t, sigmaY: 32 * t),
|
|
child: Container(
|
|
color: Colors.black.withValues(alpha: 0.28 * t),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
Opacity(
|
|
opacity: t,
|
|
child: Transform.scale(
|
|
alignment: Alignment.topRight,
|
|
scale: 0.85 + 0.15 * t,
|
|
child: child,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|