@@ -194,41 +194,18 @@ class Tooltip extends StatefulWidget {
194
194
/// * [Feedback] , for providing platform-specific feedback to certain actions.
195
195
final bool ? enableFeedback;
196
196
197
- static final List <_TooltipState > _openedTooltips = < _TooltipState > [];
198
-
199
- // Causes any current tooltips to be concealed. Only called for mouse hover enter
200
- // detections. Won't conceal the supplied tooltip.
201
- static void _concealOtherTooltips (_TooltipState current) {
202
- if (_openedTooltips.isNotEmpty) {
203
- // Avoid concurrent modification.
204
- final List <_TooltipState > openedTooltips = _openedTooltips.toList ();
205
- for (final _TooltipState state in openedTooltips) {
206
- if (state == current) {
207
- continue ;
208
- }
209
- state._concealTooltip ();
210
- }
211
- }
212
- }
213
-
214
- // Causes the most recently concealed tooltip to be revealed. Only called for mouse
215
- // hover exit detections.
216
- static void _revealLastTooltip () {
217
- if (_openedTooltips.isNotEmpty) {
218
- _openedTooltips.last._revealTooltip ();
219
- }
220
- }
197
+ static final Set <_TooltipState > _openedToolTips = < _TooltipState > {};
221
198
222
199
/// Dismiss all of the tooltips that are currently shown on the screen.
223
200
///
224
201
/// This method returns true if it successfully dismisses the tooltips. It
225
202
/// returns false if there is no tooltip shown on the screen.
226
203
static bool dismissAllToolTips () {
227
- if (_openedTooltips .isNotEmpty) {
204
+ if (_openedToolTips .isNotEmpty) {
228
205
// Avoid concurrent modification.
229
- final List <_TooltipState > openedTooltips = _openedTooltips. toList ( );
230
- for (final _TooltipState state in openedTooltips ) {
231
- state._dismissTooltip (immediately: true );
206
+ final List <_TooltipState > openedToolTips = List < _TooltipState >. from (_openedToolTips );
207
+ for (final _TooltipState state in openedToolTips ) {
208
+ state._hideTooltip (immediately: true );
232
209
}
233
210
return true ;
234
211
}
@@ -278,7 +255,7 @@ class _TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
278
255
late bool excludeFromSemantics;
279
256
late AnimationController _controller;
280
257
OverlayEntry ? _entry;
281
- Timer ? _dismissTimer ;
258
+ Timer ? _hideTimer ;
282
259
Timer ? _showTimer;
283
260
late Duration showDuration;
284
261
late Duration hoverShowDuration;
@@ -287,14 +264,10 @@ class _TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
287
264
bool _pressActivated = false ;
288
265
late TooltipTriggerMode triggerMode;
289
266
late bool enableFeedback;
290
- late bool _isConcealed;
291
- late bool _forceRemoval;
292
267
293
268
@override
294
269
void initState () {
295
270
super .initState ();
296
- _isConcealed = false ;
297
- _forceRemoval = false ;
298
271
_mouseIsConnected = RendererBinding .instance! .mouseTracker.mouseIsConnected;
299
272
_controller = AnimationController (
300
273
duration: _fadeInDuration,
@@ -360,96 +333,47 @@ class _TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
360
333
}
361
334
362
335
void _handleStatusChanged (AnimationStatus status) {
363
- // If this tip is concealed, don't remove it, even if it is dismissed, so that we can
364
- // reveal it later, unless it has explicitly been hidden with _dismissTooltip.
365
- if (status == AnimationStatus .dismissed && (_forceRemoval || ! _isConcealed)) {
366
- _removeEntry ();
336
+ if (status == AnimationStatus .dismissed) {
337
+ _hideTooltip (immediately: true );
367
338
}
368
339
}
369
340
370
- void _dismissTooltip ({ bool immediately = false }) {
341
+ void _hideTooltip ({ bool immediately = false }) {
371
342
_showTimer? .cancel ();
372
343
_showTimer = null ;
373
344
if (immediately) {
374
345
_removeEntry ();
375
346
return ;
376
347
}
377
- // So it will be removed when it's done reversing, regardless of whether it is
378
- // still concealed or not.
379
- _forceRemoval = true ;
380
348
if (_pressActivated) {
381
- _dismissTimer ?? = Timer (showDuration, _controller.reverse);
349
+ _hideTimer ?? = Timer (showDuration, _controller.reverse);
382
350
} else {
383
- _dismissTimer ?? = Timer (hoverShowDuration, _controller.reverse);
351
+ _hideTimer ?? = Timer (hoverShowDuration, _controller.reverse);
384
352
}
385
353
_pressActivated = false ;
386
354
}
387
355
388
356
void _showTooltip ({ bool immediately = false }) {
389
- _dismissTimer ? .cancel ();
390
- _dismissTimer = null ;
357
+ _hideTimer ? .cancel ();
358
+ _hideTimer = null ;
391
359
if (immediately) {
392
360
ensureTooltipVisible ();
393
361
return ;
394
362
}
395
363
_showTimer ?? = Timer (waitDuration, ensureTooltipVisible);
396
364
}
397
365
398
- void _concealTooltip () {
399
- if (_isConcealed || _forceRemoval) {
400
- // Already concealed, or it's being removed.
401
- return ;
402
- }
403
- _isConcealed = true ;
404
- _dismissTimer? .cancel ();
405
- _dismissTimer = null ;
406
- _showTimer? .cancel ();
407
- _showTimer = null ;
408
- if (_entry!= null ) {
409
- _entry! .remove ();
410
- }
411
- _controller.reverse ();
412
- }
413
-
414
- void _revealTooltip () {
415
- if (! _isConcealed) {
416
- // Already uncovered.
417
- return ;
418
- }
419
- _isConcealed = false ;
420
- _dismissTimer? .cancel ();
421
- _dismissTimer = null ;
422
- _showTimer? .cancel ();
423
- _showTimer = null ;
424
- if (! _entry! .mounted) {
425
- final OverlayState overlayState = Overlay .of (
426
- context,
427
- debugRequiredFor: widget,
428
- )! ;
429
- overlayState.insert (_entry! );
430
- }
431
- SemanticsService .tooltip (widget.message);
432
- _controller.forward ();
433
- }
434
-
435
366
/// Shows the tooltip if it is not already visible.
436
367
///
437
- /// Returns `false` when the tooltip was already visible.
368
+ /// Returns `false` when the tooltip was already visible or if the context has
369
+ /// become null.
438
370
bool ensureTooltipVisible () {
439
371
_showTimer? .cancel ();
440
372
_showTimer = null ;
441
- _forceRemoval = false ;
442
- if (_isConcealed) {
443
- if (_mouseIsConnected) {
444
- Tooltip ._concealOtherTooltips (this );
445
- }
446
- _revealTooltip ();
447
- return true ;
448
- }
449
373
if (_entry != null ) {
450
374
// Stop trying to hide, if we were.
451
- _dismissTimer ? .cancel ();
452
- _dismissTimer = null ;
375
+ _hideTimer ? .cancel ();
376
+ _hideTimer = null ;
453
377
_controller.forward ();
454
378
return false ; // Already visible.
455
379
}
@@ -458,17 +382,6 @@ class _TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
458
382
return true ;
459
383
}
460
384
461
- static final Set <_TooltipState > _mouseIn = < _TooltipState > {};
462
-
463
- void _handleMouseEnter () {
464
- _showTooltip ();
465
- }
466
-
467
- void _handleMouseExit ({bool immediately = false }) {
468
- // If the tip is currently covered, we can just remove it without waiting.
469
- _dismissTooltip (immediately: _isConcealed || immediately);
470
- }
471
-
472
385
void _createNewEntry () {
473
386
final OverlayState overlayState = Overlay .of (
474
387
context,
@@ -491,8 +404,8 @@ class _TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
491
404
height: height,
492
405
padding: padding,
493
406
margin: margin,
494
- onEnter: _mouseIsConnected ? (_ ) => _handleMouseEnter () : null ,
495
- onExit: _mouseIsConnected ? (_ ) => _handleMouseExit () : null ,
407
+ onEnter: _mouseIsConnected ? (PointerEnterEvent event ) => _showTooltip () : null ,
408
+ onExit: _mouseIsConnected ? (PointerExitEvent event ) => _hideTooltip () : null ,
496
409
decoration: decoration,
497
410
textStyle: textStyle,
498
411
animation: CurvedAnimation (
@@ -505,51 +418,36 @@ class _TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
505
418
),
506
419
);
507
420
_entry = OverlayEntry (builder: (BuildContext context) => overlay);
508
- _isConcealed = false ;
509
421
overlayState.insert (_entry! );
510
422
SemanticsService .tooltip (widget.message);
511
- if (_mouseIsConnected) {
512
- // Hovered tooltips shouldn't show more than one at once. For example, a chip with
513
- // a delete icon shouldn't show both the delete icon tooltip and the chip tooltip
514
- // at the same time.
515
- Tooltip ._concealOtherTooltips (this );
516
- }
517
- assert (! Tooltip ._openedTooltips.contains (this ));
518
- Tooltip ._openedTooltips.add (this );
423
+ Tooltip ._openedToolTips.add (this );
519
424
}
520
425
521
426
void _removeEntry () {
522
- Tooltip ._openedTooltips.remove (this );
523
- _mouseIn.remove (this );
524
- _dismissTimer? .cancel ();
525
- _dismissTimer = null ;
427
+ Tooltip ._openedToolTips.remove (this );
428
+ _hideTimer? .cancel ();
429
+ _hideTimer = null ;
526
430
_showTimer? .cancel ();
527
431
_showTimer = null ;
528
- if (! _isConcealed) {
529
- _entry? .remove ();
530
- }
531
- _isConcealed = false ;
432
+ _entry? .remove ();
532
433
_entry = null ;
533
- if (_mouseIsConnected) {
534
- Tooltip ._revealLastTooltip ();
535
- }
536
434
}
537
435
538
436
void _handlePointerEvent (PointerEvent event) {
539
437
if (_entry == null ) {
540
438
return ;
541
439
}
542
440
if (event is PointerUpEvent || event is PointerCancelEvent ) {
543
- _handleMouseExit ();
441
+ _hideTooltip ();
544
442
} else if (event is PointerDownEvent ) {
545
- _handleMouseExit (immediately: true );
443
+ _hideTooltip (immediately: true );
546
444
}
547
445
}
548
446
549
447
@override
550
448
void deactivate () {
551
449
if (_entry != null ) {
552
- _dismissTooltip (immediately: true );
450
+ _hideTooltip (immediately: true );
553
451
}
554
452
_showTimer? .cancel ();
555
453
super .deactivate ();
@@ -637,8 +535,8 @@ class _TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
637
535
// Only check for hovering if there is a mouse connected.
638
536
if (_mouseIsConnected) {
639
537
result = MouseRegion (
640
- onEnter: (_ ) => _handleMouseEnter (),
641
- onExit: (_ ) => _handleMouseExit (),
538
+ onEnter: (PointerEnterEvent event ) => _showTooltip (),
539
+ onExit: (PointerExitEvent event ) => _hideTooltip (),
642
540
child: result,
643
541
);
644
542
}
0 commit comments