Line data Source code
1 : import 'package:amadeus_proto/constants.dart';
2 : import 'package:amadeus_proto/exercises/categories/theory/note_placement/theory_dragable_item.dart';
3 : import 'package:amadeus_proto/exercises/categories/theory/note_placement/theory_dragtarget_item.dart';
4 : import 'package:amadeus_proto/exercises/providers/note_placement_provider.dart';
5 : import 'package:amadeus_proto/exercises/exercise_question.dart';
6 : import 'package:amadeus_proto/exercises/load_json.dart';
7 : import 'package:amadeus_proto/exercises/widget/animation_dialog_wrapper.dart';
8 : import 'package:amadeus_proto/exercises/widget/exercise_header.dart';
9 : import 'package:amadeus_proto/exercises/widget/exercises_end_dialog.dart';
10 : import 'package:amadeus_proto/api/models/exercise_info.dart';
11 : import 'package:amadeus_proto/widget/progress_bar.dart';
12 : import 'package:flutter/material.dart';
13 : import 'package:provider/provider.dart';
14 :
15 : class _AnimatedDialogWrapper extends StatefulWidget {
16 : final Widget child;
17 : final Animation<double> animation;
18 :
19 0 : const _AnimatedDialogWrapper({
20 : required this.child,
21 : required this.animation,
22 : });
23 :
24 0 : @override
25 0 : State<_AnimatedDialogWrapper> createState() => _AnimatedDialogWrapperState();
26 : }
27 :
28 : class _AnimatedDialogWrapperState extends State<_AnimatedDialogWrapper> {
29 : bool _isAnimationCompleted = false;
30 :
31 0 : @override
32 : void initState() {
33 0 : super.initState();
34 0 : widget.animation.addStatusListener((status) {
35 0 : if (status == AnimationStatus.completed) {
36 0 : setState(() {
37 0 : _isAnimationCompleted = true;
38 : });
39 : }
40 : });
41 : }
42 :
43 0 : @override
44 : Widget build(BuildContext context) {
45 0 : return AbsorbPointer(
46 : absorbing:
47 0 : !_isAnimationCompleted, // Disable clicks until animation completes
48 0 : child: widget.child,
49 : );
50 : }
51 : }
52 :
53 : class TheoryExercise extends StatefulWidget {
54 0 : const TheoryExercise(
55 : {super.key,
56 : required this.info});
57 :
58 : final ExerciseInfo info;
59 :
60 0 : @override
61 0 : State<TheoryExercise> createState() => _TheoryExerciseState();
62 : }
63 :
64 : class _TheoryExerciseState extends State<TheoryExercise> {
65 0 : @override
66 : void initState() {
67 0 : super.initState();
68 0 : WidgetsBinding.instance.addPostFrameCallback((_) {
69 0 : context.read<NotePlacementProvider>().reset();
70 : });
71 : }
72 :
73 : /// The implementation of [TheoryExercise.build]
74 : ///
75 : /// The build is composed by a FutureBuilder that will wait for the data
76 : /// from the [loadJsonData] asynchronous function to collect the data
77 : /// from the specific JSON file
78 : ///
79 : /// This widget display an theory exercise with a header [ExerciseHeader],
80 : /// a description [ExerciseQuestion], an image that correspond to the
81 : /// actual question, and finally a [SingleChoiceExercise] with multiples
82 : /// dragable item.
83 : ///
84 : /// This widget is a drag and drop exercise, when we need to drag the right
85 : /// [DragableItem] that stocks data, and drop it to the corresponding
86 : /// [DragTargetItem].
87 : ///
88 : /// All those is data, is initialize by the corresponding JSON file and is
89 : /// load by the corresponding [ExerciseProvider], in this case, the provider
90 : /// is [NotePlacementProvider]
91 0 : @override
92 : Widget build(BuildContext context) {
93 0 : final NotePlacementProvider theory = context.watch<NotePlacementProvider>();
94 :
95 0 : WidgetsBinding.instance.addPostFrameCallback((_) {
96 0 : if (!theory.isInitialized) {
97 0 : theory.initializeData(widget.info.exerciseData);
98 0 : theory.exerciseInfo = widget.info;
99 : }
100 0 : if (!theory.displayDialog && theory.checkEndGame()) {
101 0 : theory.displayDialog = true;
102 0 : Future.delayed(Duration.zero, () {
103 0 : if (!context.mounted) return;
104 0 : showGeneralDialog(
105 : context: context,
106 : transitionDuration: const Duration(milliseconds: fastAnimation),
107 0 : pageBuilder: (context, animation1, animation2) {
108 0 : return ExerciseEndDialog<NotePlacementProvider>(
109 0 : leavingExerciseCallback: () {
110 0 : Navigator.of(context).pop();
111 0 : Navigator.of(context).pop();
112 : },
113 0 : resetExerciseCallback: () {
114 0 : Navigator.of(context).pop();
115 0 : Future.delayed(const Duration(milliseconds: fastAnimation),
116 0 : () {
117 0 : if (mounted) {
118 0 : theory.reset();
119 : }
120 : });
121 : },
122 0 : exerciseTitle: widget.info.exerciseData!["title"],
123 0 : exerciseCategory: widget.info.exerciseData!["category"],
124 : );
125 : },
126 : transitionBuilder:
127 0 : (context, animation, secondaryAnimation, child) {
128 0 : return ScaleTransition(
129 : scale: animation,
130 0 : child: AnimatedDialogWrapper(
131 : animation: animation,
132 : child: child,
133 : ),
134 : );
135 : });
136 : });
137 : }
138 : });
139 :
140 0 : return Scaffold(
141 : backgroundColor: Colors.white,
142 0 : body: SafeArea(
143 0 : child: Column(children: [
144 0 : if (theory.isInitialized)
145 0 : const ExerciseHeader<NotePlacementProvider>(),
146 0 : const SizedBox(
147 : height: 10,
148 : ),
149 0 : if (theory.isInitialized)
150 0 : ProgressBar(
151 0 : totalAmount: theory.questions.length,
152 0 : progressAmount: theory.progressRatio(),
153 : width: 300,
154 : height: 15),
155 0 : const SizedBox(
156 : height: 10,
157 : ),
158 0 : if (theory.isInitialized)
159 0 : const ExerciseQuestion<NotePlacementProvider>(),
160 0 : const Spacer(),
161 0 : if (theory.isInitialized)
162 0 : Image(
163 0 : image: AssetImage(theory.questions[theory.index]),
164 : width: imageAssetWidth,
165 : height: imageAssetHeight,
166 : ),
167 0 : if (theory.isInitialized)
168 0 : Wrap(
169 : spacing: 200,
170 : alignment: WrapAlignment.spaceAround,
171 0 : children: List.generate(
172 : 2,
173 0 : (_) => DragTargetItem(
174 0 : imagePath: theory.answerData[theory.index]
175 0 : ["imagePath"],
176 0 : answer: theory.answerData[theory.index]["choice"],
177 : ))),
178 0 : const Spacer(),
179 0 : if (theory.isInitialized)
180 0 : Wrap(
181 : spacing: 25,
182 : crossAxisAlignment: WrapCrossAlignment.center,
183 0 : children: List.generate(
184 0 : theory.choices.length,
185 0 : (index) => DragableItem(
186 0 : imagePath: theory.choicesPath[index],
187 0 : data: theory.choices[index]),
188 : )),
189 0 : const Spacer()
190 : ]),
191 : ));
192 : }
193 : }
|