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,
);
},
);
}
}
CodePudding user response:
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(),
);
}
}
CodePudding user response:
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]
}