Learn how to implement a sleek and user-friendly popup menu in Flutter using this step-by-step tutorial. Explore the code for creating a customizable and responsive popup that enhances the user experience. Elevate your Flutter app design with this comprehensive guide. #Flutter #PopupMenu #MobileAppDevelopment
This Flutter code defines a simple app with a home screen containing a button. When the button is tapped, a popup menu is displayed with various options. Let’s break down the code step by step:
Table of Contents
Imports:
import 'package:flutter/material.dart';
This line imports the Flutter material library, which contains widgets and classes for building material design applications.
Home
Class:
class Home extends StatefulWidget {
const Home({super.key, required this.title});
final String title;
@override
State<Home> createState() => _HomeState();
}
This class represents the home screen of the app and extends StatefulWidget
. It has a required title
parameter, which is used in the AppBar
. It overrides the createState
method, creating an instance of _HomeState
.
_HomeState
Class:
class _HomeState extends State<Home> {
final GlobalKey _menuKey = GlobalKey();
// ... rest of the code
}
This class represents the state of the home screen. It has a private member _menuKey
of type GlobalKey
, which will be used to identify the position of the button to display the popup menu.
showPopUp
Method:
void showPopUp(BuildContext context) async {
// ... rest of the code
}
This method is responsible for showing the popup menu. It uses the _menuKey
to find the position of the button, calculates the offset, and creates an OverlayEntry
to display the menu.
Popup Menu Structure:
final RenderBox overlay = _menuKey.currentContext!.findRenderObject() as RenderBox;
final Offset offset = overlay.localToGlobal(Offset.zero);
final double screenWidth = MediaQuery.of(context).size.width;
OverlayEntry? overlayEntry;
overlayEntry = OverlayEntry(
builder: (context) {
// ... rest of the code
return Positioned(
// ... positioning and styling code
left: (screenWidth - 200) / 2,
top: offset.dy + overlay.size.height,
width: 200,
child: Material(
// ... material design properties
clipBehavior: Clip.antiAlias,
elevation: 3,
color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: Column(
children: [
// ... list of ListTile items representing menu options
],
),
),
);
},
);
Overlay.of(context).insert(overlayEntry);
This code defines the structure of the popup menu using the Overlay widget. It contains a Material
widget with a Column of ListTile items, each representing a different option in the menu.
build
Method:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: InkWell(
key: _menuKey,
onTap: () {
showPopUp(context);
},
child: const Text(
'Open Pop Up Menu',
),
),
),
);
}
This method builds the actual UI for the home screen. It returns a Scaffold with an AppBar and a Center widget containing an InkWell. Tapping on the InkWell triggers the showPopUp method, displaying the popup menu.
Demo / Screenshot
Full Code
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const Home(title: 'Flutter Custom Pop Up Menu'),
);
}
}
class Home extends StatefulWidget {
const Home({super.key, required this.title});
final String title;
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
final GlobalKey _menuKey = GlobalKey();
void showPopUp(BuildContext context) async {
final RenderBox overlay = _menuKey.currentContext!.findRenderObject() as RenderBox;
final Offset offset = overlay.localToGlobal(Offset.zero);
final double screenWidth = MediaQuery.of(context).size.width;
OverlayEntry? overlayEntry;
overlayEntry = OverlayEntry(
builder: (context) {
// ... rest of the code
return Positioned(
// ... positioning and styling code
left: (screenWidth - 200) / 2,
top: offset.dy + overlay.size.height,
width: 200,
child: Material(
// ... material design properties
clipBehavior: Clip.antiAlias,
elevation: 3,
color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: Column(
children: [
// ... list of ListTile items representing menu options
ListTile(
onTap: () {
overlayEntry?.remove();
},
leading: const Icon(
Icons.account_circle_outlined,
size: 22,
color: Color(0xff9994b0),
),
title: const Text(
'Profile Details',
style: TextStyle(
fontSize: 14,
color: Colors.black,
fontWeight: FontWeight.w600,
),
),
),
ListTile(
onTap: () {
overlayEntry?.remove();
},
leading: const Icon(
Icons.currency_rupee_outlined,
size: 22,
color: Color(0xff9994b0),
),
title: const Text(
'Plans & Billing',
style: TextStyle(
fontSize: 14,
color: Colors.black,
fontWeight: FontWeight.w600,
),
)),
ListTile(
onTap: () {
overlayEntry?.remove();
},
leading: const Icon(
Icons.integration_instructions_outlined,
size: 22,
color: Color(0xff9994b0),
),
title: const Text('Integrations',
style: TextStyle(
fontSize: 14,
color: Colors.black,
fontWeight: FontWeight.w600,
)),
),
ListTile(
onTap: () {
overlayEntry?.remove();
},
leading: const Icon(
Icons.help_outline,
size: 22,
color: Color(0xff9994b0),
),
title: const Text('Help Center',
style: TextStyle(
fontSize: 14,
color: Colors.black,
fontWeight: FontWeight.w600,
)),
),
ListTile(
onTap: () {
overlayEntry?.remove();
},
leading: const Icon(
Icons.credit_card,
size: 22,
color: Color(0xff9994b0),
),
title: const Text('Subscription',
style: TextStyle(
fontSize: 14,
color: Colors.black,
fontWeight: FontWeight.w600,
)),
),
ListTile(
onTap: () {
overlayEntry?.remove();
},
leading: const Icon(
Icons.logout_outlined,
size: 22,
color: Color(0xff9994b0),
),
title: const Text('Log Out',
style: TextStyle(
fontSize: 14,
color: Colors.black,
fontWeight: FontWeight.w600,
)),
)
],
),
),
);
},
);
Overlay.of(context).insert(overlayEntry);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: InkWell(
key: _menuKey,
onTap: () {
showPopUp(context);
},
child: const Text(
'Open Pop Up Menu',
),
),
),
);
}
}