feat: adopt material 3 theme and billing adjustments

This commit is contained in:
JiWoong Sul
2025-09-16 14:30:14 +09:00
parent a01d9092ba
commit 44850a53cc
85 changed files with 2957 additions and 2776 deletions

View File

@@ -18,7 +18,21 @@ class AnimatedWaveBackground extends StatelessWidget {
@override
Widget build(BuildContext context) {
final reduce = ReduceMotion.isEnabled(context);
final amp = reduce ? 0.3 : 1.0; // 효과 강도 스케일
final amp = reduce ? 0.3 : 1.0; // 기본 효과 강도 스케일
// 원 크기에 따라 속도/진폭 스케일을 동적으로 계산
// size가 클수록 느리고(차분), 작을수록 빠르고(활발) 크게 움직이게 함
MotionParams paramsFor(double size) {
const ref = 160.0; // 기준 크기
// 진폭 스케일: 0.6 ~ 1.4 사이 (연속)
final ampScale = _clamp(ref / size, 0.6, 1.4) * (reduce ? 0.6 : 1.0);
// 속도 배수: 1~3의 정수로 제한하여 래핑 시 연속성 보장
final raw = 0.8 + (ref / size) * 0.6; // 약 0.8~1.4 범위
int speedMult = raw < 1.2 ? 1 : (raw < 1.6 ? 2 : 3);
if (reduce && speedMult > 2) speedMult = 2; // 감속 모드 상한
return MotionParams(ampScale: ampScale, speedMult: speedMult);
}
return Stack(
children: [
// 웨이브 애니메이션 배경 요소 - 사인/코사인 함수 대신 더 부드러운 곡선 사용
@@ -26,22 +40,26 @@ class AnimatedWaveBackground extends StatelessWidget {
animation: controller,
builder: (context, child) {
// 0~1 사이의 값을 0~2π 사이의 값으로 변환하여 부드러운 주기 생성
final angle = controller.value * 2 * math.pi;
// 사인 함수를 사용하여 부드러운 움직임 생성
final xOffset = 20 * amp * math.sin(angle);
final yOffset = 10 * amp * math.cos(angle);
final p = paramsFor(200);
final angle = controller.value * 2 * math.pi * p.speedMult;
// 사인 함수를 사용하여 부드러운 움직임 생성 (큰 원: 차분)
final xOffset = 20 * amp * p.ampScale * math.sin(angle);
final yOffset = 10 * amp * p.ampScale * math.cos(angle);
return Positioned(
right: -40 + xOffset,
top: -60 + yOffset,
child: Transform.rotate(
// 회전도 선형적으로 변화하도록 수정
angle: 0.2 * amp * math.sin(angle * 0.5),
angle: 0.2 * amp * p.ampScale * math.sin(angle * 0.5),
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.1),
color: Theme.of(context)
.colorScheme
.onSurface
.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(100),
),
),
@@ -53,21 +71,26 @@ class AnimatedWaveBackground extends StatelessWidget {
animation: controller,
builder: (context, child) {
// 첫 번째 원과 약간 다른 위상을 가지도록 설정
final angle = (controller.value * 2 * math.pi) + (math.pi / 3);
final xOffset = 20 * amp * math.cos(angle);
final yOffset = 10 * amp * math.sin(angle);
final p = paramsFor(220);
final angle =
(controller.value * 2 * math.pi * p.speedMult) + (math.pi / 3);
final xOffset = 20 * amp * p.ampScale * math.cos(angle);
final yOffset = 10 * amp * p.ampScale * math.sin(angle);
return Positioned(
left: -80 + xOffset,
bottom: -70 + yOffset,
child: Transform.rotate(
// 반대 방향으로 회전하도록 설정
angle: -0.3 * amp * math.sin(angle * 0.5),
angle: -0.3 * amp * p.ampScale * math.sin(angle * 0.5),
child: Container(
width: 220,
height: 220,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.05),
color: Theme.of(context)
.colorScheme
.onSurface
.withValues(alpha: 0.05),
borderRadius: BorderRadius.circular(110),
),
),
@@ -80,20 +103,25 @@ class AnimatedWaveBackground extends StatelessWidget {
animation: controller,
builder: (context, child) {
// 세 번째 원은 다른 위상으로 움직이도록 설정
final angle = (controller.value * 2 * math.pi) + (math.pi * 2 / 3);
final xOffset = 15 * amp * math.sin(angle * 0.7);
final yOffset = 8 * amp * math.cos(angle * 0.7);
final p = paramsFor(120);
final angle = (controller.value * 2 * math.pi * p.speedMult) +
(math.pi * 2 / 3);
final xOffset = 15 * amp * p.ampScale * math.sin(angle * 0.9);
final yOffset = 8 * amp * p.ampScale * math.cos(angle * 0.9);
return Positioned(
right: 40 + xOffset,
bottom: -40 + yOffset,
child: Transform.rotate(
angle: 0.4 * amp * math.cos(angle * 0.5),
angle: 0.4 * amp * p.ampScale * math.cos(angle * 0.5),
child: Container(
width: 120,
height: 120,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.08),
color: Theme.of(context)
.colorScheme
.onSurface
.withValues(alpha: 0.06),
borderRadius: BorderRadius.circular(60),
),
),
@@ -112,7 +140,7 @@ class AnimatedWaveBackground extends StatelessWidget {
width: 30,
height: 30,
decoration: BoxDecoration(
color: Colors.white.withValues(
color: Theme.of(context).colorScheme.onSurface.withValues(
alpha: reduce ? 0.08 : 0.1 + 0.1 * pulseController.value),
borderRadius: BorderRadius.circular(15),
),
@@ -124,3 +152,13 @@ class AnimatedWaveBackground extends StatelessWidget {
);
}
}
// 내부 유틸리티: 값 범위 제한
double _clamp(double v, double min, double max) =>
v < min ? min : (v > max ? max : v);
class MotionParams {
final double ampScale;
final int speedMult;
MotionParams({required this.ampScale, required this.speedMult});
}