Compare commits
No commits in common. "683f04742ab968a8466f9964468dd7be0175b739" and "0fdaa1f6dd274908dd249a41a3396bdf9b506c94" have entirely different histories.
683f04742a
...
0fdaa1f6dd
740
.idea/libraries/Dart_Packages.xml
generated
@ -1,740 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="Dart Packages" type="DartPackagesLibraryType">
|
||||
<properties>
|
||||
<option name="packageNameToDirsMap">
|
||||
<entry key="_flutterfire_internals">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/_flutterfire_internals-1.3.59/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="args">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/args-2.7.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="async">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/async-2.13.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="boolean_selector">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/boolean_selector-2.1.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="characters">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/characters-1.4.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="clock">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/clock-1.1.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="code_assets">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/code_assets-1.0.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="collection">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/collection-1.19.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="crypto">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/crypto-3.0.7/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="csslib">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/csslib-1.0.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="cupertino_icons">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/cupertino_icons-1.0.8/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="dio">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/dio-5.9.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="dio_web_adapter">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/dio_web_adapter-2.1.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="fake_async">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/fake_async-1.3.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="ffi">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/ffi-2.1.5/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="file">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/file-7.0.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="firebase_auth">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_auth-5.7.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="firebase_auth_platform_interface">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_auth_platform_interface-7.7.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="firebase_auth_web">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_auth_web-5.15.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="firebase_core">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_core-3.15.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="firebase_core_platform_interface">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_core_platform_interface-6.0.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="firebase_core_web">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_core_web-2.24.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="flutter">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/../../flutter/packages/flutter/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="flutter_lints">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_lints-6.0.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="flutter_secure_storage">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage-9.2.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="flutter_secure_storage_linux">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="flutter_secure_storage_macos">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="flutter_secure_storage_platform_interface">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_platform_interface-1.1.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="flutter_secure_storage_web">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="flutter_secure_storage_windows">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="flutter_svg">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_svg-2.2.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="flutter_test">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/../../flutter/packages/flutter_test/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="flutter_web_plugins">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/../../flutter/packages/flutter_web_plugins/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="glob">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/glob-2.1.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="google_identity_services_web">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/google_identity_services_web-0.3.3+1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="google_sign_in">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/google_sign_in-6.3.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="google_sign_in_android">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/google_sign_in_android-6.2.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="google_sign_in_ios">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/google_sign_in_ios-5.9.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="google_sign_in_platform_interface">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/google_sign_in_platform_interface-2.5.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="google_sign_in_web">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/google_sign_in_web-0.12.4+4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="hooks">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/hooks-1.0.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="html">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/html-0.15.6/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="http">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/http-1.6.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="http_parser">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/http_parser-4.1.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="js">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/js-0.6.7/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="leak_tracker">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/leak_tracker-11.0.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="leak_tracker_flutter_testing">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/leak_tracker_flutter_testing-3.0.10/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="leak_tracker_testing">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/leak_tracker_testing-3.0.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="lints">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/lints-6.0.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="logging">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/logging-1.3.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="matcher">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/matcher-0.12.17/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="material_color_utilities">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/material_color_utilities-0.11.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="meta">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/meta-1.17.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="mime">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/mime-2.0.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="native_toolchain_c">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/native_toolchain_c-0.17.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="objective_c">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/objective_c-9.2.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="path">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path-1.9.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="path_parsing">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path_parsing-1.1.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="path_provider">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider-2.1.5/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="path_provider_android">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_android-2.2.22/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="path_provider_foundation">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_foundation-2.6.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="path_provider_linux">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_linux-2.2.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="path_provider_platform_interface">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_platform_interface-2.1.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="path_provider_windows">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_windows-2.3.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="petitparser">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/petitparser-7.0.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="platform">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/platform-3.1.6/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="plugin_platform_interface">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/plugin_platform_interface-2.1.8/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="pub_semver">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/pub_semver-2.2.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="sky_engine">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/../../flutter/bin/cache/pkg/sky_engine/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="source_span">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/source_span-1.10.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="stack_trace">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/stack_trace-1.12.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="stream_channel">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/stream_channel-2.1.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="string_scanner">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/string_scanner-1.4.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="term_glyph">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/term_glyph-1.2.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="test_api">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/test_api-0.7.7/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="typed_data">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/typed_data-1.4.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="vector_graphics">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/vector_graphics-1.1.19/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="vector_graphics_codec">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/vector_graphics_codec-1.1.13/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="vector_graphics_compiler">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/vector_graphics_compiler-1.1.19/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="vector_math">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/vector_math-2.2.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="video_player">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/video_player-2.10.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="video_player_android">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/video_player_android-2.9.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="video_player_avfoundation">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/video_player_avfoundation-2.8.10/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="video_player_platform_interface">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/video_player_platform_interface-6.6.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="video_player_web">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/video_player_web-2.4.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="vm_service">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/vm_service-15.0.2/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="web">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/web-1.1.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="win32">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/win32-5.15.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="xdg_directories">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/xdg_directories-1.1.0/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="xml">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/xml-6.6.1/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="yaml">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/yaml-3.1.3/lib" />
|
||||
</list>
|
||||
</value>
|
||||
</entry>
|
||||
</option>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/_flutterfire_internals-1.3.59/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/args-2.7.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/async-2.13.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/boolean_selector-2.1.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/characters-1.4.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/clock-1.1.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/code_assets-1.0.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/collection-1.19.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/crypto-3.0.7/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/csslib-1.0.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/cupertino_icons-1.0.8/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/dio-5.9.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/dio_web_adapter-2.1.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/fake_async-1.3.3/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/ffi-2.1.5/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/file-7.0.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_auth-5.7.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_auth_platform_interface-7.7.3/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_auth_web-5.15.3/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_core-3.15.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_core_platform_interface-6.0.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_core_web-2.24.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_lints-6.0.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage-9.2.4/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_platform_interface-1.1.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_svg-2.2.3/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/glob-2.1.3/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/google_identity_services_web-0.3.3+1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/google_sign_in-6.3.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/google_sign_in_android-6.2.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/google_sign_in_ios-5.9.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/google_sign_in_platform_interface-2.5.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/google_sign_in_web-0.12.4+4/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/hooks-1.0.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/html-0.15.6/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/http-1.6.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/http_parser-4.1.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/js-0.6.7/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/leak_tracker-11.0.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/leak_tracker_flutter_testing-3.0.10/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/leak_tracker_testing-3.0.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/lints-6.0.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/logging-1.3.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/matcher-0.12.17/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/material_color_utilities-0.11.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/meta-1.17.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/mime-2.0.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/native_toolchain_c-0.17.4/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/objective_c-9.2.3/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path-1.9.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path_parsing-1.1.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider-2.1.5/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_android-2.2.22/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_foundation-2.6.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_linux-2.2.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_platform_interface-2.1.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_windows-2.3.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/petitparser-7.0.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/platform-3.1.6/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/plugin_platform_interface-2.1.8/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/pub_semver-2.2.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/source_span-1.10.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/stack_trace-1.12.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/stream_channel-2.1.4/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/string_scanner-1.4.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/term_glyph-1.2.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/test_api-0.7.7/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/typed_data-1.4.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/vector_graphics-1.1.19/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/vector_graphics_codec-1.1.13/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/vector_graphics_compiler-1.1.19/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/vector_math-2.2.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/video_player-2.10.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/video_player_android-2.9.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/video_player_avfoundation-2.8.10/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/video_player_platform_interface-6.6.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/video_player_web-2.4.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/vm_service-15.0.2/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/web-1.1.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/win32-5.15.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/xdg_directories-1.1.0/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/xml-6.6.1/lib" />
|
||||
<root url="file://$USER_HOME$/AppData/Local/Pub/Cache/hosted/pub.dev/yaml-3.1.3/lib" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/pkg/sky_engine/lib" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/packages/flutter/lib" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/packages/flutter_test/lib" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/packages/flutter_web_plugins/lib" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
31
.idea/libraries/Dart_SDK.xml
generated
@ -1,31 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="Dart SDK">
|
||||
<CLASSES>
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/_internal" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/async" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/cli" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/collection" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/concurrent" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/convert" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/core" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/developer" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/ffi" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/html" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/indexed_db" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/io" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/isolate" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/js" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/js_interop" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/js_interop_unsafe" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/js_util" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/math" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/mirrors" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/svg" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/typed_data" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/web_audio" />
|
||||
<root url="file://$PROJECT_DIR$/../../flutter/bin/cache/dart-sdk/lib/web_gl" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
35
.idea/rup.iml
generated
@ -6,43 +6,8 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/linux/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_linux/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/linux/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_linux/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/linux/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_linux/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/firebase_auth/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/firebase_auth/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/firebase_auth/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/firebase_auth/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/firebase_auth/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/firebase_auth/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/firebase_core/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/firebase_core/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/firebase_core/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/firebase_core/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/firebase_core/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/firebase_core/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_windows/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/app/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/example/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@ -41,5 +41,5 @@ migration:
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- "lib/main.dart"
|
||||
- "ios/Runner.xcodeproj/project.pbxproj"
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.1 1.9H15.2V0H13.3V1.9H5.7V0H3.8V1.9H1.9C0.85215 1.9 0 2.75215 0 3.8V17.1C0 18.1479 0.85215 19 1.9 19H17.1C18.1479 19 19 18.1479 19 17.1V3.8C19 2.75215 18.1479 1.9 17.1 1.9ZM8.60415 15.5895L5.98595 13.0293L7.31405 11.6707L8.58135 12.9105L11.6745 9.78215L13.0255 11.1179L8.60415 15.5895ZM17.1 7.6H1.89905V3.8H3.8V5.7H5.7V3.8H13.3V5.7H15.2V3.8H17.1V7.6Z" fill="#C8C8C8"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 485 B |
@ -1,40 +0,0 @@
|
||||
<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1_11594)">
|
||||
<path d="M9.17107 15.5656C8.97561 15.1182 8.9191 14.6428 8.89732 14.158C8.87503 13.6655 8.99064 13.1901 9.02486 12.7017C9.03782 12.5155 9.07308 12.2485 8.96731 12.0806C8.86258 11.9147 8.67438 11.9178 8.50018 11.9058C7.97498 11.8696 7.45031 11.8338 6.92511 11.7975C6.78616 11.7882 6.68662 11.8872 6.65499 12.0064C6.51293 12.0681 6.40976 12.2335 6.50568 12.4098C6.60003 12.5835 6.56374 12.8209 6.56115 13.0117C6.55856 13.2398 6.55026 13.468 6.54041 13.6961C6.53056 13.9159 6.53264 14.1419 6.50619 14.3602C6.48494 14.5355 6.37347 14.6951 6.47405 14.8662C6.56374 15.0181 6.77164 15.0824 6.92096 15.1566C7.12731 15.2592 7.33573 15.3577 7.54622 15.4526C7.97447 15.6455 8.40945 15.8218 8.85014 15.9835C9.11093 16.0794 9.26076 15.7673 9.1721 15.5646L9.17107 15.5656Z" fill="white"/>
|
||||
<path d="M16.1529 9.47486C15.8709 9.06579 15.511 8.70235 15.1103 8.41098C14.9164 8.26996 14.7188 8.10509 14.4746 8.07502C14.2201 8.04599 13.9562 8.15383 13.7659 8.32647C13.2112 8.88226 12.5278 9.29029 11.8704 9.71698C11.773 9.7766 11.6734 9.82482 11.5635 9.85229C11.3603 9.90673 11.1337 9.87614 10.9294 9.95339C10.7993 10.0011 10.6801 10.0773 10.5624 10.1525C10.2259 10.3889 9.83082 10.5185 9.47516 10.7223C9.02773 10.9722 8.91419 11.403 8.90952 11.8862C8.8779 12.8443 8.8359 13.7879 8.69436 14.7362C8.67518 14.8704 8.656 15.0089 8.65962 15.1432C8.66066 15.8348 9.4062 15.9691 9.91533 15.9877C10.4784 16.0007 11.0948 16.0111 11.634 15.7845C12.1271 15.5828 12.5792 15.293 13.0427 15.0327C13.2013 14.9394 13.3786 14.8051 13.5679 14.8787C13.6835 14.9166 13.8089 15.0172 13.9572 15.0421C14.2517 15.1001 14.5493 14.8917 14.6115 14.5993C14.6701 14.3743 14.4937 14.1487 14.5398 13.9232C14.5657 13.7858 14.6266 13.5925 14.6872 13.466C14.9838 12.9278 15.3094 12.3829 15.3514 11.7675C15.3742 11.5155 15.354 11.2361 15.5105 11.0256C15.6889 10.8052 15.8854 10.6056 16.0539 10.3713C16.2846 10.0944 16.3717 9.8103 16.156 9.48212L16.1513 9.47434L16.1529 9.47486ZM13.2983 14.6195L13.3024 14.6242C13.3024 14.6242 13.2993 14.6216 13.2983 14.6195Z" fill="#FCF6EB"/>
|
||||
<path d="M18.9917 6.77611C19.6449 14.3611 10.4371 17.221 9.99228 16.8518C9.72216 17.42 0.582789 14.6816 0.992889 6.77611C1.14894 3.77735 3.42964 1.07671 5.73989 0.899911C7.53168 0.76252 9.11245 1.20943 9.9928 2.7591C10.8726 1.20943 12.4508 0.804515 14.2452 0.899911C16.5777 1.02434 18.734 3.78461 18.9917 6.77611Z" stroke="#C8C8C8" stroke-width="1.83041" stroke-miterlimit="10"/>
|
||||
<path d="M16.1233 8.1408C15.8599 7.61093 15.7111 7.61352 16.0434 7.03804C16.1404 6.84724 16.2 6.63053 16.2653 6.42937C16.3306 6.25309 16.3021 6.04778 16.0844 6.01253C15.8091 5.97779 15.5058 6.05141 15.287 6.22924C15.0895 6.36352 14.893 6.50247 14.7115 6.66008C14.6083 6.73526 14.4823 6.7394 14.3584 6.72126C13.8089 6.6466 13.2386 6.67252 12.7077 6.78814C12.3971 6.87006 12.3826 6.68082 12.2286 6.44233C12.0238 6.1411 11.6707 5.90883 11.3037 5.89847C10.7334 5.89224 10.7246 6.93227 10.78 7.33304C10.8319 7.64982 10.6634 7.92201 10.5612 8.20975C10.5172 8.3544 10.4928 8.50372 10.4721 8.65459C10.4254 8.9807 10.429 9.31355 10.4918 9.6381C10.5047 9.70394 10.5182 9.76979 10.5192 9.83563C10.5213 9.95799 10.4845 10.0788 10.5488 10.1327C10.6006 10.182 10.7277 10.1794 10.8075 10.1643C10.9501 10.1405 11.0932 10.1104 11.2342 10.0778C12.4521 9.84963 13.1997 9.02995 14.1604 8.35129C14.3854 8.18383 14.5041 8.25848 14.6539 8.4612C14.8328 8.68155 15.0169 8.90137 15.2165 9.10409C15.3767 9.26274 16.1575 10.0264 16.3296 9.70394C16.4841 9.27985 16.3146 8.55038 16.1264 8.14909L16.1227 8.1408H16.1233Z" fill="#D3A061"/>
|
||||
<path d="M13.9489 9.79043C13.9764 9.85316 14.0106 9.91226 14.051 9.96722C14.0697 9.99262 14.0884 10.017 14.106 10.0434C14.1153 10.0569 14.1236 10.0714 14.1314 10.0859C14.133 10.0896 14.135 10.0927 14.1366 10.0963C14.1376 10.0984 14.1423 10.1103 14.1387 10.0999C14.135 10.0896 14.1397 10.102 14.1402 10.1041C14.1418 10.1088 14.1433 10.1134 14.1449 10.1176C14.1573 10.1559 14.1993 10.1855 14.2408 10.172C14.2792 10.1596 14.3087 10.1176 14.2952 10.0761C14.2729 10.0071 14.232 9.95115 14.1895 9.89308C14.147 9.83501 14.1117 9.77591 14.0837 9.7111C14.0676 9.67377 14.0096 9.66237 13.9774 9.68311C13.9385 9.70799 13.9323 9.74947 13.9494 9.78939L13.9489 9.79043Z" fill="#C8C8C8"/>
|
||||
<path d="M14.2724 9.32312C14.1884 9.30964 14.0915 9.30497 14.0039 9.30186C13.796 9.29564 14.0474 9.5782 14.1459 9.53828C14.2221 9.52013 14.4446 9.37341 14.2781 9.32415L14.2724 9.32312Z" fill="#C8C8C8"/>
|
||||
<path d="M13.5394 8.76627C13.5424 8.58161 13.4364 8.43015 13.3027 8.42798C13.169 8.42581 13.0582 8.57375 13.0552 8.75841C13.0522 8.94308 13.1582 9.09453 13.2919 9.0967C13.4256 9.09887 13.5364 8.95093 13.5394 8.76627Z" fill="#C8C8C8"/>
|
||||
<mask id="mask0_1_11594" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="13" y="8" width="1" height="2">
|
||||
<path d="M13.5687 8.76725C13.5717 8.58259 13.4657 8.43113 13.332 8.42896C13.1983 8.42679 13.0875 8.57473 13.0845 8.75939C13.0815 8.94405 13.1875 9.09551 13.3212 9.09768C13.4549 9.09985 13.5657 8.95191 13.5687 8.76725Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_1_11594)">
|
||||
<path d="M13.3707 8.6624C13.3805 8.66811 13.3909 8.67277 13.4018 8.67537C13.4127 8.67796 13.4251 8.68003 13.437 8.68003C13.4375 8.68003 13.4712 8.67537 13.4718 8.67537C13.4826 8.67277 13.4935 8.66759 13.5034 8.66189C13.5257 8.64996 13.5366 8.63441 13.5511 8.61471C13.5511 8.61471 13.5511 8.61471 13.5511 8.61419C13.5568 8.60434 13.5604 8.59345 13.5646 8.58256C13.5646 8.58256 13.5646 8.58204 13.5646 8.58153C13.5666 8.57012 13.5718 8.51931 13.5692 8.54679C13.5692 8.54679 13.5692 8.54627 13.5692 8.54575C13.5692 8.53383 13.5677 8.5219 13.5646 8.5105C13.5583 8.48924 13.5469 8.46746 13.5303 8.45243C13.5127 8.43584 13.4956 8.42495 13.4723 8.41873C13.4489 8.41251 13.4235 8.41251 13.4002 8.41873C13.3898 8.42288 13.3795 8.42754 13.3691 8.43169C13.3691 8.43169 13.3691 8.43169 13.3686 8.43169C13.3593 8.43791 13.3504 8.44517 13.3416 8.45243C13.3416 8.45243 13.3416 8.45243 13.3411 8.45295C13.3338 8.46176 13.3261 8.47006 13.3204 8.47991C13.3204 8.47991 13.3204 8.47991 13.3204 8.48043C13.3323 8.45502 13.311 8.50116 13.3074 8.51153C13.3074 8.51153 13.3074 8.51205 13.3074 8.51257C13.3017 8.54886 13.3007 8.54679 13.3074 8.58308C13.3074 8.58308 13.3074 8.58308 13.3074 8.5836C13.311 8.59449 13.3147 8.60537 13.3209 8.61522C13.3349 8.63752 13.3458 8.64737 13.3681 8.66189C13.3774 8.66811 13.3883 8.67174 13.3992 8.67485C13.3992 8.67485 13.3992 8.67485 13.3997 8.67485C13.4116 8.67692 13.423 8.67848 13.4349 8.67951H13.436C13.4588 8.67796 13.4832 8.67485 13.5029 8.66137L13.5293 8.64115L13.5495 8.61471C13.5547 8.60486 13.5589 8.59397 13.563 8.58308C13.5682 8.56597 13.5692 8.54886 13.5651 8.53123C13.564 8.51413 13.5594 8.49805 13.5495 8.48302C13.5412 8.46798 13.5303 8.45554 13.5163 8.44673C13.5039 8.43532 13.4894 8.42754 13.4723 8.4234L13.4381 8.41873C13.4147 8.41873 13.393 8.42495 13.3727 8.43636L13.3463 8.45658C13.3302 8.47265 13.3193 8.49183 13.3131 8.51361C13.3173 8.50324 13.3219 8.49287 13.3261 8.48302C13.3261 8.48302 13.3261 8.48302 13.3261 8.48354L13.3463 8.45709L13.3727 8.43687C13.3727 8.43687 13.3727 8.43687 13.3722 8.43687L13.4033 8.42391C13.4033 8.42391 13.4033 8.42391 13.4028 8.42391L13.437 8.41925C13.437 8.41925 13.436 8.41925 13.4355 8.41925L13.4697 8.42391H13.4686L13.4998 8.43687L13.5262 8.45709L13.5464 8.48354L13.5594 8.51464C13.5594 8.51464 13.5594 8.51412 13.5594 8.51361L13.564 8.54782C13.564 8.54782 13.564 8.54679 13.564 8.54627L13.5594 8.58049C13.5594 8.58049 13.5594 8.57945 13.5594 8.57893L13.5464 8.61004C13.5464 8.61004 13.5464 8.609 13.5469 8.609L13.5267 8.63544C13.5267 8.63544 13.5272 8.63493 13.5278 8.63441L13.5013 8.65463C13.5013 8.65463 13.5018 8.65463 13.5023 8.65411L13.4712 8.66707C13.4712 8.66707 13.4723 8.66707 13.4728 8.66707L13.4386 8.67174C13.4386 8.67174 13.4396 8.67174 13.4401 8.67174L13.4059 8.66707C13.4059 8.66707 13.4064 8.66707 13.407 8.66707L13.3758 8.65411C13.3758 8.65411 13.3758 8.65411 13.3764 8.65411L13.3499 8.63389L13.3297 8.60745L13.3167 8.57634C13.3167 8.57634 13.3167 8.57686 13.3167 8.57738L13.3121 8.54316V8.5442L13.3167 8.50998C13.3167 8.50998 13.3167 8.51153 13.3167 8.51205L13.3297 8.48094C13.3297 8.48094 13.3297 8.48146 13.3292 8.48198L13.3494 8.45554C13.3494 8.45554 13.3494 8.45554 13.3489 8.45606L13.3753 8.43584C13.3753 8.43584 13.3753 8.43584 13.3748 8.43584L13.4059 8.42288C13.4059 8.42288 13.4059 8.42288 13.4054 8.42288L13.4396 8.41821C13.4396 8.41821 13.4396 8.41821 13.4391 8.41821L13.4733 8.42288C13.4733 8.42288 13.4733 8.42288 13.4728 8.42288L13.5039 8.43584C13.5039 8.43584 13.5039 8.43584 13.5034 8.43584C13.4884 8.42651 13.4723 8.42132 13.4552 8.42028C13.4381 8.41666 13.421 8.41717 13.4033 8.42236C13.3862 8.42651 13.3717 8.43428 13.3593 8.44569C13.3453 8.45502 13.3338 8.46695 13.3261 8.48198L13.3131 8.51309C13.3069 8.5359 13.3069 8.55923 13.3131 8.58204L13.3261 8.61315C13.3375 8.63285 13.353 8.64789 13.3727 8.65981L13.3707 8.6624Z" fill="white"/>
|
||||
</g>
|
||||
<path d="M15.2023 8.81394C15.2256 8.63074 15.137 8.46852 15.0044 8.45162C14.8717 8.43472 14.7453 8.56954 14.7219 8.75275C14.6986 8.93595 14.7872 9.09817 14.9199 9.11507C15.0525 9.13197 15.179 8.99715 15.2023 8.81394Z" fill="#C8C8C8"/>
|
||||
<mask id="mask1_1_11594" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="14" y="8" width="2" height="2">
|
||||
<path d="M15.1886 8.80711C15.212 8.6239 15.1234 8.46168 14.9907 8.44479C14.8581 8.42789 14.7316 8.56271 14.7083 8.74591C14.6849 8.92912 14.7735 9.09134 14.9062 9.10823C15.0388 9.12513 15.1653 8.99031 15.1886 8.80711Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_1_11594)">
|
||||
<path d="M15.0102 8.689C15.0262 8.70455 15.0465 8.71803 15.0688 8.7227C15.091 8.72736 15.1165 8.72996 15.1387 8.7227C15.1636 8.71492 15.1781 8.70663 15.1973 8.68848C15.2046 8.67967 15.2113 8.67085 15.2175 8.66204C15.2175 8.66204 15.2175 8.66152 15.2181 8.661C15.2222 8.65063 15.2269 8.64026 15.231 8.62989C15.231 8.62989 15.231 8.62938 15.231 8.62886C15.2388 8.59982 15.2362 8.58686 15.231 8.55835C15.2269 8.53554 15.2113 8.51687 15.1973 8.49976C15.1885 8.4925 15.1797 8.48576 15.1709 8.47954C15.1709 8.47954 15.1709 8.47954 15.1704 8.47954L15.1393 8.46658C15.1377 8.46658 15.1066 8.46191 15.1045 8.46191C15.1045 8.46191 15.104 8.46191 15.1035 8.46191C15.0921 8.46347 15.0802 8.46502 15.0688 8.46658C15.0688 8.46658 15.0682 8.46658 15.0677 8.46658C15.0573 8.47125 15.047 8.47539 15.0366 8.47954C15.0366 8.47954 15.0361 8.47954 15.0356 8.47954C15.0262 8.48576 15.0169 8.4925 15.0086 8.50028C15.0086 8.50028 15.0086 8.50028 15.0086 8.5008C15.0014 8.50961 14.9941 8.51791 14.9879 8.52776C14.9879 8.52776 14.9879 8.52776 14.9879 8.52828C14.9832 8.53813 14.9775 8.54901 14.9744 8.5599C14.9744 8.5599 14.9744 8.5599 14.9744 8.56042C14.9811 8.51843 14.9708 8.58375 14.9697 8.59516C14.9697 8.59619 14.9739 8.62834 14.9744 8.62989C14.9775 8.64078 14.9822 8.65167 14.9879 8.66152C14.9988 8.68018 15.0159 8.69781 15.0351 8.7087C15.0449 8.7144 15.0558 8.71855 15.0667 8.72218C15.0667 8.72218 15.0672 8.72218 15.0677 8.72218C15.0791 8.72477 15.091 8.72685 15.103 8.72685C15.103 8.72685 15.103 8.72685 15.1035 8.72685L15.1377 8.72218C15.1486 8.71959 15.1595 8.7144 15.1693 8.7087C15.1792 8.703 15.188 8.69574 15.1963 8.68796C15.2036 8.67915 15.2103 8.67033 15.2165 8.66152C15.2165 8.66152 15.2165 8.66152 15.2165 8.661C15.2258 8.64597 15.231 8.62989 15.2321 8.61279C15.2357 8.59568 15.2352 8.57857 15.23 8.56094C15.2258 8.54383 15.2181 8.52931 15.2067 8.51687C15.1973 8.50287 15.1854 8.49147 15.1704 8.48369L15.1393 8.47073C15.1164 8.46451 15.0931 8.46451 15.0703 8.47073L15.0392 8.48369C15.0195 8.4951 15.0045 8.51065 14.9925 8.53035C14.9925 8.53035 14.9925 8.53035 14.9925 8.53087L15.0128 8.50443L15.0392 8.48421C15.0392 8.48421 15.0392 8.48421 15.0387 8.48421L15.0698 8.47125C15.0698 8.47125 15.0698 8.47125 15.0693 8.47125L15.1035 8.46658C15.1035 8.46658 15.103 8.46658 15.1025 8.46658L15.1367 8.47125C15.1367 8.47125 15.1351 8.47125 15.1346 8.47125L15.1657 8.48421L15.1921 8.50443L15.2124 8.53087L15.2253 8.56198C15.2253 8.56198 15.2253 8.56146 15.2253 8.56094L15.23 8.59516V8.59412L15.2253 8.62834C15.2253 8.62834 15.2253 8.6273 15.2253 8.62678L15.2124 8.65789C15.2124 8.65789 15.2124 8.65685 15.2129 8.65685L15.1927 8.68329C15.1927 8.68329 15.1932 8.68278 15.1937 8.68226L15.1673 8.70248C15.1673 8.70248 15.1678 8.70248 15.1683 8.70196L15.1372 8.71492C15.1372 8.71492 15.1382 8.71492 15.1387 8.71492L15.1045 8.71959C15.1045 8.71959 15.1056 8.71959 15.1061 8.71959L15.0719 8.71492C15.0719 8.71492 15.0724 8.71492 15.0729 8.71492L15.0418 8.70196C15.0418 8.70196 15.0418 8.70196 15.0423 8.70196L15.0159 8.68174L14.9956 8.6553L14.9827 8.62419C14.9827 8.62419 14.9827 8.62419 14.9827 8.62471L14.978 8.59049C14.978 8.59049 14.978 8.59101 14.978 8.59153L14.9827 8.55731C14.9827 8.55731 14.9827 8.55835 14.9827 8.55887L14.9956 8.52776C14.9956 8.52776 14.9957 8.5288 14.9946 8.52931L15.0148 8.50287C15.0148 8.50287 15.0148 8.50339 15.0143 8.50339L15.0408 8.48317C15.0408 8.48317 15.0408 8.48317 15.0402 8.48317L15.0713 8.47021C15.0713 8.47021 15.0713 8.47021 15.0708 8.47021L15.105 8.46554C15.105 8.46554 15.105 8.46554 15.1045 8.46554L15.1387 8.47021C15.1387 8.47021 15.1387 8.47021 15.1382 8.47021L15.1693 8.48317L15.1958 8.50339C15.1838 8.49095 15.1704 8.48162 15.1543 8.47643C15.1387 8.46814 15.1222 8.46451 15.1045 8.46502C15.0869 8.46451 15.0698 8.46814 15.0548 8.47643C15.0387 8.48162 15.0247 8.49043 15.0133 8.50339L14.9931 8.52983C14.9811 8.55005 14.9754 8.57183 14.9754 8.59516L14.9801 8.62938C14.9863 8.65115 14.9972 8.67033 15.0133 8.68641L15.0102 8.689Z" fill="white"/>
|
||||
</g>
|
||||
<path d="M14.0282 9.46172C13.9375 9.68155 13.8566 9.90552 13.7861 10.1326C13.7565 10.2285 13.9063 10.2695 13.9359 10.1741C14.0064 9.947 14.0873 9.72303 14.178 9.5032C14.1941 9.46432 14.1609 9.41766 14.1236 9.40729C14.079 9.39484 14.0443 9.42284 14.0277 9.46172H14.0282Z" fill="#C8C8C8"/>
|
||||
<path d="M14.5301 14.3609C14.5156 14.2546 14.486 14.1541 14.4378 14.0576C14.414 14.0094 14.3855 13.9555 14.3378 13.9275C14.2662 13.885 14.1967 13.9151 14.1273 13.9415C14.0085 13.9866 13.8903 14.0307 13.7721 14.0763C13.6549 14.1214 13.5383 14.1691 13.4325 14.2375C13.3579 14.2858 13.3516 14.3754 13.4061 14.4413C13.4222 14.4605 13.4419 14.4734 13.4626 14.4833C13.545 14.6772 13.6363 14.868 13.8167 14.9898C13.9914 15.1075 14.2341 15.1101 14.3937 14.9644C14.4736 14.8918 14.5094 14.7855 14.5249 14.6813C14.541 14.574 14.5436 14.4688 14.5291 14.3609H14.5301Z" fill="#FCF6EB"/>
|
||||
<path d="M8.97949 15.4522L8.98831 11.0982C9.44662 10.6876 9.9796 10.3796 10.5903 10.178C10.1756 9.3868 10.3021 8.52979 10.7194 7.63752C10.7194 7.63752 10.4716 5.94009 11.2265 5.80788C11.9814 5.67568 12.4827 6.77792 12.4827 6.77792C13.2651 6.60838 13.98 6.58505 14.6203 6.72296C14.6203 6.72296 15.7967 5.67568 16.2405 5.94061C16.6843 6.20502 15.8553 7.63804 15.8553 7.63804C16.6848 8.96114 16.5324 10.0914 15.4473 11.037C15.4473 11.037 15.399 11.794 15.0351 12.666C14.6711 13.5381 14.498 13.9788 14.498 13.9788C14.498 13.9788 14.8515 14.7922 14.3004 15.0572C13.7493 15.3221 13.3822 14.4609 13.3822 14.4609" stroke="#C8C8C8" stroke-width="0.259229" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8.86429 12.3234C8.86429 12.3234 8.10267 16.0195 9.59739 15.9277C11.68 15.8002 10.0142 7.24042 9.29253 7.3016L5.08836 8.59307C4.46569 8.68795 3.70719 15.684 5.682 15.4989C6.98488 15.3766 6.40161 12.2124 6.40161 12.2124" fill="white"/>
|
||||
<path d="M8.86429 12.3234C8.86429 12.3234 8.10267 16.0195 9.59739 15.9277C11.68 15.8002 10.0142 7.24042 9.29253 7.3016L5.08836 8.59307C4.46569 8.68795 3.70719 15.684 5.682 15.4989C6.98488 15.3766 6.40161 12.2124 6.40161 12.2124" stroke="#C8C8C8" stroke-width="0.259229" stroke-miterlimit="10"/>
|
||||
<path d="M4.82353 3.92396C4.82353 3.92396 3.85661 1.16473 4.82353 0.752558C5.79045 0.340384 6.26536 2.33852 6.26536 2.33852L8.6653 2.41007C8.6653 2.41007 9.42588 0.235137 10.412 0.604797C11.3981 0.974457 10.2274 4.03802 10.2274 4.03802C10.2274 4.03802 10.3461 9.85253 4.82612 8.62585C4.82612 8.62585 2.25716 8.49624 4.82353 3.92448V3.92396Z" fill="white" stroke="#C8C8C8" stroke-width="0.259229" stroke-miterlimit="10"/>
|
||||
<path d="M6.19577 4.45593C6.05994 4.45593 6.04231 4.27343 6.14185 4.2247C6.1066 4.19203 6.06616 4.17285 6.02261 4.17285C5.89144 4.17285 5.78516 4.34446 5.78516 4.55651C5.78516 4.76856 5.89144 4.94017 6.02261 4.94017C6.15378 4.94017 6.26006 4.76856 6.26006 4.55651C6.26006 4.51711 6.25643 4.47978 6.24969 4.44401C6.23414 4.45178 6.21651 4.45645 6.19526 4.45645L6.19577 4.45593Z" fill="#C8C8C8"/>
|
||||
<path d="M7.64109 4.45593C7.50525 4.45593 7.48762 4.27343 7.58717 4.2247C7.55191 4.19203 7.51147 4.17285 7.46792 4.17285C7.33675 4.17285 7.23047 4.34446 7.23047 4.55651C7.23047 4.76856 7.33675 4.94017 7.46792 4.94017C7.59909 4.94017 7.70538 4.76856 7.70538 4.55651C7.70538 4.51711 7.70175 4.47978 7.69501 4.44401C7.67945 4.45178 7.66183 4.45645 7.64057 4.45645L7.64109 4.45593Z" fill="#C8C8C8"/>
|
||||
<path d="M5.21791 6.60987C5.14689 6.06497 4.68027 5.82078 4.20174 5.67353C3.94769 5.59525 3.79008 5.83218 3.82378 6.0349C3.54848 6.30657 3.58892 6.83436 3.59359 7.17654C3.59618 7.39481 3.60966 7.6079 3.84245 7.69604C4.05501 7.7764 4.35779 7.75359 4.57866 7.70952C5.06549 7.61308 5.27702 7.06196 5.21843 6.60935L5.21791 6.60987Z" fill="#C8C8C8"/>
|
||||
<path d="M4.70887 8.58105C4.70887 8.58105 4.38328 9.04663 4.90433 9.26594C5.42538 9.48525 5.95317 8.75992 5.95317 8.75992L4.70887 8.58105Z" fill="#E2B4CC" stroke="#C8C8C8" stroke-width="0.259229" stroke-miterlimit="10"/>
|
||||
<path d="M4.89737 6.24441C4.86989 6.22107 4.83619 6.20552 4.80249 6.19308C4.76879 6.18063 4.73302 6.17441 4.69724 6.17182C4.66821 6.16975 4.63762 6.17493 4.61377 6.191C4.59874 6.19619 4.58526 6.20552 4.57489 6.22056C4.55622 6.24803 4.55622 6.2864 4.5723 6.31492C4.58889 6.34395 4.61792 6.36158 4.64747 6.37454C4.67599 6.3875 4.70554 6.39839 4.73613 6.40565C4.7605 6.41135 4.78849 6.41498 4.81597 6.41394C4.82686 6.41446 4.83723 6.4129 4.84656 6.40979C4.87819 6.40305 4.90618 6.3875 4.92277 6.35639C4.94351 6.31803 4.93055 6.27136 4.89789 6.24389L4.89737 6.24441Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1_11594">
|
||||
<rect width="20" height="17.8173" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 18 KiB |
@ -1,3 +0,0 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.7 9.3L10.7 0.3C10.3 -0.1 9.7 -0.1 9.3 0.3L0.3 9.3C-0.1 9.7 -0.1 10.3 0.3 10.7C0.7 11.1 1.3 11.1 1.7 10.7L2 10.4V18C2 19.1 2.9 20 4 20H7V14C7 12.3 8.3 11 10 11C11.7 11 13 12.3 13 14V20H16C17.1 20 18 19.1 18 18V10.4L18.3 10.7C18.5 10.9 18.8 11 19 11C19.2 11 19.5 11 19.7 10.7C20.1 10.3 20.1 9.7 19.7 9.3Z" fill="#C8C8C8"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 437 B |
@ -1,4 +0,0 @@
|
||||
<svg width="18" height="20" viewBox="0 0 18 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9 10C10.4 10 11.7 9.4 12.5 8.5C13.5 7.5 14 6.3 14 5C14 3.7 13.4 2.3 12.5 1.5C11.5 0.5 10.3 0 9 0C7.7 0 6.3 0.6 5.5 1.5C4.5 2.5 4 3.7 4 5C4 6.3 4.5 7.7 5.5 8.5C6.5 9.5 7.7 10 9 10Z" fill="#C8C8C8"/>
|
||||
<path d="M16.6 13.5C15.7 12.5 14.5 12 13 12H5C3.7 12 2.5 12.5 1.5 13.4C0.5 14.3 0 15.5 0 17V20H18V17C18 15.7 17.5 14.5 16.6 13.5Z" fill="#C8C8C8"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 459 B |
@ -1,5 +0,0 @@
|
||||
<svg width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 2.75C0 1.23579 1.23579 0 2.75 0H16.75C18.2642 0 19.5 1.23579 19.5 2.75V5.35C19.5 6.06952 19.3201 6.79295 18.8708 7.38059C18.4119 7.98071 17.7255 8.36826 16.8561 8.49246C15.7712 8.64745 14.8274 8.22121 14.2194 7.5064C14.1317 7.6035 14.0388 7.69451 13.9418 7.77877C13.3344 8.30621 12.491 8.63385 11.6267 8.4898C10.862 8.36235 10.2221 7.98527 9.76535 7.45444C9.66441 7.57118 9.55592 7.67964 9.44176 7.77877C8.83442 8.30621 7.99101 8.63385 7.1267 8.4898C6.36202 8.36235 5.72209 7.98527 5.26535 7.45444C5.16441 7.57118 5.05592 7.67964 4.94176 7.77877C4.33442 8.30621 3.49101 8.63385 2.6267 8.4898C1.04724 8.22655 0 6.89826 0 5.35V2.75Z" fill="#C8C8C8"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.5 17V7.65002H1V18.5H18.5V7.65002H17V17H2.5Z" fill="#C8C8C8"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.75 11.5C11.0642 11.5 10.5 12.0642 10.5 12.75V14H13V12.75C13 12.0642 12.4358 11.5 11.75 11.5ZM9 12.75C9 11.2358 10.2358 10 11.75 10C13.2642 10 14.5 11.2358 14.5 12.75V15.5H9V12.75Z" fill="#C8C8C8"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 902 B |
|
Before Width: | Height: | Size: 887 B |
@ -1,34 +0,0 @@
|
||||
class TermsData {
|
||||
static const List<Map<String, String>> terms = [
|
||||
{
|
||||
'title': '[제1조 목적]',
|
||||
'content':
|
||||
'본 약관은 RUP(이하 "회사")가 제공하는 서비스 이용과 관련하여 회사와 회원의 권리, 의무 및 책임사항을 규정함을 목적으로 합니다.\\n\\n[제2조 정의]\\n1. "서비스"란 구현되는 단말기와 상관없이 회원이 이용할 수 있는 RUP 및 관련 제반 서비스를 의미합니다.\\n2. "회원"이란 회사의 서비스에 접속하여 본 약관에 따라 회사와 이용계약을 체결하고 회사가 제공하는 서비스를 이용하는 고객을 말합니다.',
|
||||
},
|
||||
{
|
||||
'title': '[개인정보 수집 및 이용 동의]',
|
||||
'content':
|
||||
'회사는 회원가입, 고객상담, 서비스 신청 등을 위해 아래와 같은 개인정보를 수집하고 있습니다.\\n\\n1. 수집 항목\\n- 필수항목: 이메일, 닉네임, 비밀번호, 서비스 이용 기록\\n- 선택항목: 프로필 사진, 위치 정보\\n\\n2. 수집 목적\\n- 서비스 제공, 회원 관리, 신규 서비스 개발 및 마케팅 광고에의 활용',
|
||||
},
|
||||
{
|
||||
'title': '[개인정보 제3자 제공 동의]',
|
||||
'content':
|
||||
'회사는 이용자의 개인정보를 원칙적으로 외부에 제공하지 않습니다. 다만, 아래의 경우에는 예외로 합니다.\\n\\n1. 이용자들이 사전에 동의한 경우\\n2. 법령의 규정에 의거하거나, 수사 목적으로 법령에 정해진 절차와 방법에 따라 수사기관의 요구가 있는 경우',
|
||||
},
|
||||
{
|
||||
'title': '[만 14세 이상 이용 동의]',
|
||||
'content':
|
||||
'본 서비스는 만 14세 이상만 이용 가능합니다.\\n만 14세 미만 아동의 경우 회원가입 및 서비스 이용이 제한될 수 있습니다.',
|
||||
},
|
||||
{
|
||||
'title': '[위치기반 서비스 이용약관]',
|
||||
'content':
|
||||
'본 약관은 회사가 제공하는 위치기반서비스와 관련하여 회사와 개인위치정보주체와의 권리, 의무 및 책임사항, 기타 필요한 사항을 규정함을 목적으로 합니다.\\n\\n회사는 이용자의 위치 정보를 이용하여 주변 펫 프렌들리 장소 추천 등의 서비스를 제공합니다.',
|
||||
},
|
||||
{
|
||||
'title': '[마케팅 정보 수신 동의]',
|
||||
'content':
|
||||
'회사가 제공하는 이벤트, 혜택, 뉴스레터 등 다양한 마케팅 정보를 이메일, 앱 푸시 알림 등으로 받아보실 수 있습니다.\\n\\n동의를 거부하시더라도 기본 서비스 이용에는 제한이 없으나, 이벤트 참여 및 혜택 제공이 제한될 수 있습니다.',
|
||||
},
|
||||
];
|
||||
}
|
||||
@ -1,33 +1,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'dart:developer';
|
||||
import 'screens/splash_screen.dart';
|
||||
import 'utils/log_manager.dart';
|
||||
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
import 'dart:developer';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// 글로벌 에러 핸들링
|
||||
FlutterError.onError = (FlutterErrorDetails details) {
|
||||
FlutterError.presentError(details);
|
||||
LogManager().addLog('[APP ERROR] ${details.exception}');
|
||||
};
|
||||
|
||||
PlatformDispatcher.instance.onError = (error, stack) {
|
||||
LogManager().addLog('[Uncaught Error] $error');
|
||||
return true;
|
||||
};
|
||||
|
||||
try {
|
||||
await Firebase.initializeApp();
|
||||
} catch (e) {
|
||||
log('Firebase initialization failed: $e');
|
||||
LogManager().addLog('[Firebase Init Error] $e');
|
||||
}
|
||||
runApp(const RupApp());
|
||||
runApp(RupApp());
|
||||
}
|
||||
|
||||
class RupApp extends StatelessWidget {
|
||||
@ -35,10 +18,6 @@ class RupApp extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
navigatorKey: navigatorKey,
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: const SplashScreen(),
|
||||
);
|
||||
return MaterialApp(debugShowCheckedModeBanner: false, home: SplashScreen());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'welcome_screen.dart';
|
||||
|
||||
class HomeScreen extends StatelessWidget {
|
||||
const HomeScreen({super.key});
|
||||
@ -6,16 +7,33 @@ class HomeScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: const SafeArea(
|
||||
child: Center(
|
||||
child: Text(
|
||||
'로그인 성공!\n여기는 메인 홈 화면입니다.',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 18,
|
||||
),
|
||||
appBar: AppBar(
|
||||
title: const Text(
|
||||
'홈',
|
||||
style: TextStyle(fontFamily: 'SCDream', fontWeight: FontWeight.bold),
|
||||
),
|
||||
centerTitle: true,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.logout),
|
||||
onPressed: () {
|
||||
// Mock Logout: Go back to Welcome Screen
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const WelcomeScreen()),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: const Center(
|
||||
child: Text(
|
||||
'로그인 성공!\n여기는 메인 홈 화면입니다.',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -1,141 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'main_screen.dart';
|
||||
|
||||
class IdentityVerificationScreen extends StatelessWidget {
|
||||
const IdentityVerificationScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios, color: Colors.black, size: 20),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
title: const Text(
|
||||
'본인 인증',
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontFamily: 'SCDream',
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
const Text(
|
||||
'더 안전한 서비스 이용을 위해\n본인 인증을 진행해 주세요.',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontFamily: 'SCDream',
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
|
||||
// 본인 인증 UI (Placeholder)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: Colors.grey[300]!),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.shield_outlined,
|
||||
size: 50,
|
||||
color: Color(0xFFFF7500),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const Text(
|
||||
'PASS / 문자 인증',
|
||||
style: TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const Text(
|
||||
'(현재 UI만 구현된 상태입니다)',
|
||||
style: TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontSize: 12,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const Spacer(),
|
||||
|
||||
// 건너뛰기 버튼
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// 홈 화면으로 이동 (로그인 프로세스 완료)
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const MainScreen()),
|
||||
(route) => false,
|
||||
);
|
||||
},
|
||||
child: const Text(
|
||||
'다음에 하기 (건너뛰기)',
|
||||
style: TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.grey,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
// 인증하기 버튼 (현재는 동작 X)
|
||||
SizedBox(
|
||||
height: 50,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('본인 인증 기능은 추후 구현 예정입니다.')),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFFF7500),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'인증하기',
|
||||
style: TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import 'main_screen.dart';
|
||||
import 'terms_agreement_screen.dart'; // Import TermsAgreementScreen
|
||||
import 'home_screen.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
const LoginScreen({super.key});
|
||||
@ -21,27 +20,15 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
|
||||
try {
|
||||
final authService = AuthService();
|
||||
final result = await authService.signInWithGoogle();
|
||||
final user = await authService.signInWithGoogle();
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
if (result != null) {
|
||||
final isNewUser = result['isNewUser'] as bool;
|
||||
|
||||
if (isNewUser) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
TermsAgreementScreen(idToken: result['idToken']),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const MainScreen()),
|
||||
);
|
||||
}
|
||||
if (user != null) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const HomeScreen()),
|
||||
);
|
||||
} else {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
|
||||
@ -1,118 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'home_screen.dart';
|
||||
import 'reservation_screen.dart';
|
||||
import 'mungnyangz_screen.dart';
|
||||
import 'shop_screen.dart';
|
||||
import 'my_info_screen.dart';
|
||||
|
||||
import '../theme/app_colors.dart';
|
||||
|
||||
class MainScreen extends StatefulWidget {
|
||||
const MainScreen({super.key});
|
||||
|
||||
@override
|
||||
State<MainScreen> createState() => _MainScreenState();
|
||||
}
|
||||
|
||||
class _MainScreenState extends State<MainScreen> {
|
||||
int _selectedIndex = 0;
|
||||
|
||||
// 탭별 화면 리스트
|
||||
final List<Widget> _screens = [
|
||||
const HomeScreen(),
|
||||
const ReservationScreen(),
|
||||
const MungNyangzScreen(),
|
||||
const ShopScreen(),
|
||||
const MyInfoScreen(),
|
||||
];
|
||||
|
||||
void _onItemTapped(int index) {
|
||||
setState(() {
|
||||
_selectedIndex = index;
|
||||
});
|
||||
}
|
||||
|
||||
// SVG 아이콘 빌더 (선택 여부에 따라 색상 변경)
|
||||
Widget _buildSvgIcon(String assetName, int index) {
|
||||
return SvgPicture.asset(
|
||||
assetName,
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(
|
||||
_selectedIndex == index
|
||||
? AppColors.highlight
|
||||
: AppColors.inactive, // 선택됨: 강조색, 안됨: 비활성화색
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: IndexedStack(index: _selectedIndex, children: _screens),
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
currentIndex: _selectedIndex,
|
||||
onTap: _onItemTapped,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
selectedItemColor: AppColors.highlight,
|
||||
unselectedItemColor: AppColors.inactive,
|
||||
selectedLabelStyle: TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500, // Medium
|
||||
),
|
||||
unselectedLabelStyle: TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400, // Regular
|
||||
),
|
||||
showUnselectedLabels: true,
|
||||
items: [
|
||||
BottomNavigationBarItem(
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: _buildSvgIcon('assets/icons/homeicon.svg', 0),
|
||||
),
|
||||
label: '홈',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: _buildSvgIcon('assets/icons/appointmenticon.svg', 1),
|
||||
),
|
||||
label: '예약/조회',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Image.asset(
|
||||
_selectedIndex == 2
|
||||
? 'assets/img/catdog_on.png'
|
||||
: 'assets/img/catdog_off.png',
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
),
|
||||
label: '멍냥즈',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: _buildSvgIcon('assets/icons/shopicon.svg', 3),
|
||||
),
|
||||
label: '상점',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: _buildSvgIcon('assets/icons/myicon.svg', 4),
|
||||
),
|
||||
label: '내 정보',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../utils/log_manager.dart';
|
||||
|
||||
class MungNyangzScreen extends StatelessWidget {
|
||||
const MungNyangzScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: ValueListenableBuilder<List<String>>(
|
||||
valueListenable: LogManager().logs,
|
||||
builder: (context, logs, child) {
|
||||
if (logs.isEmpty) {
|
||||
return const Center(
|
||||
child: Text('로그가 없습니다.', style: TextStyle(color: Colors.grey)),
|
||||
);
|
||||
}
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(10),
|
||||
itemCount: logs.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 5),
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black12,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: Text(
|
||||
logs[index],
|
||||
style: const TextStyle(fontSize: 12, fontFamily: 'SCDream'),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
LogManager().clear();
|
||||
},
|
||||
mini: true,
|
||||
child: const Icon(Icons.delete),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,398 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import 'welcome_screen.dart';
|
||||
import 'notice_screen.dart'; // 공지사항 화면 임포트
|
||||
import '../data/terms_data.dart'; // 데이터 임포트
|
||||
|
||||
class MyInfoScreen extends StatefulWidget {
|
||||
const MyInfoScreen({super.key});
|
||||
|
||||
@override
|
||||
State<MyInfoScreen> createState() => _MyInfoScreenState();
|
||||
}
|
||||
|
||||
class _MyInfoScreenState extends State<MyInfoScreen> {
|
||||
final AuthService _authService = AuthService();
|
||||
Map<String, dynamic>? _userInfo;
|
||||
bool _isLoading = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_fetchUserInfo();
|
||||
}
|
||||
|
||||
Future<void> _fetchUserInfo() async {
|
||||
try {
|
||||
final info = await _authService.getUserInfo();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_userInfo = info;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('정보를 불러올 수 없습니다. 잠시 후 다시 시도해주세요.')),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleLogout() async {
|
||||
await _authService.signOut();
|
||||
if (mounted) {
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
MaterialPageRoute(builder: (context) => const WelcomeScreen()),
|
||||
(route) => false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleWithdraw() async {
|
||||
bool? confirm = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('회원 탈퇴'),
|
||||
content: const Text('정말로 탈퇴하시겠습니까?\n모든 데이터가 삭제되며 복구할 수 없습니다.'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: const Text('탈퇴하기', style: TextStyle(color: Colors.red)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (confirm == true) {
|
||||
if (!mounted) return;
|
||||
final success = await _authService.withdrawAccount();
|
||||
if (success && mounted) {
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
MaterialPageRoute(builder: (context) => const WelcomeScreen()),
|
||||
(route) => false,
|
||||
);
|
||||
} else {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('탈퇴 처리에 실패했습니다. 다시 시도해주세요.')),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 통합 약관 모달 보여주기
|
||||
void _showAllTermsModal(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent, // 둥근 모서리 적용을 위해 투명
|
||||
builder: (context) {
|
||||
return DraggableScrollableSheet(
|
||||
initialChildSize: 0.85, // 화면의 85% 높이로 시작
|
||||
minChildSize: 0.5,
|
||||
maxChildSize: 0.95,
|
||||
builder: (_, controller) {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
padding: const EdgeInsets.fromLTRB(20, 10, 20, 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 핸들 바
|
||||
Center(
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 4,
|
||||
margin: const EdgeInsets.only(bottom: 20, top: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 헤더
|
||||
const Row(
|
||||
children: [
|
||||
Icon(Icons.description, size: 24, color: Colors.black87),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
'서비스 이용 약관 전체보기',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: 'SCDream',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// 스크롤 가능한 본문 (모든 약관 내용 통합)
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
controller: controller, // 드래그 가능한 스크롤 컨트롤러 연결
|
||||
itemCount: TermsData.terms.length,
|
||||
separatorBuilder: (context, index) => const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 20),
|
||||
child: Divider(color: Color(0xFFEEEEEE), thickness: 1),
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final term = TermsData.terms[index];
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
term['title']!,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFFFF7500), // 강조색
|
||||
fontFamily: 'SCDream',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
term['content']!,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
height: 1.6,
|
||||
color: Colors.black87,
|
||||
fontFamily: 'SCDream',
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 10),
|
||||
// 닫기 버튼
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 52,
|
||||
child: ElevatedButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFFF7500),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
elevation: 0,
|
||||
),
|
||||
child: const Text(
|
||||
'닫기',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: 'SCDream',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_isLoading) {
|
||||
return const Scaffold(body: Center(child: CircularProgressIndicator()));
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: _userInfo == null
|
||||
? const Center(child: Text('정보를 불러올 수 없습니다.'))
|
||||
: SafeArea(
|
||||
child: Column(
|
||||
// Column으로 변경하여 하단 고정 영역 확보
|
||||
children: [
|
||||
Expanded(
|
||||
// 상단 스크롤 영역
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const CircleAvatar(
|
||||
radius: 40,
|
||||
backgroundColor: Colors.grey,
|
||||
child: Icon(
|
||||
Icons.person,
|
||||
size: 50,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
_userInfo!['nickname'] ?? '이름 없음',
|
||||
style: const TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
_userInfo!['email'] ?? '이메일 없음',
|
||||
style: TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontSize: 14,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
_buildMenuItem(
|
||||
title: '공지사항',
|
||||
icon: Icons.campaign_outlined,
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const NoticeScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_buildMenuItem(
|
||||
title: '서비스 이용 약관',
|
||||
icon: Icons.description_outlined,
|
||||
onTap: () => _showAllTermsModal(context),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_buildMenuItem(
|
||||
title: '버전 정보',
|
||||
icon: Icons.info_outline,
|
||||
trailingText: '1.0.0',
|
||||
onTap: () {}, // 클릭 효과를 위해 빈 함수 전달
|
||||
),
|
||||
// 회원 탈퇴 버튼 removed from here
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 하단 고정 영역 (로그아웃 & 탈퇴)
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 0, 20, 30),
|
||||
child: Column(
|
||||
children: [
|
||||
// 로그아웃 버튼 (회원탈퇴 위로 이동)
|
||||
_buildMenuItem(
|
||||
title: '로그아웃',
|
||||
icon: Icons.logout,
|
||||
onTap: _handleLogout,
|
||||
),
|
||||
|
||||
const SizedBox(height: 10),
|
||||
|
||||
// 회원 탈퇴 버튼
|
||||
_buildMenuItem(
|
||||
title: '회원 탈퇴',
|
||||
icon: Icons.person_off_outlined,
|
||||
isDestructive: true,
|
||||
onTap: _handleWithdraw,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMenuItem({
|
||||
required String title,
|
||||
required IconData icon,
|
||||
VoidCallback? onTap, // onTap을 nullable로 변경
|
||||
bool isDestructive = false,
|
||||
String? trailingText, // 뒤에 텍스트를 표시할 수 있도록 추가
|
||||
}) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.all(color: Colors.grey[200]!),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 20),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, color: isDestructive ? Colors.red : Colors.black54),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500, // Medium
|
||||
color: isDestructive ? Colors.red : Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (trailingText != null)
|
||||
Text(
|
||||
trailingText,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
)
|
||||
else
|
||||
Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 16,
|
||||
color: isDestructive ? Colors.red : Colors.grey,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,100 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NoticeScreen extends StatelessWidget {
|
||||
const NoticeScreen({super.key});
|
||||
|
||||
final List<Map<String, String>> notices = const [
|
||||
{
|
||||
'title': 'RUP 서비스 런칭 안내',
|
||||
'date': '2024.01.20',
|
||||
'content': '반려동물 통합 관리 플랫폼 RUP가 정식 런칭되었습니다. 많은 이용 부탁드립니다.',
|
||||
},
|
||||
{
|
||||
'title': '시스템 점검 안내',
|
||||
'date': '2024.01.15',
|
||||
'content': '더나은 서비스를 위해 시스템 점검이 진행될 예정입니다.\n일시: 2024.01.25 02:00 ~ 04:00',
|
||||
},
|
||||
{
|
||||
'title': '이용약관 개정 안내',
|
||||
'date': '2024.01.10',
|
||||
'content': '서비스 이용약관이 일부 개정되었습니다. 주요 변경사항을 확인해주세요.',
|
||||
},
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
title: const Text(
|
||||
'공지사항',
|
||||
style: TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios, color: Colors.black, size: 20),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
body: ListView.separated(
|
||||
itemCount: notices.length,
|
||||
separatorBuilder: (context, index) =>
|
||||
const Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||
itemBuilder: (context, index) {
|
||||
final notice = notices[index];
|
||||
return ExpansionTile(
|
||||
tilePadding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 8,
|
||||
),
|
||||
title: Text(
|
||||
notice['title']!,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
subtitle: Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(
|
||||
notice['date']!,
|
||||
style: TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontSize: 12,
|
||||
color: Colors.grey[500],
|
||||
),
|
||||
),
|
||||
),
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 20,
|
||||
),
|
||||
color: Colors.grey[50],
|
||||
child: Text(
|
||||
notice['content']!,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'SCDream',
|
||||
fontSize: 14,
|
||||
height: 1.5,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ReservationScreen extends StatelessWidget {
|
||||
const ReservationScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: const SafeArea(
|
||||
child: Center(
|
||||
child: Text(
|
||||
'예약 조회 화면 준비 중입니다.',
|
||||
style: TextStyle(fontFamily: 'SCDream'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ShopScreen extends StatelessWidget {
|
||||
const ShopScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: const SafeArea(
|
||||
child: Center(
|
||||
child: Text(
|
||||
'상점 화면 준비 중입니다.',
|
||||
style: TextStyle(fontFamily: 'SCDream'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import 'main_screen.dart';
|
||||
import 'terms_agreement_screen.dart';
|
||||
import 'home_screen.dart';
|
||||
|
||||
class SignupScreen extends StatefulWidget {
|
||||
const SignupScreen({super.key});
|
||||
@ -21,30 +20,15 @@ class _SignupScreenState extends State<SignupScreen> {
|
||||
|
||||
try {
|
||||
final authService = AuthService();
|
||||
// Returns Map<String, dynamic>? { 'credential': ..., 'isNewUser': bool }
|
||||
final result = await authService.signInWithGoogle();
|
||||
final user = await authService.signInWithGoogle();
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
if (result != null) {
|
||||
final isNewUser = result['isNewUser'] as bool;
|
||||
|
||||
if (isNewUser) {
|
||||
// 신규 가입자 -> 약관 동의 화면으로 이동
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
TermsAgreementScreen(idToken: result['idToken']),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// 기존 가입자 -> 메인 화면으로 이동
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const MainScreen()),
|
||||
);
|
||||
}
|
||||
if (user != null) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const HomeScreen()),
|
||||
);
|
||||
} else {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
@ -104,7 +88,7 @@ class _SignupScreenState extends State<SignupScreen> {
|
||||
Image.asset('assets/img/foot.png', width: 30),
|
||||
const SizedBox(width: 10),
|
||||
const Text(
|
||||
'간편 로그인',
|
||||
'서비스 이용 약관',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontFamily: 'SCDream',
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
// import 'package:video_player/video_player.dart';
|
||||
import 'welcome_screen.dart';
|
||||
import 'main_screen.dart';
|
||||
import '../services/auth_service.dart'; // Import AuthService
|
||||
import '../theme/app_colors.dart';
|
||||
import 'home_screen.dart';
|
||||
|
||||
class SplashScreen extends StatefulWidget {
|
||||
const SplashScreen({super.key});
|
||||
@ -13,6 +12,9 @@ class SplashScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SplashScreenState extends State<SplashScreen> {
|
||||
// Mock login history flag
|
||||
final bool hasLoginHistory = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -20,22 +22,17 @@ class _SplashScreenState extends State<SplashScreen> {
|
||||
}
|
||||
|
||||
Future<void> _checkLoginHistory() async {
|
||||
// Simulate loading time (minimum)
|
||||
// Simulate loading time (e.g. 2 seconds)
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
final AuthService authService = AuthService();
|
||||
final String? token = await authService.getAccessToken();
|
||||
|
||||
if (token != null) {
|
||||
// 토큰이 있으면 메인 화면으로 (자동 로그인)
|
||||
if (hasLoginHistory) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const MainScreen()),
|
||||
MaterialPageRoute(builder: (context) => const HomeScreen()),
|
||||
);
|
||||
} else {
|
||||
// 토큰이 없으면 웰컴 화면으로
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const WelcomeScreen()),
|
||||
@ -46,7 +43,7 @@ class _SplashScreenState extends State<SplashScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.highlight,
|
||||
backgroundColor: const Color(0xFFFF7500),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
@ -1,404 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'identity_verification_screen.dart';
|
||||
import '../services/auth_service.dart'; // Import AuthService
|
||||
import '../data/terms_data.dart';
|
||||
|
||||
class TermsAgreementScreen extends StatefulWidget {
|
||||
final bool isViewOnly;
|
||||
final String? idToken;
|
||||
|
||||
const TermsAgreementScreen({
|
||||
super.key,
|
||||
this.isViewOnly = false,
|
||||
this.idToken,
|
||||
});
|
||||
|
||||
@override
|
||||
State<TermsAgreementScreen> createState() => _TermsAgreementScreenState();
|
||||
}
|
||||
|
||||
class _TermsAgreementScreenState extends State<TermsAgreementScreen> {
|
||||
final List<bool> _checks = [false, false, false, false, false, false];
|
||||
|
||||
bool get _isAllChecked => _checks.every((completed) => completed);
|
||||
|
||||
bool get _isRequiredChecked =>
|
||||
_checks[0] && _checks[1] && _checks[2] && _checks[3];
|
||||
|
||||
void _toggleAll() {
|
||||
setState(() {
|
||||
bool newValue = !_isAllChecked;
|
||||
for (int i = 0; i < _checks.length; i++) {
|
||||
_checks[i] = newValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _toggleItem(int index) {
|
||||
setState(() {
|
||||
_checks[index] = !_checks[index];
|
||||
});
|
||||
}
|
||||
|
||||
String _getTermContent(int index) {
|
||||
if (index >= 0 && index < TermsData.terms.length) {
|
||||
return TermsData.terms[index]['content'] ?? '내용이 없습니다.';
|
||||
}
|
||||
return '내용이 없습니다.';
|
||||
}
|
||||
|
||||
void _showTermDetail(BuildContext context, String title, int index) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (context) {
|
||||
return DraggableScrollableSheet(
|
||||
initialChildSize: 0.7,
|
||||
minChildSize: 0.5,
|
||||
maxChildSize: 0.9,
|
||||
builder: (_, controller) {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Center(
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: 'SCDream',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
controller: controller,
|
||||
child: Text(
|
||||
_getTermContent(index),
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
height: 1.6,
|
||||
fontFamily: 'SCDream',
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 52,
|
||||
child: ElevatedButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFFF7500),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
elevation: 0,
|
||||
),
|
||||
child: const Text(
|
||||
'확인',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: 'SCDream',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back_ios, color: Colors.black, size: 20),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
title: Text(
|
||||
widget.isViewOnly ? '이용 약관' : '회원가입',
|
||||
style: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: 'SCDream',
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
// Header Area
|
||||
Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/img/foot.png',
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'서비스 이용 약관',
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: 'SCDream',
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
widget.isViewOnly
|
||||
? 'RUP 서비스의 이용 약관 내용입니다.'
|
||||
: '서비스 이용을 위해 약관에 동의해주세요.',
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
color: Colors.black54,
|
||||
fontFamily: 'SCDream',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
|
||||
// All Agree Box - *ViewOnly 모드에서는 숨김*
|
||||
if (!widget.isViewOnly) ...[
|
||||
InkWell(
|
||||
onTap: () => _toggleAll(),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
horizontal: 16,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
_isAllChecked
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_off,
|
||||
color: _isAllChecked
|
||||
? const Color(0xFFFF7500)
|
||||
: Colors.grey,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Text(
|
||||
'모든 약관에 동의합니다.',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: 'SCDream',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const Divider(height: 1, color: Color(0xFFEEEEEE)),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
|
||||
// Individual Items
|
||||
_buildTermItem(0, '이용약관 동의', isRequired: true),
|
||||
_buildTermItem(1, '개인정보 수집 및 이용 동의', isRequired: true),
|
||||
_buildTermItem(2, '제 3자 제공 동의', isRequired: true),
|
||||
_buildTermItem(3, '만 14세 이상 사용자', isRequired: true),
|
||||
_buildTermItem(4, '위치정보 이용 동의', isRequired: false),
|
||||
_buildTermItem(5, '마케팅 수신 동의', isRequired: false),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Bottom Button
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 10, 20, 20),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 52,
|
||||
child: ElevatedButton(
|
||||
onPressed: widget.isViewOnly
|
||||
? () => Navigator.pop(context)
|
||||
: (_isRequiredChecked
|
||||
? () async {
|
||||
if (widget.idToken != null) {
|
||||
// 실제 가입 요청 (DB 생성)
|
||||
final success = await AuthService()
|
||||
.registerWithGoogle(widget.idToken!);
|
||||
if (success && context.mounted) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
const IdentityVerificationScreen(),
|
||||
),
|
||||
);
|
||||
} else if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('회원가입 처리에 실패했습니다.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// idToken이 없는 경우 (테스트 등) - 그냥 진행
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
const IdentityVerificationScreen(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
: null),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFFF7500),
|
||||
disabledBackgroundColor: Colors.grey[300],
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
elevation: 0,
|
||||
),
|
||||
child: Text(
|
||||
widget.isViewOnly ? '닫기' : '동의하고 본인 인증하기',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: 'SCDream',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTermItem(int index, String title, {required bool isRequired}) {
|
||||
// ViewOnly 모드에서는 체크박스 숨김
|
||||
// ViewOnly 모드에서는 텍스트 클릭 시 모달 띄우기 (토글 X)
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (!widget.isViewOnly) {
|
||||
_toggleItem(index);
|
||||
} else {
|
||||
// ViewOnly일 땐 텍스트 눌러도 모달 띄우기 (편의성)
|
||||
_showTermDetail(context, title, index);
|
||||
}
|
||||
},
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12,
|
||||
horizontal: 8,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// 체크 Icon - *ViewOnly 모드에서는 숨김*
|
||||
if (!widget.isViewOnly) ...[
|
||||
Icon(
|
||||
Icons.check,
|
||||
size: 20,
|
||||
color: _checks[index]
|
||||
? const Color(0xFFFF7500)
|
||||
: Colors.grey[300],
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
],
|
||||
|
||||
Text(
|
||||
isRequired ? '필수' : '선택',
|
||||
style: TextStyle(
|
||||
color: isRequired
|
||||
? const Color(0xFFFF7500)
|
||||
: Colors.grey,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
fontFamily: 'SCDream',
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
color: Colors.black87,
|
||||
fontFamily: 'SCDream',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => _showTermDetail(context, title, index),
|
||||
icon: const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 14,
|
||||
color: Colors.black,
|
||||
),
|
||||
splashRadius: 20,
|
||||
padding: const EdgeInsets.all(12),
|
||||
constraints: const BoxConstraints(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,256 +1,51 @@
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:google_sign_in/google_sign_in.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../main.dart';
|
||||
import '../screens/welcome_screen.dart';
|
||||
import '../utils/log_manager.dart';
|
||||
|
||||
class AuthService {
|
||||
final FirebaseAuth _auth = FirebaseAuth.instance;
|
||||
// Google Sign-In 설정 (Backend와 통신하기 위해 serverClientId 지정)
|
||||
final GoogleSignIn _googleSignIn = GoogleSignIn(
|
||||
serverClientId:
|
||||
'379988243470-g6490l8gucc3ljras93i28c3l4qlroi4.apps.googleusercontent.com',
|
||||
);
|
||||
final Dio _dio = Dio();
|
||||
final FlutterSecureStorage _storage = const FlutterSecureStorage();
|
||||
final GoogleSignIn _googleSignIn = GoogleSignIn();
|
||||
|
||||
// Backend Base URL
|
||||
final String _baseUrl = 'http://10.0.2.2:3000/auth';
|
||||
|
||||
AuthService() {
|
||||
// Dio Options 설정 (Timeout 추가)
|
||||
_dio.options.connectTimeout = const Duration(seconds: 5); // 5초 연결 타임아웃
|
||||
_dio.options.receiveTimeout = const Duration(seconds: 5); // 5초 응답 타임아웃
|
||||
|
||||
// Dio Interceptor 설정
|
||||
_dio.interceptors.add(
|
||||
InterceptorsWrapper(
|
||||
onRequest: (options, handler) async {
|
||||
// 모든 요청 헤더에 Access Token 추가
|
||||
final accessToken = await _storage.read(key: 'accessToken');
|
||||
if (accessToken != null) {
|
||||
options.headers['Authorization'] = 'Bearer $accessToken';
|
||||
}
|
||||
return handler.next(options);
|
||||
},
|
||||
onError: (DioException error, handler) async {
|
||||
// 401 에러 발생 시 (Access Token 만료)
|
||||
if (error.response?.statusCode == 401) {
|
||||
String msg1 = '[Auth] Access Token expired. Attempting refresh...';
|
||||
print(msg1);
|
||||
LogManager().addLog(msg1); // LOG
|
||||
|
||||
final isRefreshed = await _refreshToken();
|
||||
if (isRefreshed) {
|
||||
// 토큰 갱신 성공 -> 원래 요청 재시도
|
||||
final newAccessToken = await _storage.read(key: 'accessToken');
|
||||
|
||||
// 헤더 업데이트
|
||||
error.requestOptions.headers['Authorization'] =
|
||||
'Bearer $newAccessToken';
|
||||
|
||||
// 재요청
|
||||
try {
|
||||
final response = await _dio.fetch(error.requestOptions);
|
||||
return handler.resolve(response);
|
||||
} catch (e) {
|
||||
return handler.reject(error);
|
||||
}
|
||||
} else {
|
||||
// 토큰 갱신 실패 (리프레시 토큰도 만료됨) -> 로그아웃 & 화면 이동
|
||||
String msg2 = '[Auth] Refresh Token expired. Logging out...';
|
||||
print(msg2);
|
||||
LogManager().addLog(msg2); // LOG
|
||||
await signOut();
|
||||
|
||||
// Force Navigation to Welcome Screen
|
||||
navigatorKey.currentState?.pushAndRemoveUntil(
|
||||
MaterialPageRoute(builder: (context) => const WelcomeScreen()),
|
||||
(route) => false,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Log other errors
|
||||
LogManager().addLog(
|
||||
'[DioError] ${error.message} (Status: ${error.response?.statusCode})',
|
||||
);
|
||||
}
|
||||
return handler.next(error);
|
||||
},
|
||||
),
|
||||
// 구글 로그인
|
||||
Future<UserCredential?> signInWithGoogle() async {
|
||||
// 1. 구글 로그인 흐름 시작
|
||||
print('[DEBUG] Google Sign-In: Starting signIn()');
|
||||
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
|
||||
print(
|
||||
'[DEBUG] Google Sign-In: signIn() completed, user: ${googleUser?.email}',
|
||||
);
|
||||
}
|
||||
|
||||
// 토큰 갱신 로직
|
||||
Future<bool> _refreshToken() async {
|
||||
try {
|
||||
final refreshToken = await _storage.read(key: 'refreshToken');
|
||||
if (refreshToken == null) return false;
|
||||
|
||||
final response = await _dio.post(
|
||||
'$_baseUrl/refresh',
|
||||
data: {'refreshToken': refreshToken},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200 && response.data['success'] == true) {
|
||||
final newAccessToken = response.data['accessToken'];
|
||||
await _storage.write(key: 'accessToken', value: newAccessToken);
|
||||
String msg = '[Auth] Token refreshed successfully.';
|
||||
print(msg);
|
||||
LogManager().addLog(msg);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
String msg = '[Auth] Token refresh failed: $e';
|
||||
print(msg);
|
||||
LogManager().addLog(msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 구글 로그인 (Check or Login)
|
||||
Future<Map<String, dynamic>?> signInWithGoogle() async {
|
||||
try {
|
||||
LogManager().addLog('[DEBUG] Google Sign-In: Starting signIn()');
|
||||
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
|
||||
|
||||
if (googleUser == null) {
|
||||
LogManager().addLog('[DEBUG] Google Sign-In: User canceled');
|
||||
return null;
|
||||
}
|
||||
|
||||
final GoogleSignInAuthentication googleAuth =
|
||||
await googleUser.authentication;
|
||||
|
||||
if (googleAuth.idToken != null) {
|
||||
try {
|
||||
// 1. 서버에 토큰 전송 (Login Check)
|
||||
final response = await _dio.post(
|
||||
'$_baseUrl/google',
|
||||
data: {'idToken': googleAuth.idToken},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200 && response.data['success'] == true) {
|
||||
final isNewUser = response.data['isNewUser'] ?? false;
|
||||
|
||||
if (isNewUser) {
|
||||
// 신규 유저: 토큰 저장하지 않고 idToken 반환 (약관 동의 화면으로 전달)
|
||||
return {
|
||||
'isNewUser': true,
|
||||
'idToken': googleAuth.idToken,
|
||||
'email': response.data['email'],
|
||||
'nickname': response.data['nickname'],
|
||||
};
|
||||
} else {
|
||||
// 기존 유저: 토큰 저장 및 로그인 처리
|
||||
final accessToken = response.data['accessToken'];
|
||||
final refreshToken = response.data['refreshToken'];
|
||||
|
||||
await _storage.write(key: 'accessToken', value: accessToken);
|
||||
await _storage.write(key: 'refreshToken', value: refreshToken);
|
||||
|
||||
// Firebase 로그인 (선택 사항, 필요하다면 유지)
|
||||
final OAuthCredential credential = GoogleAuthProvider.credential(
|
||||
accessToken: googleAuth.accessToken,
|
||||
idToken: googleAuth.idToken,
|
||||
);
|
||||
await _auth.signInWithCredential(credential);
|
||||
|
||||
return {'isNewUser': false};
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
String msg = '[ERROR] Backend Auth API Error: $e';
|
||||
print(msg);
|
||||
LogManager().addLog(msg);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
String msg = '[ERROR] Google Sign-In Error: $e';
|
||||
print(msg);
|
||||
LogManager().addLog(msg);
|
||||
if (googleUser == null) {
|
||||
// 사용자가 로그인 창을 닫음
|
||||
print('[DEBUG] Google Sign-In: User canceled');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 구글 회원가입 (Register - 약관 동의 후 호출)
|
||||
Future<bool> registerWithGoogle(String idToken) async {
|
||||
try {
|
||||
final response = await _dio.post(
|
||||
'$_baseUrl/google/register',
|
||||
data: {'idToken': idToken},
|
||||
);
|
||||
// 2. 인증 세부 정보 요청
|
||||
print('[DEBUG] Google Sign-In: Getting authentication...');
|
||||
final GoogleSignInAuthentication googleAuth =
|
||||
await googleUser.authentication;
|
||||
print(
|
||||
'[DEBUG] Google Sign-In: Authentication received. AccessToken: ${googleAuth.accessToken != null}, IDToken: ${googleAuth.idToken != null}',
|
||||
);
|
||||
|
||||
if (response.statusCode == 200 && response.data['success'] == true) {
|
||||
final accessToken = response.data['accessToken'];
|
||||
final refreshToken = response.data['refreshToken'];
|
||||
// 3. 자격 증명 생성
|
||||
final OAuthCredential credential = GoogleAuthProvider.credential(
|
||||
accessToken: googleAuth.accessToken,
|
||||
idToken: googleAuth.idToken,
|
||||
);
|
||||
|
||||
await _storage.write(key: 'accessToken', value: accessToken);
|
||||
await _storage.write(key: 'refreshToken', value: refreshToken);
|
||||
|
||||
// Firebase 로그인 처리 (필요시) - idToken으로 credential 생성 가능하지만 access token이 없으므로 생략하거나
|
||||
// 이미 signInWithGoogle에서 받아온 credential을 재사용할 수 있으면 좋음.
|
||||
// 하지만 여기서는 백엔드 세션이 중요하므로 일단 백엔드 토큰만 저장.
|
||||
// (Firebase Auth와 Custom Backend Auth를 혼용 중이라 복잡함.
|
||||
// 일단 백엔드 로직이 메인이므로 백엔드 토큰 처리에 집중)
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
LogManager().addLog('[Auth] Register Failed: $e');
|
||||
return false;
|
||||
}
|
||||
// 4. Firebase에 로그인
|
||||
print('[DEBUG] Google Sign-In: Signing in with credential to Firebase...');
|
||||
final userCredential = await _auth.signInWithCredential(credential);
|
||||
print(
|
||||
'[DEBUG] Google Sign-In: Firebase sign-in completed. User: ${userCredential.user?.uid}',
|
||||
);
|
||||
return userCredential;
|
||||
}
|
||||
|
||||
// 로그아웃
|
||||
Future<void> signOut() async {
|
||||
await _googleSignIn.signOut();
|
||||
await _auth.signOut();
|
||||
await _storage.deleteAll(); // 토큰 삭제
|
||||
print('[DEBUG] User signed out and tokens cleared.');
|
||||
}
|
||||
|
||||
// Access Token 가져오기
|
||||
Future<String?> getAccessToken() async {
|
||||
return await _storage.read(key: 'accessToken');
|
||||
}
|
||||
|
||||
// 유저 정보 가져오기
|
||||
Future<Map<String, dynamic>?> getUserInfo() async {
|
||||
try {
|
||||
final response = await _dio.get('$_baseUrl/me');
|
||||
if (response.statusCode == 200 && response.data['success'] == true) {
|
||||
return response.data['user'];
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
LogManager().addLog('[Auth] Get User Info Failed: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 회원 탈퇴
|
||||
Future<bool> withdrawAccount() async {
|
||||
try {
|
||||
final response = await _dio.delete('$_baseUrl/withdraw');
|
||||
if (response.statusCode == 200 && response.data['success'] == true) {
|
||||
await _googleSignIn.signOut();
|
||||
await _auth.signOut();
|
||||
await _storage.deleteAll();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
LogManager().addLog('[Auth] Withdraw Account Failed: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Dio get dio => _dio;
|
||||
}
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AppColors {
|
||||
// 기본 텍스트 색상: #1F1F1F (기존 '기본색')
|
||||
static const Color text = Color(0xFF1F1F1F);
|
||||
|
||||
// (Old alias for compatibility, if needed)
|
||||
static const Color basic = text;
|
||||
|
||||
// 비활성화색: #C8C8C8
|
||||
static const Color inactive = Color(0xFFC8C8C8);
|
||||
|
||||
// 강조색: #FF7500 (버튼, 호버 등 중요!)
|
||||
static const Color highlight = Color(0xFFFF7500);
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class LogManager {
|
||||
static final LogManager _instance = LogManager._internal();
|
||||
factory LogManager() => _instance;
|
||||
LogManager._internal();
|
||||
|
||||
final ValueNotifier<List<String>> logs = ValueNotifier([]);
|
||||
|
||||
void addLog(String message) {
|
||||
try {
|
||||
final timestamp = DateTime.now().toString().split(' ')[1].split('.')[0];
|
||||
final logMessage = "[$timestamp] $message";
|
||||
|
||||
logs.value = [logMessage, ...logs.value];
|
||||
} catch (e) {
|
||||
print('LogManager Error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
logs.value = [];
|
||||
}
|
||||
}
|
||||
@ -6,10 +6,6 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
flutter_secure_storage_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
@ -7,14 +7,12 @@ import Foundation
|
||||
|
||||
import firebase_auth
|
||||
import firebase_core
|
||||
import flutter_secure_storage_macos
|
||||
import google_sign_in_ios
|
||||
import video_player_avfoundation
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
|
||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
|
||||
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
|
||||
}
|
||||
|
||||
242
app/pubspec.lock
@ -49,14 +49,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
code_assets:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_assets
|
||||
sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -65,14 +57,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -89,22 +73,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio
|
||||
sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.9.0"
|
||||
dio_web_adapter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dio_web_adapter
|
||||
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -113,22 +81,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
firebase_auth:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -190,54 +142,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
flutter_secure_storage:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_secure_storage
|
||||
sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.2.4"
|
||||
flutter_secure_storage_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_secure_storage_linux
|
||||
sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.3"
|
||||
flutter_secure_storage_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_secure_storage_macos
|
||||
sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
flutter_secure_storage_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_secure_storage_platform_interface
|
||||
sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
flutter_secure_storage_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_secure_storage_web
|
||||
sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
flutter_secure_storage_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_secure_storage_windows
|
||||
sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
flutter_svg:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -256,14 +160,6 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
google_identity_services_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -312,14 +208,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.4+4"
|
||||
hooks:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hooks
|
||||
sha256: "5d309c86e7ce34cd8e37aa71cb30cb652d3829b900ab145e4d9da564b31d59f7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -344,14 +232,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -384,14 +264,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -416,30 +288,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
native_toolchain_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: native_toolchain_c
|
||||
sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.17.4"
|
||||
objective_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: objective_c
|
||||
sha256: "9922a1ad59ac5afb154cc948aa6ded01987a75003651d0a2866afc23f4da624e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.2.3"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -456,54 +304,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.22"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -512,14 +312,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -528,14 +320,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@ -685,22 +469,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.15.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -709,14 +477,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.6.1"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.10.7 <4.0.0"
|
||||
flutter: ">=3.38.4"
|
||||
flutter: ">=3.38.0"
|
||||
|
||||
@ -39,8 +39,6 @@ dependencies:
|
||||
firebase_core: ^3.0.0 # 파이어베이스 기본 (Updated)
|
||||
firebase_auth: ^5.0.0 # 인증 기능 (Updated)
|
||||
google_sign_in: ^6.2.1 # 구글 로그인 UI/기능 (Updated)
|
||||
dio: ^5.4.0
|
||||
flutter_secure_storage: ^9.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@ -68,8 +66,6 @@ flutter:
|
||||
weight: 700
|
||||
- asset: assets/fonts/SCDream-medium.otf
|
||||
weight: 500
|
||||
- asset: assets/fonts/SCDream-regular.otf
|
||||
weight: 400
|
||||
|
||||
assets:
|
||||
- assets/img/
|
||||
|
||||
@ -11,11 +11,20 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:app/main.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('App starts with Splash Screen', (WidgetTester tester) async {
|
||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(const RupApp());
|
||||
await tester.pumpWidget(const MyApp());
|
||||
|
||||
// Verify that Splash Screen is shown
|
||||
expect(find.byType(MaterialApp), findsOneWidget);
|
||||
// Verify that our counter starts at 0.
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
expect(find.text('1'), findsNothing);
|
||||
|
||||
// Tap the '+' icon and trigger a frame.
|
||||
await tester.tap(find.byIcon(Icons.add));
|
||||
await tester.pump();
|
||||
|
||||
// Verify that our counter has incremented.
|
||||
expect(find.text('0'), findsNothing);
|
||||
expect(find.text('1'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
@ -8,13 +8,10 @@
|
||||
|
||||
#include <firebase_auth/firebase_auth_plugin_c_api.h>
|
||||
#include <firebase_core/firebase_core_plugin_c_api.h>
|
||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
FirebaseAuthPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi"));
|
||||
FirebaseCorePluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
|
||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
firebase_auth
|
||||
firebase_core
|
||||
flutter_secure_storage_windows
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
.env
|
||||
@ -1,40 +0,0 @@
|
||||
const { Sequelize } = require('sequelize');
|
||||
require('dotenv').config();
|
||||
|
||||
const sequelize = new Sequelize(
|
||||
process.env.MYSQL_DATABASE,
|
||||
process.env.MYSQL_USER,
|
||||
process.env.MYSQL_PASSWORD,
|
||||
{
|
||||
host: process.env.DB_HOST,
|
||||
dialect: 'mysql',
|
||||
logging: false,
|
||||
pool: {
|
||||
max: 5,
|
||||
min: 0,
|
||||
acquire: 30000,
|
||||
idle: 10000
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Test Connection
|
||||
const connectDB = async () => {
|
||||
let retries = 20; // Increased retries for slow DB startup
|
||||
while (retries > 0) {
|
||||
try {
|
||||
console.log(`[Database] Attempting to connect... (Retries left: ${retries})`);
|
||||
await sequelize.authenticate();
|
||||
console.log('[Database] Connection has been established successfully.');
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error(`[Database] Unable to connect. Retrying in 5s...`, error.message);
|
||||
retries -= 1;
|
||||
await new Promise(res => setTimeout(res, 5000)); // Wait 5 seconds
|
||||
}
|
||||
}
|
||||
console.error('[Database] Failed to connect after multiple attempts.');
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
module.exports = { sequelize, connectDB };
|
||||
@ -1,285 +0,0 @@
|
||||
const { OAuth2Client } = require('google-auth-library');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const User = require('../models/user');
|
||||
require('dotenv').config();
|
||||
|
||||
// Google Client ID should be in env, but for now assuming it handles verification
|
||||
const googleClient = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);
|
||||
|
||||
const generateTokens = (user) => {
|
||||
const payload = { id: user.id, email: user.email, nickname: user.nickname };
|
||||
|
||||
const accessToken = jwt.sign(payload, process.env.JWT_SECRET, {
|
||||
expiresIn: '1h',
|
||||
});
|
||||
|
||||
const refreshToken = jwt.sign({ id: user.id }, process.env.JWT_REFRESH_SECRET, {
|
||||
expiresIn: '14d',
|
||||
});
|
||||
|
||||
return { accessToken, refreshToken };
|
||||
};
|
||||
|
||||
// Generic Social Login Logic
|
||||
const socialLogin = async (provider, socialInfo, res) => {
|
||||
try {
|
||||
const { socialId, email, nickname } = socialInfo;
|
||||
|
||||
// Find or Create User
|
||||
const [user, created] = await User.findOrCreate({
|
||||
where: { provider, socialId },
|
||||
defaults: { email, nickname },
|
||||
});
|
||||
|
||||
if (!created) {
|
||||
// Update info if needed (e.g., changed nickname on social side) - Optional
|
||||
user.email = email || user.email;
|
||||
user.nickname = nickname || user.nickname;
|
||||
}
|
||||
|
||||
// Generate Tokens
|
||||
const { accessToken, refreshToken } = generateTokens(user);
|
||||
|
||||
// Save Refresh Token to DB
|
||||
user.refreshToken = refreshToken;
|
||||
await user.save();
|
||||
|
||||
console.log(`[Auth] User ${user.id} logged in via ${provider}`);
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
isNewUser: created, // 신규 가입 여부
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
nickname: user.nickname,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[Auth Error]', error);
|
||||
return res.status(500).json({ success: false, message: 'Internal Server Error' });
|
||||
}
|
||||
};
|
||||
|
||||
// Google Login Handler
|
||||
// Google Login Handler (Check only, or Login if exists)
|
||||
exports.loginWithGoogle = async (req, res) => {
|
||||
const { idToken } = req.body;
|
||||
|
||||
if (!idToken) {
|
||||
return res.status(400).json({ success: false, message: 'Missing idToken' });
|
||||
}
|
||||
|
||||
try {
|
||||
// Verify Google Token
|
||||
const ticket = await googleClient.verifyIdToken({
|
||||
idToken,
|
||||
audience: [
|
||||
process.env.GOOGLE_CLIENT_ID,
|
||||
process.env.GOOGLE_ANDROID_CLIENT_ID
|
||||
],
|
||||
});
|
||||
|
||||
const payload = ticket.getPayload();
|
||||
const socialId = payload.sub;
|
||||
|
||||
// Check if user exists
|
||||
const user = await User.findOne({ where: { provider: 'google', socialId } });
|
||||
|
||||
if (user) {
|
||||
// User exists -> Login
|
||||
const { accessToken, refreshToken } = generateTokens(user);
|
||||
user.refreshToken = refreshToken;
|
||||
await user.save();
|
||||
|
||||
console.log(`[Auth] User ${user.id} logged in via google`);
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
isNewUser: false,
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
nickname: user.nickname,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// User does not exist -> Return isNewUser: true (Do NOT create yet)
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
isNewUser: true,
|
||||
// Optional: Return partial info if needed for UI (e.g. pre-filling name)
|
||||
email: payload.email,
|
||||
nickname: payload.name,
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('[Google Verify Error]', error);
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Invalid Google Token',
|
||||
debug: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Google Register Handler (Create User)
|
||||
exports.registerWithGoogle = async (req, res) => {
|
||||
const { idToken } = req.body;
|
||||
|
||||
if (!idToken) {
|
||||
return res.status(400).json({ success: false, message: 'Missing idToken' });
|
||||
}
|
||||
|
||||
try {
|
||||
// Verify Google Token again
|
||||
const ticket = await googleClient.verifyIdToken({
|
||||
idToken,
|
||||
audience: [
|
||||
process.env.GOOGLE_CLIENT_ID,
|
||||
process.env.GOOGLE_ANDROID_CLIENT_ID
|
||||
],
|
||||
});
|
||||
|
||||
const payload = ticket.getPayload();
|
||||
const socialId = payload.sub;
|
||||
const email = payload.email;
|
||||
const nickname = payload.name;
|
||||
|
||||
// Find or Create User
|
||||
const [user, created] = await User.findOrCreate({
|
||||
where: { provider: 'google', socialId },
|
||||
defaults: { email, nickname },
|
||||
});
|
||||
|
||||
// If existing user calls register, just log them in (idempotent)
|
||||
const { accessToken, refreshToken } = generateTokens(user);
|
||||
user.refreshToken = refreshToken;
|
||||
await user.save();
|
||||
|
||||
console.log(`[Auth] User ${user.id} registered/logged in via google`);
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
isNewUser: created,
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
nickname: user.nickname,
|
||||
},
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[Google Register Error]', error);
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Invalid Google Token',
|
||||
debug: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Test Login Handler (For verification only)
|
||||
exports.testLogin = async (req, res) => {
|
||||
const { email, nickname } = req.body;
|
||||
|
||||
if (!email || !nickname) {
|
||||
return res.status(400).json({ success: false, message: 'Missing email or nickname' });
|
||||
}
|
||||
|
||||
// Use 'google' as provider to satisfy DB Enum constraint
|
||||
const socialId = `test_${email}`;
|
||||
|
||||
return socialLogin('google', { socialId, email, nickname }, res);
|
||||
};
|
||||
|
||||
// Refresh Token Handler
|
||||
exports.refreshToken = async (req, res) => {
|
||||
const { refreshToken } = req.body;
|
||||
|
||||
if (!refreshToken) {
|
||||
return res.status(400).json({ success: false, message: 'Refresh Token required' });
|
||||
}
|
||||
|
||||
try {
|
||||
const secret = process.env.JWT_REFRESH_SECRET;
|
||||
// 1. Verify Refresh Token
|
||||
const decoded = jwt.verify(refreshToken, secret);
|
||||
|
||||
// 2. Check DB
|
||||
const user = await User.findByPk(decoded.id);
|
||||
if (!user || user.refreshToken !== refreshToken) {
|
||||
return res.status(403).json({ success: false, message: 'Invalid Refresh Token' });
|
||||
}
|
||||
|
||||
// 3. Issue new Access Token
|
||||
const payload = { id: user.id, email: user.email, nickname: user.nickname };
|
||||
const newAccessToken = jwt.sign(payload, process.env.JWT_SECRET, {
|
||||
expiresIn: '1h',
|
||||
});
|
||||
|
||||
console.log(`[Auth] Access Token refreshed for User ${user.id}`);
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
accessToken: newAccessToken,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[Refresh Error]', error);
|
||||
if (error.name === 'TokenExpiredError') {
|
||||
return res.status(403).json({ success: false, message: 'Refresh Token expired' });
|
||||
}
|
||||
return res.status(403).json({ success: false, message: 'Invalid Refresh Token' });
|
||||
}
|
||||
};
|
||||
|
||||
// Get User Info
|
||||
exports.getMe = async (req, res) => {
|
||||
try {
|
||||
// req.user is set by middleware
|
||||
const user = await User.findByPk(req.user.id);
|
||||
if (!user) {
|
||||
return res.status(404).json({ success: false, message: 'User not found' });
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
nickname: user.nickname,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[GetMe Error]', error);
|
||||
return res.status(500).json({ success: false, message: 'Internal Server Error' });
|
||||
}
|
||||
};
|
||||
|
||||
// Withdraw (Delete Account)
|
||||
exports.withdraw = async (req, res) => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const user = await User.findByPk(userId);
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ success: false, message: 'User not found' });
|
||||
}
|
||||
|
||||
// Hard Delete
|
||||
await user.destroy();
|
||||
|
||||
console.log(`[Auth] User ${userId} withdrew from the service.`);
|
||||
return res.status(200).json({ success: true, message: 'Account deleted successfully' });
|
||||
} catch (error) {
|
||||
console.error('[Withdraw Error]', error);
|
||||
return res.status(500).json({ success: false, message: 'Internal Server Error' });
|
||||
}
|
||||
};
|
||||
@ -2,6 +2,7 @@ FROM node:24-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 패키지 파일 복사
|
||||
# 패키지 파일 복사
|
||||
COPY package*.json ./
|
||||
|
||||
|
||||
@ -1,34 +1,11 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const { connectDB, sequelize } = require('./config/db');
|
||||
const authRoutes = require('./routes/auth');
|
||||
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
|
||||
// Middleware
|
||||
app.use(cors());
|
||||
app.use(express.json()); // Body parser for JSON
|
||||
|
||||
// Routes
|
||||
app.use('/auth', authRoutes);
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Hello from Express Backend!');
|
||||
});
|
||||
|
||||
// Database Connection & Server Start
|
||||
const startServer = async () => {
|
||||
await connectDB();
|
||||
|
||||
// Sync models (in production, use migration instead of sync({alter: true}))
|
||||
// For dev: force: false to keep data, alter: true to update schema
|
||||
await sequelize.sync({ alter: true });
|
||||
console.log('Database synced');
|
||||
|
||||
app.listen(port, '0.0.0.0', () => {
|
||||
console.log(`Backend app listening on port ${port}`);
|
||||
});
|
||||
};
|
||||
|
||||
startServer();
|
||||
app.listen(port, '0.0.0.0', () => {
|
||||
console.log(`Backend app listening on port ${port}`);
|
||||
});
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
require('dotenv').config();
|
||||
|
||||
exports.verifyToken = (req, res, next) => {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader) {
|
||||
return res.status(401).json({ success: false, message: 'No token provided' });
|
||||
}
|
||||
|
||||
const token = authHeader.split(' ')[1]; // "Bearer <token>"
|
||||
if (!token) {
|
||||
return res.status(401).json({ success: false, message: 'Invalid token format' });
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
req.user = decoded; // { id, email, nickname, ... }
|
||||
next();
|
||||
} catch (error) {
|
||||
console.error('[Auth Middleware] Token Verification Failed:', error.message);
|
||||
if (error.name === 'TokenExpiredError') {
|
||||
return res.status(401).json({ success: false, message: 'Token expired' });
|
||||
}
|
||||
return res.status(401).json({ success: false, message: 'Invalid token' });
|
||||
}
|
||||
};
|
||||
@ -1,46 +0,0 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../config/db');
|
||||
|
||||
const User = sequelize.define('User', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
validate: {
|
||||
isEmail: true,
|
||||
},
|
||||
},
|
||||
nickname: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
provider: {
|
||||
type: DataTypes.ENUM('google', 'naver', 'kakao'),
|
||||
allowNull: false,
|
||||
comment: 'Social login provider',
|
||||
},
|
||||
socialId: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
comment: 'Unique ID from the social provider',
|
||||
},
|
||||
refreshToken: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
comment: 'Generic refresh token for valid session',
|
||||
},
|
||||
}, {
|
||||
indexes: [
|
||||
{
|
||||
unique: true,
|
||||
fields: ['provider', 'socialId'], // Prevent duplicate users for same provider
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
module.exports = User;
|
||||
1541
backend/package-lock.json
generated
@ -7,12 +7,6 @@
|
||||
"start": "node index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"google-auth-library": "^9.4.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mysql2": "^3.6.5",
|
||||
"sequelize": "^6.35.1"
|
||||
"express": "^4.18.2"
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const authController = require('../controllers/authController');
|
||||
|
||||
router.post('/google', authController.loginWithGoogle);
|
||||
router.post('/google/register', authController.registerWithGoogle); // Add register route
|
||||
router.post('/test-login', authController.testLogin);
|
||||
router.post('/refresh', authController.refreshToken);
|
||||
|
||||
const verifyToken = require('../middleware/authMiddleware').verifyToken; // Correct import
|
||||
|
||||
router.get('/test-protected', verifyToken, (req, res) => {
|
||||
res.json({ success: true, message: 'You have a valid token', user: req.user });
|
||||
});
|
||||
|
||||
router.get('/me', verifyToken, authController.getMe);
|
||||
router.delete('/withdraw', verifyToken, authController.withdraw);
|
||||
|
||||
// Future place for:
|
||||
// router.post('/kakao', authController.loginWithKakao);
|
||||
// router.post('/naver', authController.loginWithNaver);
|
||||
|
||||
module.exports = router;
|
||||
@ -1,4 +1,4 @@
|
||||
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
db:
|
||||
@ -27,7 +27,6 @@ services:
|
||||
restart: always
|
||||
ports:
|
||||
- "3000:3000"
|
||||
|
||||
env_file:
|
||||
- ./backend/.env.production
|
||||
depends_on:
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
@echo off
|
||||
cd /d "%~dp0"
|
||||
echo ==============================================
|
||||
echo [RUP Project] Cleaning and Rebuilding Backend
|
||||
echo ==============================================
|
||||
|
||||
echo 1. Stopping Docker services...
|
||||
docker compose down
|
||||
|
||||
echo.
|
||||
echo 2. Forcing removal of old backend image (to clear cache)...
|
||||
docker image rm -f rup-backend
|
||||
|
||||
echo.
|
||||
echo 3. Rebuilding Docker containers...
|
||||
docker compose up -d --build
|
||||
|
||||
echo.
|
||||
echo 4. Checking logs (Press Ctrl+C to exit log view)...
|
||||
echo ==============================================
|
||||
docker compose logs -f backend
|
||||
|
||||
pause
|
||||