
class _QuizState extends State<Quiz> {
List<String> selectedAnswers = [];
var activeScreen = 'start-screen';
void switchScreen() {
setState(() {
activeScreen = 'questions-screen';
});
}
void chooseAnswer(String answer) {
selectedAnswers.add(answer);
if (selectedAnswers.length == questions.length) {
setState(() {
activeScreen = 'results-screen';
});
}
}
void restartQuiz() {
setState(() {
selectedAnswers = [];
activeScreen = 'questions-screen';
});
}
activeScreen에 따른 다른 Ui를 보여주는 코드.
// Quiz.dart(상위 위젯)
Widget build(context) {
Widget screenWidget = StartQuiz(switchScreen);
if (activeScreen == 'questions-screen') {
screenWidget = QuestionScreen(
onSelectAnswer: chooseAnswer,
);
}
if (activeScreen == 'results-screen') {
screenWidget = ResultsScreen(
chosenAnswers: selectedAnswers,
onRestart: restartQuiz,
);
}
상위 위젯인 Quiz.dart는
State를 StartQuiz에 switchScreen 인자를 통해 전달하고,
if 연산자를 활용한 조건문 활용을 통해 QuestionScreen에 사용자의 답변 선택내용(state)을 전달한다.
class StartQuiz extends StatelessWidget {
const StartQuiz(this.startQuiz, {super.key});
final void Function() startQuiz; // 인스턴스 화
조건에 따라 렌더링할 버튼은 StartQuiz에 존재
따라서 class에서 조건에 따른 변경을 가능하게 하는 메서드를 내려받아서
버튼에 해당 함수를 달아주어야 함.
return 값의 type을 인수와 맞추어야 하는데, return 이 없기에 void
StartQuiz라는 함수에 pointer를 얻은 개념 -> Props 개념과 비슷
생성자 함수가 props에 명시된 값을 this.로 받아서 생성 -> 인스턴스 화
class QuestionScreen extends StatefulWidget {
const QuestionScreen({
super.key,
required this.onSelectAnswer,
});
final void Function(String answer) onSelectAnswer;
startQuiz와 마찬가지, 다만,
onSelectAnwer 옵션으로 유저가 선택한 답변을 전달하기때문에,
생성자 함수 생성시 인수로 가져와야함
required 자료형을 통해 Null Saftey 보장
class _QuizState extends State<Quiz> {
Widget? activeScreen;
void initState() {
activeScreen = StartScreen(swichScreen);
super.initState();
}
이렇게 State가 제공하는 iniTState함수를 override.
따라서 State클래스를 상속받는 시점에 선언 -> pointing 시 선언 이후 인스턴스화
Optional operator -> null일 수 있다는 것을 의미
initState가 QuizState로 부터 호출시 실행, 컴파일시에는 null
initState() : flutter 에 의해 실행, StatefulWidget의 2번째 class의 State객체가 생성될때 실행
build(): 위젯이 처음 build 함수가 실행될때, setState에 의해 실행
dispose(): 위젯이 삭제되기 전에 실행-> ex. 조건부 렌더링

Widget build(context) {
Widget screenWidget = StartQuiz(switchScreen);
if (activeScreen == 'questions-screen') {
screenWidget = QuestionScreen(
onSelectAnswer: chooseAnswer,
);
}
Quiz에서 QuestionScreen으로 onSelectAnswer 에 chooseAnswer()를 달아서 전달
class QuestionScreen extends StatefulWidget {
const QuestionScreen({
super.key,
required this.onSelectAnswer,
});
final void Function(String answer) onSelectAnswer;
State<QuestionScreen> createState() {
return _QuestionScreenState();
}
}
class _QuestionScreenState extends State<QuestionScreen> {
var currentQuestionIndex = 0;
void answerQuestion(String selectedAnswer) {
widget.onSelectAnswer(selectedAnswer);
setState(() {
currentQuestionIndex++;
});
}
전달받은 chooseAnswer를 담은 onSelectAnswer 값은
answerQuestion() 함수 안에서 실행.
//result_screen.dart
for (var i = 0; i < chosenAnswers.length; i++) {
summary.add(
{
'question_index': i,
'question': questions[i].text,
'correct_answer': questions[i].answers[0],
'user_answer': chosenAnswers[i]
},
);
}
💡
Iterable<T>라는 인터페이스 타입 인스턴스를 반환하기때문에, 실제 사용시에는.toList()함수로 리스트 타입 형태로 바꾸어 주어야 위젯 내에서 사용가능
final List<Map<String, Object>> summary = [];
for (var i = 0; i < chosenAnswers.length; i++) {
summary.add(
{
'question_index': i,
'question': questions[i].text,
'correct_answer': questions[i].answers[0],
'user_answer': chosenAnswers[i]
},
);
}
return summary;
다만 강의에선, summary라는 배열을 만들고, 여기에 add 메서드를 써서 for문의 내용을 집어넣어서 리스트로 만들어버리는 방식을 활용.
void main() {
// list 생성자 사용
var vegetables = List.unmodifiable([]);
// 문자열을 사용하여 list 생성
var fruits = ['apples', 'oranges'];
// list에 값 추가하기
fruits.add('bananas');
print(fruits); // [apples, oranges, banana]
final numCorrectQuestions = summaryData.where((data) {
return data['user_answer'] == data['correct_answer'];
}).length;
as 키워드 사용
위에서 말한 에러는 이렇게 화면이 시뻘겋게 보이는 게 아니라 리스트의 내용은 보이는데,
이건 아예 null 값이 전달되어 시뻘겋게 지금 너가 뭔가 잘못되었다고 단박에 알아채도록 화면을 꽉 채우는 에러가 나왔다.

이유는 데이터를 question_index로 넘겼는데
result_screen file 에서는 question-index로 되어있어서, 결과값이 없는데 결과값을 출력하라고 하니... 당연히 에러가 발생할 수 밖에 없었다.
Expanded문제인지 아니면 SingleChildScrollView의 문제인지 먼저 찾아봤는데
그쪽 문제가 아니고 오타...문제였다니.. 너무나 허무했다.