How do implement a DropdownButton using Getx in Flutter - flutter

I am trying to implement a DropdownButton with two dropdownmenuitems, spanish and english.
I want a user to select a language and will send this back to getx translations in order to change the app's language.
Here is my controller:
import 'package:get/get.dart';
class DropdownController extends GetxController {
String selectedValue;
var language = <String>['English', 'Espanol'];
void onSelected(String value) {
selectedValue = value;
}
}
Here is my settings page
class SettingsScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetBuilder<DropdownController>(builder: (controller) {
return Scaffold(
body: Column(children: [
Text('Select Language: '),
SizedBox(height: 10.0),
DropdownButton<String>(
hint: Text('Language'),
value: controller.selectedValue,
onChanged: (newValue) {
controller.onSelected(newValue);
},
elevation: 10,
items: [
DropdownMenuItem(
child: Text("English"),
value: controller.selectedValue,
),
DropdownMenuItem(
child: Text("ESpanol"),
value: controller.selectedValue,
),
]),
]),
);
});
}
}

Controller:
class DropdownController extends GetxController {
String? selectedValue;
var language = <String>['English', 'Espanol'];
void onSelected(String value) {
selectedValue = value;
update();
print(selectedValue);
changeLanguage(selectedValue);
}
changeLanguage(String? selectedLanguage) {
switch (selectedLanguage) {
case 'English':
Get.updateLocale(Locale('en_US'));
print('Changed to En');
break;
case 'Espanol':
Get.updateLocale(Locale('en_US'));
print('Changed to Es');
break;
default:
Get.updateLocale(Locale('en_US'));
print('Fallback to En');
break;
}
}
}
Settings page:
class SettingsScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetBuilder<DropdownController>(builder: (controller) {
return Scaffold(
body: Column(children: [
Text('Select Language: '),
SizedBox(height: 10.0),
DropdownButton<String>(
hint: Text('Language'),
value: controller.selectedValue,
onChanged: (newValue) {
controller.onSelected(newValue!);
},
elevation: 10,
items: [
DropdownMenuItem(
child: Text("English"),
value: 'English',
),
DropdownMenuItem(
child: Text("ESpanol"),
value: 'Espanol',
),
]),
]),
);
});
}
}

Related

Widget SwitchListTile's switch didn't work properly when it's using the widget builder method

I am making the Meal shop app which has many types of meals like vegan, vegetarian, etc as boolean var.
A page filters out which type of meal the user has to see. So, I am using the SwitchListTile widget the change its bool value. for that, I make the widget builder method but here switch button didn't work.
Here is the code.
import 'package:flutter/material.dart';
class FilterPage extends StatefulWidget {
static const route = '/filter-page';
#override
State<FilterPage> createState() => _FilterPageState();
}
class _FilterPageState extends State<FilterPage> {
var _gluttenFree = false;
var _lectosFree = false;
var _vegan = false;
var _vegitarian = false;
Widget _buildSwitch(
String title, String descreption, var curValue, Function updateValue) {
return SwitchListTile(
value: curValue,
onChanged: (_) => updateValue(),
title: Text(title),
subtitle: Text(description),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Filter settings'),
),
drawer: DrawerWidget(),
body: Column(
children: [
Text(
'Adjust your meal selection',
),
Expanded(
child: ListView(
children: [
_buildSwitch(
'Gluteen-Free',
'It is include Gluteen-free meals',
_gluttenFree,
(newValue) {
setState(() {
_gluttenFree = newValue;
});
},
),
],
),
)
],
),
);
}
}
onChanged provides a bool on callback, and it is the selection value of SwitchListTile.
It will be like.
Widget _buildSwitch(String title, String descreption, bool curValue,
Function(bool) updateValue) {
return SwitchListTile(
value: curValue,
onChanged: (value) => updateValue(value),
title: Text(title),
subtitle: Text(description),
);
}

RadioListTile is not working with setstate

