It is possible to hide the DropDownButton (only the button) when menu is open?
I need the background of the menu to be transparent, when I do it I find that the button and the menu are misaligned and also that you can see the text of the button in the background (see image)
this is my problem
I need that once the menu is opened it looks more or less like the following image
this is a fake recreation of the menu i need
If somebody know any answer that is an alternative to using the DropDownButton, made with containers, some packages or whatever, and that can do the same functions, that would be welcome too.
Actual Code
class DropDownMenuAppBar extends StatefulWidget {
const DropDownMenuAppBar({
Key? key,
}) : super(key: key);
#override
State<DropDownMenuAppBar> createState() => _DropDownMenuAppBarState();
}
class _DropDownMenuAppBarState extends State<DropDownMenuAppBar> {
String dropdownValue = 'Today';
#override
Widget build(BuildContext context) {
return DropdownButtonHideUnderline(
child: DropdownButton<String>(
alignment: Alignment.bottomLeft,
dropdownColor: Colors.white10,
value: dropdownValue,
icon: const Icon(
Icons.arrow_drop_down,
color: Colors.white,
),
elevation: 0,
style: AppTextStyle.boldLarge.copyWith(color: Colors.white),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>['Today', 'All Habits']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
);
}
}
Thanks
You can add a focus to the dropdown to know if its open or closed and set the visibility of the selected item like
class DropDownMenuAppBar extends StatefulWidget {
const DropDownMenuAppBar({Key? key}) : super(key: key);
#override
State<DropDownMenuAppBar> createState() => _DropDownMenuAppBarState();
}
class _DropDownMenuAppBarState extends State<DropDownMenuAppBar> {
String dropdownValue = 'Today';
late FocusNode _focus;
#override
void initState() {
super.initState();
_focus = FocusNode();
_focus.addListener(_handleFocusChange);
}
void _handleFocusChange() {
if (_focus.hasFocus != _focused) {
setState(() {
_focused = _focus.hasFocus;
});
}
print(!_focused);
}
bool _focused = true;
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
alignment: Alignment.bottomLeft,
dropdownColor: Colors.white10,
value: dropdownValue,
focusNode: _focus,
icon: Visibility(
visible: _focused,
child: Icon(
Icons.arrow_drop_down,
color: Colors.white,
),
),
elevation: 0,
style: const TextStyle(color: Colors.deepPurple),
selectedItemBuilder: (BuildContext context) {
return <String>['Today', 'All Habits']
.map((String value) {
return Visibility(
visible: _focused,
child: DropdownMenuItem<String>(
value: value,
child: Text(value),
),
);
}).toList();
},
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
if (_focus.hasFocus) {
_focus.unfocus();
} else {
_focus.requestFocus();
}
},
items: <String>['Today', 'All Habits']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}
Related
I have 3 variables containing text that I can pass to message.
String easyDrop = 'All ok';
String mediumDrop = '1 problem';
String hardDrop = 'All not ok';
message: easyDrop ,
I would like to change them depending on the state of my DropDownButton. How can I do this?
import 'package:flutter/material.dart';
class DropDownButtonDifficultySettingsWidget extends StatefulWidget {
DropDownButtonDifficultySettingsWidget({Key? key}) : super(key: key);
#override
State<DropDownButtonDifficultySettingsWidget> createState() => _DropDownButtonDifficultySettingsState();
}
class _DropDownButtonDifficultySettingsState extends State<DropDownButtonDifficultySettingsWidget> {
String dropdownValue = 'Medium';
#override
Widget build(BuildContext context) {
return Theme(
data: ThemeData(
splashColor: Colors.blue.withOpacity(0.4),),
child: DropdownButton<String>(
value: dropdownValue,
elevation: 8,
alignment: Alignment.centerRight,
iconDisabledColor: Colors.blue,
iconEnabledColor: Colors.blue,
underline: Container(
height: 0,
),
style: const TextStyle(color: Colors.blue, fontWeight: FontWeight.w500, ),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>['Easy', 'Medium', 'Hard']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
);
}
}
You can include a callback method to get selected item from DropDownButtonDifficultySettingsWidget
class DropDownButtonDifficultySettingsWidget extends StatefulWidget {
final Function(String? selectedValue) callback;
const DropDownButtonDifficultySettingsWidget(
{Key? key, required this.callback})
: super(key: key);
#override
State<DropDownButtonDifficultySettingsWidget> createState() =>
_DropDownButtonDifficultySettingsState();
}
And on changed
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
widget.callback(dropdownValue); //this
},
Now when ever you use DropDownButtonDifficultySettingsWidget you will get selected value on callback
DropDownButtonDifficultySettingsWidget(
callback: (selectedValue) {
print(selectedValue);
/// do the thing you like to have
},
),
I want to get the initial value of the Dropdown button from firebase;
but when I try to set the governorateDDValue = selectedUser.governorate; inside the build method
the value of Dropdown get the value from firebase but I cannot change it
DropdownButton.gif
this my code
class UserInfo extends StatefulWidget {
static const routeName = '/UserInfoScreen';
const UserInfo({Key? key}) : super(key: key);
#override
_UserInfoState createState() => _UserInfoState();
}
class _UserInfoState extends State<UserInfo> {
late User selectedUser;
final date = DateFormat('yyyy-MM-dd').format(DateTime.now()).toString();
var governorateDDValue;
#override
Widget build(BuildContext context) {
final userList= Provider.of<List<User>>(context);
final userID = ModalRoute.of(context)!.settings.arguments as String;
selectedUser =
userList.firstWhere((user) => user.id == userID);
// this line makes dropdown value always equal to value from firestore
governorateDDValue = selectedUser.governorate;
return Scaffold(
appBar: AppBar(
title: Text('report'),
),
body: SingleChildScrollView(
child: Container(
child: Row(
children: <Widget>[
Text('Governorate',),
Container(height: 5),
DropdownButton<String>(
value: governorateDDValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
onChanged: (String? newValue) {
setState(() {
governorateDDValue = newValue!;
});
},
items: Constants.governoratesOfEgypt
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
],
),
),
),
);
}
}
thanks in advance
Because you use governorateDDValue = selectedUser.governorate; inside build widget so the Dropdown menu will reset its value every time you change it
the build widget will rebuild and the value of the dropdown will stay equal to the value from firebase
you should use governorateDDValue = selectedUser.governorate; outside the build widget
this code should work will
class UserInfo extends StatefulWidget {
static const routeName = '/UserInfoScreen';
const UserInfo({Key? key}) : super(key: key);
#override
_UserInfoState createState() => _UserInfoState();
}
class _UserInfoState extends State<UserInfo> {
var loading = false;
late User selectedUser;
final date = DateFormat('yyyy-MM-dd').format(DateTime.now()).toString();
var governorateDDValue;
#override
void initState() {
super.initState();
loading = true;
print('Future.delayed outside');
print(loading);
Future.delayed(Duration.zero, () {
governorateDDValue = selectedUser.governorate;
setState(() {
loading = false;
});
});
}
#override
Widget build(BuildContext context) {
final userList = Provider.of<List<User>>(context);
final userID = ModalRoute
.of(context)!
.settings
.arguments as String;
selectedUser =
userList.firstWhere((user) => user.id == userID);
// this line makes dropdown value always equal to value from firestore
governorateDDValue = selectedUser.governorate;
return Scaffold(
appBar: AppBar(
title: Text('report'),
),
body: SingleChildScrollView(
child: Container(
child: Row(
children: <Widget>[
Text('Governorate',),
Container(height: 5),
DropdownButton<String>(
value: governorateDDValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
onChanged: (String? newValue) {
setState(() {
governorateDDValue = newValue!;
});
},
items: Constants.governoratesOfEgypt
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
],
),
),
),
);
The reason why you cannot change it is because every time setState is called your build method is called. Therefore your value will always be set to governorateDDValue = selectedUser.governorate; So to allow changes you should place this governorateDDValue = selectedUser.governorate; in iniState
Or what you can do is like this so that it will only set it once
class UserInfo extends StatefulWidget {
static const routeName = '/UserInfoScreen';
const UserInfo({Key? key}) : super(key: key);
#override
_UserInfoState createState() => _UserInfoState();
}
class _UserInfoState extends State<UserInfo> {
late User selectedUser;
final date = DateFormat('yyyy-MM-dd').format(DateTime.now()).toString();
bool initState = true; // ADD HERE
var governorateDDValue;
#override
Widget build(BuildContext context) {
final userList= Provider.of<List<User>>(context);
final userID = ModalRoute.of(context)!.settings.arguments as String;
selectedUser =
userList.firstWhere((user) => user.id == userID);
// this line makes dropdown value always equal to value from firestore
if(initState){ // ADD HERE
governorateDDValue = selectedUser.governorate;
initState = false; // ADD HERE
}
return Scaffold(
appBar: AppBar(
title: Text('report'),
),
body: SingleChildScrollView(
child: Container(
child: Row(
children: <Widget>[
Text('Governorate',),
Container(height: 5),
DropdownButton<String>(
value: governorateDDValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
onChanged: (String? newValue) {
setState(() {
governorateDDValue = newValue!;
});
},
items: Constants.governoratesOfEgypt
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
],
),
),
),
);
}
I'm new to flutter.
I have a page (Stateful Widget) in the app with a lot of widgets in a column. To improve the code readability, I took some widgets, and made them into seperate classes. For example, I made my dropdownmenu widget, into its only class, like this:
class DropDownMenuWidget extends StatefulWidget {
DropDownMenuWidget({Key? key}) : super(key: key);
#override
_DropDownMenuWidgetState createState() => _DropDownMenuWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _DropDownMenuWidgetState extends State<DropDownMenuWidget> {
String dropdownValue = 'One';
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropdownValue,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(
color: Colors.black,
fontSize: 20,
),
underline: Container(
height: 2,
color: Colors.blue,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: MASLULIM
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}
Now, in the parent class, I display the widget like this:
DropDownMenuWidget(),
However, the problem is, when the user clicks on a item, I can only retrieve that value from the DropDownMenu class, and there the setState() method is called. However, I need to read this value in the parent class. How can I get it there?
Thanks
Instead of creating your dropdownValue variable in your Widget, you can get it from the parent Widget as following with the help of ValueNotifier
class DropDownMenuWidget extends StatefulWidget {
ValueNotifier dropdownValueNotifier;
DropDownMenuWidget(this.dropdownValueNotifier, {Key key}) : super(key: key);
#override
_DropDownMenuWidgetState createState() => _DropDownMenuWidgetState();
}
class _DropDownMenuWidgetState extends State<DropDownMenuWidget> {
#override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: widget.dropdownValueNotifier,
builder: (context, dropdownValue, _) {
return DropdownButton<String>(
value: dropdownValue,
// ...
onChanged: (String newValue) {
// simply change the value. You dont need setState anymore
widget.dropdownValueNotifier.value = newValue;
},
// ...
);
},
);
}
}
In the parent Widget, create the variable and pass it like this
ValueNotifier dropdownValueNotifier = ValueNotifier('One');
// ...
DropDownMenuWidget(dropdownValueNotifier),
In this case, you can use typedef
First in a separate DrobDown menu you can create the following icon outside of the class:
typedef OnItemSelectedDropDown = Function (String value);
Now you can apply this thing as follows :
class DropDownMenuWidget extends StatefulWidget {
final OnItemSelectedDropDown onItemSelected ;
DropDownMenuWidget({Key? key}) : super(key: key);
#override
_DropDownMenuWidgetState createState() => _DropDownMenuWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _DropDownMenuWidgetState extends State<DropDownMenuWidget> {
String dropdownValue = 'One';
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropdownValue,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(
color: Colors.black,
fontSize: 20,
),
underline: Container(
height: 2,
color: Colors.blue,
),
onChanged: (String value) {
//This line return Value
widget.onItemSelected.call(value);
},
items: MASLULIM
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}
When calling the class DropDownMenuWidget, it is called as follows on another screen:
String dropdownValue ;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('DropDown Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'New Value DropDown : $dropdownValue',
),
DropDownMenuWidget(
onItemSelected :(newValue){
setState(() {
dropdownValue = newValue ;
});
}
),
],
),
),
);
}
I have two dropdown buttons that are mutually exclusive. How can I clear (or set) the value of one when the other is set?
Thanks
for 1st dropdown:
onChanged: (String newValue) {
setState(() {
dropdownValueFirst = newValue;
dropdownValueSecond = "Bangladesh";
});
},
for 2nd dropdown:
onChanged: (String newValue) {
setState(() {
dropdownValueSecond = newValue;
dropdownValueFirst ="One";
});
},
See below code:
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
String dropdownValueFirst="One";
String dropdownValueSecond="Bangladesh";
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DropdownButton<String>(
value: dropdownValueFirst,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(
color: Colors.deepPurple
),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String newValue) {
setState(() {
dropdownValueFirst = newValue;
dropdownValueSecond = "Bangladesh";
});
},
items: <String>['One', 'Two', 'Free', 'Four']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
})
.toList(),
),
const Padding(padding: EdgeInsets.only(left: 8)),
DropdownButton<String>(
value: dropdownValueSecond,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(
color: Colors.deepPurple
),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String newValue) {
setState(() {
dropdownValueSecond = newValue;
dropdownValueFirst ="One";
});
},
items: <String>['Bangladesh', 'India', 'China']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
})
.toList(),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
When 1st drop down in pressed then try to reset value of 2nd dropdown inside setState on onChanged event and vice versa,
onChanged: (String newValue) {
setState(() {
dropdownValueFirst = newValue;
dropdownValueSecond='Initial Value of second',// remeber this value must be same as initial value of 2nd dropdown =>value: 'Initial Value of second',
});
},
I have build the code of DropdownMenuItem, now when i click an item from dropdownmenuitem it should move to another screen.Below is the code
class TimesScreen extends StatefulWidget {
#override
_TimesScreenState createState() => _TimesScreenState();
}
class _TimesScreenState extends State<TimesScreen> {
var gender;
#override
Widget build(BuildContext context) {
DropdownButton(
hint: Text("Select",
style: TextStyle(color: Colors.white),),
onChanged: (val){
setState(() {
this.gender=val;
});
},
value: this.gender,
items: [
DropdownMenuItem(
//onTap:
value: 'Earth',
child: Text('Earth'
),
),
DropdownMenuItem(
//onTap:
value: 'Mars',
child: Text('Mars'
),
),)]
You can wrap your Text widget with GestureDetector to which has an onTap function which you can use to execute your desired code. For more details look at this: https://api.flutter.dev/flutter/widgets/GestureDetector-class.html
This should work:
DropdownMenuItem(
value: 'Earth',
child: GestureDetector(
onTap: () {
// navigate code...
},
child: Text('Earth')
),
),
After applying fayeed's solution, I noticed that this only makes the text inside the dropdown clickable. To fix this, you can simply use DropdownButton.onChanged.
Full widget:
class TimesScreen extends StatefulWidget {
#override
_TimesScreenState createState() => _TimesScreenState();
}
class _TimesScreenState extends State<TimesScreen> {
var gender;
#override
Widget build(BuildContext context) {
return DropdownButton(
hint: Text("Select"),
value: this.gender,
items: [
DropdownMenuItem(value: 'Earth', child: Text('Earth')),
DropdownMenuItem(value: 'Mars', child: Text('Mars')),
],
onChanged: (val) {
setState(() {
this.gender = val;
});
switch (val) {
case 'Earth':
Navigator.pushNamed(context, '/earth_target_page');
break;
case 'Mars':
Navigator.pushNamed(context, '/mars_target_page');
break;
}
},
);
}
}