Flutter DropDownSearch value resets on changing focus to other widgets

3,036

Solution 1

You can copy paste run full code below
I use official example to simulate this case
To avoid rebuild clear selected item, you need to use selectedItem: _selected
And in onChanged do _selected = data;
In working demo, you can see after Hot reload, User is still correctly selected and Person has been reset

code snippet with your code

StudyName _selected;

DropdownSearch<StudyName>(
         selectedItem: _selected,
         mode: Mode.BOTTOM_SHEET,            
         items: _studyNamemodel,
         itemAsString: (StudyName u) => u.studyName,
         onChanged: (StudyName data) { 
            _selected = data;

            print(data.modalityIDFkey);
            //FocusScope.of(context).nextFocus();
            _selectedStudyname=data.studyName;
         },
         maxHeight: 300,
         onFind: (String filter) => getData(filter),
         label: "Study Name",
       ),

code snippet with official example

UserModel _selectedItemUser;
...
DropdownSearch<UserModel>(
    selectedItem: _selectedItemUser,
    mode: Mode.BOTTOM_SHEET,
    isFilteredOnline: true,
    showClearButton: true,
    showSearchBox: true,
    label: 'User *',
    dropdownSearchDecoration: InputDecoration(
        filled: true,
        fillColor:
            Theme.of(context).inputDecorationTheme.fillColor),
    //autoValidate: true,
    validator: (UserModel u) =>
        u == null ? "user field is required " : null,
    onFind: (String filter) => getData(filter),
    onChanged: (UserModel data) {
      print(data);
      _selectedItemUser = data;
    },
    dropdownBuilder: _customDropDownExample,
    popupItemBuilder: _customPopupItemBuilderExample,
  ),

working demo

enter image description here

full code

import 'package:dio/dio.dart';
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';

class UserModel {
  final String id;
  final DateTime createdAt;
  final String name;
  final String avatar;

  UserModel({this.id, this.createdAt, this.name, this.avatar});

  factory UserModel.fromJson(Map<String, dynamic> json) {
    if (json == null) return null;
    return UserModel(
      id: json["id"],
      createdAt:
          json["createdAt"] == null ? null : DateTime.parse(json["createdAt"]),
      name: json["name"],
      avatar: json["avatar"],
    );
  }

  static List<UserModel> fromJsonList(List list) {
    if (list == null) return null;
    return list.map((item) => UserModel.fromJson(item)).toList();
  }

  ///this method will prevent the override of toString
  String userAsString() {
    return '#${this.id} ${this.name}';
  }

  ///this method will prevent the override of toString
  bool userFilterByCreationDate(String filter) {
    return this?.createdAt?.toString()?.contains(filter);
  }

  ///custom comparing function to check if two users are equal
  bool isEqual(UserModel model) {
    return this?.id == model?.id;
  }

