아직 6편 화면전환 마무리를 못 봤다면? 보러가기 👀
반갑습니다 여러분! 코딩백조입니다🦢
지난 시간으로 화면 전환을 모두 구현했어요!
비록 마지막 화면 이후로는 에러가 나지만, 그건 큰 문제가 아니에요.
오늘이 마지막 시간이죠! 오늘은 지난 시간의 에러 해결과 함께 결과 화면을 보여주도록 할게요. 😉
📍 [ 7 / 7 ] RangeError 해결, 점수 저장하기, 결과 화면 이동하기
이번에는 내용이 많아보이죠? 그래도 다 연결된 내용이라 한 번에 진도를 나가도록 할게요!
드디어 마지막이에요.. 조금만 더 화이팅! 👏
자, 이 3개가 어떻게 연결이 되어있는지 한번 볼까요?
마지막 문제에서 대답 버튼을 누르면 "결과 화면으로 이동" 하며 "에러가 해결이" 될거에요. 그리고 "결과 화면" 안에는 "점수에 따른 멘트" 가 있겠죠?
그니까 결국 대답을 누를 때마다 점수를 저장한 후, 마지막 버튼에서는 에러가 나지 않게 결과화면으로 이동을 하고, 결과 화면은 점수를 바탕으로 화면이 그려질거에요.
그러면 이 순서대로 작업을 해볼게요! 😎
- 점수를 저장하고
- 마지막 문제 이후로는 결과 화면으로 이동한 후
- 점수를 바탕으로 화면을 그리기
바로 시작합니다! 💨
점수를 저장하는 방법, 생각보다 어렵지 않아요. 😝
questionIndex 와 거의 동일하게 동작합니다!
처음에 0 으로 시작하는 변수를 하나 만들고,
버튼을 누를 때마다 그 대답에 해당하는 점수를 더해주면 될거에요.
그러면 먼저 변수를 만들어줄게요!
main.dart 로 넘어가서 questionIndex 아래에 써줍시다.
이름은.. 흠 대충 totalScore 정도로 할까요?
제 main.dart 에서는 19번째 줄에다가 적어주었습니다.
자, 이제 점수를 더하는 로직을 추가해볼까요?
이 버튼은 몇점 짜리인지 어떻게 알 수 있을까요?
우리가 써놓은 곳에서 찾으면 되겠죠!
{'text': '말은 부끄러워서 못 걸거 같다. 눈에만 담기', 'score': 2},
questionList 를 보면 text: 옆에 score: 이라고 해서 점수를 적는 부분이 있었어요.
거기서 점수를 가져오면 될거에요! 😌
흠.. 🤔 근데 어떻게 불러와야 할까요?
questionList 에 접근 할 수 있어야 될텐데요, 그쵸?
오! 생각해보니 우리는 quiz_screen.dart 에서 questionList 를 불러오기 해서 쓰고 있었어요. 그쵸?
거기서 text 값을 가져왔었잖아요!
그거랑 똑같이 score 값을 가져오면 될거 같아요.
ElevatedButton(
onPressed: () => answerPressed(),
child: Text(questionList[questionIndex]["answers"][0]["text"]),
),
근데.. 어느 자리에다가 점수를 넣어줘야 하죠?
매개변수를 사용해보는건 어떨까요? 😲
answerPressed( 4 )
이런 식으로 괄호 안에다가 점수를 넣어주는 식으로 한다면 편할 거 같아요!
answerPressed 안에다가 text 처럼 score 을 넣어준다면 우리가 원하는 대로 동작할 거에요.
첫 번째 ElevatedButton 안을 이렇게 바꿔보세요.
() => answerPressed(questionList[questionIndex]["answers"][0]["score"]),
이게 제대로 동작한다면 main.dart 에서 저 값을 totalScore 에다 저장해줄거에요! 😚
이 값을 받으려면 answerPressed() 함수에서 매개변수를 정의해야겠죠?
main.dart 로 넘어갑시다!
void answerPressed() {
setState(() {
questionIndex++;
});
print(questionIndex);
}
이번에는 positional parameter 을 만들거에요.
근데 큰 차이 없어요! named 는 {} 를 필요로 하지만 positional 은 그냥 괄호 안에 넣으면 돼요. void answerPressed(int score)
이렇게요! 😌
그 다음, totalScore 변수에 그 값을 더해주면 되겠죠?
이 점수는 현재 화면에 영향을 미치지 않으니까 setState 내부에 넣지는 않을게요.
void answerPressed(int score) {
setState(() {
questionIndex++;
});
totalScore += score;
// or
totalScore = totalScore + score;
print(totalScore);
}
편하신 대로 둘 중 하나를 적어주세요!
print 문 안에 있던 questionIndex 를 totalScore 로 바꿨어요. 이제 index 가 잘 동작하기 때문이죠! 😎
자, 그러면 Run 하고 첫 번째 버튼을 누르면 totalScore 이 변하는지 봅시다!
오오! 첫 번째 버튼을 누르면 점수들이 더해지면서 콘솔에 숫자들이 보여요.
마지막 화면에서 에러 나기 전에 숫자가 8 10 이렇게 커지는게 보이시나요?
잘 되고 있다는 뜻이네요!
quiz_screen.dart 로 넘어가서 나머지 함수에도 매개변수를 넣어줍시다!
ElevatedButton(
onPressed: () => answerPressed(
questionList[questionIndex]["answers"][0]["score"]),
child: Text(questionList[questionIndex]["answers"][0]["text"]),
),
ElevatedButton(
onPressed: () => answerPressed(
questionList[questionIndex]["answers"][1]["score"]),
child: Text(questionList[questionIndex]["answers"][1]["text"]),
),
ElevatedButton(
onPressed: () => answerPressed(
questionList[questionIndex]["answers"][2]["score"]),
child: Text(questionList[questionIndex]["answers"][2]["text"]),
),
ElevatedButton(
onPressed: () => answerPressed(
questionList[questionIndex]["answers"][3]["score"]),
child: Text(questionList[questionIndex]["answers"][3]["text"]),
),
순서대로 0번째 1번째 2번째 3번째 인것 보이시죠?
중간 숫자도 꼭 맞춰주셔야 합니다!!
이렇게 하고 다시 Run 해주면 어떤 버튼을 눌러도 totalScore 값이 더해지면서 출력 되는 걸 볼 수 있어요 🥳
1번 완료입니다!!!
1. 점수를 저장하고
2. 마지막 문제 이후로는 결과 화면으로 이동한 후
3. 점수를 바탕으로 화면을 그리기
자, 2번은 엄청 간단해요.
마지막 질문/대답이 뭔지를 알아내기만 하면 되거든요!
0번째 1번째 2번째까지는 괜찮은데 questionIndex 가 3이 되면 안되는거잖아요.
그러면 3 보다 작으면 되는거겠네요. 내 질문의 개수보다 작으면 돼요!
근데, 내 질문/대답의 수를 어떻게 알아낼 수 있을까요? 🧐
Dart 에는 리스트와 관련된 함수가 많이 있어요.
우리의 질문/대답 리스트에는 3개의 Map 이 저장되어 있어요.
그렇다면 questionList 에 몇 개의 항목이 있는 지 알려주는 함수가 있으면 좋을 것 같아요! 그쵸?
바로 그게 .length 랍니다!!
The number of objects in this list.
라고 설명되는 이 함수는 리스트 안에 있는 항목의 개수를 알려줍니다 🤩
만약 questionList.length
를 입력하면 어떤 숫자가 나올까요?
바로 3 입니다!
이 함수를 사용하면 될거같아요. 그쵸?
0번째 1번째 2번째 까지는 괜찮지만 3번째 부터는 안되는 조건!
조건문을 작성할게요.
조건문이라고 하면 if else 를 생각하기 쉬운데,
더 간단하고 쉬운 연산자가 있답니다!!
제가 정말 좋아하는 연산자에요 😍
(괄호) 안에 참/거짓의 조건을 쓰고,
? 뒤에 "참 인 경우" 에 해당하는 결과를,
: 뒤에 "거짓 인 경우" 에 해당하는 결과를 써주면 됩니다.
(is_happy) ? print("happy!") : print("not happy.")
처럼 써줄 수 있어요!
이걸 사용하면 더 간편하게 조건을 걸어줄 수 있죠.
(퀴즈 중이라면) ? 퀴즈 화면 : 결과 화면
어떤가요? 이렇게 한 줄로 조건문을 걸어줄 수 있답니다!
🗣 알겠어요. 알겠는데, 그래서 이걸 어디다 쓰라고요? 어떻게 사용하는데요?
우리의 QuizScreen 이라는 위젯이 어디에 있죠?
main.dart 의 아래에 있어요.
이 부분에 넣어줄거에요.
지금 body: 안에 QuizScreen() 이 들어가있죠?
2번을 여기서 진행합니다.
1. 점수를 저장하고
2. 마지막 문제 이후로는 결과 화면으로 이동한 후
3. 점수를 바탕으로 화면을 그리기
삼항연산자를 통해
(마지막 문제인지 아닌지) ? 퀴즈 화면 : 결과 화면
을 출력해 줄 겁니다!
현재 질문/대답의 순서를 담고 있는 변수 questionIndex 와 총 질문/대답의 개수인 questionList.length 를 사용해볼까요?
어떻게 하면 좋을까요? 🤔
questionIndex 보다 questionList.length 가 작으면 아직 퀴즈 중일거에요.
그러다 questionIndex 가 questionList.length 와 같아지면 (3) 에러가 나겠죠!
정답 공개입니다! 😲
body: (questionIndex < questionList.length)
? QuizScreen(
answerPressed: answerPressed,
questionIndex: questionIndex,
)
: ResultScreen(),
이렇게 한 번 써보세요!
첫 번째 질문에서는 ( 0 < 3 ) 이니까 QuizScreen 을,
두 번째 질문에서도 ( 1 < 3 ) 이니까 QuizScreen 을,
세 번째 질문에서도 ( 2 < 3 ) 이니까 QuizScreen 을,
그러다 세 번째에서 버튼을 누르면 (3 < 3) 이 거짓이라 ResultScreen 을 보여줄거에요. 🤩
answerPressed() 함수 안에 setState(); 가 있어서 "화면을 다시 그려!" 라고 따로 말 안해줘도 될거구요.
Run 하고 우리의 예상대로 동작하는지 볼까요?
오! 비록 결과 화면이 엉성하지만 작동을 잘 하네요!! 🥳
좋아요!! 😎 거의 다 왔습니다.
1. 점수를 저장하고
2. 마지막 문제 이후로는 결과 화면으로 이동한 후
3. 점수를 바탕으로 화면을 그리기
자, 이제 우리는 totalScore 을 기준으로 결과값을 출력할거에요.
totalScore 은 main.dart 에 있고, 결과 화면은 result_screen.dart 에 써있겠죠?
ResultScreen 위젯 안에서 totalScore 를 참고하고 싶어요. 어떻게 할까요!
네, 여기서도 named parameter 을 쓸 수 있습니다. 😉
int 형 totalScore 변수를 받아볼까요?
result_screen.dart 에 가서 이렇게 바꿔줍니다.
그러면 또! main.dart 에 오류가 뜰거에요.
마찬가지로 변수를 넣어주면 됩니다. 😌
이렇게요!
저에게는 42번째 줄인 ResultScreen 에 변경이 생겼어요.
좋아요! result_screen.dart 으로 totalScore 을 넘겨주었습니다! 😎
이제 ResultScreen 을 더 꾸며볼게요.
제가 예전에
여러분도 구간을 나눠보고 해당하는 구간의 결과 메세지도 미리 써오면 좋겠죠? 😉
라고 했었는데 혹시 하셨나요? 😂
저는
6, 8, 10 : 당신은 무해하고 귀여운 토끼입니다!
12, 14 : 당신은 신난 강아지입니다!
16, 18 : 당신은 앙칼진 고양이입니다!
20, 22, 24 : 당신은 맹렬한 호랑이입니다!
이렇게 썼습니다.
이제 totalScore 을 기준으로 출력하는 Text() 의 값을 나눠줘야 해요!
저는 어떤 식으로 할거냐면요, 결과값을 받는 String 형 변수인 resultMessage 를 만들고 조건문을 이용해서 그 resultMessage 에 미리 정해진 문장을 넣어줄겁니다. 😎
여러분도 이렇게 해보세요!
위치는 여기입니다. 제 result_screen.dart 기준으로 12번째 줄 build 아래에 넣어주었어요!
totalScore, resultMessage, if else 를 사용해서 각자의 멘트를 넣어주세요.
저는 이렇게 조건을 달아주었습니다!
이제 화면 출력을 위해 Text() 위젯 안에 있는 "result page" 대신에 우리가 정의해준 String 변수인 resultMessage
를 넣어주겠습니다.
제 화면 기준 29번째 줄이 바뀌었어요!
보기 좋게 앞뒤로 SizedBox() 도 넣은 것 보이시나요? 🤓
다시 Run 해봅시다!!
여러분도 저처럼 원하는 메세지가 잘 출력되나요? 😇
몇 번 해보니 각각 다 다른 결과 메세지가 출력됩니다!!
좋아요, 맘에 들어요! 🥳
이렇게 해서 3번까지 마무리가 되었어요.
1. 점수를 저장하고
2. 마지막 문제 이후로는 결과 화면으로 이동한 후
3. 점수를 바탕으로 화면을 그리기
근데.. 뭔가 좀 허전하죠? 😞
go back 버튼도 없고 사진도 없고..
다시 시작하려면 다시 Run 을 눌러야 하는 번거로움이 있어요.
자 그럼 문제! 이 퀴즈 앱을 다시 시작하려면 어떻게 해줘야 할까요?
더 쉽게는, 뭘 초기화 해줘야 할까요?
(두구두구)
아마 맞추셨을거 같아요!
questionIndex 와 totalScore 을 모두 0으로 바꿔줘야 합니다!! 👏
그래야 다시 첫 번째 질문을 보여줄거고, 다시 0부터 점수를 더해주겠죠?
그 함수를 빨리 구현해줄게요!
main.dart 에 가서 answerPressed() 함수 아래에 resetQuiz() 라고 void 함수를 만들어봅시다!
그리고 questionIndex 와 totalScore 을 0으로 바꿔줄게요.
👀 잊지 말기!! 변수가 바뀜에 따라 화면을 다시 그리고 싶다면?
setState(); 를 넣어줘야 합니다!
자, 이 세개를 한 번에 다 하면 이렇게 될거에요.
void resetQuiz() {
setState(() {
questionIndex = 0;
});
totalScore = 0;
}
totalScore 은 화면에 영향을 미치지 않으니 역시 setState 안에 넣지 않아도 됩니다 😉
이제 이 함수를 ResultScreen 에게 넘겨줘야 해요.
어떻게요? 바로 named parameter 을 통해서요!
result_screen.dart 로 이동해서 만들어줍시다.
제 화면 기준으로 11번과 7번째 줄에 추가해줬어요!
조금씩 익숙해져 가나요?
이 함수를 go back 버튼에 넣어줄게요!
ElevatedButton(
onPressed: () => resetQuiz(),
child: Text("reset quiz"),
),
음, 근데 이름을 통일하고 싶어요. 전 reset quiz 로 버튼 이름을 바꿨습니다!
이제 main.dart 로 넘어가서 함수를 넘겨줍시다!
제 화면에서는 51번 줄이에요!
(괄호) 없이 함수 이름만 넘겨줬습니다.
이제 결과 화면에서 "리셋" 버튼을 누르면 점수와 순서가 초기화 되어서 다시 퀴즈 화면으로 이동할거에요.
한 번 다시 Run 해보죠!
결과 화면 뿐만 아니라 다시하기도 잘 작동 하네요!! 🥳
정말 수고 많으셨습니다. 👏
아직 미적으로는 부족한 부분들이 많지만, 일단 여기서 대략적인 진도는 마무리 할게요.
글씨를 키우거나 사진을 넣는 부분은 여기(쓰는 중)를 참고해주시면 감사하겠습니다 😌
이 짧은 앱 하나 만드는 데도 이렇게 오래 걸리다니.. 막막하실 수도 있어요.
그러나!
익숙해지면 이정도는 금방 만드실거에요.
매개변수를 넣는 과정, 처음엔 이해도 안되고 뭔 소린지 모르다가도
두번 세번 네번 하다보니 점점 더 익숙해지지 않나요?
몰라도 계속 따라하시면 어느 순간 이해가 됩니다.
정말이에요! 제가 그랬으니까요.
여기까지 오시느라 정말 수고 많으셨습니다. 🥰
나만의 퀴즈 앱 만들기는 이 글로 마무리 하겠습니다!
감사합니다.
main.dart
import 'package:flutter/material.dart';
import 'package:personal_quiz/question_list.dart';
import 'package:personal_quiz/screens/quiz_screen.dart';
import 'package:personal_quiz/screens/result_screen.dart';
void main() {
runApp(
const MaterialApp(home: MyApp()),
);
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int questionIndex = 0;
int totalScore = 0;
void answerPressed(int score) {
setState(() {
questionIndex++;
});
totalScore += score;
print(totalScore);
}
void resetQuiz() {
setState(() {
questionIndex = 0;
});
totalScore = 0;
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("나만의 퀴즈 만들기"),
),
body: (questionIndex < questionList.length)
? QuizScreen(
answerPressed: answerPressed,
questionIndex: questionIndex,
)
: ResultScreen(
totalScore: totalScore,
resetQuiz: resetQuiz,
),
);
}
}
quiz_screen.dart
import 'package:flutter/material.dart';
import 'package:personal_quiz/question_list.dart';
class QuizScreen extends StatelessWidget {
const QuizScreen({
Key? key,
required this.answerPressed,
required this.questionIndex,
}) : super(key: key);
final Function answerPressed;
final int questionIndex;
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
const SizedBox(height: 100),
Text(questionList[questionIndex]["questionText"]),
const SizedBox(height: 150),
ElevatedButton(
onPressed: () => answerPressed(
questionList[questionIndex]["answers"][0]["score"]),
child: Text(questionList[questionIndex]["answers"][0]["text"]),
),
ElevatedButton(
onPressed: () => answerPressed(
questionList[questionIndex]["answers"][1]["score"]),
child: Text(questionList[questionIndex]["answers"][1]["text"]),
),
ElevatedButton(
onPressed: () => answerPressed(
questionList[questionIndex]["answers"][2]["score"]),
child: Text(questionList[questionIndex]["answers"][2]["text"]),
),
ElevatedButton(
onPressed: () => answerPressed(
questionList[questionIndex]["answers"][3]["score"]),
child: Text(questionList[questionIndex]["answers"][3]["text"]),
),
],
),
);
}
}
result_screen.dart
import 'package:flutter/material.dart';
class ResultScreen extends StatelessWidget {
const ResultScreen({
Key? key,
required this.totalScore,
required this.resetQuiz,
}) : super(key: key);
final int totalScore;
final Function resetQuiz;
Widget build(BuildContext context) {
String resultMessage;
if (totalScore <= 10) {
resultMessage = '당신은 무해하고 귀여운 토끼입니다!';
} else if (totalScore <= 14) {
resultMessage = '당신은 신난 강아지입니다!';
} else if (totalScore <= 18) {
resultMessage = '당신은 앙칼진 고양이입니다!';
} else {
resultMessage = '당신은 맹렬한 호랑이입니다!';
}
return Center(
child: Column(
children: [
SizedBox(height: 150,),
Text(resultMessage),
SizedBox(height: 100,),
ElevatedButton(
onPressed: () => resetQuiz(),
child: Text("reset quiz"),
),
],
),
);
}
}
question_list.dart
List<Map<String, dynamic>> questionList = [
{
'questionText': '평화로운 휴일!\n간만에 찾은 자유시간,\n당신은 무엇을 하시겠습니까?',
'answers': [
{'text': '나에게 휴식이란 없다. 소맥 달려~!', 'score': 8},
{'text': '운동은 못참지! 헬스장 가기', 'score': 6},
{'text': '노래 들으며 산책이나 할까?', 'score': 4},
{'text': '피곤해.. 누워서 넷플 정주행하기', 'score': 2},
],
},
{
'questionText': '카페에서 이상형을 본 당신!\n무엇을 하시겠습니까?',
'answers': [
{'text': '말은 부끄러워서 못 걸거 같다. 눈에만 담기', 'score': 2},
{'text': '가서 말을 걸어본다. 혹시 애인 있으세요?', 'score': 8},
{'text': '나 오늘 .. 상태 괜찮나? 일단 거울 보기', 'score': 4},
{'text': '그 사람 앞에 쪽지를 남기고 떠난다.', 'score': 6},
],
},
{
'questionText': '안 친한 친구에게 받은 카톡.\n너 이런 점은 고쳤으면 좋겠어.',
'answers': [
{'text': '알겠어 근데 내 일은 내가 알아서 할게..^^', 'score': 4},
{'text': '이렇게 생각할 수도 있군. 고쳐본다고 한다.', 'score': 2},
{'text': '어쩌라는거지? 참견 말라고 한 소리 한다.', 'score': 8},
{'text': '굳이 얼굴 붉히기 싫다. 읽씹한다.', 'score': 6},
],
},
];
다 읽었음!!!!