Tapiwa Takaindisa
Tapiwa Takaindisa

Reputation: 45

Flutter populate dropdown from json

I am new to flutter and i am facing problems with populating dropdown from json. I have been following a solution i found on internet but am now stuck with the exception below. I cant seem to figure out how to set the initial value. My idea though is to have the initial value set as PLEASE SELECT. Assistance is appreciated:

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following assertion was thrown building FutureBuilder<List<Country>>(dirty, state:
_FutureBuilderState<List<Country>>#6f8a4):
There should be exactly one item with [DropdownButton]'s value: Instance of 'Country'.
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
'package:flutter/src/material/dropdown.dart':
Failed assertion: line 894 pos 15: 'items == null || items.isEmpty || value == null ||
              items.where((DropdownMenuItem<T> item) {
                return item.value == value;
              }).length == 1'

My code looks like below:

import 'dart:convert';

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Location(),
  ));
}

Future<List<Country>> fetchCountries() async {
  final responseJson = json.decode("""
  {
    "data": [
         {
        "Country_str_code": "AL",
        "Region_str_code": "Eur",
        "Country_str_name": "Albania"
    },
    {
        "Country_str_code": "DZ",
        "Region_str_code": "Afr",
        "Country_str_name": "Algeria"
    }
  ]
}
  """);

  print(responseJson['data']);

  return (responseJson['data'] as List)
      .map((country) => Country.fromJson(country))
      .toList();
}

class Country {
  final String countryCode;
  final String countryName;

  Country({required this.countryCode, required this.countryName});

  factory Country.fromJson(Map<String, dynamic> json) {
    return Country(
      countryCode: json['Country_str_code'],
      countryName: json['Country_str_name'],
    );
  }
}

class Location extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => LocationState();
}

class LocationState extends State<Location> {
  late Future<List<Country>> futureCountry;
  Country _selected = Country(countryCode: "AL", countryName: "Albania");

  @override
  void initState() {
    super.initState();
    futureCountry = fetchCountries();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Registrate'),
        backgroundColor: Colors.blue[600],
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Center(
            child: FutureBuilder<List<Country>>(
              future: futureCountry,
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return DropdownButton(
                    value: _selected,
                    icon: Icon(Icons.arrow_drop_down),
                    iconSize: 30,
                    elevation: 16,
                    style: TextStyle(color: Colors.black),
                    onChanged: (newValue) {
                      setState(() {
                        _selected = newValue as Country;
                      });
                    },
                    items: snapshot.data
                        ?.map<DropdownMenuItem<Country>>((Country value) {
                      return DropdownMenuItem<Country>(
                        value: value,
                        child: Text(value.countryName),
                      );
                    }).toList(),
                  );
                } else if (snapshot.hasError) {
                  return Text("${snapshot.error}");
                }
                return CircularProgressIndicator();
              },
            ),
          ),
          SizedBox(
            width: double.maxFinite,
            child: ElevatedButton(
                onPressed: () {},
                child: Text('Continue'),
                style: ButtonStyle(
                  backgroundColor: MaterialStateProperty.all<Color>(Colors.red),
                  foregroundColor:
                      MaterialStateProperty.all<Color>(Colors.white),
                )),
          ),
        ],
      ),
    );
  }
}

Upvotes: 1

Views: 135

Answers (1)

Naj
Naj

Reputation: 853

You are creating a new Country and setting it to _selected and that country is also found within your futureCountry list creating duplicates in the list. Instead, you should set _selected to null and set a new value (Country) when Dropdown value changes.

class Location extends StatefulWidget {
   @override
    State<StatefulWidget> createState() => LocationState();
}

class LocationState extends State<Location> {
   late Future<List<Country>> futureCountry;
   Country? _selected; // make this variable to nullable

@override
void initState() {
 super.initState();
 futureCountry = fetchCountries();
}

@override
Widget build(BuildContext context) {
 return Scaffold(
  appBar: AppBar(
    title: Text('Registrate'),
    backgroundColor: Colors.blue[600],
  ),

  body: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: <Widget>[
      Center(
        child: FutureBuilder<List<Country>>(
          future: futureCountry,
          builder: (context, snapshot) {

            if (snapshot.hasData) {
              return DropdownButton(
                hint: const Text("PLEASE SELECT"), // Add a hint here
                value: _selected, // If value is null hint will be displayed.
                icon: Icon(Icons.arrow_drop_down),
                iconSize: 30,
                elevation: 16,
                style: TextStyle(color: Colors.black),

                onChanged: (newValue) {
                  setState(() {
                    _selected = newValue as Country;
                  });
                },
                items: snapshot.data
                    ?.map<DropdownMenuItem<Country>>((Country value) {
                  return DropdownMenuItem<Country>(
                    value: value,
                    child: Text(value.countryName),
                  );
                }).toList(),
              );
            } else if (snapshot.hasError) {
              return Text("${snapshot.error}");
            }
            return CircularProgressIndicator();
          },
        ),
      ),
      SizedBox(
        width: double.maxFinite,
        child: ElevatedButton(
            onPressed: () {},
            child: Text('Continue'),
            style: ButtonStyle(
              backgroundColor: MaterialStateProperty.all<Color>(Colors.red),
              foregroundColor:
                  MaterialStateProperty.all<Color>(Colors.white),
            )),
      ),
    ],
  ),
);

} }

Upvotes: 2

Related Questions