You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
RSGDoctorApp/lib/widgets/shared/user-guid/app_showcase_widget.dart

232 lines
6.3 KiB
Dart

import 'package:flutter/material.dart';
class ShowCaseWidget extends StatefulWidget {
final Builder builder;
/// Triggered when all the showcases are completed.
final VoidCallback? onFinish;
/// Triggered every time on start of each showcase.
final Function(int?, GlobalKey)? onStart;
/// Triggered every time on completion of each showcase
final Function(int?, GlobalKey)? onComplete;
/// Whether all showcases will auto sequentially start
/// having time interval of [autoPlayDelay] .
///
/// Default to `false`
final bool autoPlay;
/// Visibility time of current showcase when [autoplay] sets to true.
///
/// Default to [Duration(seconds: 3)]
final Duration autoPlayDelay;
/// Whether blocking user interaction while [autoPlay] is enabled.
///
/// Default to `false`
final bool enableAutoPlayLock;
/// Whether disabling bouncing/moving animation for all tooltips
/// while showcasing
///
/// Default to `false`
final bool disableMovingAnimation;
/// Whether disabling initial scale animation for all the default tooltips
/// when showcase is started and completed
///
/// Default to `false`
final bool disableScaleAnimation;
/// Whether disabling barrier interaction
final bool disableBarrierInteraction;
/// Provides time duration for auto scrolling when [enableAutoScroll] is true
final Duration scrollDuration;
/// Default overlay blur used by showcase. if [Showcase.blurValue]
/// is not provided.
///
/// Default value is 0.
final double blurValue;
/// While target widget is out viewport then
/// whether enabling auto scroll so as to make the target widget visible.
final bool enableAutoScroll;
/// Enable/disable showcase globally. Enabled by default.
final bool enableShowcase;
const ShowCaseWidget({
required this.builder,
this.onFinish,
this.onStart,
this.onComplete,
this.autoPlay = false,
this.autoPlayDelay = const Duration(milliseconds: 2000),
this.enableAutoPlayLock = false,
this.blurValue = 0,
this.scrollDuration = const Duration(milliseconds: 300),
this.disableMovingAnimation = false,
this.disableScaleAnimation = false,
this.enableAutoScroll = false,
this.disableBarrierInteraction = false,
this.enableShowcase = true,
});
static GlobalKey? activeTargetWidget(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType<_InheritedShowCaseView>()
?.activeWidgetIds;
}
static ShowCaseWidgetState of(BuildContext context) {
final state = context.findAncestorStateOfType<ShowCaseWidgetState>();
if (state != null) {
return state;
} else {
throw Exception('Please provide ShowCaseView context');
}
}
@override
ShowCaseWidgetState createState() => ShowCaseWidgetState();
}
class ShowCaseWidgetState extends State<ShowCaseWidget> {
List<GlobalKey>? ids;
int? activeWidgetId;
/// These properties are only here so that it can be accessed by
/// [Showcase]
bool get autoPlay => widget.autoPlay;
bool get disableMovingAnimation => widget.disableMovingAnimation;
bool get disableScaleAnimation => widget.disableScaleAnimation;
Duration get autoPlayDelay => widget.autoPlayDelay;
bool get enableAutoPlayLock => widget.enableAutoPlayLock;
bool get enableAutoScroll => widget.enableAutoScroll;
bool get disableBarrierInteraction => widget.disableBarrierInteraction;
bool get enableShowcase => widget.enableShowcase;
/// Returns value of [ShowCaseWidget.blurValue]
double get blurValue => widget.blurValue;
/// Starts Showcase view from the beginning of specified list of widget ids.
/// If this function is used when showcase has been disabled then it will
/// throw an exception.
void startShowCase(List<GlobalKey> widgetIds) {
if (!enableShowcase) {
throw Exception(
"You are trying to start Showcase while it has been disabled with "
"`enableShowcase` parameter to false from ShowCaseWidget",
);
}
if (!mounted) return;
setState(() {
ids = widgetIds;
activeWidgetId = 0;
_onStart();
});
}
/// Completes showcase of given key and starts next one
/// otherwise will finish the entire showcase view
void completed(GlobalKey? key) {
if (ids != null && ids![activeWidgetId!] == key && mounted) {
setState(() {
_onComplete();
activeWidgetId = activeWidgetId! + 1;
_onStart();
if (activeWidgetId! >= ids!.length) {
_cleanupAfterSteps();
widget.onFinish?.call();
}
});
}
}
/// Completes current active showcase and starts next one
/// otherwise will finish the entire showcase view
void next() {
if (ids != null && mounted) {
setState(() {
_onComplete();
activeWidgetId = activeWidgetId! + 1;
_onStart();
if (activeWidgetId! >= ids!.length) {
_cleanupAfterSteps();
widget.onFinish?.call();
}
});
}
}
/// Completes current active showcase and starts previous one
/// otherwise will finish the entire showcase view
void previous() {
if (ids != null && ((activeWidgetId ?? 0) - 1) >= 0 && mounted) {
setState(() {
_onComplete();
activeWidgetId = activeWidgetId! - 1;
_onStart();
if (activeWidgetId! >= ids!.length) {
_cleanupAfterSteps();
widget.onFinish?.call();
}
});
}
}
/// Dismiss entire showcase view
void dismiss() {
if (mounted) setState(_cleanupAfterSteps);
}
void _onStart() {
if (activeWidgetId! < ids!.length) {
widget.onStart?.call(activeWidgetId, ids![activeWidgetId!]);
}
}
void _onComplete() {
widget.onComplete?.call(activeWidgetId, ids![activeWidgetId!]);
}
void _cleanupAfterSteps() {
ids = null;
activeWidgetId = null;
}
@override
Widget build(BuildContext context) {
return _InheritedShowCaseView(
activeWidgetIds: ids?.elementAt(activeWidgetId!),
child: widget.builder,
);
}
}
class _InheritedShowCaseView extends InheritedWidget {
final GlobalKey? activeWidgetIds;
const _InheritedShowCaseView({
required this.activeWidgetIds,
required Widget child,
}) : super(child: child);
@override
bool updateShouldNotify(_InheritedShowCaseView oldWidget) =>
oldWidget.activeWidgetIds != activeWidgetIds;
}