5

I am new to Flutter development and I'm trying to show the selected value of a dropdown but I can't get it to work.

The dropdown doesn't show it as the chosen one, it just continues as though nothing was selected. Please help me to solve the problem.

Here is my code

    import 'dart:convert';
    import 'package:sqlliteapp/db_helper.dart';
    import 'package:sqlliteapp/user_model.dart';
    import 'package:http/http.dart' as http;

    import 'package:flutter/material.dart';

    class SqliteDropdown extends StatefulWidget {
      @override
      SqliteDropdownState createState() {
        return new SqliteDropdownState();
      }
    }

    class SqliteDropdownState extends State<SqliteDropdown> {
      DatabaseHelper db = DatabaseHelper();

      //Add data to db
      _saveData() async {
        UserModel user1 = UserModel(
          "test",
          "test",
          "[email protected]",
          "test",
        );

        UserModel user2 = UserModel(
          "test1",
          "test1",
          "[email protected]",
          "test",
        );
        await db.saveData(user1);
        await db.saveData(user2);
      }

      @override
      void initState() {
        super.initState();
        _saveData();
      }

      UserModel _currentUser;

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Fetching data from Sqlite DB - DropdownButton'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              mainAxisSize: MainAxisSize.max,
              children: <Widget>[
                FutureBuilder<List<UserModel>>(
                    future: db.getUserModelData(),
                    builder: (BuildContext context,
                        AsyncSnapshot<List<UserModel>> snapshot) {
                      if (!snapshot.hasData) return CircularProgressIndicator();
                      return DropdownButton<UserModel>(
                        items: snapshot.data
                            .map((user) => DropdownMenuItem<UserModel>(
                                  child: Text(user.name),
                                  value: user,
                                ))
                            .toList(),
                        onChanged: (UserModel value) {
                          setState(() {
                            _currentUser = value;
                          });
                        },
                        isExpanded: true,
                        //value: _currentUser,
                        hint: Text('Select User'),
                      );
                    }),
                SizedBox(height: 20.0),
                _currentUser != null
                    ? Text(
                        "Name: " +
                            _currentUser.name +
                            "\n Email: " +
                            _currentUser.email +
                            "\n Username: " +
                            _currentUser.username +
                            "\n Password: " +
                            _currentUser.password,
                      )
                    : Text("No User selected"),
              ],
            ),
          ),
        );
      }
    }

dropdown doesn't show it as the chosen one, it just keeps like if nothing was selected.Please support me to solve the problem

1
  • 2
    you have commented - value: _currentUser, - how will you see you see the selected value .? Commented Aug 30, 2019 at 8:17

4 Answers 4

7

Problems

1. Dropdown needs available options to be selected

We can see at items parameter, DropdownButton will get its options definition

DropdownButton<UserModel>(
  items: snapshot.data // <-- this define options
      .map((user) => DropdownMenuItem<UserModel>(
            child: Text(user.name),
            value: user,
          ))
      .toList(),
  onChanged: (UserModel value) {
    setState(() {
      _currentUser = value;
    });
  },
  value: _currentUser,
);

2. Dropdown will re-init multiple times if wrapped in FutureBuilder

The problem occurs after we pick one options. Dropdown will modify _currentUser in StatefulWidget and execute setState.

By triggering setState, by default, Flutter widget will trigger build method once more.

FutureBuilder<List<UserModel>>(
  future: db.getUserModelData(),
  builder: (BuildContext context, AsyncSnapshot snapshot) {
    if (!snapshot.hasData) return CircularProgressIndicator();
    return DropdownButton<UserModel>(
      items: snapshot.data
          .map((user) => DropdownMenuItem<UserModel>(
                child: Text(user.name),
                value: user,
              ))
          .toList(),
      onChanged: (UserModel value) {
        setState(() {
          _currentUser = value; // <-- Will trigger re-build on StatefulWidget
        });
      },
      value: _currentUser,
    );
}),

Error


Solution

1. Solution : Initialize options once in initState

It will be mandatory for us, not to initialize inside build method. As we need remove FutureBuilder, we also need to initialize screenStage


@override
void initState() {
  _screenStage = "loading"; // <-- set "loading" to display CircularProgress
  onceSetupDropdown();
  super.initState();
}

void onceSetupDropdown() async {
  _userSelection = await db.getUserModelData();
  _screenStage = "loaded"; // <-- set "loaded" to display DropdownButton
  setState(() {}); // <-- trigger flutter to re-execute "build" method
}

2. Then we can render properly based on screenStage value

It will be mandatory for us, not to initialize inside build method. Therefore, we can remove FutureBuilder

_screenStage == "loaded"
  ? DropdownButton<UserModel>(  // <-- rendered at second "build"
      items: _userSelection
          .map((user) => DropdownMenuItem<UserModel>(
                child: Text(user.name),
                value: user,
              ))
          .toList(),
      onChanged: onChange,
      isExpanded: true,
      value: _currentUser,
      hint: Text('Select User'),
    )
  : CircularProgressIndicator(), // <-- rendered at first "build"

Result

Success


Fully working code

You can check it in this Github Repo

Sign up to request clarification or add additional context in comments.

5 Comments

its showing error after the refresh Another exception was thrown: 'package:flutter/src/material/dropdown.dart': Failed assertion: line 608 pos 15: 'items == null || items.isEmpty || value == null || items.where((DropdownMenuItem<T> item) => item.value == value).length == 1': is not true.
then you can initialize its value, by first element of snapshot.data
okay I got the problem, it is because we reinitialize its items during build
I've added detail explanation and comparison between them. You can also build it locally by accessing provided repo link above
@ejabu The only solution, I found that became useful for me.
1

try to use String instead of UserModel when value changes on selection. String _currentUser;

onChanged: (String value) {
                          setState(() {
                            _currentUser = value;
                          });
                        },

2 Comments

Thanks Zeeshan, its showing error after changing The argument type 'Null Function(String)' can't be assigned to the parameter type 'void Function(UserModel)'
@AmitNamdeo please also change _currentUser in to String;
1

Because you are using FutureBuilder, just remove setState() inside onChanged:

onChanged: (UserModel value) {
 _currentUser = value;
}

Comments

-2

The explanation from @ejabu is helping but my code is not seem to be able to modify as same with him/her.

BTW, the idea of loading and multiple initialize get called on the widget will cause re-initialize the DropdownButton effected to not showing selected value in flutter.

I have an idea of state machine then simply added a boolean variable isJustFinishedLoading = false at my state class.

...
StreamBuilder<QuerySnapshot>(
...
if(!snapshot.hasData){
print('loading...');
...
isJustFinishedLoading = true;
}
else
{
print('loaded');
...
    if (isJustFinishedLoading)
    {
       // initialize the DropdownMenuItem ...
    }
    return DropdownButton ... onChanged ...
}

Hope it help, happy new year 2020 !

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.