mirror of https://github.com/flutter/samples.git
Cleaning up Veggie Seasons (#2416)
## Pre-launch Checklist - [x] I read the [Flutter Style Guide] _recently_, and have followed its advice. - [x] I signed the [CLA]. - [x] I read the [Contributors Guide]. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-devrel channel on [Discord]. <!-- Links --> [Flutter Style Guide]: https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md [CLA]: https://cla.developers.google.com/ [Discord]: https://github.com/flutter/flutter/blob/master/docs/contributing/Chat.md [Contributors Guide]: https://github.com/flutter/samples/blob/main/CONTRIBUTING.mdpull/2422/head
parent
61fed76690
commit
bee21b55e6
@ -1 +1,5 @@
|
||||
include: package:analysis_defaults/flutter.yaml
|
||||
|
||||
linter:
|
||||
rules:
|
||||
- prefer_relative_imports
|
@ -1,66 +0,0 @@
|
||||
// Copyright 2021, the Flutter project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class FadeTransitionPage<T> extends Page<T> {
|
||||
final Widget child;
|
||||
final Duration duration;
|
||||
|
||||
const FadeTransitionPage({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.duration = const Duration(milliseconds: 300),
|
||||
super.restorationId,
|
||||
});
|
||||
|
||||
@override
|
||||
Route<T> createRoute(BuildContext context) =>
|
||||
PageBasedFadeTransitionRoute<T>(this);
|
||||
}
|
||||
|
||||
class PageBasedFadeTransitionRoute<T> extends PageRoute<T> {
|
||||
PageBasedFadeTransitionRoute(FadeTransitionPage<T> page)
|
||||
: super(settings: page);
|
||||
|
||||
FadeTransitionPage<T> get _page => settings as FadeTransitionPage<T>;
|
||||
|
||||
@override
|
||||
Color? get barrierColor => null;
|
||||
|
||||
@override
|
||||
String? get barrierLabel => null;
|
||||
|
||||
@override
|
||||
Duration get transitionDuration => _page.duration;
|
||||
|
||||
@override
|
||||
Duration get reverseTransitionDuration => _page.duration;
|
||||
|
||||
@override
|
||||
bool get maintainState => true;
|
||||
|
||||
@override
|
||||
Widget buildPage(
|
||||
BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
) {
|
||||
return _page.child;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildTransitions(
|
||||
BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
Widget child,
|
||||
) {
|
||||
final tween = CurveTween(curve: Curves.easeInOut);
|
||||
return FadeTransition(
|
||||
opacity: animation.drive(tween),
|
||||
child: _page.child,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,249 +0,0 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:veggieseasons/data/app_state.dart';
|
||||
import 'package:veggieseasons/data/veggie.dart';
|
||||
import 'package:veggieseasons/styles.dart';
|
||||
|
||||
/// Presents a series of trivia questions about a particular widget, and tracks
|
||||
/// the user's score.
|
||||
class TriviaView extends StatefulWidget {
|
||||
final int? id;
|
||||
final String? restorationId;
|
||||
|
||||
const TriviaView({this.id, this.restorationId, super.key});
|
||||
|
||||
@override
|
||||
State<TriviaView> createState() => _TriviaViewState();
|
||||
}
|
||||
|
||||
/// Possible states of the game.
|
||||
enum PlayerStatus {
|
||||
readyToAnswer,
|
||||
wasCorrect,
|
||||
wasIncorrect,
|
||||
}
|
||||
|
||||
class _TriviaViewState extends State<TriviaView> with RestorationMixin {
|
||||
/// Current app state. This is used to fetch veggie data.
|
||||
late AppState appState;
|
||||
|
||||
/// The veggie trivia about which to show.
|
||||
late Veggie veggie;
|
||||
|
||||
/// Index of the current trivia question.
|
||||
RestorableInt triviaIndex = RestorableInt(0);
|
||||
|
||||
/// User's score on the current veggie.
|
||||
RestorableInt score = RestorableInt(0);
|
||||
|
||||
/// Trivia question currently being displayed.
|
||||
Trivia get currentTrivia => veggie.trivia[triviaIndex.value];
|
||||
|
||||
/// The current state of the game.
|
||||
_RestorablePlayerStatus status =
|
||||
_RestorablePlayerStatus(PlayerStatus.readyToAnswer);
|
||||
|
||||
@override
|
||||
String? get restorationId => widget.restorationId;
|
||||
|
||||
@override
|
||||
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
|
||||
registerForRestoration(triviaIndex, 'index');
|
||||
registerForRestoration(score, 'score');
|
||||
registerForRestoration(status, 'status');
|
||||
}
|
||||
|
||||
// Called at init and again if any dependencies (read: InheritedWidgets) on
|
||||
// on which this object relies are changed.
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
|
||||
final newAppState = Provider.of<AppState>(context);
|
||||
|
||||
setState(() {
|
||||
appState = newAppState;
|
||||
veggie = appState.getVeggie(widget.id);
|
||||
});
|
||||
}
|
||||
|
||||
// Called when the widget associated with this object is swapped out for a new
|
||||
// one. If the new widget has a different Veggie ID value, the state object
|
||||
// needs to do a little work to reset itself for the new Veggie.
|
||||
@override
|
||||
void didUpdateWidget(TriviaView oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
|
||||
if (oldWidget.id != widget.id) {
|
||||
setState(() {
|
||||
veggie = appState.getVeggie(widget.id);
|
||||
});
|
||||
|
||||
_resetGame();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
triviaIndex.dispose();
|
||||
score.dispose();
|
||||
status.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (triviaIndex.value >= veggie.trivia.length) {
|
||||
return _buildFinishedView();
|
||||
} else if (status.value == PlayerStatus.readyToAnswer) {
|
||||
return _buildQuestionView();
|
||||
} else {
|
||||
return _buildResultView();
|
||||
}
|
||||
}
|
||||
|
||||
void _resetGame() {
|
||||
setState(() {
|
||||
triviaIndex.value = 0;
|
||||
score.value = 0;
|
||||
status.value = PlayerStatus.readyToAnswer;
|
||||
});
|
||||
}
|
||||
|
||||
void _processAnswer(int answerIndex) {
|
||||
setState(() {
|
||||
if (answerIndex == currentTrivia.correctAnswerIndex) {
|
||||
status.value = PlayerStatus.wasCorrect;
|
||||
score.value++;
|
||||
} else {
|
||||
status.value = PlayerStatus.wasIncorrect;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Widget shown when the game is over. It includes the score and a button to
|
||||
// restart everything.
|
||||
Widget _buildFinishedView() {
|
||||
final themeData = CupertinoTheme.of(context);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'All done!',
|
||||
style: Styles.triviaFinishedTitleText(themeData),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text('You answered', style: themeData.textTheme.textStyle),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
Text(
|
||||
'${score.value}',
|
||||
style: Styles.triviaFinishedBigText(themeData),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(' of ', style: themeData.textTheme.textStyle),
|
||||
),
|
||||
Text(
|
||||
'${veggie.trivia.length}',
|
||||
style: Styles.triviaFinishedBigText(themeData),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text('questions correctly!', style: themeData.textTheme.textStyle),
|
||||
const SizedBox(height: 16),
|
||||
CupertinoButton(
|
||||
child: const Text('Try Again'),
|
||||
onPressed: () => _resetGame(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Presents the current trivia's question and answer choices.
|
||||
Widget _buildQuestionView() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
currentTrivia.question,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
for (int i = 0; i < currentTrivia.answers.length; i++)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: CupertinoButton(
|
||||
color: CupertinoColors.activeBlue,
|
||||
child: Text(
|
||||
currentTrivia.answers[i],
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
onPressed: () => _processAnswer(i),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Shows whether the last answer was right or wrong and prompts the user to
|
||||
// continue through the game.
|
||||
Widget _buildResultView() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
status.value == PlayerStatus.wasCorrect
|
||||
? 'That\'s right!'
|
||||
: 'Sorry, that wasn\'t the right answer.',
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
CupertinoButton(
|
||||
child: const Text('Next Question'),
|
||||
onPressed: () => setState(() {
|
||||
triviaIndex.value++;
|
||||
status.value = PlayerStatus.readyToAnswer;
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RestorablePlayerStatus extends RestorableValue<PlayerStatus> {
|
||||
_RestorablePlayerStatus(this._defaultValue);
|
||||
|
||||
final PlayerStatus _defaultValue;
|
||||
|
||||
@override
|
||||
PlayerStatus createDefaultValue() {
|
||||
return _defaultValue;
|
||||
}
|
||||
|
||||
@override
|
||||
PlayerStatus fromPrimitives(Object? data) {
|
||||
return PlayerStatus.values[data as int];
|
||||
}
|
||||
|
||||
@override
|
||||
Object toPrimitives() {
|
||||
return value.index;
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateValue(PlayerStatus? oldValue) {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// Copyright 2024, the Flutter project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class VeggieSeasonsPage<T> extends Page<T> {
|
||||
final Widget child;
|
||||
|
||||
const VeggieSeasonsPage({
|
||||
super.key,
|
||||
required this.child,
|
||||
super.restorationId,
|
||||
});
|
||||
|
||||
@override
|
||||
VeggieSeasonsPageRoute<T> createRoute(BuildContext context) =>
|
||||
VeggieSeasonsPageRoute<T>(this);
|
||||
}
|
||||
|
||||
class VeggieSeasonsPageRoute<T> extends PageRoute<T> {
|
||||
VeggieSeasonsPageRoute(VeggieSeasonsPage<T> page) : super(settings: page);
|
||||
|
||||
VeggieSeasonsPage<T> get _page => settings as VeggieSeasonsPage<T>;
|
||||
|
||||
@override
|
||||
Color? get barrierColor => null;
|
||||
|
||||
@override
|
||||
String? get barrierLabel => null;
|
||||
|
||||
@override
|
||||
bool get maintainState => true;
|
||||
|
||||
@override
|
||||
Duration get transitionDuration => Duration.zero;
|
||||
|
||||
@override
|
||||
Widget buildPage(
|
||||
BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
) =>
|
||||
_page.child;
|
||||
}
|
Loading…
Reference in new issue