퀴즈: 진행률 표시줄
Coddy Flutter 여정의 프로젝트 섹션에 포함된 레슨. 36개 중 15번째.
"5문제 중 3번째"라는 문구로 현재 위치를 알 수 있습니다. 진행할수록 채워지는 막대는 한눈에 진행 상황을 보여 줍니다. 이것은 0(비어 있음)부터 1(가득 참)까지의 value를 사용하는 LinearProgressIndicator입니다.
value가 없으면 앱에서 로딩 중임을 표시하는 방식처럼 막대가 계속 앞뒤로 움직입니다. value를 지정하면 얼마나 진행되었는지 보여 줍니다.
LinearProgressIndicator(
value: (_index + 1) / questions.length,
minHeight: 8,
borderRadius: BorderRadius.circular(4),
)첫 번째 문제에서는 1 / 5이므로 막대의 5분의 1이 채워지고, 마지막 문제에서는 5 / 5이므로 막대 전체가 채워집니다. Dart에서 /는 항상 double을 반환하므로 1 / 5는 0이 아니라 0.2입니다.
minHeight는 막대를 기본값인 4픽셀보다 두껍게 만들고, borderRadius는 버튼과 어울리도록 양 끝을 둥글게 만듭니다.
챌린지
쉬움Question 1 of 5 아래에 8픽셀 간격을 둔 다음 LinearProgressIndicator를 추가하세요:
- 질문 1에서는 5분의 1만큼 채워지고 질문 5에서는 가득 채워집니다
- 두께는 8픽셀이고 끝은 4픽셀만큼 둥글게 처리됩니다
질문 앞의 24픽셀 간격은 유지하세요.
직접 해보기
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const QuizScreen(),
);
}
}
class Question {
const Question(this.text, this.answers, this.correct);
final String text;
final List<String> answers;
final int correct;
}
const List<Question> questions = [
Question('Which widget stacks its children from top to bottom?', ['Row', 'Column', 'Stack', 'Wrap'], 1),
Question('Which language do you write Flutter apps in?', ['Kotlin', 'Swift', 'Dart', 'JavaScript'], 2),
Question('What does setState do?', ['Rebuilds the widget', 'Saves the app', 'Closes the screen', 'Plays a sound'], 0),
Question('Which widget shows a list you can scroll?', ['Container', 'SizedBox', 'Padding', 'ListView'], 3),
Question('Which widget adds space around its child?', ['Padding', 'Center', 'Icon', 'Text'], 0),
];
class QuizScreen extends StatefulWidget {
const QuizScreen({super.key});
@override
State<QuizScreen> createState() => _QuizScreenState();
}
class _QuizScreenState extends State<QuizScreen> {
int _index = 0;
int? _picked;
int _score = 0;
Question get _question => questions[_index];
bool get _answered => _picked != null;
bool get _isLast => _index == questions.length - 1;
void _pick(int answer) {
if (_answered) return;
setState(() {
_picked = answer;
if (answer == _question.correct) {
_score++;
}
});
}
void _next() {
setState(() {
_index++;
_picked = null;
});
}
Color? _colorFor(int answer) {
if (!_answered) return null;
if (answer == _question.correct) return Colors.green;
if (answer == _picked) return Colors.red;
return null;
}
@override
Widget build(BuildContext context) {
final question = _question;
final textTheme = Theme.of(context).textTheme;
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Quiz'),
actions: [
Padding(
padding: const EdgeInsets.only(right: 16),
child: Center(
child: Text('Score: $_score', style: textTheme.titleMedium),
),
),
],
),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Question ${_index + 1} of ${questions.length}',
style: textTheme.labelLarge,
),
const SizedBox(height: 24),
Text(question.text, style: textTheme.headlineSmall),
const SizedBox(height: 24),
for (int i = 0; i < question.answers.length; i++)
Padding(
padding: const EdgeInsets.only(bottom: 12),
child: FilledButton.tonal(
style: FilledButton.styleFrom(
backgroundColor: _colorFor(i),
foregroundColor: _colorFor(i) == null ? null : Colors.white,
padding: const EdgeInsets.all(16),
),
onPressed: () => _pick(i),
child: Text(question.answers[i]),
),
),
if (_answered) ...[
const SizedBox(height: 12),
Text(
_picked == question.correct
? 'Correct!'
: 'The answer is ${question.answers[question.correct]}',
textAlign: TextAlign.center,
style: textTheme.titleMedium,
),
const SizedBox(height: 16),
if (!_isLast)
FilledButton(
onPressed: _next,
child: const Text('Next'),
),
],
],
),
),
);
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
프로젝트의 모든 레슨
직접 연습해 보세요: 온라인 Flutter 컴파일러