@@ -23,16 +23,13 @@ class ShowcaseController {
2323 ///
2424 /// * [id] - Unique identifier for this showcase instance
2525 /// * [key] - Global key associated with the showcase widget
26- /// * [config ] - Configuration settings for the showcase
26+ /// * [showcaseState ] - Reference to the showcase state
2727 /// * [showCaseWidgetState] - Reference to the parent showcase widget state
28- /// * [scrollIntoViewCallback] - Optional callback to scroll the target into view
2928 ShowcaseController ({
3029 required this .id,
3130 required this .key,
32- required this .config ,
31+ required this .showcaseState ,
3332 required this .showCaseWidgetState,
34- required this .updateControllerValue,
35- this .scrollIntoViewCallback,
3633 }) {
3734 showCaseWidgetState.registerShowcaseController (
3835 controller: this ,
@@ -49,7 +46,7 @@ class ShowcaseController {
4946 final GlobalKey key;
5047
5148 /// Configuration for the showcase
52- Showcase config ;
49+ State < Showcase > showcaseState ;
5350
5451 /// Reference to the parent showcase widget state
5552 ShowCaseWidgetState showCaseWidgetState;
@@ -60,23 +57,9 @@ class ShowcaseController {
6057 /// Data model for linked showcases
6158 LinkedShowcaseDataModel ? linkedShowcaseDataModel;
6259
63- /// Callback to start the showcase
64- VoidCallback ? startShowcase;
65-
66- /// Optional function to scroll the target into view
67- final ValueGetter <Future <void >>? scrollIntoViewCallback;
68-
6960 /// Optional function to reverse the animation
7061 ValueGetter <Future <void >>? reverseAnimationCallback;
7162
72- /// Function to update the controller value
73- ///
74- /// Main use of this is to update the controller data just before overlay is
75- /// inserted so we can get the correct position. Which is need in
76- /// page transition case where page transition may take some time to reach
77- /// to it's original position
78- VoidCallback updateControllerValue;
79-
8063 /// Size of the root widget
8164 Size ? rootWidgetSize;
8265
@@ -95,6 +78,24 @@ class ShowcaseController {
9578 /// Global floating action widget to be displayed
9679 FloatingActionWidget ? globalFloatingActionWidget;
9780
81+ /// Returns the Showcase widget configuration
82+ ///
83+ /// Provides access to all properties and settings of the current showcase widget.
84+ /// This is used throughout the controller to access showcase configuration options.
85+ Showcase get config => showcaseState.widget;
86+
87+ /// Returns the BuildContext for this showcase
88+ ///
89+ /// Used for positioning calculations and widget rendering.
90+ /// This context represents the location of the showcase target in the widget tree.
91+ BuildContext get _context => showcaseState.context;
92+
93+ /// Checks if the showcase context is still valid
94+ ///
95+ /// Returns true if the context is mounted (valid) and false otherwise.
96+ /// Used to prevent operations on widgets that have been removed from the tree.
97+ bool get _mounted => showcaseState.context.mounted;
98+
9899 /// Initializes the root widget size and render object
99100 ///
100101 /// Must be called after the widget is mounted to ensure proper measurements.
@@ -121,10 +122,7 @@ class ShowcaseController {
121122 ? MediaQuery .of (context).size
122123 : rootRenderObject? .size;
123124 if (! showCaseWidgetState.enableShowcase) return ;
124- updateControllerData (
125- context.findRenderObject () as RenderBox ? ,
126- MediaQuery .of (context).size,
127- );
125+ updateControllerData ();
128126 showCaseWidgetState.updateOverlay? .call (
129127 showCaseWidgetState.isShowcaseRunning,
130128 );
@@ -136,12 +134,13 @@ class ShowcaseController {
136134 /// Rebuilds the showcase overlay with updated positioning information.
137135 /// Creates positioning data and updates the visual representation.
138136 ///
139- /// * [renderBox] The RenderBox of the target widget
140- /// * [screenSize] The current screen size
141- void updateControllerData (
142- RenderBox ? renderBox,
143- Size screenSize,
144- ) {
137+ /// Another use of this is to update the controller data just before overlay is
138+ /// inserted so we can get the correct position. Which is need in
139+ /// page transition case where page transition may take some time to reach
140+ /// to it's original position
141+ void updateControllerData () {
142+ final renderBox = _context.findRenderObject () as RenderBox ? ;
143+ final screenSize = MediaQuery .of (_context).size;
145144 final size = rootWidgetSize ?? screenSize;
146145 final newPosition = GetPosition (
147146 rootRenderObject: rootRenderObject,
@@ -248,6 +247,79 @@ class ShowcaseController {
248247 ];
249248 }
250249
250+ /// Callback to start the showcase
251+ ///
252+ /// Initializes the showcase by calculating positions and preparing visual elements.
253+ /// This method is called when a showcase is about to be displayed to ensure all
254+ /// positioning data is accurate and up-to-date.
255+ ///
256+ /// The method performs these key actions:
257+ /// - Exits early if showcases are disabled in the parent widget
258+ /// - Recalculates the root widget size to ensure accurate positioning
259+ /// - Sets up any global floating action widgets
260+ /// - Initializes position data if not already set
261+ ///
262+ /// This method is typically called internally by the showcase system but
263+ /// can also be called manually to force a recalculation of showcase elements.
264+ void startShowcase () {
265+ if (! showCaseWidgetState.enableShowcase) return ;
266+
267+ recalculateRootWidgetSize (_context);
268+ globalFloatingActionWidget = showCaseWidgetState
269+ .globalFloatingActionWidget (config.showcaseKey)
270+ ? .call (_context);
271+ final size = rootWidgetSize ?? MediaQuery .of (_context).size;
272+ position ?? = GetPosition (
273+ rootRenderObject: rootRenderObject,
274+ renderBox: _context.findRenderObject () as RenderBox ? ,
275+ padding: config.targetPadding,
276+ screenWidth: size.width,
277+ screenHeight: size.height,
278+ );
279+ }
280+
281+ /// Used to scroll the target into view
282+ ///
283+ /// Ensures the showcased widget is visible on screen by scrolling to it.
284+ /// This method handles the complete scrolling process including:
285+ ///
286+ /// - Setting visual indicators while scrolling is in progress
287+ /// - Updating the overlay to show loading state
288+ /// - Performing the actual scrolling operation
289+ /// - Refreshing the showcase display after scrolling completes
290+ ///
291+ /// The method shows a loading indicator during scrolling and updates
292+ /// the showcase position after scrolling completes. It manages the
293+ /// `isScrollRunning` state to coordinate UI updates.
294+ ///
295+ /// Note: Multi Showcase will not be scrolled into view
296+ ///
297+ /// Returns a Future that completes when scrolling is finished. If the widget
298+ /// is unmounted during scrolling, the operation will be canceled safely.
299+ Future <void > scrollIntoView () async {
300+ if (! _mounted) return ;
301+
302+ isScrollRunning = true ;
303+ updateControllerData ();
304+ startShowcase ();
305+ showCaseWidgetState.updateOverlay? .call (
306+ showCaseWidgetState.isShowcaseRunning,
307+ );
308+ await Scrollable .ensureVisible (
309+ _context,
310+ duration: showCaseWidgetState.widget.scrollDuration,
311+ alignment: config.scrollAlignment,
312+ );
313+ if (! _mounted) return ;
314+
315+ isScrollRunning = false ;
316+ updateControllerData ();
317+ startShowcase ();
318+ showCaseWidgetState.updateOverlay? .call (
319+ showCaseWidgetState.isShowcaseRunning,
320+ );
321+ }
322+
251323 /// Moves to the next showcase if any are remaining
252324 ///
253325 /// Called when a showcase is completed.
0 commit comments