What is working when i click on radio button it is not changing
class OnBoardingCheckBoxWithActionWidget extends StatefulWidget {
String label;
Function onClick;
OnBoardingCheckBoxWithActionWidget(this.label, {this.onClick});
#override
_OnBoardingCheckBoxWithActionWidgetState createState() =>
_OnBoardingCheckBoxWithActionWidgetState();
}
class _OnBoardingCheckBoxWithActionWidgetState
extends State<OnBoardingCheckBoxWithActionWidget> {
bool checked = true;
#override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: RadioListTile(
value: checked,
groupValue: checked,
toggleable: true,
title: Text("${widget.label}"),
onChanged: (val) {
if (widget.onClick != null) {
widget.onClick(val);
}
setState(() {
print("$checked");
checked = !checked;
print("$checked");
});
},
),
),
); // Card(child: Row(children: [Expanded(child: RadioListTile())],),);
}
}
You're trying to change some value from your Parent Stateful widget.
Try changing your current widget like
class OnBoardingCheckBoxWithActionWidget
extends StatelessWidget {
String label;
Function onClick;
var groupValue;
var buttonValue;
OnBoardingCheckBoxWithActionWidget(this.label, {this.onClick, this.groupValue,, this.buttonValue});
#override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: RadioListTile(
value: buttonValue,
groupValue: groupValue,
toggleable: true,
title: Text("${this.label}"),
onChanged: (val) {
if (this.onClick != null) {
this.onClick(val);
}
},
),
),
); // Card(child: Row(children: [Expanded(child: RadioListTile())],),);
}
}
And then call it like
OnBoardingCheckBoxWithActionWidget("Button1", onClick: (value){
setState((){
///Your Other Code
if(value){
groupValue = 'button1';
}
});
},
groupValue: someGroupValueVariableInState, buttonValue: 'button1')
Note that, the groupValue and buttonValue variables will always be some other variables other than booleans. When groupValue == buttonValue, the corresponding button will automatically marked as checked.

How do I change it such that the change is instant?

import 'package:flutter/material.dart';
class RandomPage extends StatefulWidget {
#override
_RandomScreenState createState() => _RandomScreenState();
}
class _RandomScreenState extends State {
String _selectedValue = 'Select';
bool appear = false;
Widget FirstDropDownButton() {
return Container(
child: DropdownButton <String> (
value: _selectedValue,
icon: Icon(Icons.arrow_drop_down),
underline: Container(
height: 1.5,
color: Colors.blueGrey,
),
onChanged: (String newValue) {
setState(() {
_selectedValue = newValue;
});},
items: <String> ['Select','One'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);}).toList(),
)
);
}
Widget FirstFlatButton() {
return FlatButton(
child: Text("Next"),
onPressed: () {
if (_selectedValue == 'Select') {
print("Cannot be NULL");
appear = false;
}
else if (_selectedValue == 'One')
appear = true;
});
}
Widget getWidget() {
if (appear == true) {
return Column(
children: <Widget>[
Text("Hello")
],
);
}
else
return Column(
children: <Widget>[
Text("Bye")
],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget> [
FirstDropDownButton(),
FirstFlatButton(),
getWidget(),
],
)
);
}
}
Hi this is a simpler version of the code of mine.
If I choose 'one' from the dropdownbutton the hello text should appear but it doesnt, only when I go ahead and change the value of the dropdownbutton again then will it reflect the change.
How do I make it such that when I select 'one' such that text hello appears immediately and when i select 'select' the text bye appears and hello disappears at the click of the flatbutton without toggling the options again for the change to be reflected?
i have taken the liberty to modify your code a bit, and made it more general.
For live demo check: codepen
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: RandomPage(),
),
);
}
class RandomPage extends StatefulWidget {
#override
_RandomScreenState createState() => _RandomScreenState();
}
class _RandomScreenState extends State {
int _selectedValue = 0;
bool appear = false;
List<String> values = ['Select', 'One', 'Two', 'Three'];
Widget FirstDropDownButton() {
return Container(
child: DropdownButton<String>(
value: values[_selectedValue],
icon: Icon(Icons.arrow_drop_down),
underline: Container(
height: 1.5,
color: Colors.blueGrey,
),
onChanged: (String newValue) {
setState(() {
_selectedValue = values.indexWhere((element) => element == newValue);
});
changeFlag();
},
items: values.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
));
}
void changeFlag() {
if (values[_selectedValue] == 'One') {
setState(() {
appear = true;
});
} else {
setState(() {
appear = false;
});
}
}
Widget FirstFlatButton() {
return FlatButton(
child: Text("Next"),
onPressed: () {
setState(() {
_selectedValue = (_selectedValue + 1) % (values.length);
});
changeFlag();
});
}
Widget getWidget() {
if (appear == true) {
return Column(
children: <Widget>[Text("Hello")],
);
} else
return Column(
children: <Widget>[Text("Bye")],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("StackOverflow")),
body: Column(
children: <Widget>[
FirstDropDownButton(),
FirstFlatButton(),
getWidget(),
],
));
}
}
Update: Now that I re-read the question,
I figured that you wanted to show either the value Hello or Bye depending upon the value on dropdown, when the user clicked Next button,
but i am sure you got the idea whatsoever,
just remove the setState on the flatbutton and changeFlag in the dropdown and you'll be fine.