  @override
  String toString() => name;
}

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      //enable this line if you want test Dark Mode
      //theme: ThemeData.dark(),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _formKey = GlobalKey<FormState>();
  String _selectedItemCountry = "Italia";
  UserModel _selectedItemUser;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("DropdownSearch Demo")),
      body: Padding(
        padding: const EdgeInsets.all(25),
        child: Form(
          key: _formKey,
          autovalidate: true,
          child: ListView(
            padding: EdgeInsets.all(4),
            children: <Widget>[
              ///Menu Mode with no searchBox
              DropdownSearch<String>(
                validator: (v) => v == null ? "required field" : null,
                hint: "Select a country",
                mode: Mode.MENU,
                showSelectedItem: true,
                items: ["Brazil", "Italia (Disabled)", "Tunisia", 'Canada'],
                label: "Menu mode *",
                showClearButton: true,
                onChanged: print,
                popupItemDisabled: (String s) => s.startsWith('I'),
                selectedItem: "Brazil",
              ),
              Divider(),
              DropdownSearch<UserModel>(
                selectedItem: _selectedItemUser,
                mode: Mode.BOTTOM_SHEET,
                isFilteredOnline: true,
                showClearButton: true,
                showSearchBox: true,
                label: 'User *',
                dropdownSearchDecoration: InputDecoration(
                    filled: true,
                    fillColor:
                        Theme.of(context).inputDecorationTheme.fillColor),
                //autoValidate: true,
                validator: (UserModel u) =>
                    u == null ? "user field is required " : null,
                onFind: (String filter) => getData(filter),
                onChanged: (UserModel data) {
                  print(data);
                  _selectedItemUser = data;
                },
                dropdownBuilder: _customDropDownExample,
                popupItemBuilder: _customPopupItemBuilderExample,
              ),
              Divider(),

              ///custom itemBuilder and dropDownBuilder
              DropdownSearch<UserModel>(
                showSelectedItem: true,
                compareFn: (UserModel i, UserModel s) => i.isEqual(s),
                label: "Person",
                onFind: (String filter) => getData(filter),
                onChanged: (UserModel data) {
                  print(data);
                },
                dropdownBuilder: _customDropDownExample,
                popupItemBuilder: _customPopupItemBuilderExample2,
              ),
              Divider(),

              ///BottomSheet Mode with no searchBox
              DropdownSearch<String>(
                mode: Mode.BOTTOM_SHEET,
                maxHeight: 300,
                items: ["Brazil", "Italia", "Tunisia", 'Canada'],
                label: "Custom BottomShet mode",
                onChanged: print,
                selectedItem: _selectedItemCountry,
                showSearchBox: true,
                searchBoxDecoration: InputDecoration(
                  border: OutlineInputBorder(),
                  contentPadding: EdgeInsets.fromLTRB(12, 12, 8, 0),
                  labelText: "Search a country",
                ),
                popupTitle: Container(
                  height: 50,
                  decoration: BoxDecoration(
                    color: Theme.of(context).primaryColorDark,
                    borderRadius: BorderRadius.only(
                      topLeft: Radius.circular(20),
                      topRight: Radius.circular(20),
                    ),
                  ),
                  child: Center(
                    child: Text(
                      'Country',
                      style: TextStyle(
                        fontSize: 24,
                        fontWeight: FontWeight.bold,
                        color: Colors.white,
                      ),
                    ),
                  ),
                ),
                popupShape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.only(
                    topLeft: Radius.circular(24),
                    topRight: Radius.circular(24),
                  ),
                ),
              ),
              Divider(),

              ///merge online and offline data in the same list and set custom max Height
              DropdownSearch<UserModel>(
                items: [
                  UserModel(name: "Offline name1", id: "999"),
                  UserModel(name: "Offline name2", id: "0101")
                ],
                maxHeight: 300,
                onFind: (String filter) => getData(filter),
                label: "choose a user",
                onChanged: print,
                showSearchBox: true,
              ),
              Divider(),
            ],
          ),
        ),
      ),
    );
  }

  Widget _customDropDownExample(
      BuildContext context, UserModel item, String itemDesignation) {
    return Container(
      child: (item?.avatar == null)
          ? ListTile(
              contentPadding: EdgeInsets.all(0),
              leading: CircleAvatar(),
              title: Text("No item selected"),
            )
          : ListTile(
              contentPadding: EdgeInsets.all(0),
              leading: CircleAvatar(
                backgroundImage: NetworkImage(item.avatar),
              ),
              title: Text("Dropdown ${item.name}"),
              subtitle: Text(
                item.createdAt.toString(),
              ),
            ),
    );
  }

  Widget _customPopupItemBuilderExample(
      BuildContext context, UserModel item, bool isSelected) {
    return Container(
      margin: EdgeInsets.symmetric(horizontal: 8),
      decoration: !isSelected
          ? null
          : BoxDecoration(
              border: Border.all(color: Theme.of(context).primaryColor),
              borderRadius: BorderRadius.circular(5),
              color: Colors.white,
            ),
      child: ListTile(
        selected: isSelected,
        title: Text("Popup ${item.name}"),
        subtitle: Text(item.createdAt.toString()),
        leading: CircleAvatar(
          backgroundImage: NetworkImage(item.avatar),
        ),
      ),
    );
  }

  Widget _customPopupItemBuilderExample2(
      BuildContext context, UserModel item, bool isSelected) {
    return Container(
      margin: EdgeInsets.symmetric(horizontal: 8),
      decoration: !isSelected
          ? null
          : BoxDecoration(
              border: Border.all(color: Theme.of(context).primaryColor),
              borderRadius: BorderRadius.circular(5),
              color: Colors.white,
            ),
      child: ListTile(
        selected: isSelected,
        title: Text(item.name),
        subtitle: Text(item.createdAt.toString()),
        leading: CircleAvatar(
          backgroundImage: NetworkImage(item.avatar),
        ),
      ),
    );
  }

  Future<List<UserModel>> getData(filter) async {
    var response = await Dio().get(
      "https://5d85ccfb1e61af001471bf60.mockapi.io/user",
      queryParameters: {"filter": filter},
    );

    var models = UserModel.fromJsonList(response.data);
    return models;
  }
}

Solution 2

This issue is fixed in version 0.4.6. Please check it. In addition, in v0.4.6 you can programmatically set a selected value without using setState(), using GlobalKey , see plugin example for more details.

Share:
3,036
Ayan Bhattacharjee
Author by

Ayan Bhattacharjee

Updated on December 07, 2022

Comments

  • Ayan Bhattacharjee
    Ayan Bhattacharjee over 1 year

    I am using a dropdownsearch for creating a dropdown search widget in a layout, the problem when I change the focus to other widgets the value kind of resets every time. The plugin also has a property called selected item, if you use that on changing the focus it resets to the initially selected item.

    enter image description here

    enter image description here/dropdown_search/example

    here is my code:

    DropdownSearch<StudyName>(
                             mode: Mode.BOTTOM_SHEET,
    
                             items: _studyNamemodel,
                             itemAsString: (StudyName u) => u.studyName,
                             onChanged: (StudyName data) { print(data.modalityIDFkey);
                             //FocusScope.of(context).nextFocus();
                             _selectedStudyname=data.studyName;
                             },
                             maxHeight: 300,
                             onFind: (String filter) => getData(filter),
                             label: "Study Name",
                           ),