I want to create a dropdown menu on flutter where the handler button that opens the dropdown uses just an icon and the menu list opened by it uses an icon and a text.
I almost manage to create it, as you can check on the following screenshots:
I'm struggling with the opened width, so my question is how to give the opened menu enough width and keep the handler button on its current width.
Notice that I want the dropdown to be at the end of the Row, so consider this black box to be an area of something else, nothing important.
I'm adding the relevant code below and the complete code on the following links.
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Question Dropdown",
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(
optionStream: BehaviorSubject<Option>(),
),
);
}
}
class HomePage extends StatelessWidget {
final BehaviorSubject<Option> optionStream;
const HomePage({
Key? key,
required this.optionStream,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Question Dropdown"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
Expanded(
child: Container(
height: 48,
color: Colors.black,
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: StreamBuilder<Option>(
initialData: Option.A,
stream: optionStream,
builder: (context, snapshot) {
final option = snapshot.data ?? Option.A;
return _dropDownMenu(context, option);
},
),
),
],
),
],
),
),
);
}
Widget _dropDownMenu(
BuildContext context,
Option option,
) {
const items = Option.values;
return DropdownButtonHideUnderline(
child: DropdownButton<Option>(
value: option,
selectedItemBuilder: (context) =>
items.map((e) => _dropdownHandler(context, e)).toList(),
items: items.map((e) => _dropdownItem(context, e)).toList(),
onChanged: (e) => optionStream.add(e ?? Option.A),
),
);
}
OptionsItemHelper _dropDownItemData(
BuildContext context,
Option option,
) {
Widget icon;
String text;
switch (option) {
case Option.A:
icon = const Icon(Icons.ac_unit);
text = "An option";
break;
case Option.B:
icon = const Icon(Icons.baby_changing_station);
text = "Best option";
break;
case Option.C:
icon = const Icon(Icons.cake_sharp);
text = "Closest option";
break;
case Option.D:
icon = const Icon(Icons.dashboard);
text = "Dumb option";
break;
}
return OptionsItemHelper(text, icon);
}
Widget _dropdownHandler(
BuildContext context,
Option option,
) {
final helper = _dropDownItemData(context, option);
return helper.icon;
}
DropdownMenuItem<Option> _dropdownItem(
BuildContext context,
Option option,
) {
final helper = _dropDownItemData(context, option);
return DropdownMenuItem<Option>(
value: option,
child: Row(
children: [
helper.icon,
const SizedBox(width: 16),
Text(helper.text),
],
),
);
}
}
enum Option {
A,
B,
C,
D,
}
class OptionsItemHelper {
final String text;
final Widget icon;
OptionsItemHelper(
this.text,
this.icon,
);
}
I did find a workaround using GestureDetector and showMenu, I'm sharing here and pushing to the repo as "workaround" commit in case you need the same as I need now, I'm keeping the question without answer in case someone finds a better way using the dropdown.
The new dropDownMenu function
Widget _dropDownMenu(
BuildContext context,
Option option,
) {
const items = Option.values;
return GestureDetector(
onTapDown: (details) async {
final offset = details.globalPosition;
final newOption = await showMenu(
context: context,
position: RelativeRect.fromLTRB(offset.dx, offset.dy, 0, 0),
items: items.map((e) => _dropdownItem(context, e, option)).toList(),
);
if (newOption != null) {
optionStream.add(newOption);
}
},
child: _dropdownHandler(context, option),
);
}
and the new dropdownItem function.
PopupMenuEntry<Option> _dropdownItem(
BuildContext context,
Option option,
Option selected,
) {
final helper = _dropDownItemData(context, option);
return CheckedPopupMenuItem<Option>(
value: option,
checked: option == selected,
child: Row(
children: [
Expanded(child: Container()),
Text(helper.text),
const SizedBox(width: 16),
helper.icon,
],
),
);
}
How it looks like
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.