Not able to prepopulate dropdown with object in flutter

3,455

Solution 1

Screenshot:

enter image description here


Code:

There were too many mistakes in your code, I just created a minimal reproducible code showing you what you want to achieve. You should use FutureBuilder if you don't want to create a local dummy DropdownMenuItem, for simplicity I created it.

class AddOrEditClaim extends StatefulWidget {
  @override
  AddOrEditClaimState createState() => AddOrEditClaimState();
}

class AddOrEditClaimState extends State<AddOrEditClaim> {
  UserPackages _selectedPackage = UserPackages(categoryName: "vehicle insurance", packageId: "5675");
  List<UserPackages> packageList = [];

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

  @override
  Widget build(BuildContext context) {
    var items = packageList.map((item) {
      return DropdownMenuItem<UserPackages>(
        child: Text(item.categoryName),
        value: item,
      );
    }).toList();

    // if list is empty, create a dummy item
    if (items.isEmpty) {
      items = [
        DropdownMenuItem(
          child: Text(_selectedPackage.categoryName),
          value: _selectedPackage,
        )
      ];
    }

    return Scaffold(
      appBar: AppBar(title: Text('Add Claim')),
      body: Center(
        child: DropdownButton<UserPackages>(
          items: items,
          onChanged: (newVal) => setState(() => _selectedPackage = newVal!),
          value: _selectedPackage,
        ),
      ),
    );
  }

  void fetchPackages() async {
    var resp = await http.get(Uri.parse('http://www.mocky.io/v2/5da94ca131000058004e0769'));
    if (resp.statusCode == 200) {
      setState(() {
        packageList = parsePackages(resp.body);
        _selectedPackage = packageList[0];
      });
    } else {
      print("Error occurred");
    }
  }

  List<UserPackages> parsePackages(String responseBody) {
    final parsedJson = json.decode(responseBody);
    final parsed = parsedJson.cast<Map<String, dynamic>>();
    return parsed.map<UserPackages>((json) => UserPackages.fromJson(json)).toList();
  }
}

class UserPackages {
  final String categoryName;
  final String packageId;

  UserPackages({required this.categoryName, required this.packageId});

  factory UserPackages.fromJson(Map<String, dynamic> json) {
    return UserPackages(
      packageId: json['packageId'] as String,
      categoryName: json['categoryName'] as String,
    );
  }
}

Solution 2

add null / empty check for packageList. because packageList is updaed after fetchPackages network call is resolved so wrap that DropdownButton in a if block as below

Form(
    key:formKey,
    child:ListView(
        children: <Widget>[
          if(packageList.isNotEmpty){
          new DropdownButton(
            items: packageList.map((item) {
              return new DropdownMenuItem<UserPackages>(
                child: new Text(item.packageName),
                value: item,
              );
            }).toList(),
            onChanged: (newVal) {
              setState(() {
                selectedPckg = newVal;
              });
            },
            value: selectedPckg ?? packageList[0],
          ),
          }
        ])
  )

Share:
3,455
sreeya chowdary
Author by

sreeya chowdary

Updated on December 14, 2022

