Overlays in Flutter are powerful tools that allow developers to display content on top of other widgets in the widget tree. They are commonly used for creating tooltips, context menus, loading indicators, and custom dialogs. In this article, we’ll explore flutter overlay, starting from basic concepts and progressing to more advanced techniques.
Table of Contents
Basic Overlay Example
Let’s start with a simple overlay that displays a loading indicator on top of the current screen.
import 'package:flutter/material.dart';
class BasicOverlayExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Basic Overlay Example')),
body: Center(
child: ElevatedButton(
child: Text('Show Overlay'),
onPressed: () {
showLoadingOverlay(context);
},
),
),
);
}
void showLoadingOverlay(BuildContext context) {
OverlayState? overlayState = Overlay.of(context);
OverlayEntry overlayEntry = OverlayEntry(
builder: (context) => Center(
child: CircularProgressIndicator(),
),
);
overlayState?.insert(overlayEntry);
// Remove the overlay after 3 seconds
Future.delayed(Duration(seconds: 3), () {
overlayEntry.remove();
});
}
}
In this basic example, we create a simple loading overlay that appears when a button is pressed and disappears after 3 seconds.
Intermediate Overlay Example
Now, let’s create a more complex overlay that displays a custom tooltip when tapping a button. This example is more suitable for mobile devices.
import 'package:flutter/material.dart';
class IntermediateOverlayExample extends StatefulWidget {
@override
_IntermediateOverlayExampleState createState() => _IntermediateOverlayExampleState();
}
class _IntermediateOverlayExampleState extends State<IntermediateOverlayExample> {
OverlayEntry? _overlayEntry;
final LayerLink _layerLink = LayerLink();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Intermediate Overlay Example')),
body: Center(
child: CompositedTransformTarget(
link: _layerLink,
child: ElevatedButton(
child: Text('Tap for Tooltip'),
onPressed: () {
if (_overlayEntry == null) {
_showTooltip(context);
} else {
_hideTooltip();
}
},
),
),
),
);
}
void _showTooltip(BuildContext context) {
_overlayEntry = OverlayEntry(
builder: (context) => GestureDetector(
onTap: _hideTooltip,
behavior: HitTestBehavior.translucent,
child: Align(
alignment: Alignment.center,
child: CompositedTransformFollower(
link: _layerLink,
offset: Offset(0, 50),
child: Material(
elevation: 4.0,
borderRadius: BorderRadius.circular(10),
child: Container(
padding: EdgeInsets.all(16),
color: Colors.amber,
child: Text('This is a custom tooltip!'),
),
),
),
),
),
);
Overlay.of(context)?.insert(_overlayEntry!);
}
void _hideTooltip() {
_overlayEntry?.remove();
_overlayEntry = null;
}
@override
void dispose() {
_hideTooltip();
super.dispose();
}
}
This intermediate example demonstrates how to create a custom tooltip that appears when tapping a button and disappears when tapping anywhere on the screen. It uses CompositedTransformTarget
and CompositedTransformFollower
to position the tooltip relative to the button, making it suitable for various screen sizes and orientations.
Advanced Overlay Example
For our advanced example, we’ll create a draggable, resizable overlay window that can be used to display additional content or controls.
import 'package:flutter/material.dart';
class AdvancedOverlayExample extends StatefulWidget {
@override
_AdvancedOverlayExampleState createState() => _AdvancedOverlayExampleState();
}
class _AdvancedOverlayExampleState extends State<AdvancedOverlayExample> {
OverlayEntry? _overlayEntry;
Offset _offset = Offset(20, 20);
Size _size = Size(200, 200);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Advanced Overlay Example')),
body: Center(
child: ElevatedButton(
child: Text('Show Draggable Overlay'),
onPressed: () => _showDraggableOverlay(context),
),
),
);
}
void _showDraggableOverlay(BuildContext context) {
_overlayEntry = OverlayEntry(
builder: (context) => Positioned(
left: _offset.dx,
top: _offset.dy,
child: GestureDetector(
onPanUpdate: (details) {
setState(() {
_offset += details.delta;
_overlayEntry?.markNeedsBuild();
});
},
child: Material(
color: Colors.transparent,
child: Container(
width: _size.width,
height: _size.height,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.8),
borderRadius: BorderRadius.circular(10),
boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 5)],
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: Icon(Icons.close),
onPressed: _hideOverlay,
),
GestureDetector(
onPanUpdate: (details) {
setState(() {
_size = Size(details.delta.dx + _size.width, details.delta.dy + _size.height);
_overlayEntry?.markNeedsBuild();
});
},
child: Icon(Icons.drag_handle),
),
],
),
Expanded(
child: Center(
child: Text('Draggable and Resizable Overlay'),
),
),
],
),
),
),
),
),
);
Overlay.of(context)?.insert(_overlayEntry!);
}
void _hideOverlay() {
_overlayEntry?.remove();
_overlayEntry = null;
}
@override
void dispose() {
_hideOverlay();
super.dispose();
}
}
This advanced example showcases a draggable and resizable overlay window. Users can move the window by dragging it and resize it by dragging the handle in the top-right corner.
Conclusion
Overlays in Flutter provide a powerful way to create dynamic and interactive user interfaces. From simple loading indicators to complex draggable windows, overlays offer flexibility and control over how content is displayed on top of your app’s main UI. As you progress from basic to advanced techniques, you’ll find that overlays can significantly enhance the user experience of your Flutter applications.
Remember to manage your overlays carefully, ensuring they are removed when no longer needed to prevent memory leaks and unexpected behavior. With practice, you’ll be able to create sophisticated and engaging UI elements using Flutter’s overlay system.