import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; // /* 숫자만 입력 위해 */ import 'package:http/http.dart' as http; import 'dart:convert'; // Server API (서버 API) import '../plugins/api.dart'; // Main Page (메인 페이지) import '../views/room/main_page.dart'; // Alert modal dialog (알람 모달창) import '../dialogs/response_dialog.dart'; class SurveyPage extends StatefulWidget { final String nickname; const SurveyPage({ Key? key, required this.nickname, }) : super(key: key); @override State createState() => _SurveyPageState(); } class _SurveyPageState extends State { int _currentIndex = 0; /* 현재 페이지 인덱스 (0~5) */ List _questions = []; /* 전체 질문 목록 (이제 6개) */ List _questionsOriginal = []; /* 질문의 한글 원본 목록 (6개) */ // 변경 포인트: 5 -> 6개 final List _answers = List.filled(6, null, growable: false); /* 사용자가 입력한 답변(6개) */ final Map _selectedRadioValue = {}; /* 각 페이지별 라디오 선택 값 */ final Map _textControllers = {}; /* 각 페이지별 텍스트필드 컨트롤러 */ @override void initState() { super.initState(); _questions = [ "Q1. Which country do you live in, ${widget.nickname}?", "Q2. How old are you, ${widget.nickname}?", "Q3. What is your occupation, ${widget.nickname}?", "Q4. How did you hear about ALLSCORE, ${widget.nickname}?", "Q5. Where have you experienced ALLSCORE, ${widget.nickname}?", "Q6. Do you plan to keep using ALLSCORE?", ]; // 한글 원본 질문도 6개 _questionsOriginal = [ "당신이 거주하는 국가는 어디인가요?", "나이는 어떻게 되나요?", "직업이 무엇인가요?", "올스코어 앱을 어떻게 알게 됐나요?", "올스코어 앱을 어디서 경험하셨나요?", "올스코어를 계속 사용할 의사가 있나요?", ]; // 페이지마다 TextEditingController 초기화 for (int i = 0; i < _questions.length; i++) { _textControllers[i] = TextEditingController(); } } @override void dispose() { // Dispose text controllers (텍스트 컨트롤러 정리) for (var ctrl in _textControllers.values) { ctrl.dispose(); } super.dispose(); } /// Exit the survey (설문 그만하기) Future _onExitSurvey() async { Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (_) => const MainPage()), (route) => false, ); } /// Next button (다음 버튼) void _onNext() { if (!_validateCurrentPage()) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please fill in all required fields.')), ); return; } if (_currentIndex < _questions.length - 1) { setState(() { _currentIndex++; }); } } /// Previous button (이전 버튼) void _onPrev() { if (_currentIndex > 0) { setState(() { _currentIndex--; }); } } /// Submit (제출하기) Future _onSubmit() async { if (!_validateCurrentPage()) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please fill in all required fields.')), ); return; } // body: {"QNA": ["질문1","답변1","질문2","답변2", ... ]} final List qnaList = []; for (int i = 0; i < _questionsOriginal.length; i++) { qnaList.add(_questionsOriginal[i]); // 한글 질문 qnaList.add(_answers[i] ?? ''); // 사용자 답변 } final requestBody = { "QNA": qnaList, }; try { final response = await Api.serverRequest(uri: '/survey/collect', body: requestBody); if (response['result'] == 'OK') { final resp = response['response'] ?? {}; if (resp['result'] == 'OK') { // Survey submitted ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Your survey has been submitted. Thank you!')), ); Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (_) => const MainPage()), (route) => false, ); } else { showResponseDialog(context, 'Error', 'Failed to submit the survey.'); } } else { showResponseDialog(context, 'Error', 'Failed to submit the survey.'); } } catch (e) { showResponseDialog(context, 'Error', 'Failed to submit the survey.'); } } /// Validate current page input (현재 페이지 입력값이 유효한지 체크) bool _validateCurrentPage() { final index = _currentIndex; String? answer; switch (index) { case 0: // [새 질문] 거주 국가 final txtCountry = _textControllers[index]?.text.trim() ?? ''; if (txtCountry.isEmpty) { return false; } answer = txtCountry; break; case 1: // Age (나이) final txtAge = _textControllers[index]?.text.trim() ?? ''; if (txtAge.isEmpty) { return false; } answer = '$txtAge yrs old'; break; case 2: // Occupation (직업) final selected = _selectedRadioValue[index]; if (selected == null || selected.isEmpty) { return false; } answer = selected; break; case 3: // How did you hear about ALLSCORE? (앱 알게된 경로) final selected2 = _selectedRadioValue[index]; if (selected2 == null || selected2.isEmpty) { return false; } if (selected2 == 'Others') { final etc = _textControllers[index]?.text.trim() ?? ''; if (etc.isEmpty) { return false; } answer = "Others($etc)"; } else { answer = selected2; } break; case 4: // Where have you experienced ALLSCORE? (어디서 경험?) final sel3 = _selectedRadioValue[index]; if (sel3 == null || sel3.isEmpty) { return false; } answer = sel3; break; case 5: // Will you continue using ALLSCORE? (계속 사용할 의사?) final sel4 = _selectedRadioValue[index]; if (sel4 == null || sel4.isEmpty) { return false; } final comment = _textControllers[index]?.text.trim() ?? ''; answer = sel4 + (comment.isNotEmpty ? " / comment: $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( 'Stop Survey', style: TextStyle(color: Colors.white), ), ), title: Text('Survey ($pageNumber/$totalPage)'), backgroundColor: Colors.black, ), body: SingleChildScrollView( child: Container( alignment: Alignment.center, padding: const EdgeInsets.all(16), child: Column( 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('Previous'), ), ), 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) ? 'Next' : 'Submit', style: const TextStyle(color: Colors.white), ), ), ), ], ), ), ), ); } /// Build UI for each page (페이지별 UI) Widget _buildSurveyPage(int index) { switch (index) { case 0: // [새 질문] 거주 국가 return Column( children: [ const Text( '(Please enter the country you live in.)', textAlign: TextAlign.center, ), const SizedBox(height: 16), TextField( controller: _textControllers[index], textAlign: TextAlign.center, decoration: const InputDecoration( labelText: 'Your country', border: OutlineInputBorder(), ), ), ], ); case 1: // [기존] 나이 입력 return Column( children: [ const Text( '(e.g. Please enter your age in digits.)', textAlign: TextAlign.center, ), const SizedBox(height: 16), TextField( controller: _textControllers[index], keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], textAlign: TextAlign.center, decoration: const InputDecoration( labelText: 'Age', border: OutlineInputBorder(), ), ), ], ); case 2: // [기존] 직업 final jobs = [ 'Student', 'Office Worker', 'Professional', 'Professor/Teacher', 'Technical', 'Government Official', 'Art/Sports', 'Others', ]; return Column( mainAxisSize: MainAxisSize.min, children: jobs.map((job) { return RadioListTile( title: Text(job, textAlign: TextAlign.center), value: job, groupValue: _selectedRadioValue[index], onChanged: (val) { setState(() { _selectedRadioValue[index] = val!; }); }, ); }).toList(), ); case 3: // [기존] 앱 알게된 경로 final paths = [ 'Friend/Acquaintance', 'Social Media', 'Blog/Online Review', 'School/Workplace', 'Others', ]; return Column( mainAxisSize: MainAxisSize.min, children: [ ...paths.map((p) { return RadioListTile( title: Text(p, textAlign: TextAlign.center), value: p, groupValue: _selectedRadioValue[index], onChanged: (val) { setState(() { _selectedRadioValue[index] = val!; }); }, ); }).toList(), if (_selectedRadioValue[index] == 'Others') Padding( padding: const EdgeInsets.only(top: 8.0), child: TextField( controller: _textControllers[index], textAlign: TextAlign.center, decoration: const InputDecoration( labelText: 'Please specify.', border: OutlineInputBorder(), ), ), ), ], ); case 4: // [기존] 어디서 경험? final places = [ 'With Family', 'With Friends', 'School (for education)', 'Work Club', 'Cafe or Public Space', 'Travel', 'Others', ]; return Column( mainAxisSize: MainAxisSize.min, children: places.map((pl) { return RadioListTile( title: Text(pl, textAlign: TextAlign.center), value: pl, groupValue: _selectedRadioValue[index], onChanged: (val) { setState(() { _selectedRadioValue[index] = val!; }); }, ); }).toList(), ); case 5: // [기존] 계속 사용할 의사? return Column( mainAxisSize: MainAxisSize.min, children: [ RadioListTile( title: const Text('Yes', textAlign: TextAlign.center), value: 'Yes', groupValue: _selectedRadioValue[index], onChanged: (val) { setState(() { _selectedRadioValue[index] = val!; }); }, ), RadioListTile( title: const Text('No', textAlign: TextAlign.center), value: 'No', groupValue: _selectedRadioValue[index], onChanged: (val) { setState(() { _selectedRadioValue[index] = val!; }); }, ), const SizedBox(height: 16), const Text( 'If you have any additional comments, feel free to write them here.', textAlign: TextAlign.center, ), const SizedBox(height: 8), TextField( controller: _textControllers[index], maxLines: 3, textAlign: TextAlign.center, decoration: const InputDecoration( hintText: 'e.g. inconveniences, improvement ideas, etc.', border: OutlineInputBorder(), ), ), ], ); default: return const Text('Survey question error', textAlign: TextAlign.center); } } }