SimformSolutionsPvtLtd / flutter_showcaseview

Flutter plugin that allows you to showcase your features on flutter application. πŸ‘ŒπŸ”πŸŽ‰
https://pub.dev/packages/showcaseview
MIT License
1.52k stars 442 forks source link

Showcase not targeting/focusing/aligning widget properly #378

Closed Ujas-Majithiya closed 1 year ago

Ujas-Majithiya commented 1 year ago

Many people are facing this same issue while showcasing.

What's happening behind the scene?

When you wrap your widget with showcase and provide a GlobalKey, it first calculates the position of the widget on the screen and then when you start showcase, it will first calculate where the tooltip needs to be placed and from the calculation, it will place the tooltip to near the showcasing widget by using the OverlayEntry.

So what's the issue?

The issue is you can't calculate the position of a widget before it's rendered on the screen. And we can't calculate it because if a widget is not placed on the screen then how will you calculate its position. It's as simple as that.

What's the solution then?

Rather than directly starting the showcase inside initState, use addPostFrameCallback from WidgetsBinding or SchedulerBinding, both are fine. What it does is it will skip a frame. So using this we will wait for the build method to be called. Once it is called we know that our showcasing widget is now rendered on the screen so we can start the showcase. Eg.

 @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback(
      (_) => ShowCaseWidget.of(context).startShowCase([...]),
    );
}

That's it!!

One extra step for widgets that animates

If your widget animates first when it is rendered for the first time on the screen then it might happen that widget is still in transition and the showcase is trying to calculate its position then it won't be able to calculate the accurate position of the widget. So to resolve this, put as much as delay the animated widget needs to complete its animation.

For example, if a widget needs 200ms to complete its animation then do something like this,

@override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback(
      (_) async {
          await Future.delay(Duration(milliseconds: 200));
          ShowCaseWidget.of(context).startShowCase([...]);
      },
    );
}
Ujas-Majithiya commented 1 year ago

I'm going to pin this issue so that people can find this.