1차 배포 준비

This commit is contained in:
eld_master 2025-01-18 18:28:24 +09:00
parent a8445b70ed
commit 858169fa8d
71 changed files with 946 additions and 173 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

View File

@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

View File

@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View 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>

View File

@ -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

View 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>

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -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++";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View 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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

View File

@ -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
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -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>

View File

@ -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>

View File

@ -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';
}

View File

@ -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),

View File

@ -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
View 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);
}
}
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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'] ?? '강제 종료 여부 확인 실패';

View File

@ -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') {
//

View File

@ -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'] ?? '유저');

View File

@ -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;

View File

@ -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();

View File

@ -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"

View File

@ -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

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
web/splash/img/dark-2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
web/splash/img/dark-3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
web/splash/img/dark-4x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
web/splash/img/light-1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
web/splash/img/light-2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
web/splash/img/light-3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
web/splash/img/light-4x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB