fix: show case #38

Merged
amirreza.chegini merged 1 commits from fix/showcase into develop 1 day ago
  1. 1
      lib/core/constants/my_constants.dart
  2. 9
      lib/core/widgets/answer_box/answer_box.dart
  3. 161
      lib/core/widgets/showcase/my_showcase_widget.dart
  4. 44
      lib/core/widgets/showcase/question_showcase.dart
  5. 47
      lib/features/question/presentation/bloc/question_bloc.dart
  6. 5
      lib/features/question/presentation/ui/question_page.dart
  7. 25
      lib/features/question/presentation/ui/screens/question_screen.dart
  8. 8
      lib/l10n/app_en.arb
  9. 36
      lib/l10n/app_localizations.dart
  10. 20
      lib/l10n/app_localizations_en.dart
  11. 4
      pubspec.lock
  12. 2
      pubspec.yaml

1
lib/core/constants/my_constants.dart

@ -16,5 +16,6 @@ class MyConstants {
static const String mainAudioService = 'MAIN_AUDIO_SERVICE';
static const String effectAudioService = 'EFFECT_AUDIO_SERVICE';
static const String firstIntro = 'FIRST_INTRO';
static const String firstShowcase = 'FIRST_SHOWCASE';
static const double mainAudioVolume = 0.3;
}

9
lib/core/widgets/answer_box/answer_box.dart

@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart';
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart';
import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart';
import 'package:hadi_hoda_flutter/core/widgets/answer_box/styles/picture_box.dart';
import 'package:hadi_hoda_flutter/core/widgets/answer_box/styles/text_box.dart';
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart';
import 'package:hadi_hoda_flutter/core/widgets/showcase/my_showcase_widget.dart';
import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart';
class AnswerBox extends StatefulWidget {
@ -13,6 +15,7 @@ class AnswerBox extends StatefulWidget {
required this.answer,
required this.correctAnswer,
required this.index,
required this.globalKey,
this.onTap,
this.onNotifTap,
});
@ -22,6 +25,7 @@ class AnswerBox extends StatefulWidget {
final void Function(bool isCorrect, int correctAnswer)? onTap;
final int index;
final Function(AnswerEntity answer)? onNotifTap;
final GlobalKey globalKey;
@override
State<AnswerBox> createState() => _AnswerBoxState();
@ -70,6 +74,10 @@ class _AnswerBoxState extends State<AnswerBox> {
PositionedDirectional(
top: setSize(context: context, mobile: MySpaces.s12, tablet: MySpaces.s20),
end: setSize(context: context, mobile: MySpaces.s8, tablet: MySpaces.s20),
child: MyShowcaseWidget(
globalKey: widget.globalKey,
type: ShowcaseTooltipType.bottom,
description: context.translate.showcase_notif,
child: GestureDetector(
onTap: () {
widget.onNotifTap?.call(widget.answer);
@ -80,6 +88,7 @@ class _AnswerBoxState extends State<AnswerBox> {
),
),
),
),
],
),
),

161
lib/core/widgets/showcase/my_showcase_widget.dart

@ -0,0 +1,161 @@
import 'package:flutter/material.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart';
import 'package:hadi_hoda_flutter/core/widgets/button/my_white_button.dart';
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart';
import 'package:hadi_hoda_flutter/core/widgets/inkwell/my_inkwell.dart';
import 'package:showcaseview/showcaseview.dart';
enum ShowcaseTooltipType {
bottom,
top,
topLeft;
static Map<ShowcaseTooltipType, Widget> toolTipAction({String? description}) => {
ShowcaseTooltipType.bottom: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
spacing: MySpaces.s4,
children: [
MyImage(
image: MyAssets.handPoint,
size: 50,
),
Text(
description ?? '',
style: MYTextStyle.titr4,
textAlign: TextAlign.center,
),
],
),
ShowcaseTooltipType.top: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
spacing: MySpaces.s4,
children: [
Text(
description ?? '',
style: MYTextStyle.titr4,
textAlign: TextAlign.center,
),
Transform.flip(
flipY: true,
flipX: true,
child: MyImage(
image: MyAssets.handPoint,
size: 50,
),
),
],
),
ShowcaseTooltipType.topLeft: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
spacing: MySpaces.s4,
children: [
Text(
description ?? '',
style: MYTextStyle.titr4,
textAlign: TextAlign.center,
),
Transform.rotate(
angle: 2.5,
child: MyImage(
image: MyAssets.handPoint,
size: 50,
),
),
],
),
};
static Map<ShowcaseTooltipType, TooltipPosition> get toolTipPosition => {
ShowcaseTooltipType.bottom: TooltipPosition.bottom,
ShowcaseTooltipType.top: TooltipPosition.top,
ShowcaseTooltipType.topLeft: TooltipPosition.top,
};
}
class MyShowcaseWidget extends StatelessWidget {
const MyShowcaseWidget({
super.key,
required this.globalKey,
required this.child,
this.description,
this.type = ShowcaseTooltipType.bottom,
});
final GlobalKey globalKey;
final String? description;
final Widget child;
final ShowcaseTooltipType type;
@override
Widget build(BuildContext context) {
return Showcase(
key: globalKey,
disableBarrierInteraction: true,
targetShapeBorder: CircleBorder(),
overlayColor: Color(0XFF0F0041),
overlayOpacity: 0.82,
/// ToolTip
tooltipPadding: EdgeInsets.zero,
tooltipBackgroundColor: MyColors.transparent,
tooltipPosition: ShowcaseTooltipType.toolTipPosition[type],
targetTooltipGap: 0,
toolTipSlideEndDistance: MySpaces.s6,
toolTipMargin: 0,
tooltipActionConfig: TooltipActionConfig(
gapBetweenContentAndAction: 0,
),
tooltipActions: [
TooltipActionButton.custom(
button: ShowcaseTooltipType.toolTipAction(
description: description)[type],
),
],
/// Floating action
floatingActionWidget: FloatingActionWidget(
bottom: type == ShowcaseTooltipType.bottom ? MySpaces.s40 : null,
top: type == ShowcaseTooltipType.bottom ? null : 44,
left: MySpaces.s16,
right: MySpaces.s16,
child: Row(
children: [
Expanded(
child: MyInkwell(
onTap: () {
ShowcaseView.get().unregister();
},
splashColor: MyColors.transparent,
highlightColor: MyColors.transparent,
child: Container(
height: 50,
alignment: Alignment.center,
child: Text(
context.translate.skip,
style: MYTextStyle.button2,
),
),
),
),
Expanded(
child: MyWhiteButton(
title: context.translate.next,
onTap: () {
ShowcaseView.get().next();
},
),
),
],
),
),
/// title & Child
description: '',
child: child,
);
}
}

44
lib/core/widgets/showcase/question_showcase.dart

@ -1,44 +0,0 @@
import 'package:flutter/material.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart';
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart';
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart';
import 'package:showcaseview/showcaseview.dart';
class QuestionShowcase extends StatelessWidget {
const QuestionShowcase({
super.key,
required this.globalKey,
required this.child,
this.description,
});
final GlobalKey globalKey;
final String? description;
final Widget child;
@override
Widget build(BuildContext context) {
return Showcase(
key: globalKey,
blurValue: 10,
targetShapeBorder: CircleBorder(),
tooltipBackgroundColor: Colors.transparent,
disableMovingAnimation: true,
textColor: MyColors.white,
descriptionTextAlign: TextAlign.center,
disableScaleAnimation: true,
tooltipPadding: EdgeInsets.only(top: 60),
floatingActionWidget: FloatingActionWidget(
height: 60,
width: 60,
right: context.widthScreen * 0.17,
top: context.widthScreen * 1.17,
child: MyImage(image: MyAssets.handPoint),
),
overlayColor: Color(0XFF0F0041),
description: description ?? '',
child: child,
);
}
}

47
lib/features/question/presentation/bloc/question_bloc.dart

@ -33,21 +33,35 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
) : super(QuestionState()) {
volumeStream = _mainAudioService.volumeStream();
stopMusic();
registerShowCase();
on<GetLevelEvent>(_getLevelEvent);
on<ChooseAnswerEvent>(_chooseAnswerEvent);
}
@override
Future<void> close() {
ShowcaseView.get().unregister();
return super.close();
}
/// ------------UseCases------------
final GetLevelUseCase _getLevelUseCase;
final GetNextLevelUseCase _getNextLevelUseCase;
/// ------------Variables------------
final List<GlobalKey> keys = [
GlobalKey(),
GlobalKey(),
GlobalKey(),
GlobalKey(),
];
final Map<String, GlobalKey> showCaseKey = {
'answer_key_0': GlobalKey(),
'answer_key_1': GlobalKey(),
'answer_key_2': GlobalKey(),
'answer_key_3': GlobalKey(),
'notif_key_0': GlobalKey(),
'notif_key_1': GlobalKey(),
'notif_key_2': GlobalKey(),
'notif_key_3': GlobalKey(),
'stepper_key': GlobalKey(),
'hadith_key': GlobalKey(),
'guide_key': GlobalKey(),
};
late final Stream<double> volumeStream;
bool isPlaying = false;
@ -56,8 +70,24 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
final AudioService _effectAudioService;
/// ------------Functions------------
void startShowCase({required BuildContext context}) {
ShowCaseWidget.of(context).startShowCase([keys[1]]);
void registerShowCase() {
ShowcaseView.register(
onStart: (showcaseIndex, key) {
LocalStorage.saveData(key: MyConstants.firstShowcase, value: 'true');
},
);
}
void startShowcase() {
if (LocalStorage.readData(key: MyConstants.firstShowcase) != 'true') {
ShowcaseView.get().startShowCase([
showCaseKey['answer_key_1']!,
showCaseKey['notif_key_0']!,
showCaseKey['stepper_key']!,
showCaseKey['hadith_key']!,
showCaseKey['guide_key']!,
]);
}
}
void showHadith({required BuildContext context}) {
@ -170,6 +200,7 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
currentQuestion: data.questions?.first,
));
await playQuestionAudio();
startShowcase();
},
(error) {
emit(state.copyWith(getQuestionStatus: BaseError(error.errorMessage)));

5
lib/features/question/presentation/ui/question_page.dart

@ -14,15 +14,12 @@ import 'package:hadi_hoda_flutter/features/question/presentation/ui/screens/diam
import 'package:hadi_hoda_flutter/features/question/presentation/ui/screens/question_screen.dart';
import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/glassy_button.dart';
import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/question_title.dart';
import 'package:showcaseview/showcaseview.dart';
class QuestionPage extends StatelessWidget {
const QuestionPage({super.key});
@override
Widget build(BuildContext context) {
return ShowCaseWidget(
builder: (context) {
return Scaffold(
body: MyPopScope(
child: Container(
@ -77,8 +74,6 @@ class QuestionPage extends StatelessWidget {
),
),
);
},
);
}
Widget _topButtons(BuildContext context) {

25
lib/features/question/presentation/ui/screens/question_screen.dart

@ -5,6 +5,7 @@ import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
import 'package:hadi_hoda_flutter/core/utils/gap.dart';
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart';
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart';
import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart';
import 'package:hadi_hoda_flutter/core/widgets/animations/fade_anim.dart';
@ -12,6 +13,7 @@ import 'package:hadi_hoda_flutter/core/widgets/animations/slide_anim.dart';
import 'package:hadi_hoda_flutter/core/widgets/animations/slide_up_fade.dart';
import 'package:hadi_hoda_flutter/core/widgets/answer_box/answer_box.dart';
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart';
import 'package:hadi_hoda_flutter/core/widgets/showcase/my_showcase_widget.dart';
import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart';
import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_bloc.dart';
import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_event.dart';
@ -39,12 +41,16 @@ class QuestionScreen extends StatelessWidget {
return BlocBuilder<QuestionBloc, QuestionState>(
buildWhen: (previous, current) =>
previous.currentQuestion?.id != current.currentQuestion?.id,
builder: (context, state) => FadeAnim(
builder: (context, state) => MyShowcaseWidget(
globalKey: context.read<QuestionBloc>().showCaseKey['stepper_key']!,
description: context.translate.showcase_stepper,
child: FadeAnim(
child: QuestionStepper(
length: state.levelEntity?.questions?.length ?? 0,
currentStep: state.currentQuestion?.order ?? 1,
),
),
),
);
}
@ -96,7 +102,11 @@ class QuestionScreen extends StatelessWidget {
: SlideAnim(
key: Key('${state.currentQuestion?.id}'),
index: index,
child: MyShowcaseWidget(
globalKey: context.read<QuestionBloc>().showCaseKey['answer_key_$index']!,
description: context.translate.showcase_answer,
child: AnswerBox(
globalKey: context.read<QuestionBloc>().showCaseKey['notif_key_$index']!,
index: state.currentQuestion?.answers?[index].order ?? 1,
answer:
state.currentQuestion?.answers?[index] ??
@ -116,6 +126,7 @@ class QuestionScreen extends StatelessWidget {
),
),
),
),
);
}
@ -126,7 +137,11 @@ class QuestionScreen extends StatelessWidget {
child: Stack(
alignment: Alignment.center,
children: [
Container(
MyShowcaseWidget(
globalKey: context.read<QuestionBloc>().showCaseKey['guide_key']!,
description: context.translate.showcase_guide,
type: ShowcaseTooltipType.top,
child: Container(
padding: EdgeInsets.all(MySpaces.s4),
decoration: BoxDecoration(
gradient: RadialGradient(
@ -142,14 +157,20 @@ class QuestionScreen extends StatelessWidget {
image: MyAssets.globe,
),
),
),
PositionedDirectional(
end: 0,
child: MyShowcaseWidget(
globalKey: context.read<QuestionBloc>().showCaseKey['hadith_key']!,
type: ShowcaseTooltipType.topLeft,
description: context.translate.showcase_hadith,
child: GlassyButton(
image: MyAssets.leaf,
onTap: () =>
context.read<QuestionBloc>().showHadith(context: context),
),
),
),
],
),
),

8
lib/l10n/app_en.arb

@ -1,7 +1,6 @@
{
"about_us": "About us",
"about_us_desc" : "Rive combines an interactive design tool, a new stateful graphics format, a lightweight multi-platform runtime, and a blazing-fast vector renderer. \nThis end-to-end pipeline brings interfaces to life with motion. It gives designers and devs the tools to build.",
"tap_to_select": "Tap the correct option to select.",
"select_language": "Select language",
"select": "Select",
"please_wait": "wait a few moments...",
@ -30,5 +29,10 @@
"cancel": "Cancel",
"exit": "Exit",
"play": "PLAY",
"no_hadith": "There isn't any hadith for this question"
"no_hadith": "There isn't any hadith for this question",
"showcase_answer": "Tap the correct option\nto select.",
"showcase_notif": "The announcer will\nread the answers to\nthe options to you.",
"showcase_stepper": "Here you will see the\nquestions for this\nstage to reach the\ndiamond.",
"showcase_hadith": "View sources and\nhadiths for this\nquestion",
"showcase_guide": "This is a guide that will\nhelp you."
}

36
lib/l10n/app_localizations.dart

@ -106,12 +106,6 @@ abstract class AppLocalizations {
/// **'Rive combines an interactive design tool, a new stateful graphics format, a lightweight multi-platform runtime, and a blazing-fast vector renderer. \nThis end-to-end pipeline brings interfaces to life with motion. It gives designers and devs the tools to build.'**
String get about_us_desc;
/// No description provided for @tap_to_select.
///
/// In en, this message translates to:
/// **'Tap the correct option to select.'**
String get tap_to_select;
/// No description provided for @select_language.
///
/// In en, this message translates to:
@ -285,6 +279,36 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'There isn\'t any hadith for this question'**
String get no_hadith;
/// No description provided for @showcase_answer.
///
/// In en, this message translates to:
/// **'Tap the correct option\nto select.'**
String get showcase_answer;
/// No description provided for @showcase_notif.
///
/// In en, this message translates to:
/// **'The announcer will\nread the answers to\nthe options to you.'**
String get showcase_notif;
/// No description provided for @showcase_stepper.
///
/// In en, this message translates to:
/// **'Here you will see the\nquestions for this\nstage to reach the\ndiamond.'**
String get showcase_stepper;
/// No description provided for @showcase_hadith.
///
/// In en, this message translates to:
/// **'View sources and\nhadiths for this\nquestion'**
String get showcase_hadith;
/// No description provided for @showcase_guide.
///
/// In en, this message translates to:
/// **'This is a guide that will\nhelp you.'**
String get showcase_guide;
}
class _AppLocalizationsDelegate

20
lib/l10n/app_localizations_en.dart

@ -15,9 +15,6 @@ class AppLocalizationsEn extends AppLocalizations {
String get about_us_desc =>
'Rive combines an interactive design tool, a new stateful graphics format, a lightweight multi-platform runtime, and a blazing-fast vector renderer. \nThis end-to-end pipeline brings interfaces to life with motion. It gives designers and devs the tools to build.';
@override
String get tap_to_select => 'Tap the correct option to select.';
@override
String get select_language => 'Select language';
@ -109,4 +106,21 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get no_hadith => 'There isn\'t any hadith for this question';
@override
String get showcase_answer => 'Tap the correct option\nto select.';
@override
String get showcase_notif =>
'The announcer will\nread the answers to\nthe options to you.';
@override
String get showcase_stepper =>
'Here you will see the\nquestions for this\nstage to reach the\ndiamond.';
@override
String get showcase_hadith => 'View sources and\nhadiths for this\nquestion';
@override
String get showcase_guide => 'This is a guide that will\nhelp you.';
}

4
pubspec.lock

@ -769,10 +769,10 @@ packages:
dependency: "direct main"
description:
name: showcaseview
sha256: "82e013ac2de1ae92cc6e652badf676606057c8e17aa3afd91e78866c4b4e85b1"
sha256: "59e5edf33cbe3afb56edcbf25de553211409a576cbbbdda226c574410a314a71"
url: "https://pub.dev"
source: hosted
version: "4.0.1"
version: "5.0.1"
sky_engine:
dependency: transitive
description: flutter

2
pubspec.yaml

@ -28,7 +28,7 @@ dependencies:
path_provider: ^2.1.5
pretty_dio_logger: ^1.4.0
shared_preferences: ^2.5.3
showcaseview: ^4.0.1
showcaseview: ^5.0.1
vector_graphics: ^1.1.19
dev_dependencies:

Loading…
Cancel
Save