DropdownMenu Widget issues = Flutter

31 views Asked by At

I am trying to create a custom widget for DropDownButton. It seems to work fine. The onChanged() func is also working when I take console print of the changed value. However, I am not able to view the text in the box after the Onchanged() func.

Can someone let me know what could be wrong with the same? My code with the widget is below:

 import 'package:flutter/material.dart';
    import 'package:shared_preferences/shared_preferences.dart';
    
    class InputScreen2 extends StatefulWidget {
      final List<double> sizes;
      const InputScreen2({Key? key, required this.sizes}) : super(key: key);
    
      @override
      State<InputScreen2> createState() => _InputScreen2State();
    }
    
    class _InputScreen2State extends State<InputScreen2> {
      Future? future;
      double screenwd = 0, screenht = 0;
      List<String> listsex = ['Male', 'Female'];
      String? sex;
    
      @override
      void initState() {
        // TODO: implement initState
        future = _future();
        super.initState();
      }
    
      Future<int> _future() async {
        SharedPreferences prefs = await SharedPreferences.getInstance();
        screenwd = widget.sizes[0];
        screenht = widget.sizes[1];
    
        return 0;
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            backgroundColor: Colors.orange,
            body: FutureBuilder(
                future: future,
                builder: (context, snapshot) {
                  if (snapshot.hasError) {
                    return Center(
                        child: Text(
                      'Data Error...Please try again',
                      style: Theme.of(context).textTheme.titleSmall,
                    ));
                  } else if (snapshot.connectionState == ConnectionState.waiting) {
                    return Text(
                      'Waiting',
                      style: Theme.of(context).textTheme.titleSmall,
                    );
                  } else if (!snapshot.hasData) {
                    return Text(
                      'No Data',
                      style: Theme.of(context).textTheme.titleSmall,
                    );
                  } else if (snapshot.hasData) {
                    //Block I====================================================
                    return SafeArea(
                      child: Center(
                        child: Container(
                          padding: const EdgeInsets.all(20.0),
                          decoration: BoxDecoration(
                              border: Border.all(width: 2.0), color: Colors.blue),
                          width: screenwd * .8,
                          height: screenht * .75,
                          child: SingleChildScrollView(
                            child: Column(
                              children: [
                                Row(
                                  mainAxisAlignment: MainAxisAlignment.start,
                                  children: [
                                    const Text(
                                      'Personal Details',
                                    ),
                                    const SizedBox(
                                      width: 23,
                                    ),
                                    dropDownMenu('Sex', sex, listsex),
                                  ],
                                ),
                              ],
                            ),
                          ),
                        ),
                      ),
                    );
                  }
                  return const Text('No DATA');
                }));
      }
    
      Widget dropDownMenu(String title, String? inputTxt, List<String> stringList) {
        return Container(
          margin: const EdgeInsets.only(top: 10.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                title,
              ),
              Container(
                width: 80,
                height: 35,
                decoration: BoxDecoration(
                    border: Border.all(color: Colors.white, width: 1.0),
                    borderRadius: const BorderRadius.all(Radius.circular(5.0))),
                child: DropdownButton<String>(
                    value: inputTxt,
                    dropdownColor: Colors.amber,
                    items: stringList.map<DropdownMenuItem<String>>((String value) {
                      return DropdownMenuItem<String>(
                          value: value,
                          child: Text(
                            value,
                            style: const TextStyle(color: Colors.white),
                          ));
                    }).toList(),
                    onChanged: (String? value) {
                      setState(() {
                        inputTxt = value!;
                      });
                    }),
              ),
            ],
          ),
        );
      }
    }
1

There are 1 answers

3
il_boga On

I tried your code in a Dartpad: you have to replace the dropDownMenu onChanged with this

 onChanged: (String? value) {
                  setState(() {
                    sex = value!;
                  });
                }),

I think you just misplaced inputTxt with sex. Probably you don't even need inputTxt in dropDownMenu at all: just replace it with the sex field from _InputScreen2State, like this:

Widget dropDownMenu(String title, List<String> stringList) {
    return Container(
      margin: const EdgeInsets.only(top: 10.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(title),
          Container(
            width: 80,
            height: 35,
            decoration: BoxDecoration(
                border: Border.all(color: Colors.white, width: 1.0),
                borderRadius: const BorderRadius.all(Radius.circular(5.0))),
            child: DropdownButton<String>(
                value: sex,
                dropdownColor: Colors.amber,
                items: stringList.map<DropdownMenuItem<String>>((String value) {
                  return DropdownMenuItem<String>(
                      value: value,
                      child: Text(
                        value,
                        style: const TextStyle(color: Colors.white),
                      ));
                }).toList(),
                onChanged: (String? value) {
                  setState(() {
                    sex = value!;
                  });
                }),
          ),
        ],
      ),
    );
  }

I tested it in the same Dartpad, and it works.


EDIT: so you need to have different dropdowns, each with its different value argument: the problem here I think is that the setState you're invoking in the onChanged argument is not the right one, because DropDownButton is itself a stateful widget, so probably you are setting the state on InputScreen2 without updating the state of DropDownButton, so leaving the field empty. The first solution that comes to mind is to write your own stateful widget that return a DropDownButton (just like your method did):

class MyDropDownButton extends StatefulWidget {
  final String title;
  final List<String> stringList;
  const MyDropDownButton(
      {super.key,
      required this.title,
      required this.stringList});

  @override
  State<MyDropDownButton> createState() => _MyDropDownButtonState();
  
}

class _MyDropDownButtonState extends State<MyDropDownButton> {
  dynamic selectedValue;

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.only(top: 10.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(widget.title),
          Container(
            width: 80,
            height: 35,
            decoration: BoxDecoration(
                border: Border.all(color: Colors.white, width: 1.0),
                borderRadius: const BorderRadius.all(Radius.circular(5.0))),
            child: DropdownButton<String>(
                value: selectedValue,
                dropdownColor: Colors.amber,
                items: widget.stringList
                    .map<DropdownMenuItem<String>>((String value) {
                  return DropdownMenuItem<String>(
                      value: value,
                      child: Text(
                        value,
                        //style: const TextStyle(color: Colors.white),
                      ));
                }).toList(),
                onChanged: (String? value) {
                  setState(() {
                    selectedValue = value!;
                  });
                }),
          ),
        ],
      ),
    );
  }
}

You still need to implement a method in the widget that gets the value from the state (if available) in order to have the value in InputScreen2