feat: 초기 프로젝트 설정 및 LunchPick 앱 구현
LunchPick(오늘 뭐 먹Z?) Flutter 앱의 초기 구현입니다. 주요 기능: - 네이버 지도 연동 맛집 추가 - 랜덤 메뉴 추천 시스템 - 날씨 기반 거리 조정 - 방문 기록 관리 - Bluetooth 맛집 공유 - 다크모드 지원 기술 스택: - Flutter 3.8.1+ - Riverpod 상태 관리 - Hive 로컬 DB - Clean Architecture 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
44
test/widget/add_restaurant_dialog_test.dart
Normal file
44
test/widget/add_restaurant_dialog_test.dart
Normal file
@@ -0,0 +1,44 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:lunchpick/presentation/pages/restaurant_list/widgets/add_restaurant_dialog.dart';
|
||||
|
||||
void main() {
|
||||
group('AddRestaurantDialog Test', () {
|
||||
testWidgets('다이얼로그에 탭이 표시되는지 확인', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const ProviderScope(
|
||||
child: MaterialApp(
|
||||
home: Scaffold(
|
||||
body: AddRestaurantDialog(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// 탭이 표시되는지 확인
|
||||
expect(find.text('직접 입력'), findsOneWidget);
|
||||
expect(find.text('네이버 지도에서 가져오기'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('네이버 지도 탭으로 전환이 되는지 확인', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const ProviderScope(
|
||||
child: MaterialApp(
|
||||
home: Scaffold(
|
||||
body: AddRestaurantDialog(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// 네이버 지도 탭 클릭
|
||||
await tester.tap(find.text('네이버 지도에서 가져오기'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// URL 입력 필드가 표시되는지 확인
|
||||
expect(find.text('네이버 지도 URL'), findsOneWidget);
|
||||
expect(find.text('가져오기'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
226
test/widget/widget_test.dart
Normal file
226
test/widget/widget_test.dart
Normal file
@@ -0,0 +1,226 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:lunchpick/core/constants/app_constants.dart';
|
||||
import 'package:lunchpick/core/constants/app_colors.dart';
|
||||
|
||||
// 테스트용 SplashScreen - 네비게이션 없음
|
||||
class TestSplashScreen extends StatefulWidget {
|
||||
const TestSplashScreen({super.key});
|
||||
|
||||
@override
|
||||
State<TestSplashScreen> createState() => _TestSplashScreenState();
|
||||
}
|
||||
|
||||
class _TestSplashScreenState extends State<TestSplashScreen>
|
||||
with TickerProviderStateMixin {
|
||||
late List<AnimationController> _foodControllers;
|
||||
late AnimationController _questionMarkController;
|
||||
late AnimationController _centerIconController;
|
||||
|
||||
final List<IconData> foodIcons = [
|
||||
Icons.rice_bowl,
|
||||
Icons.ramen_dining,
|
||||
Icons.lunch_dining,
|
||||
Icons.fastfood,
|
||||
Icons.local_pizza,
|
||||
Icons.cake,
|
||||
Icons.coffee,
|
||||
Icons.icecream,
|
||||
Icons.bakery_dining,
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeAnimations();
|
||||
// 네비게이션 제거
|
||||
}
|
||||
|
||||
void _initializeAnimations() {
|
||||
_foodControllers = List.generate(
|
||||
foodIcons.length,
|
||||
(index) => AnimationController(
|
||||
duration: Duration(seconds: 2 + index % 3),
|
||||
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,
|
||||
body: Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ScaleTransition(
|
||||
scale: Tween(begin: 0.8, end: 1.2).animate(
|
||||
CurvedAnimation(
|
||||
parent: _centerIconController,
|
||||
curve: Curves.easeInOut,
|
||||
),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.restaurant_menu,
|
||||
size: 80,
|
||||
color: isDark ? AppColors.darkPrimary : AppColors.lightPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'오늘 뭐 먹Z',
|
||||
style: TextStyle(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isDark ? AppColors.darkTextPrimary : AppColors.lightTextPrimary,
|
||||
),
|
||||
),
|
||||
AnimatedBuilder(
|
||||
animation: _questionMarkController,
|
||||
builder: (context, child) {
|
||||
final questionMarks = '?' * (((_questionMarkController.value * 3).floor() % 3) + 1);
|
||||
return Text(
|
||||
questionMarks,
|
||||
style: TextStyle(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isDark ? AppColors.darkTextPrimary : AppColors.lightTextPrimary,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 30,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Text(
|
||||
AppConstants.appCopyright,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: (isDark ? AppColors.darkTextSecondary : AppColors.lightTextSecondary)
|
||||
.withValues(alpha: 0.5),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (final controller in _foodControllers) {
|
||||
controller.dispose();
|
||||
}
|
||||
_questionMarkController.dispose();
|
||||
_centerIconController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('LunchPickApp 위젯 테스트', () {
|
||||
testWidgets('스플래시 화면이 올바르게 표시되는지 확인', (WidgetTester tester) async {
|
||||
// 테스트용 스플래시 화면 사용
|
||||
await tester.pumpWidget(
|
||||
const MaterialApp(
|
||||
home: TestSplashScreen(),
|
||||
),
|
||||
);
|
||||
|
||||
// 스플래시 화면 요소 확인
|
||||
expect(find.text('오늘 뭐 먹Z'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.restaurant_menu), findsOneWidget);
|
||||
expect(find.text(AppConstants.appCopyright), findsOneWidget);
|
||||
|
||||
// 애니메이션이 있으므로 pump를 여러 번 호출
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
|
||||
// 여전히 스플래시 화면에 있는지 확인
|
||||
expect(find.text('오늘 뭐 먹Z'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('스플래시 화면 물음표 애니메이션 확인', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const MaterialApp(
|
||||
home: TestSplashScreen(),
|
||||
),
|
||||
);
|
||||
|
||||
// 초기 상태에서 물음표가 포함된 텍스트 확인
|
||||
expect(find.textContaining('오늘 뭐 먹Z'), findsOneWidget);
|
||||
|
||||
// 애니메이션 진행
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
||||
// 여전히 제목이 표시되는지 확인
|
||||
expect(find.textContaining('오늘 뭐 먹Z'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('스플래시 화면 라이트 테마 색상 확인', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData(
|
||||
brightness: Brightness.light,
|
||||
primaryColor: AppColors.lightPrimary,
|
||||
scaffoldBackgroundColor: AppColors.lightBackground,
|
||||
),
|
||||
home: const TestSplashScreen(),
|
||||
),
|
||||
);
|
||||
|
||||
// BuildContext 가져오기
|
||||
final BuildContext context = tester.element(find.byType(TestSplashScreen));
|
||||
final theme = Theme.of(context);
|
||||
|
||||
// 라이트 테마 확인
|
||||
expect(theme.brightness, Brightness.light);
|
||||
});
|
||||
|
||||
testWidgets('스플래시 화면 다크 테마 색상 확인', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
primaryColor: AppColors.darkPrimary,
|
||||
scaffoldBackgroundColor: AppColors.darkBackground,
|
||||
),
|
||||
home: const TestSplashScreen(),
|
||||
),
|
||||
);
|
||||
|
||||
// BuildContext 가져오기
|
||||
final BuildContext context = tester.element(find.byType(TestSplashScreen));
|
||||
final theme = Theme.of(context);
|
||||
|
||||
// 다크 테마 확인
|
||||
expect(theme.brightness, Brightness.dark);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user