When a PopupMenuButton is pressed, the currently selected value is highlighted,
but when a DropdownButton is pressed, the currently selected value is not highlighted.
Is there a way to highlight the selected value of a DropdownButton?
For reference here is some sample code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: MyHomePage());
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String letter = 'A';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Popup Menu Button')),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 16.0),
Text('PopupMenuButton'),
buildPopupMenuButton(),
SizedBox(height: 16.0),
Text('DropdownButton'),
buildDropdownButton(),
],
),
);
}
Widget buildPopupMenuButton() {
return PopupMenuButton<String>(
padding: EdgeInsets.zero,
initialValue: letter,
onSelected: (val) => setState(() => letter = val),
child: ListTile(
title: Text('The letter $letter'),
),
itemBuilder: (BuildContext context) {
return <PopupMenuItem<String>>[
PopupMenuItem<String>(
value: 'A',
child: Text('The letter A'),
),
PopupMenuItem<String>(
value: 'B',
child: Text('The letter B'),
),
];
},
);
}
Widget buildDropdownButton() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: DropdownButton<String>(
value: letter,
onChanged: (val) => setState(() => letter = val),
items: [
DropdownMenuItem<String>(
value: 'A',
child: Text('The letter A'),
),
DropdownMenuItem<String>(
value: 'B',
child: Text('The letter B'),
),
],
),
);
}
}
Here's a video that shows the issue:
The DropdownMenuItem doesn't support many custom modifications on the child element, as there's no style, background, anything actually in the DropdownMenuItem attributes to help you with that. Looking at the code, it really wasn't built for that,
Yet, there's something you could add, a simple check on the child attribute of the DropdownMenuItem, and wrap the Text child element in something else or style the Text element itself if it is checked.
One example:
Widget buildDropdownButton() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: DropdownButton<String>(
value: letter,
onChanged: (val) => setState(() => letter = val),
items: [
DropdownMenuItem<String>(
value: 'A',
child: Container(
color: letter == 'A' ? Colors.black12 : null,
child: Text('The letter A'),
),
),
DropdownMenuItem<String>(
value: 'B',
child: Container(
color: letter == 'B' ? Colors.black12 : null,
child: Text('The letter B'),
),
),
],
),
);
}
Note that in a real case scenario, you would have a method with a paremeter to build each dropdown item, so the verification wouldn't have to be hardcoded like letter == 'A'.
This would be the output:
This approach allows you to style a bit, but it has an ugly result in some cases. Although it is customizable, there will always be a white margin around the item, and it also shows the same styles when the dropdown list is closed, so it gets a bit ugly on the main page.
Instead of changing the background, you can also change text colors, underline, icons on the side, something like that make it much better, like:
DropdownMenuItem<String>(
value: 'A',
child: Text('The letter A',
style: TextStyle(
color: letter == 'A' ? Colors.red : Colors.black87,
),
),
)
Well, as far as I know this grey overlay is a so called 'Ripple effect' in the material design library. It seems that Flutter does not adapt the full design in all widgets yet.
However you can try to use the InkWell widget to add this kind of animations/colors to current widgets:
https://flutter.io/docs/cookbook/gestures/ripples
E.g:
PopupMenuItem<String>(
value: 'B',
child: InkWell(child: Text('The letter B'))
),
I am not sure if the width will be correct, but at least it should show the grey overlay when you press on the entry.
You can also check the Flutter source:
https://github.com/flutter/flutter/blob/237fc2fb45639312001e947bf7465ef9f23bb699/packages/flutter/lib/src/material/popup_menu.dart#L933
Here you can see that a Inkwell is standard being used for the PopupMenuButton.
Responding to your original issue which was: "I'm interested in the darker background from the currently selected value when all of the values are shown."
Your PopupMenuButton will look at its initialValue: parameter each time it is opened--the item corresponding to this value will be highlighted. You will need to update the initialValue parameter each time using the onSelected function.
Make sure the parent widget is a StatefulWidget widget and create a reference to whatever your initialValue is. The PopupMenuButton has an onSelected parameter that takes in a function with parameter String.
Whenever you select an option from the PopupMenuButton, call
setState(() {
...
this.initialValue = value;
});
The full class will look something like this.
Class YourClass extends StatefulWidget {
#override
createState() => _YourClassState();
}
class _YourClassState extends State<YourClass> {
...
String initialValue = 'foo';
#override
Widget build(BuildContext context) {
final items = [
PopupMenuItem(
value: 'foo',
child: Text('foo'),
),
PopupMenuItem(
value: 'nice',
child: Text('nice'),
),
}
return Scaffold(
appBar: ...,
drawer: ...,
body: PopupMenuButton(
icon: ...,
itemBuilder: (_) => items,
initialValue: this.initialValue,
onSelected: (value) => bar(value),
),
);
}
void bar(String value) {
setState(() {
...
this.initialValue = value;
});
}
}
You can wrap the widget with Theme to set a highlight color.
return Theme(
data: ThemeData(highlightColor: Colors.grey[300]),
child: DropdownButton()
You may try it:
class CustomDropdownMenuItem<T> extends DropdownMenuItem<T> {
const CustomDropdownMenuItem({
super.key,
super.onTap,
super.value,
super.enabled = true,
super.alignment,
required this.current,
required super.child,
});
final T current;
#override
Widget build(BuildContext context) {
return Container(
color: current == value ? Theme.of(context).highlightColor : null,
child: super.build(context),
);
}
}
However, the element will not be completely covered in color. You can also add a check on the current device to exclude those that work correctly (web and desktop).
Basically, we have to wait for this issue to be solved.
Update:
Alternatively, you can use color selection if you use Text:
final theme = Theme.of(context);
...
return DropdownMenuItem<AppLocale>(
value: value,
onTap: () => {},
child: Text(
value.name,
style: theme.textTheme.titleMedium?.copyWith(
color: value == current ? theme.colorScheme.secondary : null),
),
);
Related
I have a DataTable in which some cells have links. Ideally, I would like to fetch a preview about the link's content whenever hovering over the link, which I was able to achieve using the Stack widget. However, since the stacked preview is inside the DataCell, it seems like I'm not able to raise its "z-index" to be on top of the rest of the table.
Is this not possible with Flutter, or is there a way around it?
The only way I imagine this working, without something to update a global z-index, would be for the cell to update a global state and then have the thumbnail preview appear on a Stack above the DataTable level. But I wish there was a less clunkier way to do it...
3 widgets I've tried but to no avail — they might work, I don't know —:
Tooltip
Overlay
FloatingActionButton
My whole app is here, and the precise commit is 0303732. The relevant code is this ClickableLink widget:
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import 'package:url_launcher/url_launcher.dart';
import '../schema/links.dart';
#immutable
class ClickableLink extends StatefulWidget {
const ClickableLink({
Key? key,
required this.link,
this.linkText,
this.color = Colors.blue,
}) : super(key: key);
final Link link;
final String? linkText;
final Color color;
#override
State<ClickableLink> createState() => _ClickableLinkState();
}
class _ClickableLinkState extends State<ClickableLink> {
Widget hoverWidget = const SizedBox.shrink();
void _fetchPreview(PointerEvent pointerEvent) {
setState(() {
if (widget.link.host == 'online-go.com' && widget.link.prePath == 'game') {
hoverWidget = Positioned(
top: 25,
child: Image.network('https://online-go.com/api/v1/games/${widget.link.id}/png'),
);
}
});
}
void _onExit(PointerEvent pointerEvent) {
setState(() {
hoverWidget = const SizedBox.shrink();
});
}
#override
Widget build(BuildContext context) {
return MouseRegion(
onHover: _fetchPreview,
onExit: _onExit,
child: Stack(
clipBehavior: Clip.none,
children: [
SelectableText.rich(
TextSpan(
text: widget.linkText ?? widget.link.id,
style: TextStyle(color: widget.color),
recognizer: TapGestureRecognizer()
..onTap = () async => launch(widget.link.completeLink),
),
),
hoverWidget,
],
),
);
}
}
The problem here is due to the fact that your Stack widget, defined inside ClickableLink, will be at a "lower" point (inside your app widget tree) than every other GameResultCell.
So even the higher z-index will still be behind the other GameResultCells.
To fix this I would reccomend changing your structure and define an higher point in your structure to show the preview.
Another way could be using a library to nest your preview inside a tooltip. Take a look at this one for example:
just_the_tooltip: ^0.0.11+2. With this package, you could even use a StatelessWidget.
The result here is more similar to what I suppose you were expecting.
class ClickableLink extends StatelessWidget {
#override
Widget build(BuildContext context) {
return JustTheTooltip(
content: Image.network(
'https://online-go.com/api/v1/games/${widget.link.id}/png',
),
child: SelectableText.rich(
TextSpan(
text: widget.linkText ?? widget.link.id,
style: TextStyle(
color: widget.color ??
(DogempTheme.currentThemeIsLight(context)
? const Color(0xff1158c7)
: Colors.orange.withOpacity(0.85)),
),
recognizer: TapGestureRecognizer()
..onTap = () async => launch(widget.link.completeLink),
),
),
);
}
}
Lastly you could use a Dialog, but the resulting behaviour is a bit different.
Take a look at this code if you want to try:
class _ClickableLinkState extends State<ClickableLink> {
Widget hoverWidget = const SizedBox.shrink();
void _fetchPreview(PointerEvent pointerEvent) {
showDialog(
context: context,
builder: (context) {
return Dialog(
backgroundColor: Colors.transparent,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.network(
'https://online-go.com/api/v1/games/${widget.link.id}/png'),
const SizedBox(
height: 16.0,
),
TextButton(
onPressed: () async => launch(widget.link.completeLink),
child: const Text('Go to complete link'))
],
),
);
},
);
}
#override
Widget build(BuildContext context) {
return MouseRegion(
onHover: _fetchPreview,
child: Stack(
clipBehavior: Clip.none,
children: [
SelectableText.rich(
TextSpan(
text: widget.linkText ?? widget.link.id,
style: TextStyle(
color: widget.color ??
(DogempTheme.currentThemeIsLight(context)
? const Color(0xff1158c7)
: Colors.orange.withOpacity(0.85)),
),
recognizer: TapGestureRecognizer()
..onTap = () async => launch(widget.link.completeLink),
),
),
],
),
);
}
}
I have a main widget screen contain two main widgets a Header (marked with red) and a list (marked with purple)
here is my code for that :
class ScreenClient extends StatefulWidget {
_ClientState createState() => _ClientState();
}
class _ClientState extends State<ScreenClient> {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
ClientHeader(), // this is my header widget red
Expanded(
child: ClientList(), // this is my list widget purple
),
],
);
}
}
the header widget has three options as you can see Tous Bloqué and ayant Retard , what I'm trying to achieve is pass the value of the clicked option to the list widget marked with purple (because those options are filters and the list elements should be shown based on the chosen option)
I have a hard time understanding state management packages and from what I understand Global Keys can do the trick but How ? .
here is my header widget code :
class ClientHeader extends StatefulWidget {
_HeaderClientState createState() => _HeaderClientState();
}
class _HeaderClientState extends State<ClientHeader> {
String nomSituation;
String option = "Tous";
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: <Widget>[
GestureDetector(
child: Text(
"Tous",
style: TextStyle(
color: option == "Tous" ? Colors.white : Colors.grey[400],
),
),
onTap: () {
setState(() {
option = "Tous";
});
},
),
GestureDetector(
child: Text(
"Bloqué",
style: TextStyle(
color: option == "Bloqué" ? Colors.white : Colors.grey[400],
),
),
onTap: () {
setState(() {
option = "Bloqué";
//add send value to ClientList widet ?
});
},
),
GestureDetector(
child: Text(
"Ayant Retard",
style: TextStyle(
color:
option == "Ayant Retard" ? Colors.white : Colors.grey[400],
),
),
onTap: () {
setState(() {
option = "Ayant Retard";
});
},
),
],
),
);
}
}
I suggest you can watch 2 examples in this video Pragmatic State Management in Flutter (Google I/O'19)about state mangement. This video helped me a lot when I learn flutter in the begining. They explain how to control the StatefulWidget from the other one:
Make state global, controlled by another widget (from 5m30s)
Use Provider, which is a very popular solution in Flutter, to control share the value between 2 widgets (from 15m05s)
You you have more time, you can study more fancy state management method like Bloc, MobX (List of state management approaches) or even the advance version of Provider named riverpod just pushish few months ago, which try to resolve some cons when using Provider.
I am working on creating a hashMap for my flutter program and would like some input on it. As I created my hashMap in another dart file that is not the main dart file and I have no idea on how to connect it even when I created constrictors for the hashMaps. This is very important as the hashMap will be used on several files within the program hence why it is not in the main dart. Therefore I would like your guys input on how I could connect the two files.
This is part of my code:
main dart file:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
//Always use Stateless first then use stateful or stateless widgets afterward
#override
Widget build(BuildContext context) {
return MaterialApp(
//only used at the beginning of the program
title: 'The Cafe',
//just a title to the app it does not show for there is nothing telling it to show on the screen
debugShowCheckedModeBanner: false,
//takes out the ribbon at the top right corner of the screen and app
theme: ThemeData(
primarySwatch: Colors.green,
brightness: Brightness.dark,
fontFamily: 'georgia',
textTheme: TextTheme(headline1: TextStyle(fontSize: 100))
//controls the color of the very top part of the application
),
home: StartPage(),
//used to connect the Stateless widget to the Stateful widget below
);
}
}
class StartPage extends StatefulWidget {
#override
_StartPageState createState() => _StartPageState();
}
// do not forget the } prior to this comment if you do it will result in error and the program does not known why either
class _StartPageState extends State<StartPage> {
String value = "";
//stating the string is not seen until you have started to compute the drop-downs
//have the drop down's take you to the item page
//void main(){
//HashMap map = new HashMap<String, double>();
// LinkedHashMap linkedHashMap = new LinkedHashMap<int, String>();
// SplayTreeMap treeMap = new SplayTreeMap<int, String>();
//}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('The Campus Cafe'),
//where the main title is computed to be shown on the screen
centerTitle: true,
//centers the title
),
body: Center(
//This is Header that is after the main Title
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
//Header Container
Expanded(
child: Image.asset('assets/images/campus-cafe-logo-350sidebar.png',)
),
Container(
padding: const EdgeInsets.all(8.0),
alignment: Alignment.center,
child: Text("Our Menu",style: TextStyle(fontSize: 30),
),
),
Expanded(
//Expands is used to create a body if you want a header and body...can also be used for other things but at the moment this is all I know
child: Column(
//there can be different types of Columns
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Flexible(
//Padding is how far away one container or item is away from another as shown below
flex:3,
child: DropdownButton<String>(
//items are basically like an array or list
items: [
DropdownMenuItem<String>(
value: "1",
child: Center(
child: Text('Grilled Cheese'),
),
),
DropdownMenuItem<String>(
value: "2",
child: Center(
child: Text('Grilled Ham & Cheese'),
),
),
DropdownMenuItem<String>(
value: "3",
child: Center(
child: Text('BLT'),
),
),
DropdownMenuItem<String>(
value: "4",
child: Center(
child: Text('Western Chicken Sandwich'),
),
),
DropdownMenuItem<String>(
value: "5",
child: Center(
child: Text('Crispy Chicken Wrap'),
),
),
DropdownMenuItem<String>(
value: "6",
child: Center(
child: Text('Cheese Steak'),
),
),
],
onChanged: (_value) => {
print(_value.toString()),
setState(() {
value = _value;
}),
},
hint: Text('Sandwiches')
//This hint displays on your drop-box before you open it to see the items list
),
),
Flexible(
flex:3,
child: DropdownButton<String>(
items: [
DropdownMenuItem<String>(
value: "1",
child: Center(
child: Text('Quantum Burger'),
),
),
DropdownMenuItem<String>(
value: "2",
child: Center(
child: Text('Cheeseburger'),
),
),
DropdownMenuItem<String>(
value: "3",
child: Center(
child: Text('Double Cheeseburger 1/4'),
),
),
DropdownMenuItem<String>(
value: "4",
child: Center(
child: Text('Hamburger 1/4'),
),
),
DropdownMenuItem<String>(
value: "5",
child: Center(
child: Text('Cheeseburger'),
),
),
DropdownMenuItem<String>(
value: "6",
child: Center(
child: Text('Veggie Burger'),
),
),
],
onChanged: (_value) => {
print(_value.toString()),
setState(() {
value = _value;
}),
},
hint: Text('Burgers')),
),
This is my hashMap file:
List<String> sandwich = ["Veggie Melt", "Crispy Chicken Wrap", "Italian Meatball Sub",
"Chicken Parm Grinder", "Grill Cheese", "Grilled Ham & Cheese", "Bacon Bagel Melt"];
List<double> sandwichPrice = [4.50, 6.95, 6.99, 6.59, 3.59, 4.59, 5.29];
Map<String, double> map1 = Map.fromIterables(sandwich, sandwichPrice);
List<String> burgers = ["Veggie Burger", "The Quantum Burger", "Cafe Melt",
"The Bull Rider", "Double Cheese Burger", "Hamburger"];
List<double> burgerPrice = [4.99, 7.25, 6.59, 5.79, 5.89, 3.99, 3.79];
Map<String, double> map2 = Map.fromIterables(burgers, burgerPrice);
List<String> otherItems = ["Chicken Quesadilla", "Cheese Quesadilla",
"Chicken Strips", "Popcorn Chicken", "Jalapeno Poppers"];
List<double> otherItemsPrice = [6.79, 6.29, 4.99, 4.59, 3.49];
Map<String, double> map3 = Map.fromIterables(otherItems, otherItemsPrice);
List<String> sides = ["French Fries", "Onion Rings", "Jalapeno Cheese Curds",
"Tater Tots", "Pretzel Bites", "Nachos & Cheese"];
List<double> sidesPrice = [3.29, 4.79, 4.99, 3.19, 4.59, 3.50];
Map<String, double> map4 = Map.fromIterables(sides, sidesPrice);
List<String> pizza = ["7-inch Cheese", "7-inc with topping"];
List<double> pizzaPrice = [4.59, 4.99];
Map<String, double> map5 = Map.fromIterables(pizza, pizzaPrice);
class Menu {
String sandwich;
String burger;
String otherItems;
String sides;
String pizza;
double sandwichPrice;
double burgerPrice;
double otherItemsPrice;
double sidesPrice;
double pizzaPrice;
Menu.s(this.sandwich, this.sandwichPrice){}
Menu.b(this.burger, this.burgerPrice){}
Menu.o(this.otherItems, this.otherItemsPrice){}
Menu.q(this.sides, this.sidesPrice){}
Menu.p(this.pizza, this.pizzaPrice){}
}
First of all, you need to import your hashMap file. Then update your MyWidget as follows:
I have displayed here an example of how you can use the Dropdown with map1 (i.e, Sandwiches).
You have to just iterate over the keys of the hashmap 'map1' & create the list of DropdownMenuItem from it & pass this list to the items property.
For each dropdown, you will need to save the selected option, so instead of using String value = '';, I have changed it to String selectedSandwich = 'Sandwiches';. This part is crucial as the Dropdown widget can have its value only as one of the options available. So, if you do not have the Sandwiches option in your dropdown list, there will be an error. Hence, I have added the Sandwiches option in the initState of your MyAppWidget.
You have to do the same for the remaining of the hashmaps. Let me know if you need any more help.
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
// Instead of value use selectedSandwich
String selectedSandwich = 'Sandwiches';
// List of sandwiches, you have to do the same for rest of the maps
// like: burgers, otherItems etc.
List<String> sandwiches = map1.keys.toList();
#override
void initState() {
super.initState();
// Adding sandwiches as an option is necessay as the dropdown's value
// must be equal to one of its options.
// I have done this only for sandwiches, but you need to do the same
// for rest.
sandwiches.insert(0, 'Sandwiches');
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
DropdownButton<String>(
items: sandwiches // using map1.keys.toList
.map(
(e) => DropdownMenuItem<String>(
value: e,
child: Center(
child: Text(e),
),
),
)
.toList(),
onChanged: (_value) => {
print(_value.toString()),
setState(() {
selectedSandwich = _value; // Set selected sandwich
}),
},
value: selectedSandwich, // Use value
),
],
),
);
}
}
I am trying to create a checkbox list like this:
my plan
I used CheckBoxListTile with dense and contentPadding to remove all possible paddings, and this is what I have:
my result
Is there any way to remove the extra "padding" between CheckBoxListTile?
Here is my code (a single checkbox):
class Checklist extends StatefulWidget {
Checklist({#required this.content});
final String content;
#override
_ChecklistState createState() => _ChecklistState();
}
class _ChecklistState extends State<Checklist> {
bool _checked = false;
#override
Widget build(BuildContext context) {
return CheckboxListTile(
contentPadding: EdgeInsets.all(0),
//dense: true,
title: Text(widget.content, style: TextStyle(color: kBlackColor),),
value: _checked,
onChanged: (bool value) {
setState(() {
_checked = value;
});
},
controlAffinity: ListTileControlAffinity.leading,
activeColor: kBlackColor,
);
}
}
Multiple checkboxes:
Column(
children: [
Checklist(content: 'Develop an app'),
Checklist(content: 'Develop a good app'),
Checklist(content: 'Develop a really good app'),
],
),
wrap your CheckBox inside SizedBox will resize the padding of the check box
SizedBox(
height: whatYouWant,
width: whatYouWant,
child: Checkbox(...),
)
please check this solution: https://stackoverflow.com/a/59420505/11989529
I have a dropdown menu with several options. For sake of simplicity lets say they are: "A", "B" and "C".
This is the snippet of my code:
children: <Widget>[
FormBuilder(
key: _fbKey,
autovalidate: true,
child: Column(
children: <Widget>[
FormBuilderDropdown(
attribute: "value",
decoration: InputDecoration(
labelText: "Choose something?"),
hint: Text('Select Option'),
validators: [FormBuilderValidators.required()],
items: user.option.map((v) {
return DropdownMenuItem(
value: v,
child: ListTile(
leading: Image.asset(
'assets/img/image.png',
width: 50,
height: 50,
),
title: Text("${v.option}"),
));
}).toList(),
),
],
),
// if v.option == "A" is selected here build Widget A()
// if v.option == "B" is selected here build Widget B()
// if v.option == "C" is selected here build Widget C()
),
So, based on Dropdown selection I want to render appropriate Widget.
How can I build widget A if A is selected in the Dropdown menu, B if B is selected or C if C is selected?
This has to change dynamically though, any help is good.
Most simple way I see it is to create a function that renders your widgets, let's call it _renderWidget(), inside of that function you could have something as follows:
_renderWidget() {
if(condition == A) {
return Text('Widget A'); // this could be any Widget
} else if(condition == B) {
return Text('Widget B');
} else {
return Text('Widget C');
}
}
Then inside your DropdownButton onChanged function, you can change the condition based on the dropdown value:
onChanged(String value) {
if(value == 'something') {
setState(() {
condition = A; // A, B or C
});
}
}
You would call your _renderWidget() function inside the widget where you want to show them, for example let's say a Container widget.
Container(
child: _renderWidget()
)
Of course, all of this needs to be done inside a StatefulWidget.
use a stateful widget. you can have a field that says wich widget to show and call set state every time a different option is selected. all three widgets could be added to the main List<Widget> using collection if as follows:
children: <Widget>[
FormBuilder(
key: _fbKey,
autovalidate: true,
child: Column(
children: <Widget>[
FormBuilderDropdown(
attribute: "value",
decoration: InputDecoration(
labelText: "Choose something?"),
hint: Text('Select Option'),
validators: [FormBuilderValidators.required()],
items: user.option.map((v) {
return DropdownMenuItem(
value: v,
child: ListTile(
leading: Image.asset(
'assets/img/image.png',
width: 50,
height: 50,
),
title: Text("${v.option}"),
));
}).toList(),
),
],
),
),
if (v.option == "A") A(),
if (v.option == "B") B(),
if (v.option == "C") C(),
]
you can make a widget that accept the option in its constructor, and call it under the Drop Down builder
Example :
class RenderOption extends StatelessWidget {
final option;
const RenderOption({Key key, this.option}) : super(key: key);
#override
Widget build(BuildContext context) {
switch (option) {
case 1:
return Container();
break;
case 2:
return Container();
break;
case 3:
return Container();
break;
default:
}
}
}
now in you code above
children: <Widget>[
FormBuilder(
key: _fbKey,
autovalidate: true,
child: Column(
children: <Widget>[
FormBuilderDropdown(
attribute: "value",
decoration: InputDecoration(
labelText: "Choose something?"),
hint: Text('Select Option'),
validators: [FormBuilderValidators.required()],
items: user.option.map((v) {
return DropdownMenuItem(
value: v,
child: ListTile(
leading: Image.asset(
'assets/img/image.png',
width: 50,
height: 50,
),
title: Text("${v.option}"),
));
}).toList(),
),
],
),
//_currentOption is declared above in the widget tree
// it indicates the current selected option
RenderOption(option:_currentOption)
),
Note : you have to make the Parent Widget (which has the column or the listView as a child ) Stateful widget to make the code run properly
Edit : I've added the _currentOption variable which indicated the current selected option and paste it to the RenderOption Widget. you should implement the onChanged function in the FormBuilderDropDown to update the selected option , like this
onChanged: (option) =>setState(()=>_currentOption = option)