feat(app): add manual entry and sharing flows

This commit is contained in:
JiWoong Sul
2025-11-19 16:36:39 +09:00
parent 5ade584370
commit 947fe59486
110 changed files with 5937 additions and 3781 deletions

View File

@@ -12,11 +12,12 @@ class SplashScreen extends StatefulWidget {
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> with TickerProviderStateMixin {
class _SplashScreenState extends State<SplashScreen>
with TickerProviderStateMixin {
late List<AnimationController> _foodControllers;
late AnimationController _questionMarkController;
late AnimationController _centerIconController;
final List<IconData> foodIcons = [
Icons.rice_bowl,
Icons.ramen_dining,
@@ -28,14 +29,14 @@ class _SplashScreenState extends State<SplashScreen> with TickerProviderStateMix
Icons.icecream,
Icons.bakery_dining,
];
@override
void initState() {
super.initState();
_initializeAnimations();
_navigateToHome();
}
void _initializeAnimations() {
// 음식 아이콘 애니메이션 (여러 개)
_foodControllers = List.generate(
@@ -45,31 +46,33 @@ class _SplashScreenState extends State<SplashScreen> with TickerProviderStateMix
vsync: this,
)..repeat(reverse: true),
);
// 물음표 애니메이션
_questionMarkController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
)..repeat();
// 중앙 아이콘 애니메이션
_centerIconController = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
)..repeat(reverse: true);
}
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
return Scaffold(
backgroundColor: isDark ? AppColors.darkBackground : AppColors.lightBackground,
backgroundColor: isDark
? AppColors.darkBackground
: AppColors.lightBackground,
body: Stack(
children: [
// 랜덤 위치 음식 아이콘들
..._buildFoodIcons(),
// 중앙 컨텐츠
Center(
child: Column(
@@ -86,23 +89,25 @@ class _SplashScreenState extends State<SplashScreen> with TickerProviderStateMix
child: Icon(
Icons.restaurant_menu,
size: 80,
color: isDark ? AppColors.darkPrimary : AppColors.lightPrimary,
color: isDark
? AppColors.darkPrimary
: AppColors.lightPrimary,
),
),
const SizedBox(height: 20),
// 앱 타이틀
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'오늘 뭐 먹Z',
style: AppTypography.heading1(isDark),
),
Text('오늘 뭐 먹Z', style: AppTypography.heading1(isDark)),
AnimatedBuilder(
animation: _questionMarkController,
builder: (context, child) {
final questionMarks = '?' * (((_questionMarkController.value * 3).floor() % 3) + 1);
final questionMarks =
'?' *
(((_questionMarkController.value * 3).floor() % 3) +
1);
return Text(
questionMarks,
style: AppTypography.heading1(isDark),
@@ -114,7 +119,7 @@ class _SplashScreenState extends State<SplashScreen> with TickerProviderStateMix
],
),
),
// 하단 카피라이트
Positioned(
bottom: 30,
@@ -123,8 +128,11 @@ class _SplashScreenState extends State<SplashScreen> with TickerProviderStateMix
child: Text(
AppConstants.appCopyright,
style: AppTypography.caption(isDark).copyWith(
color: (isDark ? AppColors.darkTextSecondary : AppColors.lightTextSecondary)
.withOpacity(0.5),
color:
(isDark
? AppColors.darkTextSecondary
: AppColors.lightTextSecondary)
.withOpacity(0.5),
),
textAlign: TextAlign.center,
),
@@ -133,14 +141,14 @@ class _SplashScreenState extends State<SplashScreen> with TickerProviderStateMix
),
);
}
List<Widget> _buildFoodIcons() {
final random = math.Random();
return List.generate(foodIcons.length, (index) {
final left = random.nextDouble() * 0.8 + 0.1;
final top = random.nextDouble() * 0.7 + 0.1;
return Positioned(
left: MediaQuery.of(context).size.width * left,
top: MediaQuery.of(context).size.height * top,
@@ -168,7 +176,7 @@ class _SplashScreenState extends State<SplashScreen> with TickerProviderStateMix
);
});
}
void _navigateToHome() {
Future.delayed(AppConstants.splashAnimationDuration, () {
if (mounted) {
@@ -176,7 +184,7 @@ class _SplashScreenState extends State<SplashScreen> with TickerProviderStateMix
}
});
}
@override
void dispose() {
for (final controller in _foodControllers) {
@@ -186,4 +194,4 @@ class _SplashScreenState extends State<SplashScreen> with TickerProviderStateMix
_centerIconController.dispose();
super.dispose();
}
}
}