Line data Source code
1 : import 'package:amadeus_proto/exercises/exercises_provider.dart';
2 : import 'package:amadeus_proto/exercises/widget/previous_button.dart';
3 : import 'package:flutter/material.dart';
4 : import 'package:flutter/scheduler.dart';
5 : import 'package:flutter_svg/svg.dart';
6 : import 'package:provider/provider.dart';
7 :
8 : /// Widget that will display the header of the exercise.
9 : ///
10 : /// This widget is generic type [T] that must extends from
11 : /// the [ExerciseProvider].
12 : ///
13 : /// This widget will display :
14 : /// - [IconButton] to quit the exercise page.
15 : /// - [ListView] of lives [Icon] in a row to display how many lives
16 : /// the user still have. We get the number of lives by its
17 : /// corresponding [ExerciseProvider]
18 :
19 : class ExerciseHeader<T extends ExerciseProvider> extends StatefulWidget {
20 60 : const ExerciseHeader({super.key});
21 :
22 0 : @override
23 0 : State<ExerciseHeader<T>> createState() => _ExerciseHeaderState<T>();
24 : }
25 :
26 : class _ExerciseHeaderState<T extends ExerciseProvider>
27 : extends State<ExerciseHeader<T>> with TickerProviderStateMixin {
28 : final List<AnimationController> _animationControllers = [];
29 : final List<Animation<Offset>> _animations = [];
30 : final List<Animation<double>> _animationOpacities = [];
31 :
32 0 : void _triggerAnimation() {
33 0 : final animationController = AnimationController(
34 : vsync: this,
35 : duration: const Duration(milliseconds: 1000),
36 : );
37 :
38 0 : final animation = Tween<Offset>(
39 : begin: const Offset(0, 0),
40 : end: const Offset(0, 0.5),
41 0 : ).animate(animationController);
42 :
43 : final animationOpacity =
44 0 : Tween<double>(begin: 1, end: 0).animate(animationController);
45 :
46 0 : setState(() {
47 0 : _animationControllers.add(animationController);
48 0 : _animations.add(animation);
49 0 : _animationOpacities.add(animationOpacity);
50 : });
51 :
52 0 : animationController.forward().whenComplete(() {
53 0 : setState(() {
54 0 : int index = _animationControllers.indexOf(animationController);
55 0 : if (index != -1) {
56 0 : _animationControllers.removeAt(index);
57 0 : _animations.removeAt(index);
58 0 : _animationOpacities.removeAt(index);
59 : }
60 : });
61 : });
62 : }
63 :
64 0 : @override
65 : void dispose() {
66 0 : for (var controller in _animationControllers) {
67 0 : controller.dispose();
68 : }
69 0 : super.dispose();
70 : }
71 :
72 0 : @override
73 : Widget build(BuildContext context) {
74 0 : return Consumer<T>(
75 0 : builder: (context, provider, child) {
76 0 : SchedulerBinding.instance.addPostFrameCallback((_) {
77 0 : if (provider.correctAnswer != null && !provider.correctAnswer!) {
78 0 : _triggerAnimation();
79 0 : provider.correctAnswer = null; // Reset flag
80 : }
81 : });
82 0 : return Row(
83 : mainAxisAlignment: MainAxisAlignment.spaceBetween,
84 0 : children: [
85 : const PreviousButton(),
86 0 : Stack(
87 0 : children: [
88 0 : ...List.generate(_animations.length, (index) {
89 0 : return AnimatedSwitcher(
90 : duration: const Duration(milliseconds: 1000),
91 0 : transitionBuilder: (Widget child, _) => FadeTransition(
92 0 : opacity: _animationOpacities[index],
93 0 : child: SlideTransition(
94 0 : position: _animations[index],
95 : child: child,
96 : ),
97 : ),
98 0 : child: Container(
99 0 : key: UniqueKey(),
100 : width: 65,
101 : height: 40,
102 : margin: const EdgeInsets.all(10),
103 0 : decoration: BoxDecoration(
104 0 : borderRadius: BorderRadius.circular(50),
105 : ),
106 0 : child: Row(
107 : crossAxisAlignment: CrossAxisAlignment.center,
108 : mainAxisAlignment: MainAxisAlignment.center,
109 0 : children: [
110 0 : SvgPicture.asset(
111 : "assets/FigmaDesign/Asset/SVG/Heart.svg",
112 : width: 25,
113 : height: 25,
114 : fit: BoxFit.fill,
115 : ),
116 : const SizedBox(width: 5),
117 : const Text(
118 : "-1",
119 : style: TextStyle(
120 : fontSize: 16,
121 : fontWeight: FontWeight.w700,
122 : fontStyle: FontStyle.normal),
123 : )
124 : ],
125 : )));
126 : // : const SizedBox.shrink());
127 : }),
128 0 : Container(
129 : width: 65,
130 : height: 40,
131 : margin: const EdgeInsets.all(10),
132 0 : decoration: BoxDecoration(
133 0 : borderRadius: BorderRadius.circular(50),
134 0 : border: Border.all(color: const Color(0xFFDFDFDF)),
135 : ),
136 0 : child: Row(
137 : crossAxisAlignment: CrossAxisAlignment.center,
138 : mainAxisAlignment: MainAxisAlignment.center,
139 0 : children: [
140 0 : SvgPicture.asset(
141 : "assets/FigmaDesign/Asset/SVG/Heart.svg",
142 : width: 25,
143 : height: 25,
144 : fit: BoxFit.fill,
145 : ),
146 : const SizedBox(width: 5),
147 0 : Text(
148 0 : provider.lives.toString(),
149 : style: const TextStyle(
150 : fontSize: 16,
151 : fontWeight: FontWeight.w700,
152 : fontStyle: FontStyle.normal),
153 : )
154 : ],
155 : )),
156 : ],
157 : ),
158 : ],
159 : );
160 : },
161 : );
162 : }
163 : }
|