I'm trying to add a custom dropdown menu whose items are just links to other pages
I tried using DropdownButton
But I failed to make its elements as a link and it requires a value, and I do not have a value to pass to it
thank you
CodePudding user response:
You can use OverlayEntry
for this case. Below is a simple working example of a dropdown using OverlayEntry
:
class TestDropdownWidget extends StatefulWidget {
TestDropdownWidget({Key? key}) : super(key: key);
@override
_TestDropdownWidgetState createState() => _TestDropdownWidgetState();
}
class _TestDropdownWidgetState extends State<TestDropdownWidget>
with TickerProviderStateMixin {
final LayerLink _layerLink = LayerLink();
late OverlayEntry _overlayEntry;
bool _isOpen = false;
//Controller Animation
late AnimationController _animationController;
late Animation<double> _expandAnimation;
@override
void dispose() {
super.dispose();
_animationController.dispose();
}
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
);
_expandAnimation = CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
);
}
@override
Widget build(BuildContext context) {
return CompositedTransformTarget(
link: _layerLink,
child: InkWell(
onTap: _toggleDropdown,
child: Text('Click Me'), //Define your child here
),
);
}
OverlayEntry _createOverlayEntry() {
return OverlayEntry(
builder: (context) => GestureDetector(
onTap: () => _toggleDropdown(close: true),
behavior: HitTestBehavior.translucent,
// full screen container to register taps anywhere and close drop down
child: SizedBox(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Stack(
children: [
Positioned(
left: 100,
top: 100.0,
width: 250,
child: CompositedTransformFollower(
//use offset to control where your dropdown appears
offset: Offset(0, 20),
link: _layerLink,
showWhenUnlinked: false,
child: Material(
elevation: 2,
borderRadius: BorderRadius.circular(6),
borderOnForeground: true,
color: Colors.white,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
border: Border.all(color: Colors.grey),
),
child: SizeTransition(
axisAlignment: 1,
sizeFactor: _expandAnimation,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
//These are the options that appear in the dropdown
Text('Option 1'),
Text('Option 2'),
Text('Option 3'),
Text('Option 4'),
Text('Option 5'),
],
),
),
),
),
),
),
],
),
),
),
);
}
void _toggleDropdown({
bool close = false,
}) async {
if (_isOpen || close) {
_animationController.reverse().then((value) {
_overlayEntry.remove();
if (mounted) {
setState(() {
_isOpen = false;
});
}
});
} else {
_overlayEntry = _createOverlayEntry();
Overlay.of(context)!.insert(_overlayEntry);
setState(() => _isOpen = true);
_animationController.forward();
}
}
}
Here's a gif to show the ui: