Menu
CoddyTech

Викторина: сообщение о результатах

Часть раздела Проекты путешествия по Flutter на Coddy. Урок 18 из 36.

«Вы набрали 5 из 5» заслуживает чего-то большего, чем просто строки текста. Завершите викторину большой иконкой и сообщением, соответствующим результату: трофей за идеальный раунд, поднятый вверх большой палец за большинство правильных ответов и слова поддержки в остальных случаях.

Три результата, проверяемые от лучшего к худшему, — это if / else if / else. Порядок важен: идеальный результат также означает, что правильных ответов больше половины, поэтому сначала проверьте его.

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 означает «не меньше половины» без округления: 3 из 5 проходят проверку, 2 из 5 — нет.

Объявите message, icon и color перед if, не присваивая им значения. Dart проверяет, что каждая ветвь присваивает каждой из них значение до того, как вы их используете.

Это последний урок викторины. Проверки охватывают целые раунды: цвета, счёт, результаты и кнопку «Играть снова».

challenge icon

Задание

Средне

На экране результатов, над счётом:

  • при максимальном счёте отображается Perfect! и значок янтарного цвета Icons.emoji_events
  • при результате не менее половины отображается Well done! и зелёный значок Icons.thumb_up
  • при меньшем результате отображается Keep practicing! и Icons.school основного цвета темы

Размер значка — 96 пикселей, затем 16 пикселей отступа, сообщение в headlineMedium, 8 пикселей и строка со счётом, теперь в titleMedium.

Попробуйте сами

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'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Проекты

Потренируйтесь самостоятельно: Онлайн-компилятор Flutter