NakedTooltip

Hover/touch triggered tooltip overlay with positioning, animation, and lifecycle callbacks

Headless tooltip component built on Flutter's RawTooltip. Handles hover/touch triggers, positioning, animations, timers, and dismissal. The builder receives an Animation for easy transitions.

When to use this

  • Help text: Explain UI elements or provide additional context
  • Icon explanations: Clarify what icon buttons do
  • Form field hints: Show validation rules or input examples
  • Feature descriptions: Brief explanations of complex features

Basic implementation

import 'package:flutter/material.dart';
import 'package:naked_ui/naked_ui.dart';

class TooltipExample extends StatelessWidget {
  const TooltipExample({super.key});

  @override
  Widget build(BuildContext context) {
    return NakedTooltip(
      positioning: const OverlayPositionConfig(
        targetAnchor: Alignment.topCenter,
        followerAnchor: Alignment.bottomCenter,
      ),
      hoverDelay: const Duration(milliseconds: 400),
      dismissDelay: const Duration(seconds: 2),
      child: Container(
        padding: const EdgeInsets.all(10),
        decoration: BoxDecoration(
          color: Colors.grey.shade900,
          borderRadius: BorderRadius.circular(8),
        ),
        child: const Icon(Icons.copy, color: Colors.white, size: 18),
      ),
      overlayBuilder: (context, animation) {
        return FadeTransition(
          opacity: animation,
          child: Material(
            elevation: 4,
            borderRadius: BorderRadius.circular(8),
            child: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
              child: const Text('Copy to clipboard'),
            ),
          ),
        );
      },
      semanticLabel: 'Copy button tooltip',
    );
  }
}

Constructor

const NakedTooltip({
  Key? key,
  required this.child,
  required this.overlayBuilder,
  this.hoverDelay = Duration.zero,
  this.touchDelay = const Duration(milliseconds: 1500),
  this.dismissDelay = const Duration(milliseconds: 100),
  this.enableTapToDismiss = true,
  this.triggerMode = TooltipTriggerMode.longPress,
  this.enableFeedback = true,
  this.onTriggered,
  this.animationStyle,
  this.positioning = const OverlayPositionConfig(),
  this.semanticLabel,
  this.excludeSemantics = false,
})

Key Parameters

  • child → trigger widget that displays the tooltip on hover/touch
  • overlayBuilder(BuildContext, Animation<double>) that returns the tooltip body; the animation drives show/hide transitions
  • hoverDelay → delay before showing once hover occurs (default: immediate)
  • dismissDelay → delay before hiding after mouse exits (default: 100ms)
  • touchDelay → how long the tooltip stays visible after touch release (default: 1500ms)
  • positioningOverlayPositionConfig controlling anchor & offset
  • triggerMode → how touch events trigger the tooltip (longPress, tap, or manual)
  • animationStyle → customize animation duration, curve, and reverse behavior
  • onTriggered → called when triggered by tap or long press (not hover)
  • semanticLabel → text announced by assistive technologies
  • excludeSemantics → hide tooltip semantics from accessibility services

Behaviour Notes

  • Tooltips open on pointer hover automatically
  • Touch triggers depend on triggerMode (longPress by default, or tap)
  • The overlayBuilder receives an Animation<double> (0→1 on show, 1→0 on hide) for smooth transitions
  • Use AnimationStyle.noAnimation to disable the built-in transition
  • The tooltip dismisses on outside tap (controllable via enableTapToDismiss)
  • Only one tooltip is shown at a time when using nested tooltips

Positioning Tips

Use OverlayPositionConfig.offset to nudge the tooltip position:

const OverlayPositionConfig(
  targetAnchor: Alignment.topCenter,
  followerAnchor: Alignment.bottomCenter,
  offset: Offset(0, -8),
);

For fully custom positioning beyond what OverlayPositionConfig exposes, drop down to Flutter's RawTooltip directly and supply your own TooltipPositionDelegate.