Related DropdownButtons and raising onChange event

I have two DropdownButton widget. Content of second one depends on first one selection. Second DropdownButton will initiate refresh of third widget. How can I initiate refresh of second DropdownButton when first one is populated? And then how can I refresh third widget when second DropdownButton populated also?
class ParentBloc {
Stream<List<Parent>> get items => _controller.asyncMap(...);
Future<List<Parent>> _callApi() {
// call endpoint /parents
}
}
class ChildBloc {
ChildBloc(this.parentId);
int parentId;
Stream<List<Child>> get items => _controller.asyncMap(...);
Future<List<Child>> _callApi() {
// call endpoint /parents/$parentId/children
}
}
// This bloc created at init state
ParentBloc parentBloc;
// This bloc will be created only after value will
// be selected in the Parent dropdownbutton because
// I need to know `parentId`.
ChildBloc childBloc;
#override
void initState() {
super.initState();
parentBloc = ParentBloc();
}
#override
Widget build(BuildContext context) {
return Row(
children: [
StreamBuilder<List<Parent>>(
stream: parentBloc.items,
builder: (context,snapshot) {
return DropdownButton(
items: snapshot.data.map((item) {
return DropdownButtonItem();
}),
);
}
),
// Content of this widget depends on above one
StreamBuilder<List<Child>>(
stream: childBloc.items,
builder: (context,snapshot) {
return DropdownButton(
items: snapshot.data.map((item) {
return DropdownButtonItem();
}),
);
}
),
// Content of this widget depends on above one
StreamBuilder<List<Grandchild>>(
stream: grandchildBloc.items,
builder: (context,snapshot) {
return ListView(),
);
}
),
]
);
}
Provided you're doing this inside a StatefulWidget, you can use setState inside one of your functions where you update the variables that in turn have to be used to control what is currently displayed in each of your widgets.
It should look something like this (inside your Dropdown):
onChanged: (newValue) {
setState(() {
_currentSelection = newValue;
});
},
Update: after discussion in the comments, here's a working example that I made of how something can be updated based on a value selected inside a dropbox, hope it helps:
import 'package:flutter/material.dart';
class ExampleWidget extends StatefulWidget {
#override
_ExampleWidgetState createState() => _ExampleWidgetState();
}
class _ExampleWidgetState extends State<ExampleWidget> {
List<String> someStringsToSelectFrom = [
'value1',
'value2',
'value3',
];
String selectedValue;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Select something:'),
DropdownButton<String>(
value: selectedValue,
icon: Icon(
Icons.arrow_downward,
color: Colors.deepPurpleAccent,
),
iconSize: 24,
elevation: 16,
style: TextStyle(
fontSize: 30.0,
color: Colors.green,
),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String newValue) {
setState(() {
selectedValue = newValue;
});
},
items: someStringsToSelectFrom.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
Text('This will update after you selected a value:'),
someStringsToSelectFrom.contains(selectedValue) ? Text(selectedValue + ' was selected') :
Text('Still waiting for user action.'),
],
),
),
);
}
}

