createTutorialOverlay function

void createTutorialOverlay({
  1. required String tagName,
  2. required BuildContext context,
  3. Function? onTap,
  4. Color? bgColor,
  5. Widget? description,
  6. bool enableHolesAnimation = true,
  7. bool enableAnimationRepeat = true,
  8. double defaultPadding = 4,
  9. List<WidgetData> widgetsData = const [],
  10. int highlightCount = 3,
  11. int animationMilliseconds = 150,
  12. int animationRepeatDelayMilliseconds = 3000,
  13. bool isOverlayBgTransparent = false,
})

Implementation

void createTutorialOverlay({
  required String tagName,
  required BuildContext context,
  Function? onTap,
  Color? bgColor,
  Widget? description,
  bool enableHolesAnimation = true,
  bool enableAnimationRepeat = true,
  double defaultPadding = 4,
  List<WidgetData> widgetsData = const [],
  int highlightCount = 3,
  int animationMilliseconds = 150,
  int animationRepeatDelayMilliseconds = 3000,
  bool isOverlayBgTransparent = false,
}) {
  final String generatedUUID = Uuid().v4();
  _printIfDebug('createTutorialOverlay', "starteed for tag $tagName");
  if (_visibleOverlayPage != null && _visibleOverlayPage!.tagName == tagName) {
    // removes shown overlay if it's beiong rewritten
    hideOverlayEntryIfExists();
  }
  int enabledVisibleWidgetsCount = 0;
  int disabledVisibleWidgetsCount = 0;
  List<GlobalKey> widgetsGlobalKeys = [];
  widgetsData.forEach((WidgetData data) {
    widgetsGlobalKeys.add(data.key);
    if (data.isEnabled) {
      enabledVisibleWidgetsCount++;
    } else {
      disabledVisibleWidgetsCount++;
    }
  });
  AnimationController? animationController;
  late CurvedAnimation animation;
  if (!isOverlayBgTransparent && enableHolesAnimation) {
    animationController = AnimationController(
        vsync: Overlay.of(context)!,
        duration: Duration(milliseconds: animationMilliseconds));
    animation = CurvedAnimation(
        parent: animationController,
        curve: Curves.easeInOut,
        reverseCurve: Curves.easeInOut);
    int animCount = 0;
    bool inTheMiddleOfFuture = false;
    animationController.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        if (animCount < highlightCount) {
          animationController!.reverse();
          animCount++;
        }
      } else if (status == AnimationStatus.dismissed) {
        if (animCount < highlightCount) {
          animationController!.forward();
        } else {
          animCount = 0;
          if (_visibleOverlayPage?.uuid == generatedUUID &&
              enableAnimationRepeat) {
            if (!inTheMiddleOfFuture) {
              inTheMiddleOfFuture = true;
              Future.delayed(
                      Duration(milliseconds: animationRepeatDelayMilliseconds))
                  .then((d) {
                if (_visibleOverlayPage?.uuid == generatedUUID) {
                  animationController!.forward();
                  inTheMiddleOfFuture = false;
                }
              });
            }
          }
        }
      }
    });
  }
  _overlays[tagName] = OverlayData(
    isOverlayBgTransparent: isOverlayBgTransparent,
    uuid: generatedUUID,
    context: context,
    animationController: animationController!,
    widgetsGlobalKeys: widgetsGlobalKeys,
    enabledVisibleWidgetsCount: enabledVisibleWidgetsCount,
    disabledVisibleWidgetsCount: disabledVisibleWidgetsCount,
    tagName: tagName,
    showOverlay: () {
      animationController?.reset();
      animationController?.forward();
    },
    hideOverlay: () {
      animationController?.reset();
    },
    entry: OverlayEntry(
      builder: (BuildContext context) => FutureBuilder(
        future: waitForFrameToEnd(),
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          if (isOverlayBgTransparent) {
            return description!;
          } else {
            return GestureDetector(
              onTap: onTap as void Function()?,
              child: ClipPath(
                clipper: InvertedClipper(
                    padding: defaultPadding,
                    animation: animation,
                    reclip: animationController,
                    widgetsData: widgetsData),
                child: CustomPaint(
                  child: Container(
                    child: description,
                  ),
                  painter: OverlayPainter(
                    padding: defaultPadding,
                    animation: animation,
                    bgColor: bgColor,
                    context: context,
                    widgetsData: widgetsData,
                  ),
                ),
              ),
            );
          }
        },
      ),
    ),
  );
}