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.
232 lines
6.3 KiB
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;
|
|
} |