Is it possible in a dropdown button the displayed value to not be in the menu?

I tried to do it but I get an error. I was wondering if there is some kind of loophole.
Also, is there a way for the menu to open below the displayed value?
Here is the code:
import 'package:flutter/material.dart';
import './grile_view.dart';
class Grile extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return GrileState();
}
}
class GrileState extends State<Grile> {
var _bGrile = ['bgrila 1', 'bgrila 2'];
var _bio = 'bgrila 1';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.only(top: 15.0, left: 10.0, right: 10.0),
child: ListView(
children: <Widget>[
// DropdownButton
DropdownButton<String>(
items: _bGrile.map((String dropDownStringItem) {
return DropdownMenuItem<String>(
value: dropDownStringItem,
child: Text(dropDownStringItem)
);
}).toList(),
onChanged: (value){
Navigator.push(context, MaterialPageRoute(builder: (context) =>
GView()));
},
value: _bio
),
// DropdownButton End
]
)
)
);
} // build
} // GrileState
I am new at programming so excuse anything dumb I wrote in the code.
The DropdownButton class contains a very handy hint property. According to the DropdownButton documentation:
If [value] is null and [hint] is non-null, the [hint] widget is
displayed as a placeholder for the dropdown button's value.
What does this mean for you?
All you have to do is add a hint widget to your DropdownButton and then remove the assignment from your dynamic value.
Here's a brief example of a DropdownButton with a hint property:
class DropDownPage extends StatefulWidget {
#override
_DropDownPageState createState() => _DropDownPageState();
}
class _DropDownPageState extends State<DropDownPage> {
int dropdownValue; // Notice how this isn't assigned a value.
#override
Widget build(BuildContext context) {
return Center(
child: DropdownButton(
// Here's the hint property. I used a Text widget,
// but you can use any widget you like
hint: Text("Pick a Widget"),
value: dropdownValue,
items: [
DropdownMenuItem(child: Text("FlatButton"), value: 0),
DropdownMenuItem(child: Text("Container"), value: 1),
DropdownMenuItem(child: Text("Scaffold"), value: 2),
DropdownMenuItem(child: Text("GestureDetector"), value: 3),
],
onChanged: (value) {
setState(() {
dropdownValue = value;
});
},
));
}
}
This is the result:
So in your code, here's what you have to do in your own code.
Edit this: var _bio = 'bgrila 1';
And change it to this: var _bio;
Then add a hint property to your DropdownButton:
DropdownButton<String>(
hint: Text("Pick a *****"), // Replace with your hint widget
items: _bGrile.map((String dropDownStringItem) {
return DropdownMenuItem<String>(
value: dropDownStringItem,
child: Text(dropDownStringItem)
);
}).toList(),
onChanged: (value){
Navigator.push(context, MaterialPageRoute(builder: (context) =>
GView()));
},
value: _bio
),
So that your entire code looks like this:
class Grile extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return GrileState();
}
}
class GrileState extends State<Grile> {
var _bGrile = ['bgrila 1', 'bgrila 2'];
var _bio;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.only(top: 15.0, left: 10.0, right: 10.0),
child: ListView(
children: <Widget>[
// DropdownButton
DropdownButton<String>(
hint: Text("Pick a *******"),
items: _bGrile.map((String dropDownStringItem) {
return DropdownMenuItem<String>(
value: dropDownStringItem,
child: Text(dropDownStringItem)
);
}).toList(),
onChanged: (value){
Navigator.push(context, MaterialPageRoute(builder: (context) =>
GView()));
},
value: _bio
),
// DropdownButton End
]
)
)
);
} // build
} // GrileState