Skip to content

Commit 4bb6360

Browse files
committed
refactor: 🐎 Overlay update optimization
1 parent d5a00e2 commit 4bb6360

18 files changed

+443
-182
lines changed

lib/src/models/action_button_icon.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,15 @@ class ActionButtonIcon {
5151

5252
/// Optional padding to apply around the icon.
5353
final EdgeInsets? padding;
54+
55+
@override
56+
bool operator ==(Object other) {
57+
if (identical(this, other)) return true;
58+
return other is ActionButtonIcon &&
59+
icon == other.icon &&
60+
padding == other.padding;
61+
}
62+
63+
@override
64+
int get hashCode => Object.hash(icon, padding);
5465
}
File renamed without changes.

lib/src/models/showcase_scope.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2020
* SOFTWARE.
2121
*/
22+
import 'package:flutter/foundation.dart';
2223
import 'package:flutter/widgets.dart';
2324

2425
import '../showcase/showcase_controller.dart';
@@ -51,4 +52,16 @@ class ShowcaseScope {
5152
/// - Key: GlobalKey of a showcase (provided by user)
5253
/// - Value: Map of showcase IDs to their controllers
5354
final Map<GlobalKey, Map<int, ShowcaseController>> controllers = {};
55+
56+
@override
57+
bool operator ==(Object other) {
58+
if (identical(this, other)) return true;
59+
return other is ShowcaseScope &&
60+
name == other.name &&
61+
showcaseView == other.showcaseView &&
62+
mapEquals(controllers, other.controllers);
63+
}
64+
65+
@override
66+
int get hashCode => Object.hash(name, showcaseView, controllers);
5467
}

lib/src/models/tooltip_action_button.dart

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2020
* SOFTWARE.
2121
*/
22+
import 'package:flutter/foundation.dart';
2223
import 'package:flutter/material.dart';
2324

2425
import '../../showcaseview.dart';
@@ -126,4 +127,44 @@ class TooltipActionButton {
126127
/// This only works for the global action widgets
127128
/// Defaults to []
128129
final List<GlobalKey> hideActionWidgetForShowcase;
130+
131+
@override
132+
bool operator ==(Object other) {
133+
if (identical(this, other)) return true;
134+
return other is TooltipActionButton &&
135+
other.type == type &&
136+
other.borderRadius == borderRadius &&
137+
other.padding == padding &&
138+
other.backgroundColor == backgroundColor &&
139+
other.textStyle == textStyle &&
140+
other.leadIcon == leadIcon &&
141+
other.tailIcon == tailIcon &&
142+
other.name == name &&
143+
other.button == button &&
144+
other.border == border &&
145+
listEquals(
146+
other.hideActionWidgetForShowcase,
147+
hideActionWidgetForShowcase,
148+
);
149+
}
150+
151+
@override
152+
int get hashCode {
153+
return Object.hashAllUnordered(
154+
[
155+
type,
156+
borderRadius,
157+
padding,
158+
hideActionWidgetForShowcase,
159+
backgroundColor,
160+
textStyle,
161+
leadIcon,
162+
tailIcon,
163+
name,
164+
onTap,
165+
button,
166+
border,
167+
],
168+
);
169+
}
129170
}

lib/src/models/tooltip_action_config.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,26 @@ class TooltipActionConfig {
5959
/// If aligning items according to their baseline, which baseline to use.
6060
/// This must be set if using baseline alignment.
6161
final TextBaseline? textBaseline;
62+
63+
@override
64+
bool operator ==(Object other) {
65+
if (identical(this, other)) return true;
66+
return other is TooltipActionConfig &&
67+
alignment == other.alignment &&
68+
actionGap == other.actionGap &&
69+
position == other.position &&
70+
gapBetweenContentAndAction == other.gapBetweenContentAndAction &&
71+
crossAxisAlignment == other.crossAxisAlignment &&
72+
textBaseline == other.textBaseline;
73+
}
74+
75+
@override
76+
int get hashCode => Object.hashAllUnordered([
77+
alignment,
78+
actionGap,
79+
position,
80+
gapBetweenContentAndAction,
81+
crossAxisAlignment,
82+
textBaseline,
83+
]);
6284
}

lib/src/showcase/showcase.dart

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,12 @@ class _ShowcaseState extends State<Showcase> {
573573
// This is to support hot reload
574574
_updateControllerValues();
575575

576-
_controller.recalculateRootWidgetSize(context);
576+
_controller.recalculateRootWidgetSize(
577+
context,
578+
shouldUpdateOverlay:
579+
_showCaseWidgetManager.showcaseView.getActiveShowcaseKey ==
580+
widget.showcaseKey,
581+
);
577582
return widget.child;
578583
}
579584

