|
|
@ -1,3 +1,4 @@ |
|
|
|
|
|
import 'package:auto_size_text/auto_size_text.dart'; |
|
|
import 'package:flutter/material.dart'; |
|
|
import 'package:flutter/material.dart'; |
|
|
import 'package:flutter_bloc/flutter_bloc.dart'; |
|
|
import 'package:flutter_bloc/flutter_bloc.dart'; |
|
|
import 'package:gif/gif.dart'; |
|
|
import 'package:gif/gif.dart'; |
|
|
@ -33,8 +34,8 @@ class QuestionScreen extends StatefulWidget { |
|
|
State<QuestionScreen> createState() => _QuestionScreenState(); |
|
|
State<QuestionScreen> createState() => _QuestionScreenState(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
class _QuestionScreenState extends State<QuestionScreen> with TickerProviderStateMixin, WidgetsBindingObserver { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _QuestionScreenState extends State<QuestionScreen> |
|
|
|
|
|
with TickerProviderStateMixin, WidgetsBindingObserver { |
|
|
@override |
|
|
@override |
|
|
void initState() { |
|
|
void initState() { |
|
|
super.initState(); |
|
|
super.initState(); |
|
|
@ -50,15 +51,16 @@ class _QuestionScreenState extends State<QuestionScreen> with TickerProviderStat |
|
|
duration: const Duration(milliseconds: 500), |
|
|
duration: const Duration(milliseconds: 500), |
|
|
reverseDuration: const Duration(milliseconds: 500), |
|
|
reverseDuration: const Duration(milliseconds: 500), |
|
|
); |
|
|
); |
|
|
if(LocalStorage.readData(key: MyConstants.firstShowcase) == 'true') { |
|
|
|
|
|
context.read<QuestionBloc>().imageAnimationController.forward(); |
|
|
|
|
|
|
|
|
if (LocalStorage.readData(key: MyConstants.firstShowcase) == 'true') { |
|
|
|
|
|
context.read<QuestionBloc>().imageAnimationController?.forward(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@override |
|
|
@override |
|
|
void didChangeAppLifecycleState(AppLifecycleState state) { |
|
|
void didChangeAppLifecycleState(AppLifecycleState state) { |
|
|
super.didChangeAppLifecycleState(state); |
|
|
super.didChangeAppLifecycleState(state); |
|
|
if(state == AppLifecycleState.paused || state == AppLifecycleState.inactive){ |
|
|
|
|
|
|
|
|
if (state == AppLifecycleState.paused || |
|
|
|
|
|
state == AppLifecycleState.inactive) { |
|
|
context.read<QuestionBloc>().pauseBackgroundPlaying(); |
|
|
context.read<QuestionBloc>().pauseBackgroundPlaying(); |
|
|
} else if (state == AppLifecycleState.resumed) { |
|
|
} else if (state == AppLifecycleState.resumed) { |
|
|
context.read<QuestionBloc>().playBackgroundPlaying(); |
|
|
context.read<QuestionBloc>().playBackgroundPlaying(); |
|
|
@ -93,12 +95,11 @@ class _QuestionScreenState extends State<QuestionScreen> with TickerProviderStat |
|
|
return FadeAnimDelayed( |
|
|
return FadeAnimDelayed( |
|
|
duration: const Duration(seconds: 1), |
|
|
duration: const Duration(seconds: 1), |
|
|
child: FadeAnimController( |
|
|
child: FadeAnimController( |
|
|
controller: context.read<QuestionBloc>().imageAnimationController, |
|
|
|
|
|
|
|
|
controller: context.read<QuestionBloc>().imageAnimationController!, |
|
|
child: Column( |
|
|
child: Column( |
|
|
children: [ |
|
|
children: [ |
|
|
10.0.gapHeight, |
|
|
|
|
|
_titles(context), |
|
|
_titles(context), |
|
|
const Spacer(), |
|
|
|
|
|
|
|
|
20.0.gapHeight, |
|
|
BlocBuilder<QuestionBloc, QuestionState>( |
|
|
BlocBuilder<QuestionBloc, QuestionState>( |
|
|
builder: (context, state) => AnswerPictureBox( |
|
|
builder: (context, state) => AnswerPictureBox( |
|
|
key: Key('${state.currentQuestion?.image}'), |
|
|
key: Key('${state.currentQuestion?.image}'), |
|
|
@ -110,7 +111,6 @@ class _QuestionScreenState extends State<QuestionScreen> with TickerProviderStat |
|
|
autostart: Autostart.loop, |
|
|
autostart: Autostart.loop, |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
const Spacer(), |
|
|
|
|
|
], |
|
|
], |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
@ -120,7 +120,7 @@ class _QuestionScreenState extends State<QuestionScreen> with TickerProviderStat |
|
|
Widget _stepper() { |
|
|
Widget _stepper() { |
|
|
return BlocBuilder<QuestionBloc, QuestionState>( |
|
|
return BlocBuilder<QuestionBloc, QuestionState>( |
|
|
buildWhen: (previous, current) => |
|
|
buildWhen: (previous, current) => |
|
|
previous.currentQuestion?.id != current.currentQuestion?.id, |
|
|
|
|
|
|
|
|
previous.currentQuestion?.id != current.currentQuestion?.id, |
|
|
builder: (context, state) => FadeAnim( |
|
|
builder: (context, state) => FadeAnim( |
|
|
child: QuestionStepper( |
|
|
child: QuestionStepper( |
|
|
length: state.levelEntity?.questions?.length ?? 0, |
|
|
length: state.levelEntity?.questions?.length ?? 0, |
|
|
@ -133,20 +133,22 @@ class _QuestionScreenState extends State<QuestionScreen> with TickerProviderStat |
|
|
Widget _titles(BuildContext context) { |
|
|
Widget _titles(BuildContext context) { |
|
|
return BlocBuilder<QuestionBloc, QuestionState>( |
|
|
return BlocBuilder<QuestionBloc, QuestionState>( |
|
|
buildWhen: (previous, current) => |
|
|
buildWhen: (previous, current) => |
|
|
previous.currentQuestion?.id != current.currentQuestion?.id, |
|
|
|
|
|
builder: (context, state) => |
|
|
|
|
|
Text( |
|
|
|
|
|
state.currentQuestion?.title ?? '', |
|
|
|
|
|
textAlign: TextAlign.center, |
|
|
|
|
|
style: MYTextStyle.titr1.copyWith( |
|
|
|
|
|
shadows: [ |
|
|
|
|
|
BoxShadow( |
|
|
|
|
|
offset: const Offset(0, 2), |
|
|
|
|
|
color: MyColors.black.withValues(alpha: 0.25), |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
|
|
|
|
|
|
previous.currentQuestion?.id != current.currentQuestion?.id, |
|
|
|
|
|
builder: (context, state) => AutoSizeText( |
|
|
|
|
|
state.currentQuestion?.title ?? '', |
|
|
|
|
|
textAlign: TextAlign.center, |
|
|
|
|
|
minFontSize: 16, |
|
|
|
|
|
maxFontSize: 20, |
|
|
|
|
|
maxLines: 8, |
|
|
|
|
|
style: MYTextStyle.titr1.copyWith( |
|
|
|
|
|
shadows: [ |
|
|
|
|
|
BoxShadow( |
|
|
|
|
|
offset: const Offset(0, 2), |
|
|
|
|
|
color: MyColors.black.withValues(alpha: 0.25), |
|
|
), |
|
|
), |
|
|
), |
|
|
|
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -156,7 +158,7 @@ class _QuestionScreenState extends State<QuestionScreen> with TickerProviderStat |
|
|
padding: const EdgeInsets.only(top: 10), |
|
|
padding: const EdgeInsets.only(top: 10), |
|
|
children: [ |
|
|
children: [ |
|
|
FadeAnimController( |
|
|
FadeAnimController( |
|
|
controller: context.read<QuestionBloc>().answerAnimationController, |
|
|
|
|
|
|
|
|
controller: context.read<QuestionBloc>().answerAnimationController!, |
|
|
child: _titles(context), |
|
|
child: _titles(context), |
|
|
), |
|
|
), |
|
|
50.0.gapHeight, |
|
|
50.0.gapHeight, |
|
|
@ -164,43 +166,215 @@ class _QuestionScreenState extends State<QuestionScreen> with TickerProviderStat |
|
|
width: context.widthScreen, |
|
|
width: context.widthScreen, |
|
|
child: BlocBuilder<QuestionBloc, QuestionState>( |
|
|
child: BlocBuilder<QuestionBloc, QuestionState>( |
|
|
buildWhen: (previous, current) => |
|
|
buildWhen: (previous, current) => |
|
|
previous.currentQuestion?.id != current.currentQuestion?.id, |
|
|
|
|
|
builder: (context, state) => |
|
|
|
|
|
Wrap( |
|
|
|
|
|
alignment: WrapAlignment.center, |
|
|
|
|
|
spacing: 10, |
|
|
|
|
|
runSpacing: 20, |
|
|
|
|
|
children: List.generate( |
|
|
|
|
|
state.currentQuestion?.answers?.length ?? 0, |
|
|
|
|
|
(index) => |
|
|
|
|
|
state.currentQuestion?.answers?[index].imageId == null |
|
|
|
|
|
? const SizedBox.shrink() |
|
|
|
|
|
: SizedBox( |
|
|
|
|
|
key: Key('${state.currentQuestion?.id}$index'), |
|
|
|
|
|
width: 180, |
|
|
|
|
|
height: 250, |
|
|
|
|
|
child: SlideAnim( |
|
|
|
|
|
controller: context.read<QuestionBloc>().answerAnimationController, |
|
|
|
|
|
index: index, |
|
|
|
|
|
child: AnswerBox( |
|
|
|
|
|
index: state.currentQuestion?.answers?[index].order ?? 1, |
|
|
|
|
|
answer: state.currentQuestion?.answers?[index] ?? AnswerEntity(), |
|
|
|
|
|
correctAnswer: state.currentQuestion?.correctAnswer ?? 0, |
|
|
|
|
|
onNotifTap: (AnswerEntity answer) { |
|
|
|
|
|
context.read<QuestionBloc>().showAnswerDialog( |
|
|
|
|
|
context: context, |
|
|
|
|
|
answerEntity: answer, |
|
|
|
|
|
|
|
|
previous.currentQuestion?.id != current.currentQuestion?.id, |
|
|
|
|
|
builder: (context, state) => Column( |
|
|
|
|
|
spacing: 30, |
|
|
|
|
|
children: [ |
|
|
|
|
|
Row( |
|
|
|
|
|
key: Key('${state.currentQuestion?.id}answer0'), |
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
|
|
children: [ |
|
|
|
|
|
Builder( |
|
|
|
|
|
key: Key('${state.currentQuestion?.id}0'), |
|
|
|
|
|
builder: (context) { |
|
|
|
|
|
if (state.currentQuestion?.answers?[0].imageId == |
|
|
|
|
|
null) { |
|
|
|
|
|
return const SizedBox.shrink(); |
|
|
|
|
|
} else { |
|
|
|
|
|
return SizedBox( |
|
|
|
|
|
width: 180, |
|
|
|
|
|
height: 250, |
|
|
|
|
|
child: SlideAnim( |
|
|
|
|
|
controller: context |
|
|
|
|
|
.read<QuestionBloc>() |
|
|
|
|
|
.answerAnimationController!, |
|
|
|
|
|
index: 0, |
|
|
|
|
|
child: AnswerBox( |
|
|
|
|
|
index: |
|
|
|
|
|
state.currentQuestion?.answers?[0].order ?? |
|
|
|
|
|
1, |
|
|
|
|
|
answer: |
|
|
|
|
|
state.currentQuestion?.answers?[0] ?? |
|
|
|
|
|
AnswerEntity(), |
|
|
|
|
|
correctAnswer: |
|
|
|
|
|
state.currentQuestion?.correctAnswer ?? 0, |
|
|
|
|
|
onNotifTap: (AnswerEntity answer) { |
|
|
|
|
|
context.read<QuestionBloc>().showAnswerDialog( |
|
|
|
|
|
context: context, |
|
|
|
|
|
answerEntity: answer, |
|
|
|
|
|
); |
|
|
|
|
|
}, |
|
|
|
|
|
onTap: (isCorrect, correctAnswer) => |
|
|
|
|
|
context.read<QuestionBloc>().add( |
|
|
|
|
|
ChooseAnswerEvent( |
|
|
|
|
|
isCorrect, |
|
|
|
|
|
correctAnswer, |
|
|
|
|
|
context, |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
), |
|
|
|
|
|
Builder( |
|
|
|
|
|
key: Key('${state.currentQuestion?.id}1'), |
|
|
|
|
|
builder: (context) { |
|
|
|
|
|
if (state.currentQuestion?.answers?[1].imageId == |
|
|
|
|
|
null) { |
|
|
|
|
|
return const SizedBox.shrink(); |
|
|
|
|
|
} else { |
|
|
|
|
|
return SizedBox( |
|
|
|
|
|
width: 180, |
|
|
|
|
|
height: 250, |
|
|
|
|
|
child: SlideAnim( |
|
|
|
|
|
controller: context |
|
|
|
|
|
.read<QuestionBloc>() |
|
|
|
|
|
.answerAnimationController!, |
|
|
|
|
|
index: 1, |
|
|
|
|
|
child: AnswerBox( |
|
|
|
|
|
index: |
|
|
|
|
|
state.currentQuestion?.answers?[1].order ?? |
|
|
|
|
|
1, |
|
|
|
|
|
answer: |
|
|
|
|
|
state.currentQuestion?.answers?[1] ?? |
|
|
|
|
|
AnswerEntity(), |
|
|
|
|
|
correctAnswer: |
|
|
|
|
|
state.currentQuestion?.correctAnswer ?? 0, |
|
|
|
|
|
onNotifTap: (AnswerEntity answer) { |
|
|
|
|
|
context.read<QuestionBloc>().showAnswerDialog( |
|
|
|
|
|
context: context, |
|
|
|
|
|
answerEntity: answer, |
|
|
|
|
|
); |
|
|
|
|
|
}, |
|
|
|
|
|
onTap: (isCorrect, correctAnswer) => |
|
|
|
|
|
context.read<QuestionBloc>().add( |
|
|
|
|
|
ChooseAnswerEvent( |
|
|
|
|
|
isCorrect, |
|
|
|
|
|
correctAnswer, |
|
|
|
|
|
context, |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
Row( |
|
|
|
|
|
key: Key('${state.currentQuestion?.id}answer1'), |
|
|
|
|
|
mainAxisAlignment: |
|
|
|
|
|
(state.currentQuestion?.answers?.length ?? 0) > 3 |
|
|
|
|
|
? MainAxisAlignment.spaceBetween |
|
|
|
|
|
: MainAxisAlignment.center, |
|
|
|
|
|
children: [ |
|
|
|
|
|
if ((state.currentQuestion?.answers?.length ?? 0) > 2) |
|
|
|
|
|
Builder( |
|
|
|
|
|
key: Key('${state.currentQuestion?.id}2'), |
|
|
|
|
|
builder: (context) { |
|
|
|
|
|
if (state.currentQuestion?.answers?[2].imageId == |
|
|
|
|
|
null) { |
|
|
|
|
|
return const SizedBox.shrink(); |
|
|
|
|
|
} else { |
|
|
|
|
|
return SizedBox( |
|
|
|
|
|
width: 180, |
|
|
|
|
|
height: 250, |
|
|
|
|
|
child: SlideAnim( |
|
|
|
|
|
controller: context |
|
|
|
|
|
.read<QuestionBloc>() |
|
|
|
|
|
.answerAnimationController!, |
|
|
|
|
|
index: 2, |
|
|
|
|
|
child: AnswerBox( |
|
|
|
|
|
index: |
|
|
|
|
|
state |
|
|
|
|
|
.currentQuestion |
|
|
|
|
|
?.answers?[2] |
|
|
|
|
|
.order ?? |
|
|
|
|
|
1, |
|
|
|
|
|
answer: |
|
|
|
|
|
state.currentQuestion?.answers?[2] ?? |
|
|
|
|
|
AnswerEntity(), |
|
|
|
|
|
correctAnswer: |
|
|
|
|
|
state.currentQuestion?.correctAnswer ?? 0, |
|
|
|
|
|
onNotifTap: (AnswerEntity answer) { |
|
|
|
|
|
context |
|
|
|
|
|
.read<QuestionBloc>() |
|
|
|
|
|
.showAnswerDialog( |
|
|
|
|
|
context: context, |
|
|
|
|
|
answerEntity: answer, |
|
|
|
|
|
); |
|
|
|
|
|
}, |
|
|
|
|
|
onTap: (isCorrect, correctAnswer) => |
|
|
|
|
|
context.read<QuestionBloc>().add( |
|
|
|
|
|
ChooseAnswerEvent( |
|
|
|
|
|
isCorrect, |
|
|
|
|
|
correctAnswer, |
|
|
|
|
|
context, |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
); |
|
|
); |
|
|
}, |
|
|
|
|
|
onTap: (isCorrect, correctAnswer) => |
|
|
|
|
|
context.read<QuestionBloc>().add( |
|
|
|
|
|
ChooseAnswerEvent(isCorrect, correctAnswer, context), |
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
), |
|
|
|
|
|
if ((state.currentQuestion?.answers?.length ?? 0) > 3) |
|
|
|
|
|
Builder( |
|
|
|
|
|
key: Key('${state.currentQuestion?.id}1'), |
|
|
|
|
|
builder: (context) { |
|
|
|
|
|
if (state.currentQuestion?.answers?[3].imageId == |
|
|
|
|
|
null) { |
|
|
|
|
|
return const SizedBox.shrink(); |
|
|
|
|
|
} else { |
|
|
|
|
|
return SizedBox( |
|
|
|
|
|
width: 180, |
|
|
|
|
|
height: 250, |
|
|
|
|
|
child: SlideAnim( |
|
|
|
|
|
controller: context |
|
|
|
|
|
.read<QuestionBloc>() |
|
|
|
|
|
.answerAnimationController!, |
|
|
|
|
|
index: 3, |
|
|
|
|
|
child: AnswerBox( |
|
|
|
|
|
index: |
|
|
|
|
|
state |
|
|
|
|
|
.currentQuestion |
|
|
|
|
|
?.answers?[3] |
|
|
|
|
|
.order ?? |
|
|
|
|
|
1, |
|
|
|
|
|
answer: |
|
|
|
|
|
state.currentQuestion?.answers?[3] ?? |
|
|
|
|
|
AnswerEntity(), |
|
|
|
|
|
correctAnswer: |
|
|
|
|
|
state.currentQuestion?.correctAnswer ?? 0, |
|
|
|
|
|
onNotifTap: (AnswerEntity answer) { |
|
|
|
|
|
context |
|
|
|
|
|
.read<QuestionBloc>() |
|
|
|
|
|
.showAnswerDialog( |
|
|
|
|
|
context: context, |
|
|
|
|
|
answerEntity: answer, |
|
|
|
|
|
); |
|
|
|
|
|
}, |
|
|
|
|
|
onTap: (isCorrect, correctAnswer) => |
|
|
|
|
|
context.read<QuestionBloc>().add( |
|
|
|
|
|
ChooseAnswerEvent( |
|
|
|
|
|
isCorrect, |
|
|
|
|
|
correctAnswer, |
|
|
|
|
|
context, |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
|
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
), |
|
|
), |
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
|
|
|
], |
|
|
), |
|
|
), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
], |
|
|
], |
|
|
@ -214,27 +388,23 @@ class _QuestionScreenState extends State<QuestionScreen> with TickerProviderStat |
|
|
child: Stack( |
|
|
child: Stack( |
|
|
alignment: AlignmentDirectional.centerStart, |
|
|
alignment: AlignmentDirectional.centerStart, |
|
|
children: [ |
|
|
children: [ |
|
|
StreamBuilder<bool>( |
|
|
|
|
|
initialData: false, |
|
|
|
|
|
stream: context.read<QuestionBloc>().playingStream, |
|
|
|
|
|
builder: (context, snapshot) => GlobeAnimation( |
|
|
|
|
|
state: snapshot.data ?? false, |
|
|
|
|
|
child: MyImage( |
|
|
|
|
|
image: MyAssets.globe, |
|
|
|
|
|
fit: BoxFit.cover, |
|
|
|
|
|
size: setSize(context: context, tablet: 120), |
|
|
|
|
|
), |
|
|
|
|
|
|
|
|
GlobeAnimation( |
|
|
|
|
|
state: true, |
|
|
|
|
|
child: MyImage( |
|
|
|
|
|
image: MyAssets.globe, |
|
|
|
|
|
fit: BoxFit.cover, |
|
|
|
|
|
size: setSize(context: context, tablet: 120), |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
Positioned( |
|
|
Positioned( |
|
|
left: 90, |
|
|
left: 90, |
|
|
right: 90, |
|
|
right: 90, |
|
|
child: FadeAnimController( |
|
|
child: FadeAnimController( |
|
|
controller: context.read<QuestionBloc>().imageAnimationController, |
|
|
|
|
|
|
|
|
controller: context.read<QuestionBloc>().imageAnimationController!, |
|
|
child: TextButton( |
|
|
child: TextButton( |
|
|
onPressed: () async { |
|
|
onPressed: () async { |
|
|
context.read<QuestionBloc>().imageAnimationController.reverse(); |
|
|
|
|
|
context.read<QuestionBloc>().answerAnimationController.forward(); |
|
|
|
|
|
|
|
|
context.read<QuestionBloc>().imageAnimationController?.reverse(); |
|
|
|
|
|
context.read<QuestionBloc>().answerAnimationController?.forward(); |
|
|
context.read<QuestionBloc>().showingAnswerSequence(show: false); |
|
|
context.read<QuestionBloc>().showingAnswerSequence(show: false); |
|
|
context.read<QuestionBloc>().pausePlaying(); |
|
|
context.read<QuestionBloc>().pausePlaying(); |
|
|
}, |
|
|
}, |
|
|
@ -243,11 +413,11 @@ class _QuestionScreenState extends State<QuestionScreen> with TickerProviderStat |
|
|
), |
|
|
), |
|
|
child: FittedBox( |
|
|
child: FittedBox( |
|
|
child: Text( |
|
|
child: Text( |
|
|
context.translate.skip, |
|
|
|
|
|
style: MYTextStyle.button2 |
|
|
|
|
|
|
|
|
context.translate.skip, |
|
|
|
|
|
style: MYTextStyle.button2, |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
|
|
|
|
|
|
) |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
PositionedDirectional( |
|
|
PositionedDirectional( |
|
|
|