skip to Main Content

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,
        );
      },
    );
  }
}

2

Answers


  1. 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(),
        );
      }
    }
    
    Login or Signup to reply.
  2. 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] 
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search