@@ -584,14 +589,15 @@ class _ShowcaseState extends State<Showcase> {
584589
id: _uniqueId,
585590
scope: _showCaseWidgetManager.name,
586591
);
587-
588592
super.dispose();
589593
}
590594

591595
void _updateControllerValues() {
592-
_showCaseWidgetManager = ShowcaseService.instance.getScope(
596+
final manager = ShowcaseService.instance.getScope(
593597
scope: _showCaseWidgetManager.name,
594598
);
599+
if (manager == _showCaseWidgetManager) return;
600+
_showCaseWidgetManager = manager;
595601
ShowcaseService.instance.addController(
596602
controller: _controller
597603
..showcaseView = _showCaseWidgetManager.showcaseView,

lib/src/showcase/showcase_controller.dart

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import 'dart:math';
2424
import 'package:flutter/foundation.dart';
2525
import 'package:flutter/widgets.dart';
2626

27-
import '../models/linked_showcase_data.dart';
27+
import '../models/linked_showcase_data_model.dart';
2828
import '../models/tooltip_action_config.dart';
2929
import '../tooltip/tooltip.dart';
3030
import '../utils/overlay_manager.dart';
@@ -134,20 +134,23 @@ class ShowcaseController {
134134
///
135135
/// This method is typically called internally by the showcase system but
136136
/// can also be called manually to force a recalculation of showcase elements.
137-
void startShowcase() {
137+
void startShowcase({bool shouldUpdateOverlay = true}) {
138138
if (!showcaseView.enableShowcase || !_mounted) return;
139139

140-
recalculateRootWidgetSize(_context);
140+
recalculateRootWidgetSize(
141+
_context,
142+
shouldUpdateOverlay: shouldUpdateOverlay,
143+
);
141144
globalFloatingActionWidget = showcaseView
142145
.getFloatingActionWidget(config.showcaseKey)
143146
?.call(_context);
144-
final size = rootWidgetSize ?? MediaQuery.sizeOf(_context);
145-
position ??= TargetPositionService(
146-
rootRenderObject: rootRenderObject,
147-
screenSize: size,
148-
renderBox: _context.findRenderObject() as RenderBox?,
149-
padding: config.targetPadding,
150-
);
147+
// final size = rootWidgetSize ?? MediaQuery.sizeOf(_context);
148+
// position ??= TargetPositionService(
149+
// rootRenderObject: rootRenderObject,
150+
// screenSize: size,
151+
// renderBox: _context.findRenderObject() as RenderBox?,
152+
// padding: config.targetPadding,
153+
// );
151154
}
152155

153156
/// Used to scroll the target into view.
@@ -165,32 +168,32 @@ class ShowcaseController {
165168
///
166169
/// Returns a Future that completes when scrolling is finished. If the widget
167170
/// is unmounted during scrolling, the operation will be canceled safely.
168-
Future<void> scrollIntoView() async {
171+
Future<void> scrollIntoView({bool shouldUpdateOverlay = true}) async {
169172
if (!_mounted) {
170173
assert(_mounted, 'Widget has been unmounted');
171174
return;
172175
}
173176

174177
isScrollRunning = true;
175-
updateControllerData();
176-
startShowcase();
177-
OverlayManager.instance.update(
178-
show: showcaseView.isShowcaseRunning,
179-
scope: showcaseView.scope,
180-
);
178+
// updateControllerData();
179+
startShowcase(shouldUpdateOverlay: shouldUpdateOverlay);
180+
// OverlayManager.instance.update(
181+
// show: showcaseView.isShowcaseRunning,
182+
// scope: showcaseView.scope,
183+
// );
181184
await Scrollable.ensureVisible(
182185
_context,
183186
duration: showcaseView.scrollDuration,
184187
alignment: config.scrollAlignment,
185188
);
186189

187190
isScrollRunning = false;
188-
updateControllerData();
189-
startShowcase();
190-
OverlayManager.instance.update(
191-
show: showcaseView.isShowcaseRunning,
192-
scope: showcaseView.scope,
193-
);
191+
// updateControllerData();
192+
startShowcase(shouldUpdateOverlay: shouldUpdateOverlay);
193+
// OverlayManager.instance.update(
194+
// show: showcaseView.isShowcaseRunning,
195+
// scope: showcaseView.scope,
196+
// );
194197
}
195198

196199
/// Handles tap on barrier area.
@@ -214,20 +217,28 @@ class ShowcaseController {
214217
///
215218
/// Parameter:
216219
/// * [context] The BuildContext of the [Showcase] widget.
217-
void recalculateRootWidgetSize(BuildContext context) {
220+
void recalculateRootWidgetSize(
221+
BuildContext context, {
222+
bool shouldUpdateOverlay = true,
223+
}) {
224+
if (!showcaseView.enableShowcase || !showcaseView.isShowcaseRunning) return;
218225
WidgetsBinding.instance.addPostFrameCallback((_) {
219-
if (!context.mounted) return;
226+
if (!context.mounted ||
227+
!showcaseView.enableShowcase ||
228+
!showcaseView.isShowcaseRunning) {
229+
return;
230+
}
220231

221232
_initRootWidget();
222-
if (!showcaseView.enableShowcase) return;
223233

224234
updateControllerData();
225-
if (!showcaseView.isShowcaseRunning) return;
226235

227-
OverlayManager.instance.update(
228-
show: showcaseView.isShowcaseRunning,
229-
scope: showcaseView.scope,
230-
);
236+
if (shouldUpdateOverlay) {
237+
OverlayManager.instance.update(
238+
show: showcaseView.isShowcaseRunning,
239+
scope: showcaseView.scope,
240+
);
241+
}
231242
});
232243
}
233244

lib/src/showcase/showcase_view.dart

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*/
2222
import 'dart:async';
2323

24+
import 'package:flutter/foundation.dart';
2425
import 'package:flutter/material.dart';
2526

2627
import '../models/tooltip_action_button.dart';
@@ -320,10 +321,7 @@ class ShowcaseView {
320321
_ids = widgetIds;
321322
_activeWidgetId = 0;
322323
_onStart();
323-
OverlayManager.instance.update(
324-
show: isShowcaseRunning,
325-
scope: scope,
326-
);
324+
// OverlayManager.instance.update(show: isShowcaseRunning, scope: scope);
327325
} else {
328326
Future.delayed(delay, () => _startShowcase(Duration.zero, widgetIds));
329327
}
@@ -410,14 +408,17 @@ class ShowcaseView {
410408
onStart?.call(_activeWidgetId, _ids![_activeWidgetId!]);
411409
final controllers = _getCurrentActiveControllers;
412410
final controllerLength = controllers.length;
413-
for (var i = 0; i < controllerLength; i++) {
414-
final controller = controllers[i];
415-
final isAutoScroll =
416-
controller.config.enableAutoScroll ?? enableAutoScroll;
417-
if (controllerLength == 1 && isAutoScroll) {
418-
await controller.scrollIntoView();
419-
} else {
420-
controller.startShowcase();
411+
final firstController = controllers.firstOrNull;
412+
413+
final isAutoScroll =
414+
firstController?.config.enableAutoScroll ?? enableAutoScroll;
415+
416+
// Auto scroll is not supported for multi-showcase feature.
417+
if (controllerLength == 1 && isAutoScroll) {
418+
await firstController?.scrollIntoView();
419+
} else {
420+
for (var i = 0; i < controllerLength; i++) {
421+
controllers[i].startShowcase(shouldUpdateOverlay: i == 0);
421422
}
422423
}
423424
}
@@ -468,4 +469,52 @@ class ShowcaseView {
468469
_ids = _activeWidgetId = null;
469470
_cancelTimer();
470471
}
472+
473+
@override
474+
bool operator ==(Object other) {
475+
if (identical(this, other)) return true;
476+
return other is ShowcaseView &&
477+
scope == other.scope &&
478+
autoPlay == other.autoPlay &&
479+
autoPlayDelay == other.autoPlayDelay &&
480+
enableAutoPlayLock == other.enableAutoPlayLock &&
481+
blurValue == other.blurValue &&
482+
scrollDuration == other.scrollDuration &&
483+
disableMovingAnimation == other.disableMovingAnimation &&
484+
disableScaleAnimation == other.disableScaleAnimation &&
485+
enableAutoScroll == other.enableAutoScroll &&
486+
disableBarrierInteraction == other.disableBarrierInteraction &&
487+
enableShowcase == other.enableShowcase &&
488+
globalTooltipActionConfig == other.globalTooltipActionConfig &&
489+
listEquals(globalTooltipActions, other.globalTooltipActions) &&
490+
listEquals(
491+
hideFloatingActionWidgetForShowcase,
492+
other.hideFloatingActionWidgetForShowcase,
493+
);
494+
}
495+
496+
@override
497+
int get hashCode {
498+
return Object.hashAllUnordered([
499+
scope,
500+
onFinish,
501+
onDismiss,
502+
onStart,
503+
onComplete,
504+
autoPlay,
505+
autoPlayDelay,
506+
enableAutoPlayLock,
507+
blurValue,
508+
scrollDuration,
509+
disableMovingAnimation,
510+
disableScaleAnimation,
511+
enableAutoScroll,
512+
disableBarrierInteraction,
513+
enableShowcase,
514+
globalTooltipActionConfig,
515+
globalTooltipActions,
516+
globalFloatingActionWidget,
517+
hideFloatingActionWidgetForShowcase,
518+
]);
519+
}
471520
}
File renamed without changes.

0 commit comments

Comments
 (0)