- lib
|- data|- models |- quiz_question.dart
|- questions.dart
1. Create QuizQuestion Class
먼저 Quiz object를 만들기 위한 class를 정의하도록 하자. 우리는 예를 들어 아래와 같은 질문지와 선택지를 만들고 싶다고 하자.
Q: What are the main componetns of Blance Sheet?
A: - Asset
- Revenue
- Operating Incom
- Liability
- Expeneses
위와 같은 format을 만들려면 우리는 string과 string으로 묶인 List형태가 필요한것을 알 수 있다. 이러한 형태를 class에 정의하고 싶다면, 아래와 같이 정의를 내린다. models/quiz_question.dart에 아래와 같이 정의를 내린다.
class QuizQuestion{
const QuizQuestion(this.text,this.answers);
final String text;
final List<String> answers;
}
위와 같이 QuizQuestion class를 정한 후에, 우리가 원하는 질문과 선택지를 적는 곳은 data/quiz_question.dart에 적시하도록 하자.
import 'package:adv_basics/models/quiz_question.dart';
const questions=[
QuizQuestion("What is the main componet of Balance Sheet?",
[
"Operating Income",
"Revenue",
"Expenses",
"Asset",
],
),
QuizQuestion("By which user-application can dierct its operating system to acess the allocated resources? ",
[
"By calling system calls",
"By activating the internal functions",
"By using cpu dependencies",
"By switching its context"
]
)
];
1.2 shuffle
선택지의 순서를 random으로 바꾸는 방법을 알아보도록 하자. 이전에 우리가 앞으로 사용할 ** List.of vs List.from **을 알아보도록 하자.
1.2.1 List.of vs List.from
List.of 와 List.from은 둘 다 기존의 list로 부터 새로운 list로 복사를 하는데 사용한다. 즉, 메모리상에 original list 와 newly created list 둘이 적제되어, 서로 상충되지 않는다. 겉보기에 같아보이는 이 옵션들에게 큰 차이점이 존재하는데, 이는 아래와 같다.
- List.of는 complier시에 적시된 data type만 따르도록 허용된다.즉, List이면, 그 모든 원소들이 int어야 한다.
- List.from은 데이터 변형이 상당히 유연하다. 유연성을 가진다는 것은, 많은 세심함이 필요하다.
void main() { List<int>data=[1,2,3,4]; List<double> data2=List.from(data.map((e)=>e.toDouble())); for(int i=0;i<data2.length;i++){ print(data2[i]); }
}
List<int>data1=List.of(data.map((e)=>e.toInt()));
1.2.2 Shuffling the selections
class 멤버함수로서 아래와 같은 method를 하나 더 추가가한다.
class QuizQuestion{
const QuizQuestion(this.text,this.answers);
final String text;
final List<String> answers;
List<String> getShuffledAnswers(){
//call built-in method 'shuffle'
final shuffledList=List.of(answers);
shuffledList.shuffle();
return shuffledList;
}
}
2. Create QuestionScreen Class
이번에는 우리가 실질적으로 question과 선택지를 집어넣을 수 있는 screen content를 만들어 보도록 합시다.
import 'package:flutter/material.dart';
class QuestionScreen extends StatefulWidget{
const QuestionScreen({super.key});
@override
State<QuestionScreen>createState(){
return _QuestionsScreenState();
}
}
class _QuestionsScreenState extends State<QuestionScreen>{
@override
Widget build(context){
return SizedBox(
width:double.infinity,
child:Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//child 01
Text(
"The questions are ...",
style:TextStyle(
color: Colors.white,
fontSize: 25,
)),
//child 02
const SizedBox(height:30),
//child 03
ElevatedButton(onPressed: (){}, child: Text("Answer01"),
),
const SizedBox(height:30),
//child 04
ElevatedButton(onPressed: (){}, child: Text("Answer02")),
const SizedBox(height:30),
//child05
ElevatedButton(onPressed: (){}, child: Text("Answer03"))
],)
);
}
}
위에 코드를 잠시 살펴보면 모든 ElevatedButton이 반복적으로 되어 있어, 가독성을 떨어뜨리는 것을 알 수 있다. 그러므로 우리는 이렇게
반복적인 형식을 하나의 template class로 만들어서 간소화시키는 작업을 실행하도록 하자.
- lib
|-main.dart
|-answer_button.dart
answer_button.dart 파일에 AnswerButton class는 StatelessWidget constructor에서 두 개의 argument를 받도록 지정해놓자. 이때, String argument를 받을 수 있는 text 그리고 void function을 받을 수 있는 두 번째 인자를 지정하도록 하자.
import 'package:flutter/material.dart';
class AnswerButton extends StatelessWidget {
//constructior - acccept the string text argumnet as the first and void function as the second
//AnswerButton(this.answerText,this.onTap,{super.key});
AnswerButton({
super.key,
required this.answerText,
required this.onTap
});
final String answerText;
final void Function() onTap;
@override
Widget build(BuildContext context){
return ElevatedButton(
onPressed: onTap,
style: ElevatedButton.styleFrom(),
child: Text(answerText)) ;
}
}
❗ constructor에서 인자를 받는 방법
constructor에서 인자를 받는 방법은 두 가지가 있다. 첫번째로 positional argument로 받거나, named argument로 받을 수 있다.
다만, curly brace안에 있는 named 인자 같은 경우에는 근본적으로 optional이기 때문에, 꼭 인자를 받아야 할 경우는 required 키워드를
사용해야한다.
위에 AnswerButton class를 생성함으로써, 우리는 기존의 answer_button.dart의 private class를 아래와 같이 다시 수정할 수 있다.
class _QuestionsScreenState extends State<QuestionScreen>{
@override
Widget build(context){
return SizedBox(
width:double.infinity,
child:Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//child 01
Text(
"The questions are ...",
style:TextStyle(
color: Colors.white,
fontSize: 25,
)),
//child 02
const SizedBox(height:30),
//child 03
AnswerButton(answerText:"Answer 1", onTap:(){},),
const SizedBox(height:30),
//child 04
AnswerButton(answerText:"Answer 2", onTap:(){},),
const SizedBox(height:30),
//child05
AnswerButton(answerText:"Answer 3", onTap:(){},),
],)
);
}
}
3. AnswerButton class (Additional Features)
이전 section에선 우리는 AnswerButton class를 생성하였다. 이렇게 class widget을 만들게 되면, 추가적인 option을 집어넣어서 이 class에 속한 모든 block들이 영향을 받게 된다.(일일이 반복해서 쓸 필요없이, 범용적으로 쓰게 되어 가독성을 높힌다는 말임)
일단, AnswerButton의 style을 좀 더 세련되게 만들기 위해서, code를 축가해보도록 하자.
import 'package:flutter/material.dart';
class AnswerButton extends StatelessWidget {
//constructior - acccept the string text argumnet as the first and void function as the second
//AnswerButton(this.answerText,this.onTap,{super.key});
AnswerButton({
super.key,
required this.answerText,
required this.onTap
});
final String answerText;
final void Function() onTap;
@override
Widget build(BuildContext context){
return ElevatedButton(
onPressed: onTap,
style: ElevatedButton.styleFrom(
padding:EdgeInsets.symmetric(vertical: 10, horizontal: 60) ,
backgroundColor: Color.fromARGB(255, 31, 3, 48),
foregroundColor: Colors.white,
shape:RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40),
),
),
child: Text(answerText)) ;
}
}
'Front-End > Flutter' 카테고리의 다른 글
5. <Quiz APP>을 통한 Flutter Tutorial (0) | 2024.11.24 |
---|---|
4. <Quiz APP>을 통한 Flutter Tutorial: Modification to Current Style and Codes (0) | 2024.11.21 |
2. <Quiz APP>을 통한 Flutter Tutorial / Render Content Conditionally, Lifting State Up (0) | 2024.11.17 |
1. <Quiz APP>을 통한 Flutter Tutorial (0) | 2024.11.15 |