Trắc nghiệm: Thông báo kết quả
Thuộc phần Dự án trong hành trình Flutter của Coddy. Bài học 18 trên 36.
“Bạn đạt 5 trên 5” xứng đáng có hơn một dòng chữ đơn giản. Kết thúc bài trắc nghiệm bằng một biểu tượng lớn và lời nhắn phù hợp với kết quả: chiếc cúp cho lượt chơi hoàn hảo, biểu tượng giơ ngón tay cái cho phần lớn câu trả lời đúng, và lời động viên trong các trường hợp khác.
Ba kết quả, được kiểm tra từ tốt nhất trở xuống, dùng if / else if / else. Thứ tự rất quan trọng: điểm tuyệt đối cũng có nghĩa là đúng hơn một nửa, vì vậy hãy kiểm tra trường hợp đó trước.
if (score == total) {
message = 'Perfect!';
icon = Icons.emoji_events;
} else if (score * 2 >= total) {
message = 'Well done!';
icon = Icons.thumb_up;
} else {
message = 'Keep practicing!';
icon = Icons.school;
}score * 2 >= total nghĩa là “ít nhất một nửa” mà không cần làm tròn: đúng 3 trên 5 câu thì đạt, còn 2 trên 5 câu thì không.
Khai báo message, icon và color trước if mà chưa gán giá trị. Dart kiểm tra để đảm bảo mỗi nhánh đều gán giá trị cho từng biến trước khi bạn dùng chúng.
Đây là bài học cuối cùng của bài trắc nghiệm. Các bước kiểm tra sẽ chạy trọn vẹn từng lượt: màu sắc, điểm số, kết quả và Chơi lại.
Thử thách
Trung bìnhTrên màn hình kết quả, phía trên điểm số:
- điểm tuyệt đối hiển thị
Perfect!và biểu tượng màu hổ pháchIcons.emoji_events - đạt ít nhất một nửa số điểm hiển thị
Well done!và biểu tượng màu xanh láIcons.thumb_up - điểm thấp hơn hiển thị
Keep practicing!vàIcons.schoolvới màu chính của giao diện
Biểu tượng có kích thước 96 pixel, tiếp theo là khoảng trống 16 pixel, thông báo dùng headlineMedium, khoảng trống 8 pixel và dòng điểm số, giờ dùng titleMedium.
Tự làm thử
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++;
}
});
}
Future<void> _next() async {
if (!_isLast) {
setState(() {
_index++;
_picked = null;
});
return;
}
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ResultsScreen(score: _score, total: questions.length),
),
);
setState(() {
_index = 0;
_picked = null;
_score = 0;
});
}
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: 8),
LinearProgressIndicator(
value: (_index + 1) / questions.length,
minHeight: 8,
borderRadius: BorderRadius.circular(4),
),
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),
FilledButton(
onPressed: _next,
child: Text(_isLast ? 'See results' : 'Next'),
),
],
],
),
),
);
}
}
class ResultsScreen extends StatelessWidget {
const ResultsScreen({super.key, required this.score, required this.total});
final int score;
final int total;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Results')),
body: Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'You scored $score out of $total',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 32),
FilledButton.icon(
onPressed: () => Navigator.pop(context),
icon: const Icon(Icons.replay),
label: const Text('Play again'),
),
],
),
),
),
);
}
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Dự án
1Dự án: Danh sách việc cần làm
Danh sách việc cần làm: Danh sách công việcDanh sách việc cần làm: Thêm công việc4Dự án: Theo dõi thói quen
Theo dõi thói quen: Thói quen của bạnTheo dõi thói quen: Tuần2Dự án: Trắc nghiệm
Trắc nghiệm: Câu hỏiTrắc nghiệm: Đúng hay saiTự luyện tập: Trình biên dịch Flutter online