Comments

  • sreeya chowdary
    sreeya chowdary over 1 year

    I've a dropdown which is populated with objects received from API. The problem is I'm not able to pre-populate the dropdown with the object when the user takes edit.

    Here is the sample JSON:

    [{"_id":"435463","userId":"3423423","username":"ma","categoryId":"5656756","insurerId":"567544","packageId":"5675","categoryName":"vehicle insurance","insurerName":"lic","packageName":"family"},      {"_id":"4564644","userId":"2342344","username":"ma","categoryId":"6575744","insurerId":"567567","packageId":"3455","categoryName":"life insurance","insurerName":"lic","packageName":"family"}]
    ``
    
    
        import 'package:flutter/material.dart';
        import 'package:http/http.dart' as http;
        import 'package:intl/intl.dart';
        import 'dart:convert';
    
        UserPackages selectedPckg;
        class AddOrEditClaim extends StatefulWidget {
          @override
          AddOrEditClaimState createState() => AddOrEditClaimState();
        }
    
        class AddOrEditClaimState extends State<AddOrEditClaim> {
          List<UserPackages> packageList = List();
          final formKey = GlobalKey<FormState>();
    
          @override
          void initState() {
                fetchPackages();
                selectedPckg= UserPackages();
                UserPackages packgToEdit = new UserPackages();
                packgToEdit.packageId="123";
                packgToEdit.packageName="family";
                selectedPckg=packgToEdit;
    
                 super.initState();
          }
    
          @override
          Widget build(BuildContext context) {
            // TODO: implement build
            return new Scaffold(
                appBar: AppBar(
                  title: Text('Add Claim'),
                ),
                body:
                    Form(
                      key:formKey,
                      child:ListView(
                          children: <Widget>[
                            new DropdownButton(
                              items: packageList.map((item) {
                                return new DropdownMenuItem<UserPackages>(
                                  child: new Text(item.packageName),
                                  value: item,
                                );
                              }).toList(),
                              onChanged: (newVal) {
                                setState(() {
                                  selectedPckg = newVal;
                                });
                              },
                              value: selectedPckg,
                            ),
                          ])
                    )
    
            );;
          }
           fetchPackages() async {
            SharedPreferences prefs = await SharedPreferences.getInstance();
            var resp = await http.get('http://www.mocky.io/v2/5da94ca131000058004e0769');
            setState(() {
              packageList= parsePackages(resp.body);
            });
          }
    
          List<UserPackages> parsePackages(String responseBody) {
            final parsedj = json.decode(responseBody);
            final parsed = parsedj.cast<Map<String, dynamic>>();
            return parsed.map<UserPackages>((json) => UserPackages.fromJson(json)).toList();
          }
        }
    
    
    Here is the Userpackages class:
    
    
        class UserPackages{
           String packageName;
           String packageId;
          UserPackages({this.packageName,this.packageId});
          factory UserPackages.fromJson(Map<String, dynamic> json){
            return UserPackages(
              packageId:json['packageId'] as String,
              packageName: json['packageName'] as String,
            );
          }
        }
    
    
    The following is being thrown
    
    
        'package:flutter/src/material/dropdown.dart': Failed assertion: line 560 pos 15: 'items == null ||items.isEmpty || value == null || items.where((DropdownMenuItem<T> item) => item.value ==value).length == 1': is not true.
    
    
    
    • CopsOnRoad
      CopsOnRoad over 4 years
      Try this value: selectedPckg ?? anyValidDefaultValue, I'm not sure what your selectedPcg is, so you need to use any default value there.
    • sreeya chowdary
      sreeya chowdary over 4 years
      selectedPckg is of type UserPackages @CopsOnRoad, I've edited the question to minimum reproducible state and included UserPackages class and API.
    • CopsOnRoad
      CopsOnRoad over 4 years
      Hi, let me know if it didn't work, if worked please accept the answer. Thanks
  • nmanikiran
    nmanikiran over 4 years
    add null check for selectedPckg at the drop-down value, so updated the code
  • sreeya chowdary
    sreeya chowdary over 4 years
    there's no change.
  • sreeya chowdary
    sreeya chowdary over 4 years
    Thank you @CopsOnRoad for correcting the mistake but the issue here is that it's always initializing the drop down with packageList[0]
  • CopsOnRoad
    CopsOnRoad over 4 years
    Yes, because I used [0] in the code, how do you want to initialize it when the data is ready?
  • sreeya chowdary
    sreeya chowdary over 4 years
    I don't want to initialize the drop down with [0] but with '_selectedPackage' in init. Yes we're initializing in init but its not happening.
  • CopsOnRoad
    CopsOnRoad over 4 years
    @sreeyachowdary Ok, for that you can simply remove _selectedPackage = packageList[0]; from above code, I will wait till you accept the answer :)
  • sreeya chowdary
    sreeya chowdary over 4 years
    If I remove that, 'items.isEmpty || value == null || items.where((DropdownMenuItem<T> item) => item.value == value).length == 1': is not true.' is being thrown.
  • CopsOnRoad
    CopsOnRoad over 4 years
    That maybe because the item you're trying to add may not be on the server which is why I used first element (0 index) when list is populated.