├── image-search-app ├── ios │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner │ │ ├── Runner-Bridging-Header.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ │ └── IDEWorkspaceChecks.plist │ │ ├── xcshareddata │ │ │ └── xcschemes │ │ │ │ └── Runner.xcscheme │ │ └── project.pbxproj │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── RunnerTests │ │ └── RunnerTests.swift │ └── .gitignore ├── build │ ├── b6b68957fc5ed9202596a46ebc5bde9b │ │ ├── _composite.stamp │ │ ├── gen_localizations.stamp │ │ └── gen_dart_plugin_registrant.stamp │ └── unit_test_assets │ │ ├── AssetManifest.json │ │ ├── NOTICES.Z │ │ ├── fonts │ │ └── MaterialIcons-Regular.otf │ │ ├── packages │ │ └── cupertino_icons │ │ │ └── assets │ │ │ └── CupertinoIcons.ttf │ │ └── FontManifest.json ├── android │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── values │ │ │ │ │ │ └── styles.xml │ │ │ │ │ └── values-night │ │ │ │ │ │ └── styles.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ └── image_search │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── .gitignore │ ├── build.gradle.kts │ └── settings.gradle.kts ├── lib │ ├── domain │ │ ├── repository │ │ │ └── photo_api_repository.dart │ │ ├── model │ │ │ └── photo.dart │ │ └── use_case │ │ │ └── get_photos_use_case.dart │ ├── data │ │ ├── data_source │ │ │ ├── result.dart │ │ │ └── pixabay_api.dart │ │ └── repository │ │ │ └── photo_api_repository_impl.dart │ ├── presentation │ │ └── home │ │ │ ├── home_ui_event.dart │ │ │ ├── home_state.dart │ │ │ ├── components │ │ │ └── photo_widget.dart │ │ │ ├── home_view_model.dart │ │ │ └── home_screen.dart │ ├── main.dart │ └── di │ │ └── provider_setup.dart ├── .gitignore ├── .metadata ├── analysis_options.yaml ├── test │ ├── ui │ │ └── home_view_model_test.dart │ └── data │ │ └── pixabay_api_test.dart ├── pubspec.yaml └── README.md ├── note_app ├── ios │ ├── Runner │ │ ├── Runner-Bridging-Header.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── RunnerTests │ │ └── RunnerTests.swift │ ├── .gitignore │ └── Podfile ├── android │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── values │ │ │ │ │ │ └── styles.xml │ │ │ │ │ └── values-night │ │ │ │ │ │ └── styles.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ └── flutter_note_app │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── .gitignore │ ├── build.gradle.kts │ └── settings.gradle.kts ├── lib │ ├── domain │ │ ├── util │ │ │ ├── order_type.dart │ │ │ └── note_order.dart │ │ ├── repository │ │ │ └── note_repository.dart │ │ ├── use_case │ │ │ ├── add_note_use_case.dart │ │ │ ├── get_note_use_case.dart │ │ │ ├── update_note_use_case.dart │ │ │ ├── delete_note_use_case.dart │ │ │ ├── use_cases.dart │ │ │ └── get_notes_use_case.dart │ │ └── model │ │ │ └── note.dart │ ├── ui │ │ └── colors.dart │ ├── presentation │ │ ├── add_edit_note │ │ │ ├── add_edit_note_ui_event.dart │ │ │ ├── add_edit_note_event.dart │ │ │ ├── add_edit_note_view_model.dart │ │ │ └── add_edit_note_screen.dart │ │ └── notes │ │ │ ├── notes_state.dart │ │ │ ├── notes_event.dart │ │ │ ├── components │ │ │ ├── note_item.dart │ │ │ └── order_section.dart │ │ │ ├── notes_view_model.dart │ │ │ └── notes_screen.dart │ ├── data │ │ ├── repository │ │ │ └── note_repository_impl.dart │ │ └── data_source │ │ │ └── note_db_helper.dart │ ├── main.dart │ └── di │ │ └── provider_setup.dart ├── .gitignore ├── test │ ├── data │ │ └── data_source │ │ │ └── note_db_helper_test.dart │ └── domain │ │ └── use_case │ │ └── get_notes_use_case_test.dart ├── .metadata ├── analysis_options.yaml ├── pubspec.yaml └── README.md ├── .metadata ├── .gitignore └── .github └── workflows └── main.yml /image-search-app/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /image-search-app/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /note_app/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /image-search-app/build/b6b68957fc5ed9202596a46ebc5bde9b/_composite.stamp: -------------------------------------------------------------------------------- 1 | {"inputs":[],"outputs":[]} -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /image-search-app/build/b6b68957fc5ed9202596a46ebc5bde9b/gen_localizations.stamp: -------------------------------------------------------------------------------- 1 | {"inputs":[],"outputs":[]} -------------------------------------------------------------------------------- /image-search-app/build/b6b68957fc5ed9202596a46ebc5bde9b/gen_dart_plugin_registrant.stamp: -------------------------------------------------------------------------------- 1 | {"inputs":[],"outputs":[]} -------------------------------------------------------------------------------- /note_app/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /note_app/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /image-search-app/build/unit_test_assets/AssetManifest.json: -------------------------------------------------------------------------------- 1 | {"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"]} -------------------------------------------------------------------------------- /image-search-app/build/unit_test_assets/NOTICES.Z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/build/unit_test_assets/NOTICES.Z -------------------------------------------------------------------------------- /note_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /note_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /note_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /note_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /note_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /note_app/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /image-search-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /image-search-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /image-search-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /image-search-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /image-search-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /image-search-app/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /image-search-app/build/unit_test_assets/fonts/MaterialIcons-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/build/unit_test_assets/fonts/MaterialIcons-Regular.otf -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /image-search-app/android/app/src/main/kotlin/com/example/image_search/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.image_search 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity : FlutterActivity() 6 | -------------------------------------------------------------------------------- /note_app/android/app/src/main/kotlin/com/example/flutter_note_app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_note_app 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity : FlutterActivity() 6 | -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /note_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /note_app/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /image-search-app/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /image-search-app/build/unit_test_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junsuk5/flutter-clean-architecture-course/HEAD/image-search-app/build/unit_test_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf -------------------------------------------------------------------------------- /image-search-app/build/unit_test_assets/FontManifest.json: -------------------------------------------------------------------------------- 1 | [{"family":"MaterialIcons","fonts":[{"asset":"fonts/MaterialIcons-Regular.otf"}]},{"family":"packages/cupertino_icons/CupertinoIcons","fonts":[{"asset":"packages/cupertino_icons/assets/CupertinoIcons.ttf"}]}] -------------------------------------------------------------------------------- /note_app/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip 6 | -------------------------------------------------------------------------------- /image-search-app/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip 6 | -------------------------------------------------------------------------------- /image-search-app/lib/domain/repository/photo_api_repository.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:image_search/data/data_source/result.dart'; 3 | import 'package:image_search/domain/model/photo.dart'; 4 | 5 | abstract class PhotoApiRepository { 6 | Future>> fetch(String query); 7 | } -------------------------------------------------------------------------------- /image-search-app/lib/data/data_source/result.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'result.freezed.dart'; 4 | 5 | @freezed 6 | sealed class Result with _$Result { 7 | const factory Result.success(T data) = Success; 8 | const factory Result.error(String e) = Error; 9 | } -------------------------------------------------------------------------------- /note_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /image-search-app/lib/presentation/home/home_ui_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'home_ui_event.freezed.dart'; 4 | 5 | @freezed 6 | sealed class HomeUiEvent with _$HomeUiEvent { 7 | const factory HomeUiEvent.showSnackBar(String message) = ShowSnackBar; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /note_app/lib/domain/util/order_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'order_type.freezed.dart'; 4 | 5 | @freezed 6 | sealed class OrderType with _$OrderType { 7 | const factory OrderType.ascending() = Ascending; 8 | const factory OrderType.descending() = Descending; 9 | } 10 | -------------------------------------------------------------------------------- /image-search-app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /note_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /image-search-app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /note_app/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | .cxx/ 9 | 10 | # Remember to never publicly share your keystore. 11 | # See https://flutter.dev/to/reference-keystore 12 | key.properties 13 | **/*.keystore 14 | **/*.jks 15 | -------------------------------------------------------------------------------- /note_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /note_app/lib/ui/colors.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | const roseBud = Color(0xFFFFAB91); 4 | const primrose = Color(0xFFE7ED9B); 5 | const wisteria = Color(0xFFCF94DA); 6 | const skyBlue = Color(0xFF81DEEA); 7 | const illusion = Color(0xFFF48FB1); 8 | 9 | const darkGray = Color(0xFF202020); 10 | const lightBlue = Color(0xFFD7E8DE); 11 | -------------------------------------------------------------------------------- /image-search-app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /note_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /image-search-app/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | .cxx/ 9 | 10 | # Remember to never publicly share your keystore. 11 | # See https://flutter.dev/to/reference-keystore 12 | key.properties 13 | **/*.keystore 14 | **/*.jks 15 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 18116933e77adc82f80866c928266a5b4f1ed645 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /image-search-app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /note_app/ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /image-search-app/ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /note_app/lib/domain/repository/note_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/domain/model/note.dart'; 2 | 3 | abstract class NoteRepository { 4 | Future> getNotes(); 5 | 6 | Future getNoteById(int id); 7 | 8 | Future insertNote(Note note); 9 | 10 | Future updateNote(Note note); 11 | 12 | Future deleteNote(Note note); 13 | } 14 | -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /note_app/lib/domain/use_case/add_note_use_case.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/domain/model/note.dart'; 2 | import 'package:flutter_note_app/domain/repository/note_repository.dart'; 3 | 4 | class AddNoteUseCase { 5 | final NoteRepository _repository; 6 | 7 | AddNoteUseCase(this._repository); 8 | 9 | Future call(Note note) async { 10 | await _repository.insertNote(note); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /note_app/lib/domain/use_case/get_note_use_case.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/domain/model/note.dart'; 2 | import 'package:flutter_note_app/domain/repository/note_repository.dart'; 3 | 4 | class GetNoteUseCase { 5 | final NoteRepository _repository; 6 | 7 | GetNoteUseCase(this._repository); 8 | 9 | Future call(int id) async { 10 | return await _repository.getNoteById(id); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /note_app/lib/domain/use_case/update_note_use_case.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/domain/model/note.dart'; 2 | import 'package:flutter_note_app/domain/repository/note_repository.dart'; 3 | 4 | class UpdateNoteUseCase { 5 | final NoteRepository repository; 6 | 7 | UpdateNoteUseCase(this.repository); 8 | 9 | Future call(Note note) async { 10 | await repository.updateNote(note); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /note_app/lib/presentation/add_edit_note/add_edit_note_ui_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'add_edit_note_ui_event.freezed.dart'; 4 | 5 | @freezed 6 | sealed class AddEditNoteUiEvent with _$AddEditNoteUiEvent { 7 | const factory AddEditNoteUiEvent.savedNote() = SavedNote; 8 | const factory AddEditNoteUiEvent.showSnackBar(String message) = ShowSnackBar; 9 | } 10 | -------------------------------------------------------------------------------- /note_app/lib/domain/use_case/delete_note_use_case.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/domain/model/note.dart'; 2 | import 'package:flutter_note_app/domain/repository/note_repository.dart'; 3 | 4 | class DeleteNoteUseCase { 5 | final NoteRepository _repository; 6 | 7 | DeleteNoteUseCase(this._repository); 8 | 9 | Future call(Note note) async { 10 | await _repository.deleteNote(note); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /note_app/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /note_app/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /image-search-app/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /image-search-app/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /image-search-app/lib/domain/model/photo.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'photo.freezed.dart'; 4 | 5 | part 'photo.g.dart'; 6 | 7 | @freezed 8 | abstract class Photo with _$Photo { 9 | factory Photo({ 10 | required int id, 11 | required String tags, 12 | required String previewURL, 13 | }) = _Photo; 14 | 15 | factory Photo.fromJson(Map json) => _$PhotoFromJson(json); 16 | } -------------------------------------------------------------------------------- /note_app/lib/presentation/add_edit_note/add_edit_note_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'add_edit_note_event.freezed.dart'; 4 | 5 | @freezed 6 | sealed class AddEditNoteEvent with _$AddEditNoteEvent { 7 | const factory AddEditNoteEvent.changeColor(int color) = ChangeColor; 8 | const factory AddEditNoteEvent.saveNote( 9 | int? id, 10 | String title, 11 | String content, 12 | ) = SaveNote; 13 | } 14 | -------------------------------------------------------------------------------- /note_app/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /note_app/lib/domain/model/note.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'note.freezed.dart'; 4 | 5 | part 'note.g.dart'; 6 | 7 | @freezed 8 | abstract class Note with _$Note { 9 | factory Note({ 10 | required String title, 11 | required String content, 12 | required int color, 13 | required int timestamp, 14 | int? id, 15 | }) = _Note; 16 | 17 | factory Note.fromJson(Map json) => _$NoteFromJson(json); 18 | } 19 | -------------------------------------------------------------------------------- /image-search-app/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /note_app/lib/domain/util/note_order.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/domain/util/order_type.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'note_order.freezed.dart'; 5 | 6 | @freezed 7 | sealed class NoteOrder with _$NoteOrder { 8 | const factory NoteOrder.title(OrderType orderType) = NoteOrderTitle; 9 | const factory NoteOrder.date(OrderType orderType) = NoteOrderDate; 10 | const factory NoteOrder.color(OrderType orderType) = NoteOrderColor; 11 | } 12 | -------------------------------------------------------------------------------- /image-search-app/lib/presentation/home/home_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:image_search/domain/model/photo.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'home_state.freezed.dart'; 5 | 6 | part 'home_state.g.dart'; 7 | 8 | @freezed 9 | abstract class HomeState with _$HomeState { 10 | factory HomeState( 11 | List photos, 12 | bool isLoading, 13 | ) = _HomeState; 14 | 15 | factory HomeState.fromJson(Map json) => _$HomeStateFromJson(json); 16 | } -------------------------------------------------------------------------------- /note_app/lib/presentation/notes/notes_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/domain/model/note.dart'; 2 | import 'package:flutter_note_app/domain/util/note_order.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'notes_state.freezed.dart'; 6 | 7 | @freezed 8 | abstract class NotesState with _$NotesState { 9 | factory NotesState({ 10 | required List notes, 11 | required NoteOrder noteOrder, 12 | required bool isOrderSectionVisible, 13 | }) = _NotesState; 14 | } 15 | -------------------------------------------------------------------------------- /note_app/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /image-search-app/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /note_app/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /image-search-app/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /note_app/android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() 9 | rootProject.layout.buildDirectory.value(newBuildDir) 10 | 11 | subprojects { 12 | val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) 13 | project.layout.buildDirectory.value(newSubprojectBuildDir) 14 | } 15 | subprojects { 16 | project.evaluationDependsOn(":app") 17 | } 18 | 19 | tasks.register("clean") { 20 | delete(rootProject.layout.buildDirectory) 21 | } 22 | -------------------------------------------------------------------------------- /image-search-app/android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() 9 | rootProject.layout.buildDirectory.value(newBuildDir) 10 | 11 | subprojects { 12 | val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) 13 | project.layout.buildDirectory.value(newSubprojectBuildDir) 14 | } 15 | subprojects { 16 | project.evaluationDependsOn(":app") 17 | } 18 | 19 | tasks.register("clean") { 20 | delete(rootProject.layout.buildDirectory) 21 | } 22 | -------------------------------------------------------------------------------- /note_app/lib/presentation/notes/notes_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/domain/model/note.dart'; 2 | import 'package:flutter_note_app/domain/util/note_order.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part 'notes_event.freezed.dart'; 6 | 7 | @freezed 8 | sealed class NotesEvent with _$NotesEvent { 9 | const factory NotesEvent.loadNotes() = LoadNotes; 10 | const factory NotesEvent.deleteNote(Note note) = DeleteNote; 11 | const factory NotesEvent.restoreNote() = RestoreNote; 12 | const factory NotesEvent.changeOrder(NoteOrder noteOrder) = ChangeOrder; 13 | const factory NotesEvent.toggleOrderSection() = ToggleOrderSection; 14 | } 15 | -------------------------------------------------------------------------------- /image-search-app/lib/presentation/home/components/photo_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:image_search/domain/model/photo.dart'; 3 | 4 | class PhotoWidget extends StatelessWidget { 5 | final Photo photo; 6 | 7 | const PhotoWidget({ 8 | super.key, 9 | required this.photo, 10 | }); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Container( 15 | decoration: BoxDecoration( 16 | borderRadius: const BorderRadius.all(Radius.circular(16.0)), 17 | image: DecorationImage( 18 | fit: BoxFit.cover, 19 | image: NetworkImage(photo.previewURL), 20 | ), 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /note_app/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /image-search-app/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /image-search-app/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:image_search/di/provider_setup.dart'; 3 | import 'package:image_search/presentation/home/home_screen.dart'; 4 | import 'package:provider/provider.dart'; 5 | 6 | void main() { 7 | runApp( 8 | MultiProvider( 9 | providers: globalProviders, 10 | child: const MyApp(), 11 | ), 12 | ); 13 | } 14 | 15 | class MyApp extends StatelessWidget { 16 | const MyApp({super.key}); 17 | 18 | // This widget is the root of your application. 19 | @override 20 | Widget build(BuildContext context) { 21 | return MaterialApp( 22 | title: 'Flutter Demo', 23 | theme: ThemeData( 24 | primarySwatch: Colors.blue, 25 | ), 26 | home: const HomeScreen(), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /image-search-app/lib/data/repository/photo_api_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:image_search/data/data_source/pixabay_api.dart'; 2 | import 'package:image_search/data/data_source/result.dart'; 3 | import 'package:image_search/domain/model/photo.dart'; 4 | import 'package:image_search/domain/repository/photo_api_repository.dart'; 5 | 6 | class PhotoApiRepositoryImpl implements PhotoApiRepository { 7 | PixabayApi api; 8 | 9 | PhotoApiRepositoryImpl(this.api); 10 | 11 | @override 12 | Future>> fetch(String query) async { 13 | final Result result = await api.fetch(query); 14 | 15 | return switch (result) { 16 | Success(:final data) => Result.success(data.map((e) => Photo.fromJson(e)).toList()), 17 | Error(:final e) => Result.error(e), 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /note_app/lib/domain/use_case/use_cases.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/domain/use_case/add_note_use_case.dart'; 2 | import 'package:flutter_note_app/domain/use_case/delete_note_use_case.dart'; 3 | import 'package:flutter_note_app/domain/use_case/get_note_use_case.dart'; 4 | import 'package:flutter_note_app/domain/use_case/get_notes_use_case.dart'; 5 | import 'package:flutter_note_app/domain/use_case/update_note_use_case.dart'; 6 | 7 | class UseCases { 8 | final AddNoteUseCase addNote; 9 | final DeleteNoteUseCase deleteNote; 10 | final GetNoteUseCase getNote; 11 | final GetNotesUseCase getNotes; 12 | final UpdateNoteUseCase updateNote; 13 | 14 | UseCases({ 15 | required this.addNote, 16 | required this.deleteNote, 17 | required this.getNote, 18 | required this.getNotes, 19 | required this.updateNote, 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /image-search-app/lib/data/data_source/pixabay_api.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:http/http.dart' as http; 4 | import 'package:image_search/data/data_source/result.dart'; 5 | 6 | class PixabayApi { 7 | final http.Client client; 8 | 9 | PixabayApi(this.client); 10 | 11 | static const baseUrl = 'https://pixabay.com/api/'; 12 | static const key = '10711147-dc41758b93b263957026bdadb'; 13 | 14 | Future> fetch(String query) async { 15 | try { 16 | final response = await client 17 | .get(Uri.parse('$baseUrl?key=$key&q=$query&image_type=photo')); 18 | Map jsonResponse = jsonDecode(response.body); 19 | Iterable hits = jsonResponse['hits']; 20 | return Result.success(hits); 21 | } catch (e) { 22 | return const Result.error('네트워크 에러'); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /note_app/android/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | val flutterSdkPath = run { 3 | val properties = java.util.Properties() 4 | file("local.properties").inputStream().use { properties.load(it) } 5 | val flutterSdkPath = properties.getProperty("flutter.sdk") 6 | require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } 7 | flutterSdkPath 8 | } 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id("dev.flutter.flutter-plugin-loader") version "1.0.0" 21 | id("com.android.application") version "8.7.3" apply false 22 | id("org.jetbrains.kotlin.android") version "2.1.0" apply false 23 | } 24 | 25 | include(":app") 26 | -------------------------------------------------------------------------------- /image-search-app/android/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | val flutterSdkPath = run { 3 | val properties = java.util.Properties() 4 | file("local.properties").inputStream().use { properties.load(it) } 5 | val flutterSdkPath = properties.getProperty("flutter.sdk") 6 | require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } 7 | flutterSdkPath 8 | } 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id("dev.flutter.flutter-plugin-loader") version "1.0.0" 21 | id("com.android.application") version "8.7.3" apply false 22 | id("org.jetbrains.kotlin.android") version "2.1.0" apply false 23 | } 24 | 25 | include(":app") 26 | -------------------------------------------------------------------------------- /note_app/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /image-search-app/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /image-search-app/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .pub-cache/ 33 | .pub/ 34 | /build/ 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Android Studio will place build artifacts here 43 | /android/app/debug 44 | /android/app/profile 45 | /android/app/release 46 | -------------------------------------------------------------------------------- /note_app/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | .claude -------------------------------------------------------------------------------- /note_app/lib/data/repository/note_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/data/data_source/note_db_helper.dart'; 2 | import 'package:flutter_note_app/domain/model/note.dart'; 3 | import 'package:flutter_note_app/domain/repository/note_repository.dart'; 4 | 5 | class NoteRepositoryImpl implements NoteRepository { 6 | final NoteDbHelper _db; 7 | 8 | NoteRepositoryImpl(this._db); 9 | 10 | @override 11 | Future deleteNote(Note note) async { 12 | await _db.deleteNote(note); 13 | } 14 | 15 | @override 16 | Future getNoteById(int id) async { 17 | return await _db.getNoteById(id); 18 | } 19 | 20 | @override 21 | Future> getNotes() async { 22 | return await _db.getNotes(); 23 | } 24 | 25 | @override 26 | Future insertNote(Note note) async { 27 | await _db.insertNote(note); 28 | } 29 | 30 | @override 31 | Future updateNote(Note note) async { 32 | await _db.updateNote(note); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /image-search-app/lib/domain/use_case/get_photos_use_case.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:image_search/data/data_source/result.dart'; 4 | import 'package:image_search/domain/model/photo.dart'; 5 | import 'package:image_search/domain/repository/photo_api_repository.dart'; 6 | 7 | class GetPhotosUseCase { 8 | // 캡슐화 9 | final PhotoApiRepository _repository; 10 | 11 | GetPhotosUseCase(this._repository); 12 | 13 | Future>> call(String query) async { 14 | final result = await _repository.fetch(query); 15 | 16 | return switch (result) { 17 | // Success(:final data) => Result.success(data.sublist(0, min(3, data.length))), 18 | // Error(:final e) => Result.error(e), 19 | // 위 코드와 아래 코드는 동일함. 편한거 쓰면 됨 20 | // 21 | Success>() => Result.success( 22 | result.data.sublist(0, min(3, result.data.length)), 23 | ), 24 | Error>() => Result.error(result.e), 25 | }; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | 48 | .idea/ 49 | *.g.dart 50 | *.mocks.dart 51 | *.freezed.dart -------------------------------------------------------------------------------- /note_app/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /note_app/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /image-search-app/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /image-search-app/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the "master" branch 8 | push: 9 | branches: [ "master" ] 10 | pull_request: 11 | branches: [ "master" ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | - uses: actions/checkout@v3 26 | - uses: actions/setup-java@v2 27 | with: 28 | distribution: 'zulu' 29 | java-version: '11' 30 | - uses: subosito/flutter-action@v2 31 | with: 32 | channel: 'stable' 33 | - run: flutter pub get 34 | - run: flutter test 35 | # - run: flutter build apk 36 | # - run: flutter build appbundle 37 | 38 | -------------------------------------------------------------------------------- /note_app/lib/data/data_source/note_db_helper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/domain/model/note.dart'; 2 | import 'package:sqflite/sqflite.dart'; 3 | 4 | class NoteDbHelper { 5 | final Database _db; 6 | 7 | NoteDbHelper(this._db); 8 | 9 | Future getNoteById(int id) async { 10 | // SELECT * FROM note WHERE id = 1 11 | final List> maps = await _db.query( 12 | 'note', 13 | where: 'id = ?', 14 | whereArgs: [id], 15 | ); 16 | 17 | if (maps.isNotEmpty) { 18 | return Note.fromJson(maps.first); 19 | } 20 | 21 | return null; 22 | } 23 | 24 | Future> getNotes() async { 25 | final maps = await _db.query('note'); 26 | return maps.map((e) => Note.fromJson(e)).toList(); 27 | } 28 | 29 | Future insertNote(Note note) async { 30 | await _db.insert('note', note.toJson()); 31 | } 32 | 33 | Future updateNote(Note note) async { 34 | await _db.update( 35 | 'note', 36 | note.toJson(), 37 | where: 'id = ?', 38 | whereArgs: [note.id], 39 | ); 40 | } 41 | 42 | Future deleteNote(Note note) async { 43 | await _db.delete('note', where: 'id = ?', whereArgs: [note.id]); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /note_app/test/data/data_source/note_db_helper_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/data/data_source/note_db_helper.dart'; 2 | import 'package:flutter_note_app/domain/model/note.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | import 'package:sqflite_common_ffi/sqflite_ffi.dart'; 5 | 6 | void main() { 7 | test('db test', () async { 8 | final db = await databaseFactoryFfi.openDatabase(inMemoryDatabasePath); 9 | 10 | await db.execute( 11 | 'CREATE TABLE note (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, content TEXT, color INTEGER, timestamp INTEGER)', 12 | ); 13 | 14 | final noteDbHelper = NoteDbHelper(db); 15 | 16 | await noteDbHelper.insertNote( 17 | Note(title: 'test', content: 'test', color: 1, timestamp: 1), 18 | ); 19 | 20 | expect((await noteDbHelper.getNotes()).length, 1); 21 | 22 | Note note = (await noteDbHelper.getNoteById(1))!; 23 | expect(note.id, 1); 24 | 25 | await noteDbHelper.updateNote(note.copyWith(title: 'change')); 26 | 27 | note = (await noteDbHelper.getNoteById(1))!; 28 | expect(note.title, 'change'); 29 | 30 | await noteDbHelper.deleteNote(note); 31 | expect((await noteDbHelper.getNotes()).length, 0); 32 | 33 | await db.close(); 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /image-search-app/lib/presentation/home/home_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:image_search/data/data_source/result.dart'; 5 | import 'package:image_search/domain/model/photo.dart'; 6 | import 'package:image_search/domain/use_case/get_photos_use_case.dart'; 7 | import 'package:image_search/presentation/home/home_state.dart'; 8 | import 'package:image_search/presentation/home/home_ui_event.dart'; 9 | 10 | class HomeViewModel with ChangeNotifier { 11 | final GetPhotosUseCase getPhotosUseCase; 12 | 13 | HomeState _state = HomeState([], false); 14 | 15 | HomeState get state => _state; 16 | 17 | final _eventController = StreamController(); 18 | 19 | Stream get eventStream => _eventController.stream; 20 | 21 | HomeViewModel(this.getPhotosUseCase); 22 | 23 | Future fetch(String query) async { 24 | _state = state.copyWith(isLoading: true); 25 | notifyListeners(); 26 | 27 | final Result> result = await getPhotosUseCase(query); 28 | 29 | switch (result) { 30 | case Success(:final data): 31 | _state = state.copyWith(photos: data); 32 | notifyListeners(); 33 | case Error(:final e): 34 | _eventController.add(HomeUiEvent.showSnackBar(e)); 35 | } 36 | _state = state.copyWith(isLoading: false); 37 | notifyListeners(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /note_app/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_note_app/di/provider_setup.dart'; 3 | import 'package:flutter_note_app/presentation/notes/notes_screen.dart'; 4 | import 'package:flutter_note_app/ui/colors.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | void main() async { 8 | // 플랫폼 채널의 위젯 바인딩을 보장 9 | WidgetsFlutterBinding.ensureInitialized(); 10 | 11 | final providers = await getProviders(); 12 | 13 | runApp(MultiProvider(providers: providers, child: const MyApp())); 14 | } 15 | 16 | class MyApp extends StatelessWidget { 17 | const MyApp({Key? key}) : super(key: key); 18 | 19 | // This widget is the root of your application. 20 | @override 21 | Widget build(BuildContext context) { 22 | return MaterialApp( 23 | title: 'Flutter Demo', 24 | theme: ThemeData( 25 | unselectedWidgetColor: Colors.white, 26 | primaryColor: Colors.white, 27 | scaffoldBackgroundColor: darkGray, 28 | canvasColor: darkGray, 29 | floatingActionButtonTheme: Theme.of(context).floatingActionButtonTheme 30 | .copyWith(backgroundColor: Colors.white, foregroundColor: darkGray), 31 | appBarTheme: Theme.of( 32 | context, 33 | ).appBarTheme.copyWith(backgroundColor: darkGray), 34 | textTheme: Theme.of(context).textTheme.apply(bodyColor: Colors.white), 35 | ), 36 | home: const NotesScreen(), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /note_app/lib/domain/use_case/get_notes_use_case.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/domain/model/note.dart'; 2 | import 'package:flutter_note_app/domain/repository/note_repository.dart'; 3 | import 'package:flutter_note_app/domain/util/note_order.dart'; 4 | import 'package:flutter_note_app/domain/util/order_type.dart'; 5 | 6 | class GetNotesUseCase { 7 | final NoteRepository _repository; 8 | 9 | GetNotesUseCase(this._repository); 10 | 11 | Future> call(NoteOrder noteOrder) async { 12 | List notes = await _repository.getNotes(); 13 | 14 | switch (noteOrder) { 15 | case NoteOrderTitle(:final orderType): 16 | switch (orderType) { 17 | case Ascending(): 18 | notes.sort((a, b) => a.title.compareTo(b.title)); 19 | case Descending(): 20 | notes.sort((a, b) => -a.title.compareTo(b.title)); 21 | } 22 | case NoteOrderDate(:final orderType): 23 | switch (orderType) { 24 | case Ascending(): 25 | notes.sort((a, b) => a.timestamp.compareTo(b.timestamp)); 26 | case Descending(): 27 | notes.sort((a, b) => -a.timestamp.compareTo(b.timestamp)); 28 | } 29 | case NoteOrderColor(:final orderType): 30 | switch (orderType) { 31 | case Ascending(): 32 | notes.sort((a, b) => a.color.compareTo(b.color)); 33 | case Descending(): 34 | notes.sort((a, b) => -a.color.compareTo(b.color)); 35 | } 36 | } 37 | 38 | return notes; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /image-search-app/lib/di/provider_setup.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:image_search/data/data_source/pixabay_api.dart'; 3 | import 'package:image_search/data/repository/photo_api_repository_impl.dart'; 4 | import 'package:image_search/domain/repository/photo_api_repository.dart'; 5 | import 'package:image_search/domain/use_case/get_photos_use_case.dart'; 6 | import 'package:image_search/presentation/home/home_view_model.dart'; 7 | import 'package:provider/provider.dart'; 8 | import 'package:provider/single_child_widget.dart'; 9 | import 'package:http/http.dart' as http; 10 | 11 | // 1. Provider 전체 12 | List globalProviders = [ 13 | ...independentModels, 14 | ...dependentModels, 15 | ...viewModels, 16 | ]; 17 | 18 | // 2. 독립적인 객체 19 | List independentModels = [ 20 | Provider( 21 | create: (context) => http.Client(), 22 | ), 23 | ]; 24 | 25 | // 3. 2번에 의존성 있는 객체 26 | List dependentModels = [ 27 | ProxyProvider( 28 | update: (context, client, _) => PixabayApi(client), 29 | ), 30 | ProxyProvider( 31 | update: (context, api, _) => PhotoApiRepositoryImpl(api), 32 | ), 33 | ProxyProvider( 34 | update: (context, repository, _) => GetPhotosUseCase(repository), 35 | ), 36 | ]; 37 | 38 | // 4. ViewModels 39 | List viewModels = [ 40 | ChangeNotifierProvider( 41 | create: (context) => HomeViewModel(context.read()), 42 | ), 43 | ]; 44 | -------------------------------------------------------------------------------- /note_app/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "8defaa71a77c16e8547abdbfad2053ce3a6e2d5b" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 17 | base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 18 | - platform: android 19 | create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 20 | base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 21 | - platform: ios 22 | create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 23 | base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 24 | - platform: macos 25 | create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 26 | base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 27 | - platform: web 28 | create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 29 | base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 30 | 31 | # User provided section 32 | 33 | # List of Local paths (relative to this file) that should be 34 | # ignored by the migrate tool. 35 | # 36 | # Files that are not part of the templates will be ignored by default. 37 | unmanaged_files: 38 | - 'lib/main.dart' 39 | - 'ios/Runner.xcodeproj/project.pbxproj' 40 | -------------------------------------------------------------------------------- /note_app/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '12.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | 33 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 34 | target 'RunnerTests' do 35 | inherit! :search_paths 36 | end 37 | end 38 | 39 | post_install do |installer| 40 | installer.pods_project.targets.each do |target| 41 | flutter_additional_ios_build_settings(target) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /image-search-app/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "8defaa71a77c16e8547abdbfad2053ce3a6e2d5b" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 17 | base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 18 | - platform: android 19 | create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 20 | base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 21 | - platform: ios 22 | create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 23 | base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 24 | - platform: macos 25 | create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 26 | base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 27 | - platform: web 28 | create_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 29 | base_revision: 8defaa71a77c16e8547abdbfad2053ce3a6e2d5b 30 | 31 | # User provided section 32 | 33 | # List of Local paths (relative to this file) that should be 34 | # ignored by the migrate tool. 35 | # 36 | # Files that are not part of the templates will be ignored by default. 37 | unmanaged_files: 38 | - 'lib/main.dart' 39 | - 'ios/Runner.xcodeproj/project.pbxproj' 40 | -------------------------------------------------------------------------------- /image-search-app/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /image-search-app/android/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | id("kotlin-android") 4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 5 | id("dev.flutter.flutter-gradle-plugin") 6 | } 7 | 8 | android { 9 | namespace = "com.example.image_search" 10 | compileSdk = flutter.compileSdkVersion 11 | ndkVersion = flutter.ndkVersion 12 | 13 | compileOptions { 14 | sourceCompatibility = JavaVersion.VERSION_11 15 | targetCompatibility = JavaVersion.VERSION_11 16 | } 17 | 18 | kotlinOptions { 19 | jvmTarget = JavaVersion.VERSION_11.toString() 20 | } 21 | 22 | defaultConfig { 23 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 24 | applicationId = "com.example.image_search" 25 | // You can update the following values to match your application needs. 26 | // For more information, see: https://flutter.dev/to/review-gradle-config. 27 | minSdk = flutter.minSdkVersion 28 | targetSdk = flutter.targetSdkVersion 29 | versionCode = flutter.versionCode 30 | versionName = flutter.versionName 31 | } 32 | 33 | buildTypes { 34 | release { 35 | // TODO: Add your own signing config for the release build. 36 | // Signing with the debug keys for now, so `flutter run --release` works. 37 | signingConfig = signingConfigs.getByName("debug") 38 | } 39 | } 40 | } 41 | 42 | flutter { 43 | source = "../.." 44 | } 45 | -------------------------------------------------------------------------------- /note_app/android/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | id("kotlin-android") 4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 5 | id("dev.flutter.flutter-gradle-plugin") 6 | } 7 | 8 | android { 9 | namespace = "com.example.flutter_note_app" 10 | compileSdk = flutter.compileSdkVersion 11 | ndkVersion = flutter.ndkVersion 12 | 13 | compileOptions { 14 | sourceCompatibility = JavaVersion.VERSION_11 15 | targetCompatibility = JavaVersion.VERSION_11 16 | } 17 | 18 | kotlinOptions { 19 | jvmTarget = JavaVersion.VERSION_11.toString() 20 | } 21 | 22 | defaultConfig { 23 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 24 | applicationId = "com.example.flutter_note_app" 25 | // You can update the following values to match your application needs. 26 | // For more information, see: https://flutter.dev/to/review-gradle-config. 27 | minSdk = flutter.minSdkVersion 28 | targetSdk = flutter.targetSdkVersion 29 | versionCode = flutter.versionCode 30 | versionName = flutter.versionName 31 | } 32 | 33 | buildTypes { 34 | release { 35 | // TODO: Add your own signing config for the release build. 36 | // Signing with the debug keys for now, so `flutter run --release` works. 37 | signingConfig = signingConfigs.getByName("debug") 38 | } 39 | } 40 | } 41 | 42 | flutter { 43 | source = "../.." 44 | } 45 | -------------------------------------------------------------------------------- /note_app/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /note_app/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | 31 | # Freezed 예외 32 | analyzer: 33 | exclude: 34 | - "**/*.g.dart" 35 | - "**/*.freezed.dart" 36 | errors: 37 | invalid_annotation_target: ignore -------------------------------------------------------------------------------- /note_app/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_note_app 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | image_search 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /note_app/lib/presentation/notes/components/note_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_note_app/domain/model/note.dart'; 3 | import 'package:flutter_note_app/ui/colors.dart'; 4 | 5 | class NoteItem extends StatelessWidget { 6 | final Note note; 7 | final Function? onDeleteTap; 8 | 9 | const NoteItem({super.key, required this.note, this.onDeleteTap}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Padding( 14 | padding: const EdgeInsets.all(8.0), 15 | child: Stack( 16 | children: [ 17 | Container( 18 | width: double.infinity, 19 | padding: const EdgeInsets.all(16), 20 | decoration: BoxDecoration( 21 | borderRadius: BorderRadius.circular(10), 22 | color: Color(note.color), 23 | ), 24 | child: Column( 25 | crossAxisAlignment: CrossAxisAlignment.start, 26 | children: [ 27 | Text( 28 | note.title, 29 | maxLines: 1, 30 | overflow: TextOverflow.ellipsis, 31 | style: Theme.of( 32 | context, 33 | ).textTheme.titleLarge!.apply(color: darkGray), 34 | ), 35 | const SizedBox(height: 8), 36 | Text( 37 | note.content, 38 | maxLines: 10, 39 | overflow: TextOverflow.ellipsis, 40 | style: Theme.of( 41 | context, 42 | ).textTheme.bodyMedium!.apply(color: darkGray), 43 | ), 44 | ], 45 | ), 46 | ), 47 | Positioned( 48 | bottom: 8, 49 | right: 8, 50 | child: GestureDetector( 51 | onTap: () { 52 | onDeleteTap?.call(); 53 | }, 54 | child: const Icon(Icons.delete), 55 | ), 56 | ), 57 | ], 58 | ), 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /note_app/lib/presentation/notes/notes_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_note_app/domain/model/note.dart'; 3 | import 'package:flutter_note_app/domain/use_case/use_cases.dart'; 4 | import 'package:flutter_note_app/domain/util/note_order.dart'; 5 | import 'package:flutter_note_app/domain/util/order_type.dart'; 6 | import 'package:flutter_note_app/presentation/notes/notes_event.dart'; 7 | import 'package:flutter_note_app/presentation/notes/notes_state.dart'; 8 | 9 | class NotesViewModel with ChangeNotifier { 10 | final UseCases useCases; 11 | 12 | NotesState _state = NotesState( 13 | notes: [], 14 | noteOrder: const NoteOrder.date(OrderType.descending()), 15 | isOrderSectionVisible: false, 16 | ); 17 | 18 | NotesState get state => _state; 19 | 20 | Note? _recentlyDeletedNote; 21 | 22 | NotesViewModel(this.useCases) { 23 | _loadNotes(); 24 | } 25 | 26 | void onEvent(NotesEvent event) { 27 | switch (event) { 28 | case LoadNotes(): 29 | _loadNotes(); 30 | case DeleteNote(:final note): 31 | _deleteNote(note); 32 | case RestoreNote(): 33 | _restoreNote(); 34 | case ChangeOrder(:final noteOrder): 35 | _state = state.copyWith(noteOrder: noteOrder); 36 | _loadNotes(); 37 | case ToggleOrderSection(): 38 | _state = state.copyWith( 39 | isOrderSectionVisible: !state.isOrderSectionVisible, 40 | ); 41 | notifyListeners(); 42 | } 43 | } 44 | 45 | Future _loadNotes() async { 46 | List notes = await useCases.getNotes(state.noteOrder); 47 | _state = state.copyWith(notes: notes); 48 | notifyListeners(); 49 | } 50 | 51 | Future _deleteNote(Note note) async { 52 | await useCases.deleteNote(note); 53 | _recentlyDeletedNote = note; 54 | 55 | await _loadNotes(); 56 | } 57 | 58 | Future _restoreNote() async { 59 | if (_recentlyDeletedNote != null) { 60 | await useCases.addNote(_recentlyDeletedNote!); 61 | _recentlyDeletedNote = null; 62 | 63 | _loadNotes(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /note_app/lib/di/provider_setup.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/data/data_source/note_db_helper.dart'; 2 | import 'package:flutter_note_app/data/repository/note_repository_impl.dart'; 3 | import 'package:flutter_note_app/domain/repository/note_repository.dart'; 4 | import 'package:flutter_note_app/domain/use_case/add_note_use_case.dart'; 5 | import 'package:flutter_note_app/domain/use_case/delete_note_use_case.dart'; 6 | import 'package:flutter_note_app/domain/use_case/get_note_use_case.dart'; 7 | import 'package:flutter_note_app/domain/use_case/get_notes_use_case.dart'; 8 | import 'package:flutter_note_app/domain/use_case/update_note_use_case.dart'; 9 | import 'package:flutter_note_app/domain/use_case/use_cases.dart'; 10 | import 'package:flutter_note_app/presentation/add_edit_note/add_edit_note_view_model.dart'; 11 | import 'package:flutter_note_app/presentation/notes/notes_view_model.dart'; 12 | import 'package:provider/provider.dart'; 13 | import 'package:provider/single_child_widget.dart'; 14 | import 'package:sqflite/sqflite.dart'; 15 | 16 | Future> getProviders() async { 17 | Database database = await openDatabase( 18 | 'notes_db', 19 | version: 1, 20 | onCreate: (db, version) async { 21 | await db.execute( 22 | 'CREATE TABLE note (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, content TEXT, color INTEGER, timestamp INTEGER)', 23 | ); 24 | }, 25 | ); 26 | 27 | NoteDbHelper noteDbHelper = NoteDbHelper(database); 28 | NoteRepository repository = NoteRepositoryImpl(noteDbHelper); 29 | UseCases useCases = UseCases( 30 | addNote: AddNoteUseCase(repository), 31 | deleteNote: DeleteNoteUseCase(repository), 32 | getNote: GetNoteUseCase(repository), 33 | getNotes: GetNotesUseCase(repository), 34 | updateNote: UpdateNoteUseCase(repository), 35 | ); 36 | NotesViewModel notesViewModel = NotesViewModel(useCases); 37 | AddEditNoteViewModel addEditNoteViewModel = AddEditNoteViewModel(repository); 38 | 39 | return [ 40 | ChangeNotifierProvider(create: (_) => notesViewModel), 41 | ChangeNotifierProvider(create: (_) => addEditNoteViewModel), 42 | ]; 43 | } 44 | -------------------------------------------------------------------------------- /note_app/test/domain/use_case/get_notes_use_case_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_note_app/domain/model/note.dart'; 2 | import 'package:flutter_note_app/domain/repository/note_repository.dart'; 3 | import 'package:flutter_note_app/domain/use_case/get_notes_use_case.dart'; 4 | import 'package:flutter_note_app/domain/util/note_order.dart'; 5 | import 'package:flutter_note_app/domain/util/order_type.dart'; 6 | import 'package:flutter_test/flutter_test.dart'; 7 | import 'package:mockito/annotations.dart'; 8 | import 'package:mockito/mockito.dart'; 9 | 10 | import 'get_notes_use_case_test.mocks.dart'; 11 | 12 | @GenerateMocks([NoteRepository]) 13 | void main() { 14 | test('정렬 기능이 잘 동작해야 한다', () async { 15 | final repository = MockNoteRepository(); 16 | final getNotes = GetNotesUseCase(repository); 17 | 18 | // 동작 정의 19 | when(repository.getNotes()).thenAnswer( 20 | (_) async => [ 21 | Note(title: 'title', content: 'content', timestamp: 0, color: 1), 22 | Note(title: 'title2', content: 'content2', timestamp: 2, color: 2), 23 | ], 24 | ); 25 | 26 | List result = await getNotes( 27 | const NoteOrder.date(OrderType.descending()), 28 | ); 29 | expect(result, isA>()); 30 | expect(result.first.timestamp, 2); 31 | verify(repository.getNotes()); 32 | 33 | result = await getNotes(const NoteOrder.date(OrderType.ascending())); 34 | expect(result.first.timestamp, 0); 35 | verify(repository.getNotes()); 36 | 37 | result = await getNotes(const NoteOrder.title(OrderType.ascending())); 38 | expect(result.first.title, 'title'); 39 | verify(repository.getNotes()); 40 | 41 | result = await getNotes(const NoteOrder.title(OrderType.descending())); 42 | expect(result.first.title, 'title2'); 43 | verify(repository.getNotes()); 44 | 45 | result = await getNotes(const NoteOrder.color(OrderType.ascending())); 46 | expect(result.first.color, 1); 47 | verify(repository.getNotes()); 48 | 49 | result = await getNotes(const NoteOrder.color(OrderType.descending())); 50 | expect(result.first.color, 2); 51 | verify(repository.getNotes()); 52 | 53 | verifyNoMoreInteractions(repository); 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /note_app/lib/presentation/add_edit_note/add_edit_note_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_note_app/domain/model/note.dart'; 5 | import 'package:flutter_note_app/domain/repository/note_repository.dart'; 6 | import 'package:flutter_note_app/presentation/add_edit_note/add_edit_note_event.dart'; 7 | import 'package:flutter_note_app/presentation/add_edit_note/add_edit_note_ui_event.dart'; 8 | import 'package:flutter_note_app/ui/colors.dart'; 9 | 10 | class AddEditNoteViewModel with ChangeNotifier { 11 | final NoteRepository repository; 12 | 13 | int _color = roseBud.toARGB32(); 14 | 15 | int get color => _color; 16 | 17 | final _eventController = StreamController.broadcast(); 18 | 19 | Stream get eventStream => _eventController.stream; 20 | 21 | AddEditNoteViewModel(this.repository); 22 | 23 | void onEvent(AddEditNoteEvent event) { 24 | switch (event) { 25 | case ChangeColor(:final color): 26 | _changColor(color); 27 | case SaveNote(:final id, :final title, :final content): 28 | _saveNote(id, title, content); 29 | } 30 | } 31 | 32 | Future _changColor(int color) async { 33 | _color = color; 34 | notifyListeners(); 35 | } 36 | 37 | Future _saveNote(int? id, String title, String content) async { 38 | if (title.isEmpty || content.isEmpty) { 39 | _eventController.add( 40 | const AddEditNoteUiEvent.showSnackBar('제목이나 내용이 비어 있습니다'), 41 | ); 42 | return; 43 | } 44 | 45 | if (id == null) { 46 | await repository.insertNote( 47 | Note( 48 | title: title, 49 | content: content, 50 | color: _color, 51 | timestamp: DateTime.now().millisecondsSinceEpoch, 52 | ), 53 | ); 54 | } else { 55 | await repository.updateNote( 56 | Note( 57 | id: id, 58 | title: title, 59 | content: content, 60 | color: _color, 61 | timestamp: DateTime.now().millisecondsSinceEpoch, 62 | ), 63 | ); 64 | } 65 | 66 | _eventController.add(const AddEditNoteUiEvent.savedNote()); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /note_app/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /image-search-app/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 18 | 22 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 36 | 37 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /note_app/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /note_app/lib/presentation/notes/components/order_section.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_note_app/domain/util/note_order.dart'; 3 | import 'package:flutter_note_app/domain/util/order_type.dart'; 4 | 5 | class OrderSection extends StatelessWidget { 6 | final NoteOrder noteOrder; 7 | final Function(NoteOrder noteOrder) onOrderChanged; 8 | 9 | const OrderSection({ 10 | super.key, 11 | required this.noteOrder, 12 | required this.onOrderChanged, 13 | }); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Column( 18 | children: [ 19 | Row( 20 | children: [ 21 | Radio( 22 | value: NoteOrder.title(noteOrder.orderType), 23 | groupValue: noteOrder, 24 | onChanged: (NoteOrder? value) { 25 | onOrderChanged(NoteOrder.title(noteOrder.orderType)); 26 | }, 27 | activeColor: Colors.white, 28 | ), 29 | const Text('제목'), 30 | Radio( 31 | value: NoteOrder.date(noteOrder.orderType), 32 | groupValue: noteOrder, 33 | onChanged: (NoteOrder? value) { 34 | onOrderChanged(NoteOrder.date(noteOrder.orderType)); 35 | }, 36 | activeColor: Colors.white, 37 | ), 38 | const Text('날짜'), 39 | Radio( 40 | value: NoteOrder.color(noteOrder.orderType), 41 | groupValue: noteOrder, 42 | onChanged: (NoteOrder? value) { 43 | onOrderChanged(NoteOrder.color(noteOrder.orderType)); 44 | }, 45 | activeColor: Colors.white, 46 | ), 47 | const Text('색상'), 48 | ], 49 | ), 50 | Row( 51 | children: [ 52 | Radio( 53 | value: const OrderType.ascending(), 54 | groupValue: noteOrder.orderType, 55 | onChanged: (OrderType? value) { 56 | onOrderChanged( 57 | noteOrder.copyWith(orderType: const OrderType.ascending()), 58 | ); 59 | }, 60 | activeColor: Colors.white, 61 | ), 62 | const Text('오름차순'), 63 | Radio( 64 | value: const OrderType.descending(), 65 | groupValue: noteOrder.orderType, 66 | onChanged: (OrderType? value) { 67 | onOrderChanged( 68 | noteOrder.copyWith(orderType: const OrderType.descending()), 69 | ); 70 | }, 71 | activeColor: Colors.white, 72 | ), 73 | const Text('내림차순'), 74 | ], 75 | ), 76 | ], 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /note_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /image-search-app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /image-search-app/test/ui/home_view_model_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | import 'package:image_search/data/data_source/result.dart'; 3 | import 'package:image_search/domain/repository/photo_api_repository.dart'; 4 | import 'package:image_search/domain/model/photo.dart'; 5 | import 'package:image_search/domain/use_case/get_photos_use_case.dart'; 6 | import 'package:image_search/presentation/home/home_view_model.dart'; 7 | 8 | void main() { 9 | test('Stream이 잘 동작해야 한다', () async { 10 | final viewModel = HomeViewModel(GetPhotosUseCase(FakePhotoApiRepository())); 11 | 12 | await viewModel.fetch('apple'); 13 | 14 | final List result = fakeJson.map((e) => Photo.fromJson(e)).toList(); 15 | 16 | expect(viewModel.state.photos, result); 17 | }); 18 | } 19 | 20 | class FakePhotoApiRepository extends PhotoApiRepository { 21 | @override 22 | Future>> fetch(String query) async { 23 | Future.delayed(const Duration(milliseconds: 500)); 24 | 25 | return Result.success(fakeJson.map((e) => Photo.fromJson(e)).toList()); 26 | } 27 | } 28 | 29 | List> fakeJson = [ 30 | { 31 | "id": 410311, 32 | "pageURL": 33 | "https://pixabay.com/photos/iphone-hand-screen-smartphone-apps-410311/", 34 | "type": "photo", 35 | "tags": "iphone, hand, screen", 36 | "previewURL": 37 | "https://cdn.pixabay.com/photo/2014/08/05/10/27/iphone-410311_150.jpg", 38 | "previewWidth": 150, 39 | "previewHeight": 99, 40 | "webformatURL": 41 | "https://pixabay.com/get/ga40944969ab1ca0cb5e5e2a753382c5ef38aa9b1bdf195f44a6e8c7def03f5b2ce08c74211f5bd254565642907f5e7b5_640.jpg", 42 | "webformatWidth": 640, 43 | "webformatHeight": 426, 44 | "largeImageURL": 45 | "https://pixabay.com/get/gac97151d90f6f74f39ba9a6013d97a3e0c8b3b2673356bef20a65b9a253d439913d8d3566a6e8485773b9aea90170c38a538a3582b0a2af3e51efe53ebc8885b_1280.jpg", 46 | "imageWidth": 1920, 47 | "imageHeight": 1280, 48 | "imageSize": 416413, 49 | "views": 441374, 50 | "downloads": 213676, 51 | "collections": 2913, 52 | "likes": 573, 53 | "comments": 146, 54 | "user_id": 264599, 55 | "user": "JESHOOTS-com", 56 | "userImageURL": 57 | "https://cdn.pixabay.com/user/2014/06/08/15-27-10-248_250x250.jpg" 58 | }, 59 | { 60 | "id": 620817, 61 | "pageURL": 62 | "https://pixabay.com/photos/office-notes-notepad-entrepreneur-620817/", 63 | "type": "photo", 64 | "tags": "office, notes, notepad", 65 | "previewURL": 66 | "https://cdn.pixabay.com/photo/2015/02/02/11/08/office-620817_150.jpg", 67 | "previewWidth": 150, 68 | "previewHeight": 99, 69 | "webformatURL": 70 | "https://pixabay.com/get/g5dce019c1f10360baae95dd11b0a474f4a88609aa453b6fb63eb21af5ced9f66512d0eecdb37d13d65aece68c04ac30f_640.jpg", 71 | "webformatWidth": 640, 72 | "webformatHeight": 425, 73 | "largeImageURL": 74 | "https://pixabay.com/get/g288340ffe42c24238450b87b53b341663b8a4d5a34cd29ddfe5bfb2ea8ebad94f954e901100c4cd89e7e821fb78c1262034505ae2109e88ecf14df55589fddb1_1280.jpg", 75 | "imageWidth": 4288, 76 | "imageHeight": 2848, 77 | "imageSize": 2800224, 78 | "views": 631369, 79 | "downloads": 269069, 80 | "collections": 3062, 81 | "likes": 1062, 82 | "comments": 242, 83 | "user_id": 663163, 84 | "user": "Firmbee", 85 | "userImageURL": 86 | "https://cdn.pixabay.com/user/2020/11/25/09-38-28-431_250x250.png" 87 | }, 88 | ]; 89 | -------------------------------------------------------------------------------- /image-search-app/lib/presentation/home/home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:image_search/presentation/home/home_view_model.dart'; 5 | import 'package:image_search/presentation/home/home_ui_event.dart'; 6 | import 'package:image_search/presentation/home/components/photo_widget.dart'; 7 | import 'package:provider/provider.dart'; 8 | 9 | class HomeScreen extends StatefulWidget { 10 | const HomeScreen({super.key}); 11 | 12 | @override 13 | State createState() => _HomeScreenState(); 14 | } 15 | 16 | class _HomeScreenState extends State { 17 | final _controller = TextEditingController(); 18 | StreamSubscription? _subscription; 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | 24 | Future.microtask(() { 25 | // context 를 안전하게 사용하기 위해 검사 26 | if (mounted) { 27 | final viewModel = context.read(); 28 | _subscription = viewModel.eventStream.listen((event) { 29 | switch (event) { 30 | case ShowSnackBar(:final message): 31 | if (mounted) { 32 | final snackBar = SnackBar(content: Text(message)); 33 | ScaffoldMessenger.of(context).showSnackBar(snackBar); 34 | } 35 | } 36 | }); 37 | } 38 | }); 39 | } 40 | 41 | @override 42 | void dispose() { 43 | _subscription?.cancel(); 44 | _controller.dispose(); 45 | super.dispose(); 46 | } 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | final viewModel = context.watch(); 51 | final state = viewModel.state; 52 | 53 | return Scaffold( 54 | appBar: AppBar( 55 | centerTitle: true, 56 | title: const Text('이미지 검색 앱', style: TextStyle(color: Colors.black)), 57 | backgroundColor: Colors.white, 58 | elevation: 0, 59 | ), 60 | body: Column( 61 | children: [ 62 | Padding( 63 | padding: const EdgeInsets.all(16.0), 64 | child: TextField( 65 | controller: _controller, 66 | decoration: InputDecoration( 67 | border: const OutlineInputBorder( 68 | borderRadius: BorderRadius.all(Radius.circular(10.0)), 69 | ), 70 | suffixIcon: IconButton( 71 | onPressed: () async { 72 | viewModel.fetch(_controller.text); 73 | }, 74 | icon: const Icon(Icons.search), 75 | ), 76 | ), 77 | ), 78 | ), 79 | state.isLoading 80 | ? const CircularProgressIndicator() 81 | : Expanded( 82 | child: GridView.builder( 83 | padding: const EdgeInsets.all(16.0), 84 | itemCount: state.photos.length, 85 | gridDelegate: 86 | const SliverGridDelegateWithFixedCrossAxisCount( 87 | crossAxisCount: 2, 88 | crossAxisSpacing: 16, 89 | mainAxisSpacing: 16, 90 | ), 91 | itemBuilder: (context, index) { 92 | final photo = state.photos[index]; 93 | return PhotoWidget(photo: photo); 94 | }, 95 | ), 96 | ), 97 | ], 98 | ), 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /note_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /image-search-app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /note_app/lib/presentation/notes/notes_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_note_app/presentation/add_edit_note/add_edit_note_screen.dart'; 3 | import 'package:flutter_note_app/presentation/notes/components/order_section.dart'; 4 | import 'package:flutter_note_app/presentation/notes/notes_event.dart'; 5 | import 'package:flutter_note_app/presentation/notes/notes_view_model.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | import 'components/note_item.dart'; 9 | 10 | class NotesScreen extends StatelessWidget { 11 | const NotesScreen({super.key}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | final viewModel = context.watch(); 16 | final state = viewModel.state; 17 | 18 | return Scaffold( 19 | appBar: AppBar( 20 | title: const Text( 21 | 'Your note', 22 | style: TextStyle(fontSize: 30, color: Colors.white), 23 | ), 24 | actions: [ 25 | IconButton( 26 | onPressed: () { 27 | viewModel.onEvent(const NotesEvent.toggleOrderSection()); 28 | }, 29 | icon: const Icon(Icons.sort), 30 | ), 31 | ], 32 | elevation: 0, 33 | ), 34 | floatingActionButton: FloatingActionButton( 35 | onPressed: () async { 36 | bool? isSaved = await Navigator.push( 37 | context, 38 | MaterialPageRoute(builder: (context) => const AddEditNoteScreen()), 39 | ); 40 | 41 | if (isSaved != null && isSaved) { 42 | viewModel.onEvent(const NotesEvent.loadNotes()); 43 | } 44 | }, 45 | child: const Icon(Icons.add), 46 | ), 47 | body: Padding( 48 | padding: const EdgeInsets.all(8.0), 49 | child: ListView( 50 | children: [ 51 | AnimatedSwitcher( 52 | duration: const Duration(milliseconds: 300), 53 | child: state.isOrderSectionVisible 54 | ? OrderSection( 55 | noteOrder: state.noteOrder, 56 | onOrderChanged: (noteOrder) { 57 | viewModel.onEvent(NotesEvent.changeOrder(noteOrder)); 58 | }, 59 | ) 60 | : Container(), 61 | ), 62 | ...state.notes 63 | .map( 64 | (note) => GestureDetector( 65 | onTap: () async { 66 | bool? isSaved = await Navigator.push( 67 | context, 68 | MaterialPageRoute( 69 | builder: (context) => AddEditNoteScreen(note: note), 70 | ), 71 | ); 72 | 73 | if (isSaved != null && isSaved) { 74 | viewModel.onEvent(const NotesEvent.loadNotes()); 75 | } 76 | }, 77 | child: NoteItem( 78 | note: note, 79 | onDeleteTap: () { 80 | viewModel.onEvent(NotesEvent.deleteNote(note)); 81 | 82 | final snackBar = SnackBar( 83 | content: const Text('노트가 삭제되었습니다'), 84 | action: SnackBarAction( 85 | label: '취소', 86 | onPressed: () { 87 | viewModel.onEvent(const NotesEvent.restoreNote()); 88 | }, 89 | ), 90 | ); 91 | 92 | ScaffoldMessenger.of(context).showSnackBar(snackBar); 93 | }, 94 | ), 95 | ), 96 | ) 97 | .toList(), 98 | ], 99 | ), 100 | ), 101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /image-search-app/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: image_search 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ^3.8.0 22 | 23 | # Dependencies specify other packages that your package needs in order to work. 24 | # To automatically upgrade your package dependencies to the latest versions 25 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 26 | # dependencies can be manually updated by changing the version numbers below to 27 | # the latest version available on pub.dev. To see which dependencies have newer 28 | # versions available, run `flutter pub outdated`. 29 | dependencies: 30 | flutter: 31 | sdk: flutter 32 | 33 | 34 | # The following adds the Cupertino Icons font to your application. 35 | # Use with the CupertinoIcons class for iOS style icons. 36 | cupertino_icons: ^1.0.8 37 | http: ^1.2.2 38 | json_annotation: ^4.9.0 39 | freezed_annotation: ^3.0.0 40 | provider: ^6.1.2 41 | 42 | dev_dependencies: 43 | flutter_test: 44 | sdk: flutter 45 | 46 | # The "flutter_lints" package below contains a set of recommended lints to 47 | # encourage good coding practices. The lint set provided by the package is 48 | # activated in the `analysis_options.yaml` file located at the root of your 49 | # package. See that file for information about deactivating specific lint 50 | # rules and activating additional ones. 51 | flutter_lints: ^6.0.0 52 | mockito: ^5.4.4 53 | build_runner: ^2.4.13 54 | json_serializable: ^6.8.0 55 | freezed: ^3.0.0 56 | 57 | # For information on the generic Dart part of this file, see the 58 | # following page: https://dart.dev/tools/pub/pubspec 59 | 60 | # The following section is specific to Flutter. 61 | flutter: 62 | 63 | # The following line ensures that the Material Icons font is 64 | # included with your application, so that you can use the icons in 65 | # the material Icons class. 66 | uses-material-design: true 67 | 68 | # To add assets to your application, add an assets section, like this: 69 | # assets: 70 | # - images/a_dot_burr.jpeg 71 | # - images/a_dot_ham.jpeg 72 | 73 | # An image asset can refer to one or more resolution-specific "variants", see 74 | # https://flutter.dev/assets-and-images/#resolution-aware. 75 | 76 | # For details regarding adding assets from package dependencies, see 77 | # https://flutter.dev/assets-and-images/#from-packages 78 | 79 | # To add custom fonts to your application, add a fonts section here, 80 | # in this "flutter" section. Each entry in this list should have a 81 | # "family" key with the font family name, and a "fonts" key with a 82 | # list giving the asset and other descriptors for the font. For 83 | # example: 84 | # fonts: 85 | # - family: Schyler 86 | # fonts: 87 | # - asset: fonts/Schyler-Regular.ttf 88 | # - asset: fonts/Schyler-Italic.ttf 89 | # style: italic 90 | # - family: Trajan Pro 91 | # fonts: 92 | # - asset: fonts/TrajanPro.ttf 93 | # - asset: fonts/TrajanPro_Bold.ttf 94 | # weight: 700 95 | # 96 | # For details regarding fonts from package dependencies, 97 | # see https://flutter.dev/custom-fonts/#from-packages 98 | -------------------------------------------------------------------------------- /note_app/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_note_app 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ^3.8.0 22 | 23 | # Dependencies specify other packages that your package needs in order to work. 24 | # To automatically upgrade your package dependencies to the latest versions 25 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 26 | # dependencies can be manually updated by changing the version numbers below to 27 | # the latest version available on pub.dev. To see which dependencies have newer 28 | # versions available, run `flutter pub outdated`. 29 | dependencies: 30 | flutter: 31 | sdk: flutter 32 | 33 | 34 | # The following adds the Cupertino Icons font to your application. 35 | # Use with the CupertinoIcons class for iOS style icons. 36 | cupertino_icons: ^1.0.2 37 | provider: ^6.1.5 38 | sqflite: ^2.4.2 39 | json_annotation: ^4.9.0 40 | freezed_annotation: ^3.0.0 41 | 42 | dev_dependencies: 43 | flutter_test: 44 | sdk: flutter 45 | 46 | # The "flutter_lints" package below contains a set of recommended lints to 47 | # encourage good coding practices. The lint set provided by the package is 48 | # activated in the `analysis_options.yaml` file located at the root of your 49 | # package. See that file for information about deactivating specific lint 50 | # rules and activating additional ones. 51 | flutter_lints: ^6.0.0 52 | json_serializable: ^6.9.5 53 | freezed: ^3.0.0 54 | build_runner: ^2.5.3 55 | sqflite_common_ffi: ^2.1.0 56 | mockito: ^5.4.6 57 | 58 | # For information on the generic Dart part of this file, see the 59 | # following page: https://dart.dev/tools/pub/pubspec 60 | 61 | # The following section is specific to Flutter. 62 | flutter: 63 | 64 | # The following line ensures that the Material Icons font is 65 | # included with your application, so that you can use the icons in 66 | # the material Icons class. 67 | uses-material-design: true 68 | 69 | # To add assets to your application, add an assets section, like this: 70 | # assets: 71 | # - images/a_dot_burr.jpeg 72 | # - images/a_dot_ham.jpeg 73 | 74 | # An image asset can refer to one or more resolution-specific "variants", see 75 | # https://flutter.dev/assets-and-images/#resolution-aware. 76 | 77 | # For details regarding adding assets from package dependencies, see 78 | # https://flutter.dev/assets-and-images/#from-packages 79 | 80 | # To add custom fonts to your application, add a fonts section here, 81 | # in this "flutter" section. Each entry in this list should have a 82 | # "family" key with the font family name, and a "fonts" key with a 83 | # list giving the asset and other descriptors for the font. For 84 | # example: 85 | # fonts: 86 | # - family: Schyler 87 | # fonts: 88 | # - asset: fonts/Schyler-Regular.ttf 89 | # - asset: fonts/Schyler-Italic.ttf 90 | # style: italic 91 | # - family: Trajan Pro 92 | # fonts: 93 | # - asset: fonts/TrajanPro.ttf 94 | # - asset: fonts/TrajanPro_Bold.ttf 95 | # weight: 700 96 | # 97 | # For details regarding fonts from package dependencies, 98 | # see https://flutter.dev/custom-fonts/#from-packages 99 | -------------------------------------------------------------------------------- /note_app/lib/presentation/add_edit_note/add_edit_note_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_note_app/domain/model/note.dart'; 5 | import 'package:flutter_note_app/presentation/add_edit_note/add_edit_note_event.dart'; 6 | import 'package:flutter_note_app/presentation/add_edit_note/add_edit_note_ui_event.dart'; 7 | import 'package:flutter_note_app/presentation/add_edit_note/add_edit_note_view_model.dart'; 8 | import 'package:flutter_note_app/ui/colors.dart'; 9 | import 'package:provider/provider.dart'; 10 | 11 | class AddEditNoteScreen extends StatefulWidget { 12 | final Note? note; 13 | 14 | const AddEditNoteScreen({super.key, this.note}); 15 | 16 | @override 17 | State createState() => _AddEditNoteScreenState(); 18 | } 19 | 20 | class _AddEditNoteScreenState extends State { 21 | final _titleController = TextEditingController(); 22 | final _contentController = TextEditingController(); 23 | StreamSubscription? _streamSubscription; 24 | 25 | final List noteColors = [ 26 | roseBud, 27 | primrose, 28 | wisteria, 29 | skyBlue, 30 | illusion, 31 | ]; 32 | 33 | @override 34 | void initState() { 35 | super.initState(); 36 | 37 | if (widget.note != null) { 38 | _titleController.text = widget.note!.title; 39 | _contentController.text = widget.note!.content; 40 | } 41 | 42 | Future.microtask(() { 43 | if (mounted) { 44 | final viewModel = context.read(); 45 | 46 | _streamSubscription = viewModel.eventStream.listen((event) { 47 | if (mounted) { 48 | switch (event) { 49 | case SavedNote(): 50 | Navigator.pop(context, true); 51 | case ShowSnackBar(:final message): 52 | final snackBar = SnackBar(content: Text(message)); 53 | ScaffoldMessenger.of(context).showSnackBar(snackBar); 54 | } 55 | } 56 | }); 57 | } 58 | }); 59 | } 60 | 61 | @override 62 | void dispose() { 63 | _streamSubscription?.cancel(); 64 | _titleController.dispose(); 65 | _contentController.dispose(); 66 | super.dispose(); 67 | } 68 | 69 | @override 70 | Widget build(BuildContext context) { 71 | final viewModel = context.watch(); 72 | 73 | return Scaffold( 74 | floatingActionButton: FloatingActionButton( 75 | onPressed: () { 76 | viewModel.onEvent( 77 | AddEditNoteEvent.saveNote( 78 | widget.note?.id, 79 | _titleController.text, 80 | _contentController.text, 81 | ), 82 | ); 83 | }, 84 | child: const Icon(Icons.save), 85 | ), 86 | body: AnimatedContainer( 87 | padding: const EdgeInsets.only( 88 | left: 16, 89 | right: 16, 90 | bottom: 16, 91 | top: 48, 92 | ), 93 | color: Color(viewModel.color), 94 | duration: const Duration(milliseconds: 500), 95 | child: ListView( 96 | children: [ 97 | Row( 98 | mainAxisAlignment: MainAxisAlignment.spaceAround, 99 | children: noteColors 100 | .map( 101 | (color) => InkWell( 102 | onTap: () { 103 | viewModel.onEvent( 104 | AddEditNoteEvent.changeColor(color.toARGB32()), 105 | ); 106 | }, 107 | child: _buildBackgroundColor( 108 | color: color, 109 | selected: viewModel.color == color.toARGB32(), 110 | ), 111 | ), 112 | ) 113 | .toList(), 114 | ), 115 | TextField( 116 | controller: _titleController, 117 | maxLines: 1, 118 | style: Theme.of( 119 | context, 120 | ).textTheme.headlineSmall!.copyWith(color: darkGray), 121 | decoration: const InputDecoration( 122 | hintText: '제목을 입력하세요', 123 | border: InputBorder.none, 124 | ), 125 | ), 126 | TextField( 127 | controller: _contentController, 128 | maxLines: null, 129 | style: Theme.of( 130 | context, 131 | ).textTheme.bodyLarge!.copyWith(color: darkGray), 132 | decoration: const InputDecoration( 133 | hintText: '내용을 입력하세요', 134 | border: InputBorder.none, 135 | ), 136 | ), 137 | ], 138 | ), 139 | ), 140 | ); 141 | } 142 | 143 | Widget _buildBackgroundColor({required Color color, required bool selected}) { 144 | return Container( 145 | width: 48, 146 | height: 48, 147 | decoration: BoxDecoration( 148 | color: color, 149 | shape: BoxShape.circle, 150 | boxShadow: [ 151 | BoxShadow( 152 | color: Colors.black.withValues(alpha: 0.2), 153 | blurRadius: 5.0, 154 | spreadRadius: 1.0, 155 | ), 156 | ], 157 | border: selected ? Border.all(color: Colors.black, width: 3.0) : null, 158 | ), 159 | ); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /image-search-app/README.md: -------------------------------------------------------------------------------- 1 | # Flutter Clean Architecture - Freezed 3.0 업데이트 가이드 2 | 3 | 안녕하세요! 이 문서는 강의에서 사용하던 이미지 검색 앱을 Freezed 3.0으로 업데이트한 내용을 정리한 것입니다. 변경된 부분들을 자세히 설명드리니, 코드를 따라가시면서 이해해보시기 바랍니다. 4 | 5 | ## 🎯 왜 업데이트를 했을까요? 6 | 7 | 기존에 사용하던 Freezed 2.x에서 3.0으로 업데이트하면서 다음과 같은 장점들을 얻을 수 있습니다: 8 | 9 | - **더 안전한 코드**: sealed 클래스로 패턴 매칭이 더 엄격해졌어요 10 | - **성능 향상**: switch 문은 기본 Dart 문법이에요. when() 메서드는 제거 됐어요. 11 | - **현대적인 문법**: Dart 3의 패턴 매칭(switch)을 활용할 수 있어요 12 | - **더 깔끔한 코드**: 코드가 더 읽기 쉬워졌어요 13 | 14 | ## 📚 주요 변경 사항 설명 15 | 16 | ### 1. 의존성 업데이트 17 | 18 | ```yaml 19 | # pubspec.yaml에서 변경된 부분 20 | dev_dependencies: 21 | flutter_lints: ^6.0.0 # 기존 4.0.0에서 업데이트 22 | ``` 23 | 24 | **왜 업데이트했나요?** 25 | - 최신 Flutter 린트 규칙을 사용하여 더 좋은 코드 품질을 유지하기 위해서입니다. 26 | 27 | ### 2. Freezed 클래스 선언 방식 변경 28 | 29 | #### 🔍 **일반 데이터 클래스의 경우** 30 | 31 | ```dart 32 | // ✅ 변경 후 (Photo, HomeState) 33 | @freezed 34 | abstract class Photo with _$Photo { 35 | factory Photo({ 36 | required int id, 37 | required String tags, 38 | required String previewURL, 39 | }) = _Photo; 40 | 41 | factory Photo.fromJson(Map json) => _$PhotoFromJson(json); 42 | } 43 | ``` 44 | 45 | **설명**: 일반적인 데이터를 담는 클래스에는 `abstract` 키워드를 붙입니다. 46 | 47 | #### 🔍 **sealed 클래스의 경우** 48 | 49 | ```dart 50 | // ✅ 변경 후 (Result, HomeUiEvent) 51 | @freezed 52 | sealed class Result with _$Result { 53 | const factory Result.success(T data) = Success; 54 | const factory Result.error(String e) = Error; 55 | } 56 | ``` 57 | 58 | **설명**: 여러 가지 경우(성공/실패 등)를 나타내는 클래스에는 `sealed` 키워드를 사용합니다. 59 | 60 | 61 | ### 3. 패턴 매칭: when() → switch 문으로 변경 62 | 63 | 이 부분이 가장 큰 변화입니다! 기존의 `.when()` 메서드를 모두 switch 문으로 바꿨어요. 64 | 65 | #### 🔍 **ViewModel에서의 변경** 66 | 67 | ```dart 68 | // ❌ 기존 방식 (when 메서드) 69 | result.when( 70 | success: (photos) { 71 | _state = state.copyWith(photos: photos); 72 | notifyListeners(); 73 | }, 74 | error: (message) { 75 | _eventController.add(HomeUiEvent.showSnackBar(message)); 76 | }, 77 | ); 78 | 79 | // ✅ 새로운 방식 (switch 문) 80 | switch (result) { 81 | case Success(:final data): 82 | _state = state.copyWith(photos: data); 83 | notifyListeners(); 84 | case Error(:final e): 85 | _eventController.add(HomeUiEvent.showSnackBar(e)); 86 | } 87 | ``` 88 | 89 | **장점**: 90 | - 더 빠른 성능 91 | - 컴파일러가 모든 경우를 다뤘는지 더 엄격하게 검사 92 | - Dart의 최신 패턴 매칭 문법 활용 93 | 94 | #### 🔍 **UseCase에서의 변경** 95 | 96 | ```dart 97 | // ❌ 기존 방식 98 | return result.when( 99 | success: (photos) => Result.success(photos.sublist(0, min(3, photos.length))), 100 | error: (message) => Result.error(message), 101 | ); 102 | 103 | // ✅ 새로운 방식 (switch 표현식) 104 | return switch (result) { 105 | Success(:final data) => Result.success(data.sublist(0, min(3, data.length))), 106 | Error(:final e) => Result.error(e), 107 | }; 108 | ``` 109 | 110 | **설명**: 간단한 값 반환은 switch 표현식으로 한 줄에 처리할 수 있어요. 111 | 112 | #### 🔍 **UI 이벤트 처리에서의 변경** 113 | 114 | ```dart 115 | // ❌ 기존 방식 116 | event.when(showSnackBar: (message) { 117 | final snackBar = SnackBar(content: Text(message)); 118 | ScaffoldMessenger.of(context).showSnackBar(snackBar); 119 | }); 120 | 121 | // ✅ 새로운 방식 122 | switch (event) { 123 | case ShowSnackBar(:final message): 124 | final snackBar = SnackBar(content: Text(message)); 125 | ScaffoldMessenger.of(context).showSnackBar(snackBar); 126 | } 127 | ``` 128 | 129 | ### 4. 패턴 매칭 문법 이해하기 130 | 131 | 새로운 문법이 어려우실 수 있어서 자세히 설명드릴게요: 132 | 133 | #### 🔍 **`:final` 문법** 134 | 135 | ```dart 136 | // 이 두 코드는 같은 의미입니다 137 | case Success(:final data): // 짧은 문법 138 | case Success(data: final data): // 긴 문법 139 | 140 | // 즉, Success의 data 필드를 data 변수에 저장한다는 뜻 141 | ``` 142 | 143 | #### 🔍 **다양한 패턴 매칭 방식** 144 | 145 | ```dart 146 | // 방법 1: 필드 이름 그대로 사용 147 | case Success(:final data): 148 | 149 | // 방법 2: 다른 이름으로 받기 150 | case Success(data: final photos): 151 | 152 | // 방법 3: 타입 명시하기 (더 명확함) 153 | case Success>(): 154 | // result.data로 접근 155 | ``` 156 | 157 | ### 5. 불필요한 import 정리 158 | 159 | ```dart 160 | // ❌ 제거된 import들 161 | import 'package:json_annotation/json_annotation.dart'; 162 | 163 | // ✅ 이유: freezed_annotation이 이미 포함하고 있어서 중복 164 | ``` 165 | 166 | ## 🛠️ 개발 환경 설정 167 | 168 | 업데이트된 코드를 실행하려면: 169 | 170 | ```bash 171 | # 1. 의존성 설치 172 | flutter pub get 173 | 174 | # 2. 코드 생성 (중요!) 175 | flutter packages pub run build_runner build 176 | 177 | # 3. 정적 분석 확인 178 | flutter analyze 179 | 180 | # 4. 앱 실행 181 | flutter run 182 | ``` 183 | 184 | ## 📁 변경된 파일들 185 | 186 | ### 데이터 모델 187 | - `lib/domain/model/photo.dart` - abstract 키워드 추가 188 | - `lib/presentation/home/home_state.dart` - abstract 키워드 추가 189 | 190 | ### 유니온 타입 191 | - `lib/data/data_source/result.dart` - sealed 키워드로 변경 192 | - `lib/presentation/home/home_ui_event.dart` - sealed 키워드로 변경 193 | 194 | ### 비즈니스 로직 195 | - `lib/domain/use_case/get_photos_use_case.dart` - switch 표현식 사용 196 | - `lib/data/repository/photo_api_repository_impl.dart` - switch 표현식 사용 197 | 198 | ### UI 레이어 199 | - `lib/presentation/home/home_view_model.dart` - switch 문 사용 200 | - `lib/presentation/home/home_screen.dart` - switch 문 사용, import 추가 201 | 202 | ## ⚠️ 주의사항 203 | 204 | ### 코드 생성을 잊지 마세요! 205 | ```bash 206 | flutter packages pub run build_runner build 207 | ``` 208 | Freezed 클래스를 수정한 후에는 반드시 이 명령어를 실행해야 합니다. 209 | 210 | ### switch 문의 exhaustive 검사 211 | sealed 클래스를 사용하면 컴파일러가 모든 경우를 처리했는지 검사합니다. 만약 케이스를 빠뜨리면 컴파일 에러가 발생해요. 212 | 213 | ### 패턴 매칭 문법 연습 214 | 새로운 문법이 익숙하지 않으실 수 있어요. 천천히 따라해보시면서 익혀보세요! 215 | 216 | ## 🎓 학습 포인트 217 | 218 | 이번 업데이트를 통해 다음을 배울 수 있습니다: 219 | 220 | 1. **Dart 3의 패턴 매칭**: 현대적인 Dart 문법 221 | 2. **sealed 클래스**: 타입 안전성 향상 222 | 3. **성능 최적화**: switch가 when()보다 빠른 이유 223 | 4. **코드 품질**: 최신 린트 규칙 적용 224 | 225 | ## 💡 추가 학습 자료 226 | 227 | - [Dart 패턴 매칭 공식 문서](https://dart.dev/language/patterns) 228 | - [Freezed 3.0 마이그레이션 가이드](https://pub.dev/packages/freezed) 229 | - [sealed 클래스 이해하기](https://dart.dev/language/class-modifiers#sealed) 230 | 231 | ## 🤝 질문이 있으시다면 232 | 233 | 업데이트 과정에서 궁금한 점이나 에러가 발생하면 언제든 질문해주세요. 함께 해결해보겠습니다! 234 | 235 | --- 236 | 237 | **마지막으로**: 이런 업데이트들은 Flutter/Dart 생태계가 계속 발전하고 있다는 증거입니다. 최신 기술을 따라가면서 더 좋은 앱을 만들어보세요! 🚀 -------------------------------------------------------------------------------- /note_app/README.md: -------------------------------------------------------------------------------- 1 | # Note App - Freezed 3.0 및 Dart 3 패턴 매칭 마이그레이션 2 | 3 | ## 📋 변경 사항 개요 4 | 5 | 이 프로젝트는 **Freezed 2.x**에서 **Freezed 3.0**으로 마이그레이션하고, **Dart 3의 패턴 매칭** 기능을 적용하여 코드의 안전성과 가독성을 향상시켰습니다. 6 | 7 | ## 🔄 주요 변경 사항 8 | 9 | ### 1. 의존성 업데이트 10 | 11 | ```diff 12 | # pubspec.yaml 13 | dependencies: 14 | - freezed_annotation: ^2.2.0 15 | + freezed_annotation: ^3.0.0 16 | 17 | dev_dependencies: 18 | - freezed: ^2.3.5 19 | + freezed: ^3.0.0 20 | ``` 21 | 22 | ### 2. Union Types를 Sealed Classes로 변경 23 | 24 | #### OrderType 변경사항 25 | ```diff 26 | # lib/domain/util/order_type.dart 27 | @freezed 28 | - abstract class OrderType with _$OrderType { 29 | + sealed class OrderType with _$OrderType { 30 | const factory OrderType.ascending() = Ascending; 31 | const factory OrderType.descending() = Descending; 32 | } 33 | ``` 34 | 35 | #### NoteOrder 변경사항 36 | ```diff 37 | # lib/domain/util/note_order.dart 38 | @freezed 39 | - abstract class NoteOrder with _$NoteOrder { 40 | + sealed class NoteOrder with _$NoteOrder { 41 | const factory NoteOrder.title(OrderType orderType) = NoteOrderTitle; 42 | const factory NoteOrder.date(OrderType orderType) = NoteOrderDate; 43 | const factory NoteOrder.color(OrderType orderType) = NoteOrderColor; 44 | } 45 | ``` 46 | 47 | #### Event Classes 변경사항 48 | ```diff 49 | # lib/presentation/notes/notes_event.dart 50 | @freezed 51 | - abstract class NotesEvent with _$NotesEvent { 52 | + sealed class NotesEvent with _$NotesEvent { 53 | const factory NotesEvent.loadNotes() = LoadNotes; 54 | const factory NotesEvent.deleteNote(Note note) = DeleteNote; 55 | const factory NotesEvent.restoreNote() = RestoreNote; 56 | const factory NotesEvent.changeOrder(NoteOrder noteOrder) = ChangeOrder; 57 | const factory NotesEvent.toggleOrderSection() = ToggleOrderSection; 58 | } 59 | ``` 60 | 61 | ```diff 62 | # lib/presentation/add_edit_note/add_edit_note_event.dart 63 | @freezed 64 | - abstract class AddEditNoteEvent with _$AddEditNoteEvent { 65 | + sealed class AddEditNoteEvent with _$AddEditNoteEvent { 66 | const factory AddEditNoteEvent.changeColor(int color) = ChangeColor; 67 | const factory AddEditNoteEvent.saveNote(int? id, String title, String content) = SaveNote; 68 | } 69 | ``` 70 | 71 | ```diff 72 | # lib/presentation/add_edit_note/add_edit_note_ui_event.dart 73 | @freezed 74 | - abstract class AddEditNoteUiEvent with _$AddEditNoteUiEvent { 75 | + sealed class AddEditNoteUiEvent with _$AddEditNoteUiEvent { 76 | - const factory AddEditNoteUiEvent.saveNote() = SaveNote; 77 | + const factory AddEditNoteUiEvent.savedNote() = SavedNote; 78 | const factory AddEditNoteUiEvent.showSnackBar(String message) = ShowSnackBar; 79 | } 80 | ``` 81 | 82 | ### 3. .when() 메소드를 Switch 패턴 매칭으로 변경 83 | 84 | #### GetNotesUseCase에서의 변경 85 | ```diff 86 | # lib/domain/use_case/get_notes_use_case.dart 87 | + import 'package:flutter_note_app/domain/util/order_type.dart'; 88 | 89 | - noteOrder.when( 90 | - title: (orderType) { 91 | - orderType.when( 92 | - ascending: () { 93 | - notes.sort((a, b) => a.title.compareTo(b.title)); 94 | - }, 95 | - descending: () { 96 | - notes.sort((a, b) => -a.title.compareTo(b.title)); 97 | - }, 98 | - ); 99 | - }, 100 | - date: (orderType) { 101 | - orderType.when( 102 | - ascending: () { 103 | - notes.sort((a, b) => a.timestamp.compareTo(b.timestamp)); 104 | - }, 105 | - descending: () { 106 | - notes.sort((a, b) => -a.timestamp.compareTo(b.timestamp)); 107 | - }, 108 | - ); 109 | - }, 110 | - color: (orderType) { 111 | - orderType.when( 112 | - ascending: () { 113 | - notes.sort((a, b) => a.color.compareTo(b.color)); 114 | - }, 115 | - descending: () { 116 | - notes.sort((a, b) => -a.color.compareTo(b.color)); 117 | - }, 118 | - ); 119 | - }, 120 | - ); 121 | 122 | + switch (noteOrder) { 123 | + case NoteOrderTitle(:final orderType): 124 | + switch (orderType) { 125 | + case Ascending(): 126 | + notes.sort((a, b) => a.title.compareTo(b.title)); 127 | + case Descending(): 128 | + notes.sort((a, b) => -a.title.compareTo(b.title)); 129 | + } 130 | + case NoteOrderDate(:final orderType): 131 | + switch (orderType) { 132 | + case Ascending(): 133 | + notes.sort((a, b) => a.timestamp.compareTo(b.timestamp)); 134 | + case Descending(): 135 | + notes.sort((a, b) => -a.timestamp.compareTo(b.timestamp)); 136 | + } 137 | + case NoteOrderColor(:final orderType): 138 | + switch (orderType) { 139 | + case Ascending(): 140 | + notes.sort((a, b) => a.color.compareTo(b.color)); 141 | + case Descending(): 142 | + notes.sort((a, b) => -a.color.compareTo(b.color)); 143 | + } 144 | + } 145 | ``` 146 | 147 | #### NotesViewModel에서의 변경 148 | ```diff 149 | # lib/presentation/notes/notes_view_model.dart 150 | void onEvent(NotesEvent event) { 151 | - event.when( 152 | - loadNotes: _loadNotes, 153 | - deleteNote: _deleteNote, 154 | - restoreNote: _restoreNote, 155 | - changeOrder: (NoteOrder noteOrder) { 156 | - _state = state.copyWith(noteOrder: noteOrder); 157 | - _loadNotes(); 158 | - }, 159 | - toggleOrderSection: () { 160 | - _state = state.copyWith( 161 | - isOrderSectionVisible: !state.isOrderSectionVisible, 162 | - ); 163 | - notifyListeners(); 164 | - }, 165 | - ); 166 | 167 | + switch (event) { 168 | + case LoadNotes(): 169 | + _loadNotes(); 170 | + case DeleteNote(:final note): 171 | + _deleteNote(note); 172 | + case RestoreNote(): 173 | + _restoreNote(); 174 | + case ChangeOrder(:final noteOrder): 175 | + _state = state.copyWith(noteOrder: noteOrder); 176 | + _loadNotes(); 177 | + case ToggleOrderSection(): 178 | + _state = state.copyWith( 179 | + isOrderSectionVisible: !state.isOrderSectionVisible, 180 | + ); 181 | + notifyListeners(); 182 | + } 183 | } 184 | ``` 185 | 186 | #### AddEditNoteViewModel에서의 변경 187 | ```diff 188 | # lib/presentation/add_edit_note/add_edit_note_view_model.dart 189 | void onEvent(AddEditNoteEvent event) { 190 | - event.when( 191 | - changeColor: _changColor, 192 | - saveNote: _saveNote, 193 | - ); 194 | 195 | + switch (event) { 196 | + case ChangeColor(:final color): 197 | + _changColor(color); 198 | + case SaveNote(:final id, :final title, :final content): 199 | + _saveNote(id, title, content); 200 | + } 201 | } 202 | 203 | - _eventController.add(const AddEditNoteUiEvent.showSnackBar('제목이나 내용이 비어 있습니다')); 204 | - _eventController.add(const AddEditNoteUiEvent.saveNote()); 205 | + _eventController.add(const AddEditNoteUiEvent.showSnackBar('제목이나 내용이 비어 있습니다')); 206 | + _eventController.add(const AddEditNoteUiEvent.savedNote()); 207 | ``` 208 | 209 | #### AddEditNoteScreen에서의 변경 210 | ```diff 211 | # lib/presentation/add_edit_note/add_edit_note_screen.dart 212 | _streamSubscription = viewModel.eventStream.listen((event) { 213 | - event.when( 214 | - saveNote: () { 215 | - Navigator.pop(context, true); 216 | - }, 217 | - showSnackBar: (String message) { 218 | - final snackBar = SnackBar(content: Text(message)); 219 | - ScaffoldMessenger.of(context).showSnackBar(snackBar); 220 | - }, 221 | - ); 222 | 223 | + switch (event) { 224 | + case SavedNote(): 225 | + Navigator.pop(context, true); 226 | + case ShowSnackBar(:final message): 227 | + final snackBar = SnackBar(content: Text(message)); 228 | + ScaffoldMessenger.of(context).showSnackBar(snackBar); 229 | + } 230 | }); 231 | ``` 232 | 233 | ## 🎯 마이그레이션의 장점 234 | 235 | ### 1. **컴파일 타임 안전성 향상** 236 | - Sealed classes를 사용하여 모든 케이스가 처리되었는지 컴파일 타임에 검증 237 | - 새로운 variant 추가 시 누락된 케이스를 컴파일러가 감지 238 | 239 | ### 2. **코드 가독성 향상** 240 | - Switch 패턴 매칭으로 더 직관적인 코드 작성 241 | - Destructuring을 통해 데이터 접근이 더 명확해짐 242 | 243 | ### 3. **성능 향상** 244 | - Switch 표현식이 .when() 메소드보다 더 효율적 245 | - 패턴 매칭이 런타임 오버헤드를 줄임 246 | 247 | ### 4. **이름 충돌 해결** 248 | - `SaveNote` 클래스명 중복을 `SavedNote`로 변경하여 완전히 해결 249 | - 별칭(alias) 사용 없이도 깔끔한 코드 구조 유지 250 | - 더 명확한 의미 전달 (`saveNote` → `savedNote`) 251 | 252 | ## 🔧 마이그레이션 후 실행 방법 253 | 254 | 1. **의존성 업데이트** 255 | ```bash 256 | flutter pub get 257 | ``` 258 | 259 | 2. **코드 생성** 260 | ```bash 261 | dart run build_runner build --delete-conflicting-outputs 262 | ``` 263 | 264 | 3. **분석 및 테스트** 265 | ```bash 266 | flutter analyze 267 | flutter test 268 | ``` 269 | 270 | 4. **앱 실행** 271 | ```bash 272 | flutter run 273 | ``` 274 | 275 | ## 📚 Dart 3 패턴 매칭 문법 정리 276 | 277 | ### 기본 패턴 매칭 278 | ```dart 279 | switch (value) { 280 | case Pattern1(): 281 | // 처리 로직 282 | case Pattern2(): 283 | // 처리 로직 284 | } 285 | ``` 286 | 287 | ### Destructuring 패턴 288 | ```dart 289 | switch (event) { 290 | case DeleteNote(:final note): 291 | // note 변수에 직접 접근 가능 292 | case ChangeOrder(:final noteOrder): 293 | // noteOrder 변수에 직접 접근 가능 294 | } 295 | ``` 296 | 297 | ### 중첩 패턴 매칭 298 | ```dart 299 | switch (noteOrder) { 300 | case NoteOrderTitle(:final orderType): 301 | switch (orderType) { 302 | case Ascending(): 303 | // 처리 로직 304 | case Descending(): 305 | // 처리 로직 306 | } 307 | } 308 | ``` 309 | 310 | ### Named Parameter Destructuring 311 | ```dart 312 | switch (event) { 313 | case SaveNote(:final id, :final title, :final content): 314 | // id, title, content 변수에 직접 접근 가능 315 | } 316 | ``` 317 | 318 | ## 📝 마이그레이션 체크리스트 319 | 320 | - [x] **의존성 업데이트**: `freezed` 및 `freezed_annotation` 3.0으로 업데이트 321 | - [x] **Union Types 변경**: `abstract class` → `sealed class` 322 | - [x] `OrderType` 323 | - [x] `NoteOrder` 324 | - [x] `NotesEvent` 325 | - [x] `AddEditNoteEvent` 326 | - [x] `AddEditNoteUiEvent` 327 | - [x] **패턴 매칭 적용**: `.when()` → `switch` 문 328 | - [x] `GetNotesUseCase` 329 | - [x] `NotesViewModel` 330 | - [x] `AddEditNoteViewModel` 331 | - [x] `AddEditNoteScreen` 332 | - [x] **이름 충돌 해결**: `SaveNote` → `SavedNote` 변경 333 | - [x] **코드 생성**: `build_runner` 실행 334 | 335 | ## 🚨 주의사항 336 | 337 | 1. **코드 생성 필수**: sealed class 변경 후 반드시 `build_runner` 실행 338 | 2. **이름 충돌 해결**: 339 | - `SaveNote` 중복 시 `SavedNote`로 변경하여 의미 구분 340 | - 클래스명 변경으로 별칭 사용 불필요 341 | 3. **모든 케이스 처리**: sealed class 사용 시 모든 케이스를 처리해야 함 342 | 4. **기존 .when() 코드**: 점진적으로 switch 패턴 매칭으로 변경 권장 343 | 5. **Dart 3 필수**: 패턴 매칭 기능을 위해 Dart 3.0 이상 필요 344 | 345 | ## 📖 참고 자료 346 | 347 | - [Dart 3 Pattern Matching](https://dart.dev/language/patterns) 348 | - [Freezed 3.0 Documentation](https://pub.dev/packages/freezed) 349 | - [Sealed Classes in Dart](https://dart.dev/language/class-modifiers#sealed) 350 | - [Switch Expressions](https://dart.dev/language/branches#switch-expressions) 351 | 352 | -------------------------------------------------------------------------------- /image-search-app/test/data/pixabay_api_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | import 'package:image_search/data/data_source/pixabay_api.dart'; 3 | import 'package:image_search/data/data_source/result.dart'; 4 | import 'package:image_search/data/repository/photo_api_repository_impl.dart'; 5 | import 'package:image_search/domain/model/photo.dart'; 6 | import 'package:mockito/annotations.dart'; 7 | import 'package:http/http.dart' as http; 8 | import 'package:mockito/mockito.dart'; 9 | 10 | import 'pixabay_api_test.mocks.dart'; 11 | 12 | @GenerateMocks([http.Client]) 13 | void main() { 14 | test('Pixabay 데이터를 잘 가져와야 한다', () async { 15 | final client = MockClient(); 16 | final api = PhotoApiRepositoryImpl(PixabayApi(client)); 17 | 18 | when(client.get(Uri.parse( 19 | '${PixabayApi.baseUrl}?key=${PixabayApi.key}&q=iphone&image_type=photo'))) 20 | .thenAnswer((_) async => http.Response(fakeJsonBody, 200)); 21 | 22 | final Result> result = await api.fetch('iphone'); 23 | 24 | expect((result as Success>).data.first.id, 410311); 25 | 26 | verify(client.get(Uri.parse( 27 | '${PixabayApi.baseUrl}?key=${PixabayApi.key}&q=iphone&image_type=photo'))); 28 | }); 29 | } 30 | 31 | String fakeJsonBody = """ 32 | {"total":1320,"totalHits":500,"hits":[{"id":410311,"pageURL":"https://pixabay.com/photos/iphone-hand-screen-smartphone-apps-410311/","type":"photo","tags":"iphone, hand, screen","previewURL":"https://cdn.pixabay.com/photo/2014/08/05/10/27/iphone-410311_150.jpg","previewWidth":150,"previewHeight":99,"webformatURL":"https://pixabay.com/get/ga40944969ab1ca0cb5e5e2a753382c5ef38aa9b1bdf195f44a6e8c7def03f5b2ce08c74211f5bd254565642907f5e7b5_640.jpg","webformatWidth":640,"webformatHeight":426,"largeImageURL":"https://pixabay.com/get/gac97151d90f6f74f39ba9a6013d97a3e0c8b3b2673356bef20a65b9a253d439913d8d3566a6e8485773b9aea90170c38a538a3582b0a2af3e51efe53ebc8885b_1280.jpg","imageWidth":1920,"imageHeight":1280,"imageSize":416413,"views":441374,"downloads":213676,"collections":2913,"likes":573,"comments":146,"user_id":264599,"user":"JESHOOTS-com","userImageURL":"https://cdn.pixabay.com/user/2014/06/08/15-27-10-248_250x250.jpg"},{"id":620817,"pageURL":"https://pixabay.com/photos/office-notes-notepad-entrepreneur-620817/","type":"photo","tags":"office, notes, notepad","previewURL":"https://cdn.pixabay.com/photo/2015/02/02/11/08/office-620817_150.jpg","previewWidth":150,"previewHeight":99,"webformatURL":"https://pixabay.com/get/g5dce019c1f10360baae95dd11b0a474f4a88609aa453b6fb63eb21af5ced9f66512d0eecdb37d13d65aece68c04ac30f_640.jpg","webformatWidth":640,"webformatHeight":425,"largeImageURL":"https://pixabay.com/get/g288340ffe42c24238450b87b53b341663b8a4d5a34cd29ddfe5bfb2ea8ebad94f954e901100c4cd89e7e821fb78c1262034505ae2109e88ecf14df55589fddb1_1280.jpg","imageWidth":4288,"imageHeight":2848,"imageSize":2800224,"views":631369,"downloads":269069,"collections":3062,"likes":1062,"comments":242,"user_id":663163,"user":"Firmbee","userImageURL":"https://cdn.pixabay.com/user/2020/11/25/09-38-28-431_250x250.png"},{"id":410324,"pageURL":"https://pixabay.com/photos/iphone-smartphone-apps-apple-inc-410324/","type":"photo","tags":"iphone, smartphone, apps","previewURL":"https://cdn.pixabay.com/photo/2014/08/05/10/30/iphone-410324_150.jpg","previewWidth":150,"previewHeight":99,"webformatURL":"https://pixabay.com/get/g16340bb72d787b64ec4aecea4e8bbe1384641da311bbeb252eec9d8276e0a889b00f38465010ff532f2be3f9374068dc_640.jpg","webformatWidth":640,"webformatHeight":426,"largeImageURL":"https://pixabay.com/get/g31e63ef550debc504b73ce92f5a82c64db4d54d270129bc052ee7a7dbb87b0dd988da39b698c6a4ed17cc59082fa156693317b39e111010f40df583fb0f9cef2_1280.jpg","imageWidth":2180,"imageHeight":1453,"imageSize":477025,"views":567395,"downloads":313685,"collections":3416,"likes":746,"comments":177,"user_id":264599,"user":"JESHOOTS-com","userImageURL":"https://cdn.pixabay.com/user/2014/06/08/15-27-10-248_250x250.jpg"},{"id":1807521,"pageURL":"https://pixabay.com/photos/hot-air-balloons-bagan-sunset-1807521/","type":"photo","tags":"hot air balloons, bagan, sunset","previewURL":"https://cdn.pixabay.com/photo/2016/11/08/05/18/hot-air-balloons-1807521_150.jpg","previewWidth":105,"previewHeight":150,"webformatURL":"https://pixabay.com/get/ga6b9f79fa9561bf4099e6d76066baa419c1f9e7b10859e25f4d6caa81228cda783514a0da431fa7e06ff5e4442b97dc01592e80504c6d3dc9bf80989a0dae584_640.jpg","webformatWidth":450,"webformatHeight":640,"largeImageURL":"https://pixabay.com/get/g1d725b3d314bafcc9e5de999c28871bfb2721938333719dbfbe834b12f9cb52039a846f81f8a0e948448b0b3b5e3fb2054c644abb6c36e11c33893f5264446ef_1280.jpg","imageWidth":2504,"imageHeight":3558,"imageSize":2054554,"views":377842,"downloads":225496,"collections":1421,"likes":445,"comments":31,"user_id":3639875,"user":"sasint","userImageURL":"https://cdn.pixabay.com/user/2016/10/30/05-50-54-750_250x250.jpg"},{"id":2846221,"pageURL":"https://pixabay.com/photos/business-computer-mobile-smartphone-2846221/","type":"photo","tags":"business, computer, mobile","previewURL":"https://cdn.pixabay.com/photo/2017/10/12/22/17/business-2846221_150.jpg","previewWidth":150,"previewHeight":99,"webformatURL":"https://pixabay.com/get/g028d38050445de1458bd84ef1d707a97d319cfb7474af3180e2e77b77bf476e24142bfee5416c5c8413273faa5df6f0f78deec58c9ba522a5e4159ec708d0e82_640.jpg","webformatWidth":640,"webformatHeight":426,"largeImageURL":"https://pixabay.com/get/g6156afe1eb14faf5c8e5984150e14a019e799ad169c630cc452df8b3f69da60fc497319dda203bef4c8198e95a8850d85a763a23b60bef1c523bd67d5313c33f_1280.jpg","imageWidth":4608,"imageHeight":3072,"imageSize":2543501,"views":312720,"downloads":221383,"collections":2213,"likes":563,"comments":80,"user_id":6689062,"user":"6689062","userImageURL":""},{"id":1851497,"pageURL":"https://pixabay.com/photos/bicycle-building-city-1851497/","type":"photo","tags":"bicycle, building, city","previewURL":"https://cdn.pixabay.com/photo/2016/11/23/00/39/bicycle-1851497_150.jpg","previewWidth":105,"previewHeight":150,"webformatURL":"https://pixabay.com/get/g1999e20b5b69fd4f55deb9d90cfe02c6e93ae806ed4522113982392cd182dc1d28871e74968dfadf85b4e001259ca798ed4d63517ab1f383d7b90d2d8d166b48_640.jpg","webformatWidth":447,"webformatHeight":640,"largeImageURL":"https://pixabay.com/get/g8ddb0178d2c135598df80089986b348f7881392f46d7ffcb8a43c43ad34b0b07922d88914c0ef0c569ba9d4a0b784ef50f11eb53f150ce3652fd38105754011f_1280.jpg","imageWidth":2859,"imageHeight":4096,"imageSize":2369529,"views":231438,"downloads":166426,"collections":1765,"likes":433,"comments":40,"user_id":2286921,"user":"Pexels","userImageURL":"https://cdn.pixabay.com/user/2016/03/26/22-06-36-459_250x250.jpg"},{"id":1867761,"pageURL":"https://pixabay.com/photos/home-office-computer-desk-display-1867761/","type":"photo","tags":"home office, computer, desk","previewURL":"https://cdn.pixabay.com/photo/2016/11/29/06/18/home-office-1867761_150.jpg","previewWidth":150,"previewHeight":100,"webformatURL":"https://pixabay.com/get/g43bdb579cf458dc60d6a7d03a4c76f1a236191977d606971b27c4fd48b14d7bb5883f3616a605f2c271a118dca471fa2c451c2be34529f8d1a43617f6a25f459_640.jpg","webformatWidth":640,"webformatHeight":427,"largeImageURL":"https://pixabay.com/get/g4d7add6dc54e6fe052cec9eca36c6d45cae99ee9c0a427d4ac4baa1ef4bcf61f9478c20df21f81ff8858156ed698b72f80df320e1f41e3486d6353343531cdc7_1280.jpg","imageWidth":4000,"imageHeight":2669,"imageSize":2381783,"views":151271,"downloads":90765,"collections":1385,"likes":331,"comments":45,"user_id":2286921,"user":"Pexels","userImageURL":"https://cdn.pixabay.com/user/2016/03/26/22-06-36-459_250x250.jpg"},{"id":459196,"pageURL":"https://pixabay.com/photos/macbook-laptop-google-display-459196/","type":"photo","tags":"macbook, laptop, google","previewURL":"https://cdn.pixabay.com/photo/2014/09/24/14/29/macbook-459196_150.jpg","previewWidth":150,"previewHeight":92,"webformatURL":"https://pixabay.com/get/g0791feb464387f8cf93c366710d526158a9d37f8d8ed20d254123d96c9f4f7ebfc8117abfb2d46481e599a7a7541ddb2_640.jpg","webformatWidth":640,"webformatHeight":396,"largeImageURL":"https://pixabay.com/get/g5cfc1c58c7f0554d7cbc9d3c0407c85a5358a3f5f324bec38eeb9f958c3458f53dea75d1d03eac8aaf6c8cc33b365c0b5206427f019980ee8d1e4c37c901bf24_1280.jpg","imageWidth":3888,"imageHeight":2406,"imageSize":2883743,"views":459130,"downloads":224390,"collections":2788,"likes":726,"comments":185,"user_id":377053,"user":"377053","userImageURL":""},{"id":1979674,"pageURL":"https://pixabay.com/photos/relaxing-lounging-saturday-cozy-1979674/","type":"photo","tags":"relaxing, lounging, saturday","previewURL":"https://cdn.pixabay.com/photo/2017/01/14/15/11/relaxing-1979674_150.jpg","previewWidth":150,"previewHeight":102,"webformatURL":"https://pixabay.com/get/ge5faf76e80277d3cba937bb839bcbeca09aff9cbcd3d729718bf5b5fab8d228db8d8545cd639091f89a713dd5ba0af3375a998fcbb7dc5465e295c2e25258ae3_640.jpg","webformatWidth":640,"webformatHeight":438,"largeImageURL":"https://pixabay.com/get/gfe5058e2a45cbdbbc63b2c54e2c8964ab66abd33b72f97357b0f5b01d24ba5bf66cb0fbbdc8699a7e5a1c490904d27b164fdb260dde58c7317cc5ffa0bdf54c2_1280.jpg","imageWidth":5310,"imageHeight":3637,"imageSize":3751070,"views":341483,"downloads":197853,"collections":2271,"likes":1034,"comments":123,"user_id":334088,"user":"JillWellington","userImageURL":"https://cdn.pixabay.com/user/2018/06/27/01-23-02-27_250x250.jpg"},{"id":3076954,"pageURL":"https://pixabay.com/photos/desk-smartphone-iphone-notebook-3076954/","type":"photo","tags":"desk, smartphone, iphone","previewURL":"https://cdn.pixabay.com/photo/2018/01/11/21/27/desk-3076954_150.jpg","previewWidth":150,"previewHeight":99,"webformatURL":"https://pixabay.com/get/g5045887afe1b52b145675a243284e523b3f1cbdc7743dab6c0565271e4bd825a049910d8df67085ddbd5bc7e13cbd606283e44938eb1611f6407c58bf8408136_640.jpg","webformatWidth":640,"webformatHeight":426,"largeImageURL":"https://pixabay.com/get/g71d994f5f3b8e7938d24ce4fac362c367ecf9b5a95c23275eef125df708c5f399ef8d3baa080f5fcbfe311a68208efa3ee9cf68d81f00f9cf66a0dbc44db041e_1280.jpg","imageWidth":5304,"imageHeight":3531,"imageSize":2258922,"views":192889,"downloads":122410,"collections":1958,"likes":507,"comments":94,"user_id":2218222,"user":"Ylanite","userImageURL":"https://cdn.pixabay.com/user/2021/11/18/21-11-44-855_250x250.png"},{"id":791450,"pageURL":"https://pixabay.com/photos/iphone-iphone-6-iphone-6-plus-apple-791450/","type":"photo","tags":"iphone, iphone 6, iphone 6 plus","previewURL":"https://cdn.pixabay.com/photo/2015/05/31/12/14/iphone-791450_150.jpg","previewWidth":150,"previewHeight":99,"webformatURL":"https://pixabay.com/get/gb96d53f674c35b67efa014d33f77441fc5fd06e956d9011191247537040700f8e85b1309d427e958cc7e9f4c19d3404a_640.jpg","webformatWidth":640,"webformatHeight":426,"largeImageURL":"https://pixabay.com/get/g03ece4df7c6ce682777e12f7fa59b4f9089e5ce99d0ada41edbe9357cc0a4548cb1ffb881780c7e97beafa8bb23fd6ce2d25f72f663caafa10ada6a75f9ea581_1280.jpg","imageWidth":5254,"imageHeight":3503,"imageSize":1909143,"views":122994,"downloads":78303,"collections":1409,"likes":338,"comments":56,"user_id":1013994,"user":"kaboompics","userImageURL":"https://cdn.pixabay.com/user/2018/04/09/00-31-22-504_250x250.jpg"},{"id":763731,"pageURL":"https://pixabay.com/photos/social-media-facebook-smartphone-763731/","type":"photo","tags":"social media, facebook, smartphone","previewURL":"https://cdn.pixabay.com/photo/2015/05/12/09/13/social-media-763731_150.jpg","previewWidth":150,"previewHeight":99,"webformatURL":"https://pixabay.com/get/g0843745593cfb6b7801088c9b85a7bef4da82297b86028cd02fc8135188d1020a3d3046d6a3124f7c3d411e258d53210_640.jpg","webformatWidth":640,"webformatHeight":425,"largeImageURL":"https://pixabay.com/get/g095901d8b4af23e0df080c477212f558083d8725828277b4fbfcabcad21fe89b397c6c9f2dc087682fb421958a336b51a78d42132703fada98f8f48078c2ad5b_1280.jpg","imageWidth":3967,"imageHeight":2635,"imageSize":2511740,"views":198159,"downloads":115232,"collections":1666,"likes":411,"comments":89,"user_id":663163,"user":"Firmbee","userImageURL":"https://cdn.pixabay.com/user/2020/11/25/09-38-28-431_250x250.png"},{"id":3713473,"pageURL":"https://pixabay.com/photos/asia-rain-street-illumination-3713473/","type":"photo","tags":"asia, rain, street","previewURL":"https://cdn.pixabay.com/photo/2018/09/30/13/17/asia-3713473_150.jpg","previewWidth":120,"previewHeight":150,"webformatURL":"https://pixabay.com/get/g1be944de92e2a81733cac6d1948329c1bff652eef75985911555a8f24ef95f54b10b3d866e101197093597c91f45bc5a0903d2736e4be95071b99372052f52ad_640.jpg","webformatWidth":512,"webformatHeight":640,"largeImageURL":"https://pixabay.com/get/ga5164c9ffa9601a834b09858a030b7f48abe1a7bab0986ccc76e4e0f5d9852ea3a32acfbc911a61860ab045f41dd5acc1f598f2878d0787cdc47498588946703_1280.jpg","imageWidth":2410,"imageHeight":3013,"imageSize":1813305,"views":104169,"downloads":59614,"collections":1148,"likes":213,"comments":18,"user_id":4601460,"user":"4601460","userImageURL":""},{"id":499585,"pageURL":"https://pixabay.com/photos/boat-lake-reflection-water-calm-499585/","type":"photo","tags":"boat, lake, reflection","previewURL":"https://cdn.pixabay.com/photo/2014/10/23/11/21/boat-499585_150.jpg","previewWidth":98,"previewHeight":150,"webformatURL":"https://pixabay.com/get/ga93fe0b31a8aeb90936e6f337e098007424546d6df539c6ea81dbcd62231ad1fff6e11c0135085ed0a0ce9404f438f3f_640.jpg","webformatWidth":420,"webformatHeight":640,"largeImageURL":"https://pixabay.com/get/g69158feae6299ceab0df1b2d359d6a77bffa0de1a08b9d4ad13d3239c0edf851e3e8a43038bf764f015ceb2a065f2a4f21292d3c5d2c42522012e6087886a584_1280.jpg","imageWidth":3177,"imageHeight":4839,"imageSize":2043537,"views":109644,"downloads":55364,"collections":697,"likes":218,"comments":30,"user_id":516512,"user":"DuncanNelson","userImageURL":"https://cdn.pixabay.com/user/2014/10/18/11-16-04-114_250x250.jpg"},{"id":1192032,"pageURL":"https://pixabay.com/photos/girl-smartphone-iphone-1192032/","type":"photo","tags":"girl, smartphone, iphone","previewURL":"https://cdn.pixabay.com/photo/2016/02/10/16/39/girl-1192032_150.jpg","previewWidth":150,"previewHeight":99,"webformatURL":"https://pixabay.com/get/g7b501cefa4d5870d43e0ff233320b276f31decd6325e2f2a414a52da7228f3eff95a6e597819de4112bcbbcb6a03ae865c0d2a43542cf66123ff58bd8f4ac807_640.jpg","webformatWidth":640,"webformatHeight":426,"largeImageURL":"https://pixabay.com/get/ge49fe9c0820da438b148fc3ab2e48e802832d6dc1b4f86377d9d4febfd5a08cca747a8c0bbedb857a0229787ac343ada28938071ec4672755d599ffd04acc3f2_1280.jpg","imageWidth":2048,"imageHeight":1367,"imageSize":299773,"views":94102,"downloads":44522,"collections":546,"likes":188,"comments":16,"user_id":725943,"user":"janeb13","userImageURL":"https://cdn.pixabay.com/user/2016/02/25/23-52-46-967_250x250.jpg"},{"id":1867461,"pageURL":"https://pixabay.com/photos/apple-iphone-close-up-smartphone-1867461/","type":"photo","tags":"apple, iphone, close up","previewURL":"https://cdn.pixabay.com/photo/2016/11/29/05/08/apple-1867461_150.jpg","previewWidth":150,"previewHeight":100,"webformatURL":"https://pixabay.com/get/g3db401e81862617155a4729552195f7cfa3929b097676c39972dcdd798a8528a32d2f743aa71cfbb718636ca1c634c60abe2e831cb9318fd9e1133ff9084e8ef_640.jpg","webformatWidth":640,"webformatHeight":427,"largeImageURL":"https://pixabay.com/get/g12bbaf72c4dd6987cccfcbe53af3b6ff5a6e7a692402c71c54fdbfe0bf3a24305fe0d1ae1c67c12f5bd8749837987350f7ea21c20c80e82bc86494b024e7ab14_1280.jpg","imageWidth":5252,"imageHeight":3507,"imageSize":1410507,"views":52540,"downloads":30619,"collections":631,"likes":102,"comments":17,"user_id":2286921,"user":"Pexels","userImageURL":"https://cdn.pixabay.com/user/2016/03/26/22-06-36-459_250x250.jpg"},{"id":5537230,"pageURL":"https://pixabay.com/photos/iphone-hand-phone-mobile-phone-5537230/","type":"photo","tags":"iphone, hand, phone","previewURL":"https://cdn.pixabay.com/photo/2020/09/02/03/26/iphone-5537230_150.jpg","previewWidth":150,"previewHeight":100,"webformatURL":"https://pixabay.com/get/g458ee681b3c8e5e5126059aff95c19b6c0e2afb3e395efb564c66b671fe8efa6ad8de17322f7583d42e777ec90967cbfe9157ad38685858a6f6f9b88e1b3b2e3_640.jpg","webformatWidth":640,"webformatHeight":427,"largeImageURL":"https://pixabay.com/get/g8bb221e070fc8a286f546a8fecd5629a0713d0dcaf251a4d31f56dcc7fcf45e9ee8dfd9760ad00c337163e557d39dda91d8f5c1ba25258aa449bc1ccb6e05eec_1280.jpg","imageWidth":6000,"imageHeight":4000,"imageSize":2408760,"views":43364,"downloads":26871,"collections":735,"likes":71,"comments":9,"user_id":498081,"user":"tranmautritam","userImageURL":"https://cdn.pixabay.com/user/2015/01/22/05-13-32-817_250x250.jpg"},{"id":4599956,"pageURL":"https://pixabay.com/photos/cellular-man-person-technology-4599956/","type":"photo","tags":"cellular, man, person","previewURL":"https://cdn.pixabay.com/photo/2019/11/04/01/11/cellular-4599956_150.jpg","previewWidth":150,"previewHeight":99,"webformatURL":"https://pixabay.com/get/g1bbb61a217bb2ba938e518e7cd246f4d26433a8ef51df779b9a33221abf910843ddf3022efa3c6f1ab9ef64967301d7690b6a22f500c5e4660da79f092e54059_640.jpg","webformatWidth":640,"webformatHeight":426,"largeImageURL":"https://pixabay.com/get/g380e0308e913063c6020c1fe00f211e1fd9b36dbc66166dffd6e7f32e10574d1f3de5b460dbca2210e175d1376d5b896421d272f40beaaed461c43b8c0f9300c_1280.jpg","imageWidth":5184,"imageHeight":3456,"imageSize":5727471,"views":48861,"downloads":31696,"collections":672,"likes":109,"comments":21,"user_id":10110273,"user":"GustavoWandalen","userImageURL":"https://cdn.pixabay.com/user/2021/08/16/16-33-21-80_250x250.jpg"},{"id":500291,"pageURL":"https://pixabay.com/photos/iphone-hand-girl-smartphone-500291/","type":"photo","tags":"iphone, hand, girl","previewURL":"https://cdn.pixabay.com/photo/2014/10/23/20/51/iphone-500291_150.jpg","previewWidth":150,"previewHeight":99,"webformatURL":"https://pixabay.com/get/ge19b8a9d1d9dce7708482878a2b59f13a10949fee838d3d84018fcc770dc51931365d8e5c084e11ad4b330b09883c988_640.jpg","webformatWidth":640,"webformatHeight":426,"largeImageURL":"https://pixabay.com/get/g254ebcf0ae5cb5c27ffbe4eb0a35bfe1784cbd0f8b8b529ba18bdf198d53daabcfa26315db5179acbc4e05ac35a890ce510d4601d6e20905c59efae7c7fc6752_1280.jpg","imageWidth":1920,"imageHeight":1280,"imageSize":521447,"views":144488,"downloads":68565,"collections":661,"likes":265,"comments":44,"user_id":264599,"user":"JESHOOTS-com","userImageURL":"https://cdn.pixabay.com/user/2014/06/08/15-27-10-248_250x250.jpg"},{"id":1867636,"pageURL":"https://pixabay.com/photos/water-pool-turquoise-background-1867636/","type":"photo","tags":"water, pool, turquoise","previewURL":"https://cdn.pixabay.com/photo/2016/11/29/05/49/water-1867636_150.jpg","previewWidth":100,"previewHeight":150,"webformatURL":"https://pixabay.com/get/g01e156151600b1554e75174b1ea48fa5bd5d1b05793a163d665b09b5a66a838b3c49454f1d95d040ce0c868277aca6c39e475ffabf2e8d336bb5768087033616_640.jpg","webformatWidth":426,"webformatHeight":640,"largeImageURL":"https://pixabay.com/get/gf34f13355929c4faea22023d1dd7268a33386317fe18ec4a3fec3bfd57401725bab0bb6262f541e0f950cef647eb138ce608831640f73cda4bbbdc32fb7837bb_1280.jpg","imageWidth":3648,"imageHeight":5472,"imageSize":8220680,"views":149903,"downloads":95061,"collections":924,"likes":231,"comments":10,"user_id":2286921,"user":"Pexels","userImageURL":"https://cdn.pixabay.com/user/2016/03/26/22-06-36-459_250x250.jpg"}]} 33 | """; 34 | -------------------------------------------------------------------------------- /image-search-app/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | ); 26 | name = "Embed Frameworks"; 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 9740EEB11CF90186004384FC /* Flutter */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 65 | ); 66 | name = Flutter; 67 | sourceTree = ""; 68 | }; 69 | 97C146E51CF9000F007C117D = { 70 | isa = PBXGroup; 71 | children = ( 72 | 9740EEB11CF90186004384FC /* Flutter */, 73 | 97C146F01CF9000F007C117D /* Runner */, 74 | 97C146EF1CF9000F007C117D /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 97C146EF1CF9000F007C117D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 97C146EE1CF9000F007C117D /* Runner.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 97C146F01CF9000F007C117D /* Runner */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 92 | 97C147021CF9000F007C117D /* Info.plist */, 93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 97 | ); 98 | path = Runner; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | 97C146ED1CF9000F007C117D /* Runner */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 107 | buildPhases = ( 108 | 9740EEB61CF901F6004384FC /* Run Script */, 109 | 97C146EA1CF9000F007C117D /* Sources */, 110 | 97C146EB1CF9000F007C117D /* Frameworks */, 111 | 97C146EC1CF9000F007C117D /* Resources */, 112 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = Runner; 120 | productName = Runner; 121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | 97C146E61CF9000F007C117D /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 1020; 131 | ORGANIZATIONNAME = ""; 132 | TargetAttributes = { 133 | 97C146ED1CF9000F007C117D = { 134 | CreatedOnToolsVersion = 7.3.1; 135 | LastSwiftMigration = 1100; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 140 | compatibilityVersion = "Xcode 9.3"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | Base, 146 | ); 147 | mainGroup = 97C146E51CF9000F007C117D; 148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 97C146ED1CF9000F007C117D /* Runner */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 97C146EC1CF9000F007C117D /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXShellScriptBuildPhase section */ 172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 173 | isa = PBXShellScriptBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | inputPaths = ( 178 | ); 179 | name = "Thin Binary"; 180 | outputPaths = ( 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | shellPath = /bin/sh; 184 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 185 | }; 186 | 9740EEB61CF901F6004384FC /* Run Script */ = { 187 | isa = PBXShellScriptBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | inputPaths = ( 192 | ); 193 | name = "Run Script"; 194 | outputPaths = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | shellPath = /bin/sh; 198 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 199 | }; 200 | /* End PBXShellScriptBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 97C146EA1CF9000F007C117D /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 208 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXSourcesBuildPhase section */ 213 | 214 | /* Begin PBXVariantGroup section */ 215 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 97C146FB1CF9000F007C117D /* Base */, 219 | ); 220 | name = Main.storyboard; 221 | sourceTree = ""; 222 | }; 223 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 97C147001CF9000F007C117D /* Base */, 227 | ); 228 | name = LaunchScreen.storyboard; 229 | sourceTree = ""; 230 | }; 231 | /* End PBXVariantGroup section */ 232 | 233 | /* Begin XCBuildConfiguration section */ 234 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_ANALYZER_NONNULL = YES; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 240 | CLANG_CXX_LIBRARY = "libc++"; 241 | CLANG_ENABLE_MODULES = YES; 242 | CLANG_ENABLE_OBJC_ARC = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INFINITE_RECURSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 258 | CLANG_WARN_STRICT_PROTOTYPES = YES; 259 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 260 | CLANG_WARN_UNREACHABLE_CODE = YES; 261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 263 | COPY_PHASE_STRIP = NO; 264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 276 | MTL_ENABLE_DEBUG_INFO = NO; 277 | SDKROOT = iphoneos; 278 | SUPPORTED_PLATFORMS = iphoneos; 279 | TARGETED_DEVICE_FAMILY = "1,2"; 280 | VALIDATE_PRODUCT = YES; 281 | }; 282 | name = Profile; 283 | }; 284 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 285 | isa = XCBuildConfiguration; 286 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | CLANG_ENABLE_MODULES = YES; 290 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 291 | ENABLE_BITCODE = NO; 292 | INFOPLIST_FILE = Runner/Info.plist; 293 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 294 | PRODUCT_BUNDLE_IDENTIFIER = com.example.imageSearch; 295 | PRODUCT_NAME = "$(TARGET_NAME)"; 296 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 297 | SWIFT_VERSION = 5.0; 298 | VERSIONING_SYSTEM = "apple-generic"; 299 | }; 300 | name = Profile; 301 | }; 302 | 97C147031CF9000F007C117D /* Debug */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ALWAYS_SEARCH_USER_PATHS = NO; 306 | CLANG_ANALYZER_NONNULL = YES; 307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 308 | CLANG_CXX_LIBRARY = "libc++"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_COMMA = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_EMPTY_BODY = YES; 318 | CLANG_WARN_ENUM_CONVERSION = YES; 319 | CLANG_WARN_INFINITE_RECURSION = YES; 320 | CLANG_WARN_INT_CONVERSION = YES; 321 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 322 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 323 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 324 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 325 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 326 | CLANG_WARN_STRICT_PROTOTYPES = YES; 327 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 328 | CLANG_WARN_UNREACHABLE_CODE = YES; 329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 330 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 331 | COPY_PHASE_STRIP = NO; 332 | DEBUG_INFORMATION_FORMAT = dwarf; 333 | ENABLE_STRICT_OBJC_MSGSEND = YES; 334 | ENABLE_TESTABILITY = YES; 335 | GCC_C_LANGUAGE_STANDARD = gnu99; 336 | GCC_DYNAMIC_NO_PIC = NO; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_OPTIMIZATION_LEVEL = 0; 339 | GCC_PREPROCESSOR_DEFINITIONS = ( 340 | "DEBUG=1", 341 | "$(inherited)", 342 | ); 343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 345 | GCC_WARN_UNDECLARED_SELECTOR = YES; 346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 347 | GCC_WARN_UNUSED_FUNCTION = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 350 | MTL_ENABLE_DEBUG_INFO = YES; 351 | ONLY_ACTIVE_ARCH = YES; 352 | SDKROOT = iphoneos; 353 | TARGETED_DEVICE_FAMILY = "1,2"; 354 | }; 355 | name = Debug; 356 | }; 357 | 97C147041CF9000F007C117D /* Release */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | ALWAYS_SEARCH_USER_PATHS = NO; 361 | CLANG_ANALYZER_NONNULL = YES; 362 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 363 | CLANG_CXX_LIBRARY = "libc++"; 364 | CLANG_ENABLE_MODULES = YES; 365 | CLANG_ENABLE_OBJC_ARC = YES; 366 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 367 | CLANG_WARN_BOOL_CONVERSION = YES; 368 | CLANG_WARN_COMMA = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 371 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 372 | CLANG_WARN_EMPTY_BODY = YES; 373 | CLANG_WARN_ENUM_CONVERSION = YES; 374 | CLANG_WARN_INFINITE_RECURSION = YES; 375 | CLANG_WARN_INT_CONVERSION = YES; 376 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 377 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 378 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 379 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 380 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 381 | CLANG_WARN_STRICT_PROTOTYPES = YES; 382 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 383 | CLANG_WARN_UNREACHABLE_CODE = YES; 384 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 385 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 386 | COPY_PHASE_STRIP = NO; 387 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 388 | ENABLE_NS_ASSERTIONS = NO; 389 | ENABLE_STRICT_OBJC_MSGSEND = YES; 390 | GCC_C_LANGUAGE_STANDARD = gnu99; 391 | GCC_NO_COMMON_BLOCKS = YES; 392 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 393 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 394 | GCC_WARN_UNDECLARED_SELECTOR = YES; 395 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 396 | GCC_WARN_UNUSED_FUNCTION = YES; 397 | GCC_WARN_UNUSED_VARIABLE = YES; 398 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 399 | MTL_ENABLE_DEBUG_INFO = NO; 400 | SDKROOT = iphoneos; 401 | SUPPORTED_PLATFORMS = iphoneos; 402 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 403 | TARGETED_DEVICE_FAMILY = "1,2"; 404 | VALIDATE_PRODUCT = YES; 405 | }; 406 | name = Release; 407 | }; 408 | 97C147061CF9000F007C117D /* Debug */ = { 409 | isa = XCBuildConfiguration; 410 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 411 | buildSettings = { 412 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 413 | CLANG_ENABLE_MODULES = YES; 414 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 415 | ENABLE_BITCODE = NO; 416 | INFOPLIST_FILE = Runner/Info.plist; 417 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 418 | PRODUCT_BUNDLE_IDENTIFIER = com.example.imageSearch; 419 | PRODUCT_NAME = "$(TARGET_NAME)"; 420 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 421 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 422 | SWIFT_VERSION = 5.0; 423 | VERSIONING_SYSTEM = "apple-generic"; 424 | }; 425 | name = Debug; 426 | }; 427 | 97C147071CF9000F007C117D /* Release */ = { 428 | isa = XCBuildConfiguration; 429 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 430 | buildSettings = { 431 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 432 | CLANG_ENABLE_MODULES = YES; 433 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 434 | ENABLE_BITCODE = NO; 435 | INFOPLIST_FILE = Runner/Info.plist; 436 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 437 | PRODUCT_BUNDLE_IDENTIFIER = com.example.imageSearch; 438 | PRODUCT_NAME = "$(TARGET_NAME)"; 439 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 440 | SWIFT_VERSION = 5.0; 441 | VERSIONING_SYSTEM = "apple-generic"; 442 | }; 443 | name = Release; 444 | }; 445 | /* End XCBuildConfiguration section */ 446 | 447 | /* Begin XCConfigurationList section */ 448 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 449 | isa = XCConfigurationList; 450 | buildConfigurations = ( 451 | 97C147031CF9000F007C117D /* Debug */, 452 | 97C147041CF9000F007C117D /* Release */, 453 | 249021D3217E4FDB00AE95B9 /* Profile */, 454 | ); 455 | defaultConfigurationIsVisible = 0; 456 | defaultConfigurationName = Release; 457 | }; 458 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | 97C147061CF9000F007C117D /* Debug */, 462 | 97C147071CF9000F007C117D /* Release */, 463 | 249021D4217E4FDB00AE95B9 /* Profile */, 464 | ); 465 | defaultConfigurationIsVisible = 0; 466 | defaultConfigurationName = Release; 467 | }; 468 | /* End XCConfigurationList section */ 469 | }; 470 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 471 | } 472 | --------------------------------------------------------------------------------