Creating a Dynamic Dropdown Form Field in flutter - #100DaysOfCode - Day 13

curtlycritchlow

Curtly Critchlow

Posted on July 8, 2021

Creating a Dynamic Dropdown Form Field in flutter - #100DaysOfCode - Day 13

Introduction

This post is part of my 100DaysOfCode series. In this series, I write about what I am learning on this challenge. For this challenge, I will be learning flutter and firebase by building an Agriculture Management Information System.

Recap

On Day 12 we updated the add farmer screen and discussed my failed attempt at creating a dynamic dropdown form field.

Overview

In this post, we will discuss how to create a dynamic dropdown form field in flutter. We will be using two DropdownFormField() widget. A region dropdown field and a district dropdown field.

A region is the USA equivalent of a state and a district is a sub location within a region. These two fields along with other fields comprise our Farmer Register Form as shown in the screenshot below

Updated Add Farmer Screen

My goal is to filter the options in the district depending on the region selected by the user. The purpose of this methodology is to reduce the number of options in the district dropdown field.

Region Dropdown Form Field



class RegionDropdownFormField extends StatelessWidget {
  const RegionDropdownFormField({
    Key? key,
    required this.state,
  }) : super(key: key);

  final _AddFarmerScreenController state;

  @override
  Widget build(BuildContext context) {
    return Expanded(
      flex: 5,
      child: DropdownButtonFormField(
        focusNode: state.regionFocusNode,
        decoration: FormStyles.textFieldDecoration(labelText: 'Region'),
        onChanged: (String? value) {
          state.setState(() {
            state.dropdownMenuItems = state._districtItem(value!);
            state.value = state.dropdownMenuItems!.first.value;
          });
        },
        validator: state.farmer.validateRequiredField,
        onSaved: state.farmer.saveFarmerCategory,
        items: Region.all
            .map((e) => DropdownMenuItem(
                  child: Text(e),
                  value: e,
                ))
            .toList(),
      ),
    );
  }
}


Enter fullscreen mode Exit fullscreen mode

When the use selects an option from the RegionDropdownFormField() the onChanged: function will be triggered.



(String? value) {
          state.setState(() {
            state.districtDropdownMenuItems = state._getDistrictItems(value!);
            state.districtValue = state.districtDropdownMenuItems!.first.value;
          });
        }


Enter fullscreen mode Exit fullscreen mode

This function calls setState() since we want the UI to update. Within setstate(), state.districtDropdownMenuItems = state._getDistrictItems(value!); creates a list of dropdownMenuItem() based on the value selected by the user. state.districtDropdownMenuItems will be assigned to the items: property of the DistrictDropdownFormField().

state.districtValue = state.districtDropdownMenuItems!.first.value; selects the first value of the newly created state.districtDropdownMenuItems. This variable will be assigned to the value: property of the DistrictDropdownFormField(). Failure to do this will create the below error.



════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building Builder(dirty, dependencies: [_FocusMarker]):
There should be exactly one item with [DropdownButton]'s value: District 6. 
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
'package:flutter/src/material/dropdown.dart':
Failed assertion: line 850 pos 15: 'items == null || items.isEmpty || value == null ||
              items.where((DropdownMenuItem<T> item) {
                return item.value == value;
              }).length == 1'



Enter fullscreen mode Exit fullscreen mode

District Dropdown FormField



class DistrictDropdownFormField extends StatelessWidget {
  const DistrictDropdownFormField({
    Key? key,
    required this.state,
  }) : super(key: key);

  final _AddFarmerScreenController state;

  @override
  Widget build(BuildContext context) {
    return Expanded(
      flex: 5,
      child: DropdownButtonFormField(
        focusNode: state.districtFocusNode,
        decoration: FormStyles.textFieldDecoration(labelText: 'District'),
        onChanged: (value) =>
            state._handleDropdownOnChanged(state.districtFocusNode),
        validator: state.farmer.validateRequiredField,
        onSaved: state.farmer.saveDistrict,
        value: state.districtValue,
        items: state.districtDropdownMenuItems,
      ),
    );
  }
}


Enter fullscreen mode Exit fullscreen mode

This widget is a typical DropdownFormField() but note, value: state.districtValue, and items: state.districtDropdownMenuItems, are dependent on the region selected in the DistrictDropdownFormField().

Wrap Up

In this post, we discussed how to create a dynamic dropdown form field and prevent the error that occured on day 12.

Connect with me

Thank you for reading my post. Feel free to subscribe below to join me on the #100DaysOfCodeChallenge or connect with me on LinkedIn and Twitter. You can also buy me a book to show your support.

💖 💪 🙅 🚩
curtlycritchlow
Curtly Critchlow

Posted on July 8, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related