Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ Check example project for usage and/or clarifications.

</br>

## Close tooltip by clicking outside

```dart
OverlayTooltipItem(
dismissOnTap: true,
```

## Getting Started

Expand Down
155 changes: 80 additions & 75 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,15 @@ class _MySamplePageState extends State<MySamplePage> {
Widget build(BuildContext context) {
return OverlayTooltipScaffold(
// overlayColor: Colors.red.withOpacity(.4),
height: 1800,
tooltipAnimationCurve: Curves.linear,
tooltipAnimationDuration: const Duration(milliseconds: 1000),
controller: _controller,
startWhen: (initializedWidgetLength) async {
await Future.delayed(const Duration(milliseconds: 500));
return initializedWidgetLength == 3 && !done;
},
dismissOnTap: true,
preferredOverlay: GestureDetector(
onTap: () {
_controller.dismiss();
Expand All @@ -72,87 +74,90 @@ class _MySamplePageState extends State<MySamplePage> {
color: Colors.blue.withOpacity(.2),
),
),
builder: (context) => Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
toolbarHeight: 70,
builder: (context) {
return Scaffold(
backgroundColor: Colors.white,
elevation: 0,
actions: [
OverlayTooltipItem(
displayIndex: 1,
tooltip: (controller) => Padding(
padding: const EdgeInsets.only(right: 15),
child: MTooltip(title: 'Button', controller: controller),
),
child: Center(
child: Container(
width: 40,
height: 40,
margin: const EdgeInsets.only(right: 15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
gradient: const LinearGradient(
colors: [Colors.orange, Colors.deepOrange])),
child: const Icon(
Icons.add,
color: Colors.white,
appBar: AppBar(
toolbarHeight: 70,
backgroundColor: Colors.white,
elevation: 0,
actions: [
OverlayTooltipItem(
displayIndex: 1,
tooltip: (controller) => Padding(
padding: const EdgeInsets.only(right: 15),
child: MTooltip(title: 'Button', controller: controller),
),
child: Center(
child: Container(
width: 40,
height: 40,
margin: const EdgeInsets.only(right: 15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
gradient: const LinearGradient(
colors: [Colors.orange, Colors.deepOrange])),
child: const Icon(
Icons.add,
color: Colors.white,
),
alignment: Alignment.center,
),
alignment: Alignment.center,
),
),
)
],
),
floatingActionButton: OverlayTooltipItem(
displayIndex: 2,
tooltip: (controller) => Padding(
padding: const EdgeInsets.only(bottom: 15),
child: MTooltip(title: 'Floating button', controller: controller),
)
],
),
tooltipVerticalPosition: TooltipVerticalPosition.TOP,
child: FloatingActionButton(
backgroundColor: Colors.purple,
onPressed: () {},
child: const Icon(Icons.message),
floatingActionButton: OverlayTooltipItem(
displayIndex: 2,
tooltip: (controller) => Padding(
padding: const EdgeInsets.only(bottom: 15),
child: MTooltip(title: 'Floating button', controller: controller),
),
tooltipVerticalPosition: TooltipVerticalPosition.TOP,
child: FloatingActionButton(
backgroundColor: Colors.purple,
onPressed: () {},
child: const Icon(Icons.message),
),
),
),
body: ListView(
children: [
...[
_sampleWidget(),
OverlayTooltipItem(
displayIndex: 0,
tooltip: (controller) => Padding(
padding: const EdgeInsets.only(left: 15),
child: MTooltip(
title: 'Text Tile', controller: controller),
),
child: _sampleWidget()),
_sampleWidget(),
body: ListView(
children: [
...[
_sampleWidget(),
OverlayTooltipItem(
displayIndex: 0,
tooltip: (controller) => Padding(
padding: const EdgeInsets.only(left: 15),
child: MTooltip(
title: 'Text Tile', controller: controller),
),
child: _sampleWidget()),
_sampleWidget(),
],
TextButton(
onPressed: () {
setState(() {
done = false;
});
},
child: const Text('reset Tooltip')),
TextButton(
onPressed: () {
//_controller.start();
OverlayTooltipScaffold.of(context)?.controller.start();
},
child: const Text('Start Tooltip manually')),
TextButton(
onPressed: () {
//_controller.start(1);
OverlayTooltipScaffold.of(context)?.controller.start(1);
},
child: const Text('Start at second item')),
for (var i = 0; i < 11; i++) _sampleWidget(),
],
TextButton(
onPressed: () {
setState(() {
done = false;
});
},
child: const Text('reset Tooltip')),
TextButton(
onPressed: () {
//_controller.start();
OverlayTooltipScaffold.of(context)?.controller.start();
},
child: const Text('Start Tooltip manually')),
TextButton(
onPressed: () {
//_controller.start(1);
OverlayTooltipScaffold.of(context)?.controller.start(1);
},
child: const Text('Start at second item')),
],
),
),
),
);
},
);
}

Expand Down
3 changes: 3 additions & 0 deletions lib/src/core/overlay_tooltip_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class _OverlayTooltipItemImplState extends State<OverlayTooltipItemImpl> {
void _addToPlayableWidget() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
try {
if (!mounted) {
return;
}
OverlayTooltipScaffold.of(context)?.addPlayableWidget(
OverlayTooltipModel(
absorbPointer: widget.absorbPointer,
Expand Down
125 changes: 80 additions & 45 deletions lib/src/core/overlay_tooltip_scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ abstract class OverlayTooltipScaffoldImpl extends StatefulWidget {
final Color overlayColor;
final Duration tooltipAnimationDuration;
final Curve tooltipAnimationCurve;
final bool dismissOnTap;
final Widget? preferredOverlay;
final double? height;

OverlayTooltipScaffoldImpl({
Key? key,
Expand All @@ -23,7 +25,11 @@ abstract class OverlayTooltipScaffoldImpl extends StatefulWidget {
required this.startWhen,
required this.tooltipAnimationDuration,
required this.tooltipAnimationCurve,

/// If true, the tooltip will be dismissed when the user taps on the screen at any area exclude tooltip.
this.dismissOnTap = false,
this.preferredOverlay,
this.height,
}) : super(key: key) {
if (startWhen != null) controller.setStartWhen(startWhen!);
}
Expand All @@ -45,54 +51,83 @@ class OverlayTooltipScaffoldImplState
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: Stack(
fit: StackFit.expand,
children: [
Positioned.fill(child: Builder(builder: (context) {
return widget.builder(context);
})),
StreamBuilder<OverlayTooltipModel?>(
stream: widget.controller.widgetsPlayStream,
builder: (context, snapshot) {
return snapshot.data == null ||
snapshot.data!.widgetKey.globalPaintBounds == null
? SizedBox.shrink()
: Positioned.fill(
child: Stack(
children: [
widget.preferredOverlay ??
Container(
height: double.infinity,
width: double.infinity,
color: widget.overlayColor,
),
TweenAnimationBuilder(
key: ValueKey(snapshot.data!.displayIndex),
tween: Tween<double>(begin: 0, end: 1),
duration: widget.tooltipAnimationDuration,
curve: widget.tooltipAnimationCurve,
builder: (_, double val, child) {
val = min(val, 1);
val = max(val, 0);
return Opacity(
opacity: val,
child: child,
);
},
child: _TooltipLayout(
model: snapshot.data!,
controller: widget.controller,
),
),
],
),
);
},
)
],
body: StreamBuilder<OverlayTooltipModel?>(
stream: widget.controller.widgetsPlayStream,
builder: (context, snapshot) {
final show = snapshot.data == null ||
snapshot.data!.widgetKey.globalPaintBounds == null;
if (widget.height == null) {
return _stackBody(show, snapshot);
}
final baseHeight = MediaQuery.of(context).size.height;
final height = show ? baseHeight : (widget.height ?? baseHeight);
return SingleChildScrollView(
physics: show ? NeverScrollableScrollPhysics() : null,
child: SizedBox(
height: height,
child: _stackBody(show, snapshot),
),
);
},
),
);
}

Stack _stackBody(bool show, AsyncSnapshot<OverlayTooltipModel?> snapshot) {
return Stack(
key: ValueKey('OverlayTooltipScaffoldStack'),
fit: StackFit.expand,
children: [
Positioned.fill(child: Builder(builder: (context) {
return widget.builder(context);
})),
show
? SizedBox.shrink()
: Container(
child: widget.dismissOnTap
? GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
widget.controller.dismiss();
},
child: _bodyToolTip(snapshot),
)
: _bodyToolTip(snapshot),
),
],
);
}

Widget _bodyToolTip(AsyncSnapshot<OverlayTooltipModel?> snapshot) {
return Stack(
children: [
widget.preferredOverlay ??
Container(
height: double.infinity,
width: double.infinity,
color: widget.overlayColor,
),
TweenAnimationBuilder(
key: ValueKey(snapshot.data!.displayIndex),
tween: Tween<double>(begin: 0, end: 1),
duration: widget.tooltipAnimationDuration,
curve: widget.tooltipAnimationCurve,
builder: (_, double val, child) {
val = min(val, 1);
val = max(val, 0);
return Opacity(
opacity: val,
child: child,
);
},
child: _TooltipLayout(
model: snapshot.data!,
controller: widget.controller,
),
),
],
);
}
}

class _TooltipLayout extends StatelessWidget {
Expand Down
12 changes: 12 additions & 0 deletions lib/src/impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ class OverlayTooltipScaffold extends OverlayTooltipScaffoldImpl {
final Duration tooltipAnimationDuration;

final Curve tooltipAnimationCurve;
final double? height;

/// If true, the tooltip will be dismissed when the user taps on the screen at any area exclude tooltip.
final bool dismissOnTap;
// Set a preferred overlay widget.
// This can be useful for gesture detection on your custom overlays
final Widget? preferredOverlay;
Expand All @@ -34,7 +37,14 @@ class OverlayTooltipScaffold extends OverlayTooltipScaffoldImpl {
this.startWhen,
this.tooltipAnimationDuration = const Duration(milliseconds: 500),
this.tooltipAnimationCurve = Curves.decelerate,

/// If true, the tooltip will be dismissed when the user taps on the screen at any area exclude tooltip.
this.dismissOnTap = false,
this.preferredOverlay,

/// The height of the overlay, if null, the overlay will take the height of the screen
/// Need for show big tooltip on small screen
this.height,
}) : super(
key: key,
controller: controller,
Expand All @@ -43,7 +53,9 @@ class OverlayTooltipScaffold extends OverlayTooltipScaffoldImpl {
startWhen: startWhen,
tooltipAnimationDuration: tooltipAnimationDuration,
tooltipAnimationCurve: tooltipAnimationCurve,
dismissOnTap: dismissOnTap,
preferredOverlay: preferredOverlay,
height: height,
);

static OverlayTooltipScaffoldImplState? of(BuildContext context) {
Expand Down