DropDownButton item not being selected - flutter

I am trying to put a DropdownButton on one of my screens. I have followed several examples but I can not get it to show the selected item. It keeps showing the first item in the list.
String _trxnStatus = 'Listed';
DropdownButton<String>(
hint: Text('Please choose transaction status'),
value: _trxnStatus,
onChanged: (value) {
setState(() {
_trxnStatus = value;
});
},
items: <String>['Listed', 'Under Contract', 'Closed'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
I have traced the value through the debugger. onChange works fine and shows the selected value. However, when it comes to mapping the list and returning the DropdownMenuItem the var value = 'Listed'.
How do I get this to work?
Thanks.

You are possibly initializing the _trxnStatus within the build function. You need to initialize _trxnStatus outside of the build function. Please see the working code below:
import 'package:flutter/material.dart';
final Color darkBlue = const Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
String _trxnStatus = 'Listed';
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
hint: Text('Please choose transaction status'),
value: _trxnStatus,
onChanged: (value) {
setState(() {
_trxnStatus = value;
});
},
items: <String>['Listed', 'Under Contract', 'Closed']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}

Related

Stack overflow when testing a function passed to a widget

I made a small app to test passing a function to a widget, I have no compile errors, however, when I run that app, I see 45 times the call to buildSwitchListTile.
I don't understand why it's being called so many times. I am running latest versions for VSCode, Flutter, and Dart.
import 'package:flutter/material.dart';
import './widgets/build_switch_list_tile.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Function to Widget'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _option1 = false;
bool _option2 = true;
bool _option3 = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// use widget here
buildSwitchListTile(
'Option 1',
'From widget Select option 1',
_option1,
(newValue) {
if (newValue != _option1) {
setState(() {
_option1 = newValue;
});
}
},
),
// end test widget
buildSwitchListTile(
'Option 2',
'From widget Select option 2',
_option2,
(newValue) {
if (newValue != _option2) {
setState(() {
_option2 = newValue;
});
}
},
),
buildSwitchListTile(
'Option 3',
'From widget Select option 3',
_option3,
(newValue) {
if (newValue != _option3) {
setState(() {
_option3 = newValue;
});
}
},
),
],
),
),
);
}
}
The widget:
import 'package:flutter/material.dart';
Widget buildSwitchListTile(
String title,
String description,
bool currentValue,
void Function(bool)? updateValue,
) {
return buildSwitchListTile(
title,
description,
currentValue,
updateValue,
);
}
I try to avoid calling setState if the switch is not modified.
The reason you are getting error is because you are calling buildSwitchListTile inside buildSwitchListTile. This will make the method call itself recursively forever until the app crash.
If you are trying to add a SwitchListTile widget in your app, probably you would want to add SwitchListTile (without the "build"). Then your method will be something like this:
Widget buildSwitchListTile(
String title,
String description,
bool currentValue,
void Function(bool)? updateValue,
) {
return SwitchListTile(
title: Column(
children: [
Text(title),
Text(description),
],
),
value: currentValue,
onChanged: updateValue,
);
}
For more information check the API documentation of the SwitchListTile widget.
Note: it's better to create a custom widget to wrap that code and not just use a helper method, as explained in this video from the Flutter YouTube channel

How to get Value to update in Flutter DropdownButton from stream?

I'm trying to get a list from my firebase firestore and provide it as a dropdown button, but when the user selects the option it does not update on GUI.
I think the problems is where I instantiate the dropdownValue variable but I don't where else to place it.
class _LocationNameListState extends State<LocationNameList> {
#override
Widget build(BuildContext context) {
List dropdownOptions = <String>[];
String? dropdownValue;
return StreamBuilder(
stream: LocationController().getAllLocations(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text("This is something wrong");
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
for (var i = 0; i < snapshot.data!.docs.length; i++) {
dropdownOptions.add("${snapshot.data!.docs[i]['name']}");
}
print(dropdownOptions);
String dropdownValue = dropdownOptions[0];
return DropdownButton(
items: dropdownOptions
.map((e) => DropdownMenuItem(
value: e,
child: Text(e),
))
.toList(),
onChanged: (value) {
setState(() {
dropdownValue = value.toString();
print(dropdownValue);
});
},
value: dropdownValue,
);
},
);
}
}
The problem is that your dropDown value is set within your Build method:
Widget build(BuildContext context) {
List dropdownOptions = <String>[];
String? dropdown value;
return StreamBuilder(
...
So every setState it gets reset, since the build rebuilds.
To fix the error, move your value outside of the build method:
class _LocationNameListState extends State<LocationNameList> {
// --> Add this variable over here
List dropdownOptions = <String>[];
String? dropdownValue;
#override
Widget build(BuildContext context) {
...
}
I've managed to reproduce your problem with a simplified example. As you see dropdownValue will be reset, since it's within the build method:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyDropdown(),
),
),
);
}
}
class MyDropdown extends StatefulWidget {
const MyDropdown({Key? key}) : super(key: key);
#override
State<MyDropdown> createState() => _MyDropdownState();
}
class _MyDropdownState extends State<MyDropdown> {
#override
Widget build(BuildContext context) {
String dropdownValue = 'One';
return DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>['One', 'Two', 'Free', 'Four']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}
And to solve the issue:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyDropdown(),
),
),
);
}
}
class MyDropdown extends StatefulWidget {
const MyDropdown({Key? key}) : super(key: key);
#override
State<MyDropdown> createState() => _MyDropdownState();
}
class _MyDropdownState extends State<MyDropdown> {
// -->Simply set the value here
String dropdownValue = 'One';
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>['One', 'Two', 'Free', 'Four']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}
Every time setState is called, build method gets called. It means
String dropdownValue = dropdownOptions[0]; is called as well setting the value of variable to first item of the list.
You need to move dropdownValue to class level variable of your state class.
(String? dropdownValue = null)
Then replace above mentioned line with
if(dropdownValue == null) {
dropdownValue = dropdownOptions[0]
}

