Search code examples
flutterdrop-down-menumulti-level

Multi-level dropdown buttons in flutter - can we eliminate the if statements?


I implemented a two levels of dropdown buttons where button# 2 depends on the choice of button #1. first drop down is a list of countries, depending on the user's selection of the country, a list of cities will be available in the second drop down. The main question is about the link between the two drop-downs, I am using a chain of if-statements to tell the code that if I choose country x, display cities of x etc. In the example code below I am including only 6 countries, so writing if statements is still clean and manageable, but what if the list of countries goes to 150? will I need to write 150 lines of if-statements to link countries to their cities? Is there a smarter way to do this? can I exploit the similarities in the country name and the name of the list containing its cities? e.g. 'USA' and 'cityUSA'

import 'package:flutter/material.dart';
void main() {
  runApp(MyApp());}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
String? dropdownValue;
String? dropdownCity;
List<String> selectedCities=[];
String? selectedCountry;
@override

Widget build(BuildContext context) {
  final List<String> country = ['USA', 'Egypt', 'Spain', 'Mexico', 'Russia', 'China'];
  final List<String> cityEgypt =['Cairo', 'Alexandria'];
  final List<String> cityUSA = ['Houston', 'Dallas'];
  final List<String> citySpain = ['Madrid', 'Barcelona'];
  final List<String> cityMexico = ['Cancun', 'Mexico City'];
  final List<String> cityRussia=['Moscow', 'Saint Petersburg'];
  final List<String> cityChina = ['Gowanzo', 'Beijing'];
  return MaterialApp(
      title: 'Drop down fun',
      home: Scaffold(
      backgroundColor: Colors.greenAccent,
      body:Column(
      children: <Widget>[
      Padding(
      padding: const EdgeInsets.all(8.0),
  child: Text('Please choose your location', style:TextStyle(fontWeight: FontWeight.bold, fontSize: 25)),
  ),

    Padding(
  padding: const EdgeInsets.all(10.0),
  child: DropdownButton<String>(
  value: dropdownValue,
  icon: Icon(Icons.map),
  disabledHint: Text('Choose country first'),

  style:TextStyle(color: Colors.black, fontSize: 15) ,
  onChanged: (String? newValue) {
  setState(() {
  dropdownValue = newValue;
  selectedCountry = dropdownValue;
  dropdownCity = null;
  if(selectedCountry=='Egypt'){selectedCities=cityEgypt;}
  else if(selectedCountry=='USA'){selectedCities=cityUSA;}
  else if(selectedCountry=='Spain'){selectedCities=citySpain;}
  else if(selectedCountry=='Mexico'){selectedCities=cityMexico;}
  else if(selectedCountry=='Russia'){selectedCities=cityRussia;}
  else if(selectedCountry=='China'){selectedCities=cityChina;}
  });
  },
  items: country.map((String item){
  return DropdownMenuItem<String>(
  value: item,
  child: Container(
  color: Colors.greenAccent,
    child: Text(item),
  ),
  );
  }
  ).toList()
  ),
  ),
        Padding(
          padding: const EdgeInsets.all(10.0),
          child: DropdownButton<String>(
              value: dropdownCity,
              icon: Icon(Icons.map),

              style:TextStyle(color: Colors.black, fontSize: 15) ,
              onChanged: (String? newValue) {

                setState(() {
                  dropdownCity = newValue;});
              },
              items: selectedCities.map((String item){
                return DropdownMenuItem<String>(
                  value: item,
                  child: Container(
                    color: Colors.greenAccent,
                    child: Text(item),
                  ),
                );
              }
              ).toList()
          ),
        ),
  ],
  ),
  ),);
}
}

Solution

  • Usually, in any language not specific of dart or being a flutter dropdown button functionality, in such cases you relay over the data encapsulation which you will prepare a list of countries objects, each of them contains id, name and a list of their own cities

    class Country {
       int id;
       String name;
       List<City> cities;
    }
    
    class City {
       int id;
       String name;
    }
    

    so In such case you will have a dropdown of Country not a String

    DropdownButton<Country>
              
    

    and instead of long "if-else if-else ... else", you will have a direct mapping of cities

    selectedCities = selectedCountry.cities