1차 배포 준비
BIN
android/app/src/main/res/drawable-hdpi/splash.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
android/app/src/main/res/drawable-mdpi/splash.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
android/app/src/main/res/drawable-v21/background.png
Normal file
After Width: | Height: | Size: 69 B |
@ -1,12 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
<item>
|
||||
<bitmap android:gravity="fill" android:src="@drawable/background"/>
|
||||
</item>
|
||||
<item>
|
||||
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
||||
</item>
|
||||
</layer-list>
|
||||
|
BIN
android/app/src/main/res/drawable-xhdpi/splash.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
android/app/src/main/res/drawable-xxhdpi/splash.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
android/app/src/main/res/drawable-xxxhdpi/splash.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
android/app/src/main/res/drawable/background.png
Normal file
After Width: | Height: | Size: 69 B |
@ -1,12 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
<item>
|
||||
<bitmap android:gravity="fill" android:src="@drawable/background"/>
|
||||
</item>
|
||||
<item>
|
||||
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
||||
</item>
|
||||
</layer-list>
|
||||
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 17 KiB |
19
android/app/src/main/res/values-night-v31/styles.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:forceDarkAllowed">false</item>
|
||||
<item name="android:windowFullscreen">false</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
@ -5,6 +5,10 @@
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
<item name="android:forceDarkAllowed">false</item>
|
||||
<item name="android:windowFullscreen">false</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
|
19
android/app/src/main/res/values-v31/styles.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:forceDarkAllowed">false</item>
|
||||
<item name="android:windowFullscreen">false</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
@ -5,6 +5,10 @@
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
<item name="android:forceDarkAllowed">false</item>
|
||||
<item name="android:windowFullscreen">false</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
|
BIN
assets/icons/allscore_icon.png
Normal file
After Width: | Height: | Size: 15 KiB |
@ -427,7 +427,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
@ -484,7 +484,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 182 KiB |
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 840 B |
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 14 KiB |
21
ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "background.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png
vendored
Normal file
After Width: | Height: | Size: 69 B |
@ -1,23 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 12 KiB |
@ -16,13 +16,19 @@
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
||||
</imageView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
|
||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="3T2-ad-Qdv"/>
|
||||
<constraint firstItem="tWc-Dq-wcI" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="RPx-PI-7Xg"/>
|
||||
<constraint firstItem="tWc-Dq-wcI" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="SdS-ul-q2q"/>
|
||||
<constraint firstAttribute="trailing" secondItem="tWc-Dq-wcI" secondAttribute="trailing" id="Swv-Gf-Rwn"/>
|
||||
<constraint firstAttribute="trailing" secondItem="YRO-k0-Ey4" secondAttribute="trailing" id="TQA-XW-tRk"/>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="duK-uY-Gun"/>
|
||||
<constraint firstItem="tWc-Dq-wcI" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="kV7-tw-vXt"/>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="xPn-NY-SIU"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
@ -32,6 +38,7 @@
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="LaunchImage" width="168" height="185"/>
|
||||
<image name="LaunchImage" width="203" height="209"/>
|
||||
<image name="LaunchBackground" width="1" height="1"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
@ -1,53 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>올스코어</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>allscore_app</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>GADApplicationIdentifier</key>
|
||||
<string>ca-app-pub-3151339278746301~1689299887</string> <!-- AdMob 앱 ID 추가 -->
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>이 앱이 사진 라이브러리에 접근할 수 있도록 허용합니다.</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>올스코어</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>allscore_app</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>GADApplicationIdentifier</key>
|
||||
<string>ca-app-pub-3151339278746301~1689299887</string>
|
||||
<!-- AdMob 앱 ID 추가 -->
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>이 앱이 사진 라이브러리에 접근할 수 있도록 허용합니다.</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -1,4 +1,8 @@
|
||||
class Config {
|
||||
// 테스트 광고 단위 ID
|
||||
static const String testAdUnitId = 'ca-app-pub-3940256099942544/6300978111';
|
||||
// 실제 광고 단위 ID
|
||||
static const String realAdUnitId = 'ca-app-pub-3940256099942544/6300978111';
|
||||
// 서버 주소
|
||||
static const String baseUrl = 'https://eldsoft.com:8097';
|
||||
}
|
@ -1,37 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
// 예시: 설문 참여 버튼을 눌렀을 때 이동할 임시 설문 페이지
|
||||
// 실제 구현에서는 SurveyPage를 만들어 사용하시면 됩니다.
|
||||
class SurveyPage extends StatelessWidget {
|
||||
const SurveyPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('임시 설문 페이지'),
|
||||
backgroundColor: Colors.black,
|
||||
),
|
||||
body: const Center(
|
||||
child: Text('여기는 설문 페이지입니다 (임시).'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
import '../survey/survey_page.dart';
|
||||
|
||||
/// 팝업(모달) 자체를 보여주는 함수
|
||||
Future<void> showSurveyDialog(BuildContext context) async {
|
||||
Future<void> showSurveyDialog(BuildContext context, String nickname) async {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false, // 바깥 영역 터치로 닫기 방지
|
||||
builder: (_) => const SurveyDialog(),
|
||||
builder: (_) => SurveyDialog(nickname: nickname),
|
||||
);
|
||||
}
|
||||
|
||||
/// 실제 AlertDialog 형태의 위젯
|
||||
class SurveyDialog extends StatefulWidget {
|
||||
const SurveyDialog({Key? key}) : super(key: key);
|
||||
final String nickname;
|
||||
const SurveyDialog({Key? key, required this.nickname}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SurveyDialog> createState() => _SurveyDialogState();
|
||||
@ -99,7 +82,7 @@ class _SurveyDialogState extends State<SurveyDialog> {
|
||||
// 임시 설문 페이지로 이동
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => const SurveyPage()),
|
||||
MaterialPageRoute(builder: (_) => SurveyPage(nickname: widget.nickname)),
|
||||
);
|
||||
},
|
||||
style: TextButton.styleFrom(backgroundColor: Colors.black),
|
||||
|
@ -2,9 +2,10 @@ import 'package:http/http.dart' as http;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import '../config/config.dart';
|
||||
|
||||
class Api {
|
||||
static const String baseUrl = 'https://eldsoft.com:8097';
|
||||
static const String baseUrl = Config.baseUrl;
|
||||
|
||||
// 사용자 정보를 업데이트하는 메서드
|
||||
static Future<Map<String, dynamic>> serverRequest({
|
||||
@ -32,8 +33,6 @@ class Api {
|
||||
if (response.statusCode == 200) {
|
||||
String responseBody = utf8.decode(response.bodyBytes);
|
||||
final Map<String, dynamic> jsonResponse = jsonDecode(responseBody);
|
||||
print('응답: $jsonResponse');
|
||||
print('응답[result]: ${jsonResponse['result']}');
|
||||
|
||||
await prefs.setString('auth_token', jsonResponse['auth']['token']);
|
||||
res = {
|
||||
|
442
lib/survey/survey_page.dart
Normal file
@ -0,0 +1,442 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart'; // ★ 숫자만 입력 위해
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
|
||||
// 서버 API
|
||||
import '../plugins/api.dart';
|
||||
|
||||
// 메인 페이지
|
||||
import '../views/room/main_page.dart';
|
||||
|
||||
// 알람 모달창
|
||||
import '../dialogs/response_dialog.dart';
|
||||
|
||||
class SurveyPage extends StatefulWidget {
|
||||
final String nickname;
|
||||
|
||||
const SurveyPage({
|
||||
Key? key,
|
||||
required this.nickname,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SurveyPage> createState() => _SurveyPageState();
|
||||
}
|
||||
|
||||
class _SurveyPageState extends State<SurveyPage> {
|
||||
// 현재 페이지 인덱스 (0~4)
|
||||
int _currentIndex = 0;
|
||||
|
||||
// 전체 질문 목록 (5개 예시)
|
||||
List<String> _questions = [];
|
||||
List<String> _questionsOriginal = [];
|
||||
|
||||
/// 사용자가 입력한 답변(5개)
|
||||
final List<String?> _answers = List.filled(5, null, growable: false);
|
||||
|
||||
// 각 페이지별 라디오 선택 값
|
||||
final Map<int, String> _selectedRadioValue = {};
|
||||
|
||||
// 각 페이지별 텍스트필드 컨트롤러
|
||||
final Map<int, TextEditingController> _textControllers = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// 질문들 세팅
|
||||
_questions = [
|
||||
"Q1. ${widget.nickname}님의 나이는 어떻게 되나요?",
|
||||
"Q2. ${widget.nickname}님의 직업이 무엇인가요?",
|
||||
"Q3. ${widget.nickname}님은 올스코어 앱을 어떻게 알게 됐나요?",
|
||||
"Q4. ${widget.nickname}님은 올스코어 앱을 어디서 경험하셨나요?",
|
||||
"Q5. 올스코어를 계속 사용할 의사가 있나요?",
|
||||
];
|
||||
|
||||
_questionsOriginal = [
|
||||
"나이는 어떻게 되나요?",
|
||||
"직업이 무엇인가요?",
|
||||
"올스코어 앱을 어떻게 알게 됐나요?",
|
||||
"올스코어 앱을 어디서 경험하셨나요?",
|
||||
"올스코어를 계속 사용할 의사가 있나요?",
|
||||
];
|
||||
|
||||
// 페이지마다 TextEditingController 초기화
|
||||
for (int i = 0; i < _questions.length; i++) {
|
||||
_textControllers[i] = TextEditingController();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// TextController 정리
|
||||
for (var ctrl in _textControllers.values) {
|
||||
ctrl.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// (좌측 상단) 설문 그만하기
|
||||
Future<void> _onExitSurvey() async {
|
||||
// 필요 시 "정말 나가시겠습니까?" 등 확인 모달 추가 가능
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => const MainPage()),
|
||||
(route) => false,
|
||||
);
|
||||
}
|
||||
|
||||
/// 다음 버튼
|
||||
void _onNext() {
|
||||
if (!_validateCurrentPage()) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('값을 모두 입력해 주세요.')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentIndex < _questions.length - 1) {
|
||||
setState(() {
|
||||
_currentIndex++;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 이전 버튼
|
||||
void _onPrev() {
|
||||
if (_currentIndex > 0) {
|
||||
setState(() {
|
||||
_currentIndex--;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 제출하기
|
||||
Future<void> _onSubmit() async {
|
||||
// 마지막 페이지도 값 확인
|
||||
if (!_validateCurrentPage()) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('값을 모두 입력해 주세요.')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// body: {"QNA": ["질문1","답변1","질문2","답변2", ... ]}
|
||||
final List<String> qnaList = [];
|
||||
for (int i = 0; i < _questionsOriginal.length; i++) {
|
||||
qnaList.add(_questionsOriginal[i]);
|
||||
qnaList.add(_answers[i] ?? '');
|
||||
}
|
||||
|
||||
final requestBody = {
|
||||
"QNA": qnaList.toString(),
|
||||
};
|
||||
print('requestBody: $requestBody');
|
||||
|
||||
try {
|
||||
final response = await Api.serverRequest(uri: '/survey/collect', body: requestBody);
|
||||
if (response['result'] == 'OK') {
|
||||
final resp = response['response'] ?? {};
|
||||
if (resp['result'] == 'OK') {
|
||||
// 설문 제출 성공
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('설문이 제출되었습니다. 감사합니다!')),
|
||||
);
|
||||
Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (_) => const MainPage()), (route) => false);
|
||||
} else {
|
||||
showResponseDialog(context, '오류', '설문 제출 실패');
|
||||
}
|
||||
} else {
|
||||
showResponseDialog(context, '오류', '설문 제출 실패');
|
||||
}
|
||||
} catch (e) {
|
||||
showResponseDialog(context, '오류', '설문 제출 실패');
|
||||
}
|
||||
}
|
||||
|
||||
/// 현재 페이지 입력값이 유효한지 체크 + _answers에 저장
|
||||
bool _validateCurrentPage() {
|
||||
final index = _currentIndex;
|
||||
String? answer;
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
// 나이(숫자만)
|
||||
final txt = _textControllers[index]?.text.trim() ?? '';
|
||||
if (txt.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
answer = '$txt 세';
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// 직업
|
||||
final selected = _selectedRadioValue[index];
|
||||
if (selected == null || selected.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
answer = selected;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// 앱 알게된 경로
|
||||
final selected2 = _selectedRadioValue[index];
|
||||
if (selected2 == null || selected2.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
if (selected2 == '기타') {
|
||||
final etc = _textControllers[index]?.text.trim() ?? '';
|
||||
if (etc.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
answer = "기타($etc)";
|
||||
} else {
|
||||
answer = selected2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// 어디서 경험?
|
||||
final sel3 = _selectedRadioValue[index];
|
||||
if (sel3 == null || sel3.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
answer = sel3;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// 계속 사용할 의사?
|
||||
final sel4 = _selectedRadioValue[index];
|
||||
if (sel4 == null || sel4.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
final comment = _textControllers[index]?.text.trim() ?? '';
|
||||
answer = sel4 + (comment.isNotEmpty ? " / 의견: $comment" : "");
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
_answers[index] = answer;
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final questionText = _questions[_currentIndex];
|
||||
final pageNumber = _currentIndex + 1;
|
||||
final totalPage = _questions.length;
|
||||
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
_onExitSurvey();
|
||||
return false;
|
||||
},
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
// 좌측 위 '설문 그만하기' 버튼
|
||||
leadingWidth: 120, // 버튼 가로폭 살짝 넓힘
|
||||
leading: TextButton(
|
||||
onPressed: _onExitSurvey,
|
||||
child: const Text(
|
||||
'설문 그만하기',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
title: Text('설문조사 ($pageNumber/$totalPage)'),
|
||||
backgroundColor: Colors.black,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
// 중앙 정렬
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// 질문
|
||||
Text(
|
||||
questionText,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
// 페이지별 UI
|
||||
_buildSurveyPage(_currentIndex),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: Container(
|
||||
color: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
if (_currentIndex > 0)
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.grey),
|
||||
onPressed: _onPrev,
|
||||
child: const Text('이전'),
|
||||
),
|
||||
),
|
||||
if (_currentIndex > 0) const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.black),
|
||||
onPressed: (_currentIndex < totalPage - 1) ? _onNext : _onSubmit,
|
||||
child: Text(
|
||||
(_currentIndex < totalPage - 1) ? '다음' : '제출하기',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 페이지별 UI
|
||||
Widget _buildSurveyPage(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
// 나이 (숫자 입력)
|
||||
return Column(
|
||||
children: [
|
||||
const Text('(예: 나이를 숫자로 입력해 주세요.)',
|
||||
textAlign: TextAlign.center),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: _textControllers[index],
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly], // 숫자만 입력
|
||||
textAlign: TextAlign.center, // 중앙정렬
|
||||
decoration: const InputDecoration(
|
||||
labelText: '나이',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
case 1:
|
||||
// 직업
|
||||
final jobs = ['학생', '회사원', '전문직', '교수/교사', '기술직', '공무원', '예술/스포츠', '기타'];
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: jobs.map((job) {
|
||||
return RadioListTile<String>(
|
||||
title: Text(job, textAlign: TextAlign.center),
|
||||
value: job,
|
||||
groupValue: _selectedRadioValue[index],
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
_selectedRadioValue[index] = val!;
|
||||
});
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
|
||||
case 2:
|
||||
// 알게된 경로
|
||||
final paths = ['친구/지인 추천', '소셜 미디어', '블로그/온라인 리뷰', '학교나 직장', '기타'];
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
...paths.map((p) {
|
||||
return RadioListTile<String>(
|
||||
title: Text(p, textAlign: TextAlign.center),
|
||||
value: p,
|
||||
groupValue: _selectedRadioValue[index],
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
_selectedRadioValue[index] = val!;
|
||||
});
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
if (_selectedRadioValue[index] == '기타')
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: TextField(
|
||||
controller: _textControllers[index],
|
||||
textAlign: TextAlign.center,
|
||||
decoration: const InputDecoration(
|
||||
labelText: '기타 내용을 입력해 주세요.',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
case 3:
|
||||
// 어디서 경험?
|
||||
final places = ['가족과 함께', '친구들과 모임', '학교 교육 목적', '직장 동호회', '카페나 공공장소', '여행 중', '기타'];
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: places.map((pl) {
|
||||
return RadioListTile<String>(
|
||||
title: Text(pl, textAlign: TextAlign.center),
|
||||
value: pl,
|
||||
groupValue: _selectedRadioValue[index],
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
_selectedRadioValue[index] = val!;
|
||||
});
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
|
||||
case 4:
|
||||
// 계속 사용할 의사?
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
RadioListTile<String>(
|
||||
title: const Text('네', textAlign: TextAlign.center),
|
||||
value: '네',
|
||||
groupValue: _selectedRadioValue[index],
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
_selectedRadioValue[index] = val!;
|
||||
});
|
||||
},
|
||||
),
|
||||
RadioListTile<String>(
|
||||
title: const Text('아니오', textAlign: TextAlign.center),
|
||||
value: '아니오',
|
||||
groupValue: _selectedRadioValue[index],
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
_selectedRadioValue[index] = val!;
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text('추가 의견이 있다면 자유롭게 작성해 주세요.', textAlign: TextAlign.center),
|
||||
const SizedBox(height: 8),
|
||||
TextField(
|
||||
controller: _textControllers[index],
|
||||
maxLines: 3,
|
||||
textAlign: TextAlign.center,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'ex) 불편사항, 개선 아이디어 등',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
default:
|
||||
return const Text('설문 문항 오류', textAlign: TextAlign.center);
|
||||
}
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ class _FinishPrivatePageState extends State<FinishPrivatePage> {
|
||||
String _roomTitle = '';
|
||||
DateTime? _startDt;
|
||||
DateTime? _endDt;
|
||||
int _masterUserSeq = 0; // 방장 user_seq (ADMIN과는 별개)
|
||||
String _masterUserSeq = '0'; // 방장 user_seq (ADMIN과는 별개)
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -70,7 +70,7 @@ class _FinishPrivatePageState extends State<FinishPrivatePage> {
|
||||
_roomInfo = rInfo;
|
||||
_userMap = uInfo;
|
||||
_roomTitle = rTitle.isNotEmpty ? rTitle : '종료된 방(개인전)';
|
||||
_masterUserSeq = mSeq;
|
||||
_masterUserSeq = mSeq.toString();
|
||||
|
||||
if (sdt != null && sdt is String && sdt.contains('T')) {
|
||||
_startDt = DateTime.tryParse(sdt);
|
||||
@ -154,7 +154,7 @@ class _FinishPrivatePageState extends State<FinishPrivatePage> {
|
||||
final score = (user['score'] ?? 0) as int;
|
||||
var nickname = user['nickname'] ?? '유저';
|
||||
final profileImg = user['profile_img'] ?? '';
|
||||
final userSeq = user['user_seq'] ?? 0;
|
||||
final userSeq = user['user_seq'].toString() ?? '0';
|
||||
final participantType = user['participant_type'] ?? '';
|
||||
|
||||
if (_masterUserSeq == userSeq) {
|
||||
|
@ -30,7 +30,7 @@ class _FinishTeamPageState extends State<FinishTeamPage> {
|
||||
List<Map<String, dynamic>> _userList = [];
|
||||
|
||||
String _roomTitle = '';
|
||||
int _masterUserSeq = 0;
|
||||
String _masterUserSeq = '0';
|
||||
DateTime? _startDt;
|
||||
DateTime? _endDt;
|
||||
|
||||
@ -70,7 +70,7 @@ class _FinishTeamPageState extends State<FinishTeamPage> {
|
||||
_roomInfo = rInfo;
|
||||
_userMap = uInfo;
|
||||
_roomTitle = rTitle.isNotEmpty ? rTitle : '종료된 팀전';
|
||||
_masterUserSeq = mSeq;
|
||||
_masterUserSeq = mSeq.toString();
|
||||
|
||||
if (sdt != null && sdt is String && sdt.contains('T')) {
|
||||
_startDt = DateTime.tryParse(sdt);
|
||||
@ -233,7 +233,7 @@ class _FinishTeamPageState extends State<FinishTeamPage> {
|
||||
final score = (user['score'] ?? 0) as int;
|
||||
var nickname = user['nickname'] ?? '유저';
|
||||
final profileImg = user['profile_img'] ?? '';
|
||||
final userSeq = user['user_seq'] ?? 0;
|
||||
final userSeq = user['user_seq'].toString() ?? '0';
|
||||
final participantType = user['participant_type'] ?? '';
|
||||
|
||||
if (_masterUserSeq == userSeq) {
|
||||
|
@ -51,16 +51,13 @@ class _MainPageState extends State<MainPage> {
|
||||
// (A) 메인페이지 들어올 때 전체 FRD 연결 해제
|
||||
FirebaseDatabase.instance.goOffline();
|
||||
|
||||
// (B) 강제 종료 여부 확인 후 재입장 시도
|
||||
// (B) 랜딩 페이지 정보 확인
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_checkForcedExitStatus();
|
||||
_checkLandingMainPageInfo();
|
||||
});
|
||||
|
||||
// (C) 배너 광고 초기화
|
||||
_initBannerAd();
|
||||
|
||||
// (D) 설문조사 팝업 표시
|
||||
_checkSurveyPopup();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -92,18 +89,6 @@ class _MainPageState extends State<MainPage> {
|
||||
_bannerAd?.load();
|
||||
}
|
||||
|
||||
/// "오늘 하루 보지 않기" 체크 여부 확인 후, 모달 보여줄지 결정
|
||||
Future<void> _checkSurveyPopup() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final shownToday = prefs.getString('survey_popup_today') ?? 'N';
|
||||
if (shownToday == 'N') {
|
||||
// 아직 오늘은 안 봤으므로 팝업 띄우기
|
||||
Future.delayed(Duration.zero, () {
|
||||
showSurveyDialog(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _onWillPop() async {
|
||||
final now = DateTime.now();
|
||||
if (_lastPressedTime == null ||
|
||||
@ -124,11 +109,11 @@ class _MainPageState extends State<MainPage> {
|
||||
}
|
||||
|
||||
/// (B) 서버에 "강제 종료 여부" 확인 → 방이 있으면 재입장
|
||||
Future<void> _checkForcedExitStatus() async {
|
||||
Future<void> _checkLandingMainPageInfo() async {
|
||||
try {
|
||||
final Map<String, dynamic> requestBody = {};
|
||||
final response = await Api.serverRequest(
|
||||
uri: '/room/score/enter/running/room',
|
||||
uri: '/room/score/landing/mainpage/info',
|
||||
body: requestBody,
|
||||
);
|
||||
|
||||
@ -171,6 +156,17 @@ class _MainPageState extends State<MainPage> {
|
||||
);
|
||||
}
|
||||
}
|
||||
final surveyYn = (data['survey_yn'] ?? 'N').toString().toUpperCase();
|
||||
final joinGameYn = (data['join_game_yn'] ?? 'N').toString().toUpperCase();
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final surveyShownToday = prefs.getString('survey_popup_today') ?? 'N';
|
||||
final nickname = (data['nickname'] ?? '').toString();
|
||||
if (surveyYn == 'N' && joinGameYn == 'Y' && surveyShownToday == 'N') {
|
||||
// 설문조사 팝업 표시
|
||||
Future.delayed(Duration.zero, () {
|
||||
showSurveyDialog(context, nickname);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
final msgTitle = resp['response_info']?['msg_title'] ?? '오류';
|
||||
final msgContent = resp['response_info']?['msg_content'] ?? '강제 종료 여부 확인 실패';
|
||||
|
@ -132,9 +132,10 @@ class _PlayingPrivatePageState extends State<PlayingPrivatePage> {
|
||||
}
|
||||
|
||||
final data = snapshot.value as Map<dynamic, dynamic>? ?? {};
|
||||
print('data111111111 $data');
|
||||
final roomInfoData = data['roomInfo'] as Map<dynamic, dynamic>? ?? {};
|
||||
final userInfoData = data['userInfo'] as Map<dynamic, dynamic>? ?? {};
|
||||
final userListData = data['userList'] as Map<dynamic, dynamic>?;
|
||||
final userListData = data['userList'] as Map<dynamic, dynamic>? ?? {};
|
||||
|
||||
// 방 상태 체크
|
||||
final roomStatus = (roomInfoData['room_status'] ?? 'WAIT').toString().toUpperCase();
|
||||
@ -167,15 +168,16 @@ class _PlayingPrivatePageState extends State<PlayingPrivatePage> {
|
||||
_userListMap.clear();
|
||||
if (userListData != null) {
|
||||
userListData.forEach((k, v) {
|
||||
_userListMap[k.toString()] = (v == true);
|
||||
_userListMap[k.toString().replaceAll('_', '')] = (v == true);
|
||||
});
|
||||
}
|
||||
|
||||
// 전체 유저 목록
|
||||
final List<Map<String, dynamic>> rawList = [];
|
||||
userInfoData.forEach((uSeq, uData) {
|
||||
uSeq = uSeq.toString().replaceAll('_', '');
|
||||
// 방장 표시
|
||||
if (uSeq.toString() == roomInfoData['master_user_seq'].toString()) {
|
||||
if (uSeq == roomInfoData['master_user_seq'].toString()) {
|
||||
uData['nickname'] = '★' + (uData['nickname'] ?? '유저');
|
||||
} else if ((uData['participant_type'] ?? '').toString().toUpperCase() == 'ADMIN') {
|
||||
// 관리자 표시
|
||||
|
@ -138,7 +138,7 @@ class _PlayingTeamPageState extends State<PlayingTeamPage> {
|
||||
final data = snapshot.value as Map<dynamic, dynamic>? ?? {};
|
||||
final roomInfoData = data['roomInfo'] as Map<dynamic, dynamic>? ?? {};
|
||||
final userInfoData = data['userInfo'] as Map<dynamic, dynamic>? ?? {};
|
||||
final userListData = data['userList'] as Map<dynamic, dynamic>?;
|
||||
final userListData = data['userList'] as Map<dynamic, dynamic>? ?? {};
|
||||
|
||||
final roomStatus = (roomInfoData['room_status'] ?? 'WAIT').toString().toUpperCase();
|
||||
if (roomStatus == 'FINISH') {
|
||||
@ -169,13 +169,14 @@ class _PlayingTeamPageState extends State<PlayingTeamPage> {
|
||||
_userListMap.clear();
|
||||
if (userListData != null) {
|
||||
userListData.forEach((k, v) {
|
||||
_userListMap[k.toString()] = (v == true);
|
||||
_userListMap[k.toString().replaceAll('_', '')] = (v == true);
|
||||
});
|
||||
}
|
||||
|
||||
// 전체 유저
|
||||
final List<Map<String, dynamic>> rawList = [];
|
||||
userInfoData.forEach((uSeq, uData) {
|
||||
uSeq = uSeq.toString().replaceAll('_', '');
|
||||
// 방장 표시
|
||||
if (uSeq.toString() == roomInfoData['master_user_seq'].toString()) {
|
||||
uData['nickname'] = '★' + (uData['nickname'] ?? '유저');
|
||||
|
@ -62,6 +62,9 @@ class _WaitingRoomPrivatePageState extends State<WaitingRoomPrivatePage> {
|
||||
// 내 user_seq
|
||||
String mySeq = '0';
|
||||
|
||||
// 준비 버튼 3초 딜레이
|
||||
bool _readyButtonEnabled = true; // true: 클릭 가능, false: 클릭 불가
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// 1시간 카운트다운
|
||||
// ─────────────────────────────────────────
|
||||
@ -77,7 +80,7 @@ class _WaitingRoomPrivatePageState extends State<WaitingRoomPrivatePage> {
|
||||
String adUnitId = Config.testAdUnitId;
|
||||
|
||||
// 방장 SEQ 저장
|
||||
String _masterSeqString = '';
|
||||
String _masterSeqString = '0';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -121,12 +124,14 @@ class _WaitingRoomPrivatePageState extends State<WaitingRoomPrivatePage> {
|
||||
Future<void> _initRoomRef() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
mySeq = prefs.getInt('my_user_seq')?.toString() ?? '0';
|
||||
print('mySeq111111111 $mySeq');
|
||||
|
||||
final roomKey = 'korea-${widget.roomSeq}';
|
||||
_roomRef = FirebaseDatabase.instance.ref('rooms/$roomKey');
|
||||
|
||||
// onDisconnect + connect_yn='Y'
|
||||
final myUserRef = _roomRef.child('userInfo').child(mySeq);
|
||||
final myUserRef = _roomRef.child('userInfo').child('_${mySeq}');
|
||||
print('myUserRef111111111 $myUserRef');
|
||||
if (_roomRef.child('userList').child(mySeq) == true) {
|
||||
myUserRef.onDisconnect().update({'connect_yn': 'N'});
|
||||
}
|
||||
@ -151,7 +156,7 @@ class _WaitingRoomPrivatePageState extends State<WaitingRoomPrivatePage> {
|
||||
|
||||
final data = snapshot.value as Map<dynamic, dynamic>? ?? {};
|
||||
final roomInfoData = data['roomInfo'] as Map<dynamic, dynamic>? ?? {};
|
||||
final userInfoDynamic = data['userInfo']; // 이건 List일 수 있음
|
||||
final userInfoDynamic = data['userInfo'] as Map<dynamic, dynamic>? ?? {};
|
||||
|
||||
final roomStatus = (roomInfoData['room_status'] ?? 'WAIT').toString().toUpperCase();
|
||||
|
||||
@ -160,7 +165,7 @@ class _WaitingRoomPrivatePageState extends State<WaitingRoomPrivatePage> {
|
||||
if (userInfoDynamic is Map) {
|
||||
userInfoDynamic.forEach((key, val) {
|
||||
if (val is Map) {
|
||||
final tempUserSeq = val['user_seq'] ?? '0';
|
||||
final tempUserSeq = val['user_seq'].toString() ?? '0';
|
||||
if (tempUserSeq != '0') {
|
||||
tempList.add({
|
||||
'user_seq': tempUserSeq,
|
||||
@ -190,13 +195,13 @@ class _WaitingRoomPrivatePageState extends State<WaitingRoomPrivatePage> {
|
||||
|
||||
// 방장 여부
|
||||
roomMasterYn = 'N';
|
||||
final masterSeq = roomInfoData['master_user_seq'];
|
||||
if (masterSeq != null && masterSeq.toString() == mySeq) {
|
||||
final masterSeq = roomInfoData['master_user_seq'].toString();
|
||||
if (masterSeq != null && masterSeq == mySeq) {
|
||||
roomMasterYn = 'Y';
|
||||
}
|
||||
|
||||
if (masterSeq != null) {
|
||||
_masterSeqString = masterSeq.toString();
|
||||
_masterSeqString = masterSeq;
|
||||
} else {
|
||||
_masterSeqString = '';
|
||||
}
|
||||
@ -237,7 +242,7 @@ class _WaitingRoomPrivatePageState extends State<WaitingRoomPrivatePage> {
|
||||
}
|
||||
|
||||
// (D) 내가 목록에서 사라졌는지 => 강퇴 판별
|
||||
final amIStillHere = _userList.any((u) => u['user_seq'].toString() == mySeq);
|
||||
final amIStillHere = _userList.any((u) => (u['user_seq'].toString() ?? '0') == mySeq);
|
||||
if (!amIStillHere && !_kickedOut && roomMasterYn != 'Y') {
|
||||
_kickedOut = true;
|
||||
if (_roomExitYn == 'N') {
|
||||
@ -363,7 +368,7 @@ class _WaitingRoomPrivatePageState extends State<WaitingRoomPrivatePage> {
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
final myUserRef = _roomRef.child('userInfo').child(mySeq);
|
||||
final myUserRef = _roomRef.child('userInfo').child('_${mySeq}');
|
||||
myUserRef.onDisconnect().cancel();
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
@ -404,7 +409,7 @@ class _WaitingRoomPrivatePageState extends State<WaitingRoomPrivatePage> {
|
||||
Widget _buildTopButtons() {
|
||||
if (_isLoading) return const SizedBox();
|
||||
|
||||
final me = _userList.firstWhere((u) => (u['user_seq'] ?? '0') == mySeq, orElse: () => {});
|
||||
final me = _userList.firstWhere((u) => (u['user_seq'].toString() ?? '0') == mySeq, orElse: () => {});
|
||||
final myReadyYn = (me['ready_yn'] ?? 'N').toString().toUpperCase();
|
||||
final bool isReady = (myReadyYn == 'Y');
|
||||
final readyLabel = isReady ? '준비완료' : '준비';
|
||||
@ -434,7 +439,7 @@ class _WaitingRoomPrivatePageState extends State<WaitingRoomPrivatePage> {
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: ElevatedButton(
|
||||
style: btnStyle,
|
||||
onPressed: _onToggleReady,
|
||||
onPressed: _readyButtonEnabled ? _onToggleReady : null,
|
||||
child: Text(readyLabel),
|
||||
),
|
||||
),
|
||||
@ -470,7 +475,7 @@ class _WaitingRoomPrivatePageState extends State<WaitingRoomPrivatePage> {
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: ElevatedButton(
|
||||
style: btnStyle,
|
||||
onPressed: _onToggleReady,
|
||||
onPressed: _readyButtonEnabled ? _onToggleReady : null,
|
||||
child: Text(readyLabel),
|
||||
),
|
||||
),
|
||||
@ -481,17 +486,44 @@ class _WaitingRoomPrivatePageState extends State<WaitingRoomPrivatePage> {
|
||||
}
|
||||
|
||||
Future<void> _onToggleReady() async {
|
||||
// (A) 버튼이 비활성화 상태면 리턴
|
||||
if (!_readyButtonEnabled) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('잠시 후에 다시 눌러주세요. (3초 대기)'),
|
||||
duration: Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// (B) 버튼 비활성화
|
||||
setState(() {
|
||||
_readyButtonEnabled = false;
|
||||
});
|
||||
|
||||
try {
|
||||
final me = _userList.firstWhere((u) => (u['user_seq'] ?? '0') == mySeq, orElse: () => {});
|
||||
final me = _userList.firstWhere((u) => (u['user_seq'].toString() ?? '0') == mySeq, orElse: () => {});
|
||||
final myReadyYn = (me['ready_yn'] ?? 'N').toString().toUpperCase();
|
||||
final isReady = (myReadyYn == 'Y');
|
||||
final newYn = isReady ? 'N' : 'Y';
|
||||
|
||||
final userRef = _roomRef.child('userInfo').child(mySeq);
|
||||
final userRef = _roomRef.child('userInfo').child('_${mySeq}');
|
||||
await userRef.update({"ready_yn": newYn});
|
||||
} catch (e) {
|
||||
print('READY 설정 실패: $e');
|
||||
showResponseDialog(context, '오류', 'READY 설정 실패했습니다.');
|
||||
}
|
||||
|
||||
// (C) 3초 후 다시 버튼 활성화
|
||||
Future.delayed(const Duration(seconds: 3), () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_readyButtonEnabled = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _onOpenRoomSetting() async {
|
||||
@ -617,7 +649,7 @@ class _WaitingRoomPrivatePageState extends State<WaitingRoomPrivatePage> {
|
||||
|
||||
Widget _buildPlayerSection() {
|
||||
final playerList = _userList.where((u) {
|
||||
final t = (u['user_seq'] ?? null);
|
||||
final t = (u['user_seq'].toString() ?? null);
|
||||
return t != null;
|
||||
}).toList();
|
||||
// final playerList = _userList;
|
||||
|
@ -66,6 +66,9 @@ class _WaitingRoomTeamPageState extends State<WaitingRoomTeamPage> {
|
||||
|
||||
String mySeq = '0';
|
||||
|
||||
// 준비 버튼 3초 딜레이
|
||||
bool _readyButtonEnabled = true; // true: 클릭 가능, false: 클릭 불가
|
||||
|
||||
// (★) 1시간 카운트다운
|
||||
Timer? _countdownTimer;
|
||||
Duration _remaining = const Duration(hours: 1);
|
||||
@ -79,7 +82,7 @@ class _WaitingRoomTeamPageState extends State<WaitingRoomTeamPage> {
|
||||
String adUnitId = Config.testAdUnitId;
|
||||
|
||||
// 방장 SEQ 저장
|
||||
String _masterSeqString = '';
|
||||
String _masterSeqString = '0';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -99,7 +102,7 @@ class _WaitingRoomTeamPageState extends State<WaitingRoomTeamPage> {
|
||||
_roomRef = FirebaseDatabase.instance.ref('rooms/$roomKey');
|
||||
|
||||
// onDisconnect + connect_yn='Y'
|
||||
final myUserRef = _roomRef.child('userInfo').child(mySeq);
|
||||
final myUserRef = _roomRef.child('userInfo').child('_${mySeq}');
|
||||
if (_roomRef.child('userList').child(mySeq) == true) {
|
||||
myUserRef.onDisconnect().update({'connect_yn': 'N'});
|
||||
}
|
||||
@ -154,8 +157,7 @@ class _WaitingRoomTeamPageState extends State<WaitingRoomTeamPage> {
|
||||
|
||||
final data = snapshot.value as Map<dynamic, dynamic>? ?? {};
|
||||
final roomInfoData = data['roomInfo'] as Map<dynamic, dynamic>? ?? {};
|
||||
// final userInfoData = data['userInfo'] as Map<dynamic, dynamic>? ?? {};
|
||||
final userInfoDynamic = data['userInfo']; // 이건 List일 수 있음
|
||||
final userInfoDynamic = data['userInfo'] as Map<dynamic, dynamic>? ?? {};
|
||||
|
||||
final roomStatus = (roomInfoData['room_status'] ?? 'WAIT').toString().toUpperCase();
|
||||
|
||||
@ -163,7 +165,7 @@ class _WaitingRoomTeamPageState extends State<WaitingRoomTeamPage> {
|
||||
// userList
|
||||
if (userInfoDynamic is Map) {
|
||||
userInfoDynamic.forEach((key, val) {
|
||||
final tempUserSeq = val['user_seq'] ?? '0';
|
||||
final tempUserSeq = val['user_seq'].toString() ?? '0';
|
||||
if (tempUserSeq != '0') {
|
||||
if (val is Map) {
|
||||
tempList.add({
|
||||
@ -202,15 +204,15 @@ class _WaitingRoomTeamPageState extends State<WaitingRoomTeamPage> {
|
||||
|
||||
// 방장
|
||||
roomMasterYn = 'N';
|
||||
final masterSeq = roomInfoData['master_user_seq'];
|
||||
if (masterSeq != null && masterSeq.toString() == mySeq) {
|
||||
final masterSeq = roomInfoData['master_user_seq'].toString();
|
||||
if (masterSeq != null && masterSeq == mySeq) {
|
||||
roomMasterYn = 'Y';
|
||||
}
|
||||
|
||||
if (masterSeq != null) {
|
||||
_masterSeqString = masterSeq.toString();
|
||||
_masterSeqString = masterSeq;
|
||||
} else {
|
||||
_masterSeqString = '';
|
||||
_masterSeqString = '0';
|
||||
}
|
||||
|
||||
_userList = tempList;
|
||||
@ -248,7 +250,7 @@ class _WaitingRoomTeamPageState extends State<WaitingRoomTeamPage> {
|
||||
}
|
||||
|
||||
// (D) 내가 목록에서 사라졌는지 => 강퇴 판별
|
||||
final amIStillHere = _userList.any((u) => u['user_seq'].toString() == mySeq);
|
||||
final amIStillHere = _userList.any((u) => (u['user_seq'].toString() ?? '0') == mySeq);
|
||||
if (!amIStillHere && !_kickedOut && roomMasterYn != 'Y') {
|
||||
_kickedOut = true;
|
||||
if (_roomExitYn == 'N') {
|
||||
@ -373,7 +375,7 @@ class _WaitingRoomTeamPageState extends State<WaitingRoomTeamPage> {
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
final myUserRef = _roomRef.child('userInfo').child(mySeq);
|
||||
final myUserRef = _roomRef.child('userInfo').child('_${mySeq}');
|
||||
myUserRef.onDisconnect().cancel();
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
@ -442,7 +444,7 @@ class _WaitingRoomTeamPageState extends State<WaitingRoomTeamPage> {
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: ElevatedButton(
|
||||
style: btnStyle,
|
||||
onPressed: _onToggleReady,
|
||||
onPressed: _readyButtonEnabled ? _onToggleReady : null,
|
||||
child: Text(readyLabel),
|
||||
),
|
||||
),
|
||||
@ -479,7 +481,7 @@ class _WaitingRoomTeamPageState extends State<WaitingRoomTeamPage> {
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: ElevatedButton(
|
||||
style: btnStyle,
|
||||
onPressed: _onToggleReady,
|
||||
onPressed: _readyButtonEnabled ? _onToggleReady : null,
|
||||
child: Text(readyLabel),
|
||||
),
|
||||
),
|
||||
@ -490,17 +492,44 @@ class _WaitingRoomTeamPageState extends State<WaitingRoomTeamPage> {
|
||||
}
|
||||
|
||||
Future<void> _onToggleReady() async {
|
||||
// (A) 버튼이 비활성화 상태면 리턴
|
||||
if (!_readyButtonEnabled) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('잠시 후에 다시 눌러주세요. (3초 대기)'),
|
||||
duration: Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// (B) 버튼 비활성화
|
||||
setState(() {
|
||||
_readyButtonEnabled = false;
|
||||
});
|
||||
|
||||
try {
|
||||
final me = _userList.firstWhere((u) => (u['user_seq'].toString() ?? '') == mySeq, orElse: () => {});
|
||||
final myReadyYn = (me['ready_yn'] ?? 'N').toString().toUpperCase();
|
||||
final isReady = (myReadyYn == 'Y');
|
||||
final newYn = isReady ? 'N' : 'Y';
|
||||
|
||||
final userRef = _roomRef.child('userInfo').child(mySeq);
|
||||
final userRef = _roomRef.child('userInfo').child('_${mySeq}');
|
||||
await userRef.update({"ready_yn": newYn});
|
||||
} catch (e) {
|
||||
showResponseDialog(context, '오류', 'READY 설정에 실패했습니다.');
|
||||
}
|
||||
|
||||
// (C) 3초 후 다시 버튼 활성화
|
||||
Future.delayed(const Duration(seconds: 3), () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_readyButtonEnabled = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _onOpenRoomSetting() async {
|
||||
@ -643,7 +672,7 @@ class _WaitingRoomTeamPageState extends State<WaitingRoomTeamPage> {
|
||||
|
||||
Widget _buildTeamSection() {
|
||||
final players = _userList.where((u) {
|
||||
final t = (u['user_seq'] ?? null);
|
||||
final t = (u['user_seq'].toString() ?? null);
|
||||
return t != null;
|
||||
}).toList();
|
||||
// final players = _userList.toList();
|
||||
|
128
pubspec.lock
@ -9,6 +9,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.48"
|
||||
ansicolor:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ansicolor
|
||||
sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: "6199c74e3db4fbfbd04f66d739e72fe11c8a8957d5f219f1f4482dbde6420b5a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -33,6 +57,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: checked_yaml
|
||||
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
cli_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_util
|
||||
sha256: "66f86e916d285c1a93d3b79587d94bd71984a66aac4ff74e524cfa7877f1395c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.5"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -65,6 +105,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csslib
|
||||
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -206,6 +254,14 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_launcher_icons
|
||||
sha256: "02dcaf49d405f652b7160e882bacfc02cb497041bb2eab2a49b1c393cf9aac12"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@ -214,6 +270,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
flutter_native_splash:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_native_splash
|
||||
sha256: "7062602e0dbd29141fb8eb19220b5871ca650be5197ab9c1f193a28b17537bc7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.4"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -288,6 +352,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.2+1"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: html
|
||||
sha256: "1fc58edeaec4307368c60d59b7e15b9d658b57d7f3125098b6294153c75337ec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.5"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -304,6 +376,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.1"
|
||||
image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
sha256: "8346ad4b5173924b5ddddab782fc7d8a6300178c8b1dc427775405a01701c4a6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.2"
|
||||
image_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -376,6 +456,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.9.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -472,6 +560,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.2"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -488,6 +584,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
posix:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: posix
|
||||
sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.1"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -605,6 +709,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
universal_io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: universal_io
|
||||
sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -669,6 +781,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.5.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.6.0 <4.0.0"
|
||||
flutter: ">=3.24.0"
|
||||
|
16
pubspec.yaml
@ -24,11 +24,15 @@ dependencies:
|
||||
google_sign_in: ^5.4.0
|
||||
cupertino_icons: ^1.0.8
|
||||
fluttertoast: ^8.0.9
|
||||
flutter_launcher_icons: ^0.12.0
|
||||
flutter_native_splash: ^2.2.15
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
flutter_launcher_icons: ^0.12.0
|
||||
flutter_native_splash: ^2.2.15
|
||||
|
||||
# ───────────────────────────────────
|
||||
# 플러터 섹션
|
||||
@ -45,3 +49,15 @@ flutter:
|
||||
- assets/images/icons8-google-logo-96.png
|
||||
- assets/images/icons8-google-logo-144.png
|
||||
- assets/images/icons8-google-logo-192.png
|
||||
|
||||
flutter_icons:
|
||||
android: true
|
||||
ios: true
|
||||
|
||||
image_path: "assets/icons/allscore_icon.png"
|
||||
|
||||
flutter_native_splash:
|
||||
color: "#ffffff"
|
||||
image: assets/icons/allscore_icon.png
|
||||
android: true
|
||||
ios: true
|
||||
|
@ -1,6 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!DOCTYPE html><html><head>
|
||||
<!--
|
||||
If you are serving your web app in a path other than the root, change the
|
||||
href value below to reflect the base path you are serving from.
|
||||
@ -27,12 +25,84 @@
|
||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
<link rel="icon" type="image/png" href="favicon.png">
|
||||
|
||||
<title>allscore_app</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<style id="splash-screen-style">
|
||||
html {
|
||||
height: 100%
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100%;
|
||||
background-color: #ffffff;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.center {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.contain {
|
||||
display:block;
|
||||
width:100%; height:100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.stretch {
|
||||
display:block;
|
||||
width:100%; height:100%;
|
||||
}
|
||||
|
||||
.cover {
|
||||
display:block;
|
||||
width:100%; height:100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
-ms-transform: translate(-50%, 0);
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
.bottomLeft {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.bottomRight {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
||||
<script id="splash-screen-script">
|
||||
function removeSplashFromWeb() {
|
||||
document.getElementById("splash")?.remove();
|
||||
document.getElementById("splash-branding")?.remove();
|
||||
document.body.style.background = "transparent";
|
||||
}
|
||||
</script>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
</head>
|
||||
<body>
|
||||
<script src="flutter_bootstrap.js" async></script>
|
||||
</body>
|
||||
</html>
|
||||
<picture id="splash">
|
||||
<source srcset="splash/img/light-1x.png 1x, splash/img/light-2x.png 2x, splash/img/light-3x.png 3x, splash/img/light-4x.png 4x" media="(prefers-color-scheme: light)">
|
||||
<source srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x" media="(prefers-color-scheme: dark)">
|
||||
<img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt="">
|
||||
</picture>
|
||||
<script src="flutter_bootstrap.js" async=""></script>
|
||||
|
||||
|
||||
</body></html>
|
BIN
web/splash/img/dark-1x.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
web/splash/img/dark-2x.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
web/splash/img/dark-3x.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
web/splash/img/dark-4x.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
web/splash/img/light-1x.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
web/splash/img/light-2x.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
web/splash/img/light-3x.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
web/splash/img/light-4x.png
Normal file
After Width: | Height: | Size: 18 KiB |