2

I have a JSON object here:

{
    "error": "0",
    "message": "Succesfully fetched",
    "data": [
        {
            "status": true,
            "_id": "5df0b94841f0331baf1357bb",
            "name": "test group",
            "description": "test description",
            "created_date": "2019-12-11T09:39:20.151Z",
            "__v": 0
        },
        {
            "status": true,
            "_id": "5df0df507091683d2f1ad0cf",
            "name": "new group",
            "created_date": "2019-12-11T12:21:36.283Z",
            "__v": 0
        }
    ]
}

I want to fetch the name parameter under data to a DropDownMenuList. I have a data model here:

class dataArray {
//  final dynamic status;
  final dynamic id;
  final dynamic groupName;

//  final dynamic description;
//  final dynamic created_date;
//  final dynamic v;


  dataArray(this.groupName, this.id);


  dataArray.fromJson(Map jsonMap)
      : groupName = jsonMap['name'],
        id = jsonMap['_id'];


  Map toMapData(){
    var mapGroup = new Map<String, dynamic>();
    mapGroup["name"] = groupName;
    mapGroup['_id'] = id;
    return mapGroup;

  }

}

Function to fetch:

Future<List<dataArray>> gettaskData() async {
  List<dataArray> list;
  String link = ""; //Cannot provide this due to confidentiality
  var res = await http
      .get(Uri.encodeFull(link), headers: {"Accept": "application/json"});
  print(res.body);
  if (res.statusCode == 200) {
    var data = json.decode(res.body);
    var rest = data["data"] as List;
    var error = data['error'];
    print("this is error = $error");
    print(rest);
    list = rest.map<dataArray>((json) => dataArray.fromJson(json)).toList();
  }
  print("List Size: ${list.length}");
  return list;
}

This method fetches the item successfully into a ListView.builder widget but I am a bit lost on how to fetch this to a List<DropdownMenuItem<T>> items.

I have tried to go through these solutions:

The first link seems to be fetching a list instead of a Map and the second displays a map whereas, in my JSON list, I have to display a value from a list of maps.

EDIT: based on the accepted answer, i have also modified the initJson method to this -

  Future initJson() async {
    _list = await loadJsonFromAsset();
//print("Printing _List = ${_list[0].groupName}");
//    if (_list.length > 0) {
    setState(() {
      for(int i =0; i<=_list.length - 1; i++) {
        _selectedMenuItem = _list[i];
      }
    });

//      }
  }

this displayed the name parameter of every object present in the api.

2 Answers2

0

you can make dropdown like this,

FutureBuilder<List<dataArray>>(
        future: gettaskData(),
        builder: (BuildContext, snapshot){
          if (snapshot.data != null) {
            //print('project snapshot data is: ${projectSnap.data}');
            return Container(
              decoration: BoxDecoration(
                  borderRadius: new BorderRadius.circular(3),
                  color: Colors.grey[300]),
              child: DropdownButtonHideUnderline(
                child: DropdownButton(
                  items: snapshot.data.map((value) {
                    return new DropdownMenuItem(
                      value: value.id,
                      child: new Text(
                        value.groupName,
                      ),
                    );
                  }).toList(),
                  value: select_dataItem == "" ? null : select_dataItem,
                  onChanged: (v) {
                    FocusScope.of(context).requestFocus(new FocusNode());
                    setState(() {
                      select_dataItem = v;
                      print("selected ${select_dataItem}");
                    });
                  },
                  isExpanded: true,
                  hint: Text(
                    'Select Source',
                  ),
                ),
              ),
            );
          }
          if(snapshot.data == null){
            return Text("No Data");
          }
          return Container();
        },
      ),
0

Here is a working solution, Since, you don't like to provide your api url, I added provided json to a file and loaded it to elaborate this:

Few thing to keep in mind:

  • Note that your naming conventions not great. I don't like that because of that I renamed dataArray to DataModel(this not a great name but because of demo, ok).
  • If you want to display something while fetch your data, you can wrap DropDownButton with FutureBuilder with a default value.
  • You can avoid dynamic with final because Dart can infer the data type. Anyway, I always use static type as possible as I can to avoid complexity and make my life easy(because this gives compile time error messages).

In my example I didn't used FutureBuilder. You can refer to the other answer for that.

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:so_answers/drop_down/model.dart';

class DropDownDemo extends StatefulWidget {
  @override
  _DropDownDemoState createState() => _DropDownDemoState();
}

class _DropDownDemoState extends State<DropDownDemo> {
  List<DataModel> _list = [];
  DataModel _selectedMenuItem;

  String selected = "hello";

  Future<List<DataModel>> loadJsonFromAsset() async {
    String data =
        await DefaultAssetBundle.of(context).loadString("assets/data.json");
    final decoded = json.decode(data);
    try {
      return (decoded != null)
          ? decoded["data"]
              .map<DataModel>((item) => DataModel.fromJson(item))
              .toList()
          : [];
    } catch (e) {
      debugPrint(e.toString());
      return [];
    }
  }

  Future initJson() async {
    _list = await loadJsonFromAsset();

    if (_list.length > 0) {
      _selectedMenuItem = _list[0];
    }
  }

  DropdownMenuItem<DataModel> buildDropdownMenuItem(DataModel item) {
    return DropdownMenuItem(
      value: item, // you must provide a value
      child: Padding(
        padding: const EdgeInsets.all(12.0),
        child: Text(item.groupName ?? ""),
      ),
    );
  }

  Widget buildDropdownButton() {
    return Center(
      child: Padding(
        padding: EdgeInsets.all(20),
        child: DropdownButton<DataModel>(
          elevation: 1,
          hint: Text("Select one"),
          isExpanded: true,
          underline: Container(
            height: 2,
            color: Colors.black12,
          ),
          items: _list.map((item) => buildDropdownMenuItem(item)).toList(),
          value: _selectedMenuItem, // values should match
          onChanged: (DataModel item) {
            setState(() => _selectedMenuItem = item);
          },
        ),
      ),
    );
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: buildDropdownButton(),
    );
  }
}

About DropDownButton from doc:

/// A material design button for selecting from a list of items.
///
/// A dropdown button lets the user select from a number of items. The button
/// shows the currently selected item as well as an arrow that opens a menu for
/// selecting another item.
///
/// The type `T` is the type of the [value] that each dropdown item represents.
/// All the entries in a given menu must represent values with consistent types.
/// Typically, an enum is used. Each [DropdownMenuItem] in [items] must be
/// specialized with that same type argument.
///
/// The [onChanged] callback should update a state variable that defines the
/// dropdown's value. It should also call [State.setState] to rebuild the
/// dropdown with the new value.

Refer doc for more info: https://api.flutter.dev/flutter/material/DropdownButton-class.html

Blasanka
  • 21,001
  • 12
  • 102
  • 104
  • 1
    Thanks for this answer @Blasanka! I replaced the method ```loadJsonFromAsset``` to ```gettaskData``` (this method contains the fetch with the api url) and now it is correctly displaying the values. –  Dec 13 '19 at 05:33