Change DropdownButtonFormField value programmatically

I'm trying to change the DropdownButtonFormField value on event (button press for example) using setState. But it's not working.
Note: it works in case I use DropdownButton, but with DropdownButtonFormField it's not responding.
Here is a simple code showing what I'm trying to implement.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Testing(),
);
}
}
class Testing extends StatefulWidget {
#override
_TestingState createState() => _TestingState();
}
class _TestingState extends State<Testing> {
String selectedValue;
#override
Widget build(BuildContext context) {
return Material(
child: Column(
children: <Widget>[
DropdownButtonFormField(
value: selectedValue,
items: ['one', 'two'].map((value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (value) {
setState(() {
selectedValue = value;
});
},
),
RaisedButton(
child: Text('test'),
onPressed: (){
setState(() {
selectedValue = 'two';
});
},
),
],
),
);
}
}
Define instance variable from Global Key and pass it to DropdownButtonFormField
final dropdownState = GlobalKey<FormFieldState>();
You can change the value of dropDownFieldItem by calling this method
dropdownState.currentState.didChange('two');
final code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Testing(),
);
}
}
class Testing extends StatefulWidget {
#override
_TestingState createState() => _TestingState();
}
class _TestingState extends State<Testing> {
String selectedValue;
final dropdownState = GlobalKey<FormFieldState>();
#override
Widget build(BuildContext context) {
return Material(
child: Column(
children: <Widget>[
DropdownButtonFormField(
key: dropdownState,
value: selectedValue,
items: ['one', 'two'].map((value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (value) {
setState(() {
selectedValue = value;
});
},
),
RaisedButton(
child: Text('test'),
onPressed: () {
dropdownState.currentState.didChange('one');
},
),
],
),
);
}
}
Here working normally with DropdownButtonFormField and DropdownButton.
flutter --version
Flutter 1.12.13+hotfix.9 • channel stable •
In Flutter version 1.17.2 that bug was fixed, so be sure to upgrade.
Github issue: https://github.com/flutter/flutter/issues/56898
Fixed in version 1.17.2: https://github.com/flutter/flutter/wiki/Hotfixes-to-the-Stable-Channel#1172-may-28-2020

Flutter rebuild parent widget

I need help. I have a Dropdown widget in LanguageDropdown class, where the user can select the language. And the widget is inside a settings page widget in Settings class. The language changes on other pages, but not on current one. How can I rebuild that specific page, so the language changes on this one also?
See the code below
import 'package:jptapp/features/settings/change_language/app_localization.dart';
class LanguageDropDown extends StatefulWidget {
#override
_LanguageDropDownState createState() {
return _LanguageDropDownState();
}
}
class _LanguageDropDownState extends State<LanguageDropDown> {
String _value = allTranslations.currentLanguage;
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
items: [
DropdownMenuItem<String>(
child: Text('English'),
value: 'en',
),
DropdownMenuItem<String>(
child: Text('Magyar'),
value: 'hu',
),
DropdownMenuItem<String>(
child: Text('Srpski'),
value: 'rs',
),
],
onChanged: (String value) {
setState(() async{
_value = value;
await allTranslations.setNewLanguage(_value);
});
},
hint: Text(_value),
value: _value,
);
}
}
import 'package:jptapp/core/constants/colors.dart';
import 'package:jptapp/features/settings/change_language/app_localization.dart';
import 'package:jptapp/features/settings/widgets/widgets.dart';
class Settings extends StatefulWidget {
#override
_SettingsState createState() => _SettingsState();
}
class _SettingsState extends State<Settings> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: MyColors.appBarColor,
title: Text(
allTranslations.text('settings'),
),
),
body: ListView(
children: ListTile.divideTiles(
context: context,
tiles: [
ListTile(
trailing: ThemeChangerAnimationButton(),
title: Text(
allTranslations.text('darkmode'),
),
),
ListTile(
trailing: LanguageDropDown(),
title: Text(
allTranslations.text('language'),
),
),
],
).toList(),
),
);
}
}
I'm not sure this will work but try this:
import 'package:flutter/material.dart';
import 'package:jptapp/features/settings/change_language/app_localization.dart';
class LanguageDropDown extends StatefulWidget {
#override
_LanguageDropDownState createState() {
return _LanguageDropDownState();
}
}
class _LanguageDropDownState extends State<LanguageDropDown> {
String _value = allTranslations.currentLanguage;
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
items: [
DropdownMenuItem<String>(
child: Text('English'),
value: 'en',
),
DropdownMenuItem<String>(
child: Text('Magyar'),
value: 'hu',
),
DropdownMenuItem<String>(
child: Text('Srpski'),
value: 'rs',
),
],
onChanged: (String value) {
setState(() async {
_value = value;
await allTranslations.setNewLanguage(_value);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Settings()
));
});
},
hint: Text(_value),
value: _value,
);
}
}

How to update child dropdown after parent dropdown's selection changes?

So I have a column of dropdowns, each dropdown a prerequisite of the dropdown below. When Dropdown A changes its value, I'd make an API call and populate Dropdown B, Dropdown C's options are dependent on the value of B, and so on...
I accomplished this scenario fine, but the problem I'm having is when Dropdowns A to D have been selected, and I change the value of Dropdown B, Dropdowns C and D doesn't change its value.
SOS & TY
Refer the given example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: Center(
child: MyStatefulWidget(),
),
),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
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.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
items: <String>['One', 'Two', 'Free', 'Four']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}