Andrii Havrylyak
Andrii Havrylyak

Reputation: 675

dynamic number of drop-down lists in flutter widget?

I'm working on a very difficult task, (I'm new to flutter). I have JSON which I will get through api. JSON structure (one question - many answers) .Here he is:

"regionId": "f5e16375-5b03",
"objectid": "75226ff5-7124",
"actionid": "541ed524-7122",
"additions":
              {
         "name": "type of installation",
        "id": "afa6cc6f-e2f8-11ec",
         "variant": [
                     {
                    "name": "In the wall",
                   "id": "afa6cc6f-e2f8",
                    "additions": 
                                {
                       "name": "Wall type",
                       "id": "afa6cc6f-e2f8",
                       "variant":  [
                                     {
                                     "name": "Brick",
                                     "id": "afa6cc6f-e2f8",
                                     "additions": {
                                        "name": null,
                                        "id": null,
                                        "variants": null
                                    }          
                                     },
                                                                                                          {
                                     "name": "foam block",
                                     "id": "afa6cc6f-e2f9",
                                     "additions": {
                                        "name": null,
                                        "id": null,
                                        "variants": null
                                    }          
                                     },
                              
                                  ] 
                                }
                                         
                   }
                 ]
     }
        
}

I have questions and answers in JSON. And the next question depends on the previous answer. This is a hierarchy of questions and answers. My goal is to make these questions appear on the page gradually (one question after another). That is my API in the block works. I get the answer, I parse it. And the first question in the answer should appear on the page (drop-down list). Accordingly, after answering the first question there should be a second question with options, and so on indefinitely (conditionally). Until I choose the answer in which there will be no additional questions. And I can't figure out how to implement it. There may be as many as 2, 10 or 15 (any number) additional fees. Maybe someone has implemented something similar or there are examples. I can't figure out how to put everything on the shelves (question 1 - answer 1, question 2, answer 2). And I will be grateful for any help. Thank you) 2 main issues:

Upvotes: 2

Views: 224

Answers (2)

ABV
ABV

Reputation: 910

I have checked your question and based on that I have prepared the answer.

To get the data similar to the demo you have shared, I prepared one model

class DropDownModel{

  DropDownModel({this.name, this.id, this.variant, this.additions});

  String? name;
  String? id;
  List<DropDownModel>? variant;
  DropDownModel? additions;
}

To make the list as you are getting,

DropDownModel getModel() {
    DropDownModel wallTypeBrick = DropDownModel(
        name: "Brick", id: "afa6cc6f-e2f81", additions: DropDownModel());
    DropDownModel wallTypeFoamBlock = DropDownModel(
        name: "foam block", id: "afa6cc6f-e2f82", additions: DropDownModel());

    DropDownModel outwallTypeBrick = DropDownModel(
        name: "Out side Brick",
        id: "afa6cc6f-e2f91",
        additions: DropDownModel());
    DropDownModel outwallTypeFoamBlock = DropDownModel(
        name: "Out side foam block",
        id: "afa6cc6f-e2f92",
        additions: DropDownModel());

    DropDownModel parent = DropDownModel();
    parent.name = "type of installation";
    parent.id = "afa6cc6f-e2f8-11ec";

    DropDownModel wallTypeVariant = DropDownModel();
    wallTypeVariant.name = "Wall type";
    wallTypeVariant.id = "afa6cc6f-e2f8";
    wallTypeVariant.variant = [wallTypeBrick, wallTypeFoamBlock];

    DropDownModel inthewallvarient = DropDownModel();
    inthewallvarient.name = "In the wall";
    inthewallvarient.id = "afa6cc6f-e2f8";
    inthewallvarient.additions = wallTypeVariant;

    DropDownModel outwallTypeVariant = DropDownModel();
    outwallTypeVariant.name = "Out Wall type";
    outwallTypeVariant.id = "afa6cc6f-e2f9";
    outwallTypeVariant.variant = [outwallTypeBrick, outwallTypeFoamBlock];

    DropDownModel outinthewallvarient = DropDownModel();
    outinthewallvarient.name = "Out side the wall";
    outinthewallvarient.id = "afa6cc6f-e2f9";
    outinthewallvarient.additions = outwallTypeVariant;

    parent.variant = [inthewallvarient, outinthewallvarient];

    return parent;
  }

Prepared one model class to manage items

class Item{
  Item({this.name, this.id, this.isSelected = false});
 String? name;
 String? id;
 bool isSelected;
}

Below is the build method code.

@override
  Widget build(BuildContext context) {
    final Size size = MediaQuery.of(context).size;
    return Scaffold(
      //backgroundColor:AppGreen,
      appBar: AppBar(
        systemOverlayStyle: SystemUiOverlayStyle.dark.copyWith(
          statusBarColor: Colors.red,
        ),
        backgroundColor: Colors.red,
        elevation: .10,
      ),
      body: Container(
          color: Colors.white,
          child: Column(
            children:
                selectedQuestions.map((e) => getDropDownWidget(e)).toList(),
          )),
    );
  }

  Widget getDropDownWidget(Map<Item, List<Item?>> question) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Text(question.keys.toList()[0].name ?? ""),
        DropdownButton<Item>(
          value: selectedItems[question.keys.toList()[0]],
          icon: const Icon(Icons.arrow_drop_down),
          elevation: 16,
          style: const TextStyle(color: Colors.black),
          underline: Container(
            height: 2,
            color: Colors.red,
          ),
          onChanged: (Item? newValue) {
            setState(() {
              var oldValue = selectedItems[question.keys.toList()[0]];
              oldValue?.isSelected = false;
              newValue?.isSelected = true;
              selectedItems[question.keys.toList()[0]] = newValue!;
              allQuestions.forEach((question) {
                if (question.keys.toList()[0].id == newValue.id) {
                  selectedQuestions.add(question);
                }
              });

              allQuestions.forEach((question) {
                if (question.keys.toList()[0].id == oldValue?.id) {
                  selectedQuestions.remove(question);
                }
              });
            });
          },
          items: ((question.values.toList()[0] ?? []) as List<Item>)
              .map((Item value) {
            return DropdownMenuItem<Item>(
              value: value,
              child: Text(value.name ?? ""),
            );
          }).toList(),
        ),
      ],
    );
  }

From the question model, I have prepared the list of questions by checking variant or additions

     void getListOfQuestions(DropDownModel model) {
        print("Check for ${model.name} and id >> ${model.id}");
        if (model.variant != null || model.additions != null) {
          if (model.variant != null) {
            var item = getQuestion(model);
            if (item != null) {
              allQuestions.add(item);
            }
          } else if (model.additions != null) {
            getListOfQuestions(model.additions!);
          }
        }
      }
    
      Map<Item, List<Item>>? getQuestion(DropDownModel model) {
        print("getQuestion for ${model.name} and id >> ${model.id}");
        if (model.variant != null) {
          var options = <Item>[];
          model.variant!.forEach((element) {
            getListOfQuestions(element);
            options.add(Item(id: element.id, name: element.name));
          });
          if (model.id != null) {
            return {Item(id: model.id, name: model.name): options};
          } else {
            return null;
          }
        } else {
          return null;
        }
      }

Complete code

class Home extends StatefulWidget {
  Home({Key? key}) : super(key: key);

  @override
  State<Home> createState() => _HomeState();
}


class _HomeState extends State<Home> {
  List<Map<Item, List<Item>>> allQuestions = [];
  List<Map<Item, List<Item>>> selectedQuestions = [];
  Map<Item, Item> selectedItems = HashMap();

  @override
  void initState() {
    super.initState();
    
    getListOfQuestions(getModel());
    selectedQuestions.add(allQuestions[allQuestions.length - 1]);
  }

  @override
  Widget build(BuildContext context) {
    final Size size = MediaQuery.of(context).size;
    return Scaffold(
      //backgroundColor:AppGreen,
      appBar: AppBar(
        systemOverlayStyle: SystemUiOverlayStyle.dark.copyWith(
          statusBarColor: Colors.red,
        ),
        backgroundColor: Colors.red,
        elevation: .10,
      ),
      body: Container(
          color: Colors.white,
          child: Column(
            children:
                selectedQuestions.map((e) => getDropDownWidget(e)).toList(),
          )),
    );
  }

  Widget getDropDownWidget(Map<Item, List<Item?>> question) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Text(question.keys.toList()[0].name ?? ""),
        DropdownButton<Item>(
          value: selectedItems[question.keys.toList()[0]],
          icon: const Icon(Icons.arrow_drop_down),
          elevation: 16,
          style: const TextStyle(color: Colors.black),
          underline: Container(
            height: 2,
            color: Colors.red,
          ),
          onChanged: (Item? newValue) {
            setState(() {
              var oldValue = selectedItems[question.keys.toList()[0]];
              oldValue?.isSelected = false;
              newValue?.isSelected = true;
              selectedItems[question.keys.toList()[0]] = newValue!;
              allQuestions.forEach((question) {
                if (question.keys.toList()[0].id == newValue.id) {
                  selectedQuestions.add(question);
                }
              });

              allQuestions.forEach((question) {
                if (question.keys.toList()[0].id == oldValue?.id) {
                  selectedQuestions.remove(question);
                }
              });
            });
          },
          items: ((question.values.toList()[0] ?? []) as List<Item>)
              .map((Item value) {
            return DropdownMenuItem<Item>(
              value: value,
              child: Text(value.name ?? ""),
            );
          }).toList(),
        ),
      ],
    );
  }

  DropDownModel getModel() {
    DropDownModel wallTypeBrick = DropDownModel(
        name: "Brick", id: "afa6cc6f-e2f81", additions: DropDownModel());
    DropDownModel wallTypeFoamBlock = DropDownModel(
        name: "foam block", id: "afa6cc6f-e2f82", additions: DropDownModel());

    DropDownModel outwallTypeBrick = DropDownModel(
        name: "Out side Brick",
        id: "afa6cc6f-e2f91",
        additions: DropDownModel());
    DropDownModel outwallTypeFoamBlock = DropDownModel(
        name: "Out side foam block",
        id: "afa6cc6f-e2f92",
        additions: DropDownModel());

    DropDownModel parent = DropDownModel();
    parent.name = "type of installation";
    parent.id = "afa6cc6f-e2f8-11ec";

    DropDownModel wallTypeVariant = DropDownModel();
    wallTypeVariant.name = "Wall type";
    wallTypeVariant.id = "afa6cc6f-e2f8";
    wallTypeVariant.variant = [wallTypeBrick, wallTypeFoamBlock];

    DropDownModel inthewallvarient = DropDownModel();
    inthewallvarient.name = "In the wall";
    inthewallvarient.id = "afa6cc6f-e2f8";
    inthewallvarient.additions = wallTypeVariant;

    DropDownModel outwallTypeVariant = DropDownModel();
    outwallTypeVariant.name = "Out Wall type";
    outwallTypeVariant.id = "afa6cc6f-e2f9";
    outwallTypeVariant.variant = [outwallTypeBrick, outwallTypeFoamBlock];

    DropDownModel outinthewallvarient = DropDownModel();
    outinthewallvarient.name = "Out side the wall";
    outinthewallvarient.id = "afa6cc6f-e2f9";
    outinthewallvarient.additions = outwallTypeVariant;

    parent.variant = [inthewallvarient, outinthewallvarient];

    return parent;
  }

  void getListOfQuestions(DropDownModel model) {
    print("Check for ${model.name} and id >> ${model.id}");
    if (model.variant != null || model.additions != null) {
      if (model.variant != null) {
        var item = getQuestion(model);
        if (item != null) {
          allQuestions.add(item);
        }
      } else if (model.additions != null) {
        getListOfQuestions(model.additions!);
      }
    }
  }

  Map<Item, List<Item>>? getQuestion(DropDownModel model) {
    print("getQuestion for ${model.name} and id >> ${model.id}");
    if (model.variant != null) {
      var options = <Item>[];
      model.variant!.forEach((element) {
        getListOfQuestions(element);
        options.add(Item(id: element.id, name: element.name));
      });
      if (model.id != null) {
        return {Item(id: model.id, name: model.name): options};
      } else {
        return null;
      }
    } else {
      return null;
    }
  }
}

class Item {
  Item({this.name, this.id, this.isSelected = false});

  String? name;
  String? id;
  bool isSelected;
}

Please let me know if this works for you. If you are facing issue, please let me know.

Upvotes: 0

Kaushik Chandru
Kaushik Chandru

Reputation: 17812

You can try this

{
 'q0a0':{
   'que': 'first question here',
   'options':['option1', 'option2', 'option3']
  },
  'q1a0':{
   'que': 'second question here',
   'options':['option1', 'option2', 'option3']
  },
  'q1a1':{
   'que': 'second question here',
   'options':['option1', 'option2', 'option3']
  },
'q1a2':{
   'que': 'second question here',
   'options':['option1', 'option2', 'option3']
  },
}

Here you can loop through each question. If user finished question 3 with answer 2 search for q3a2 and display it's question and answer

Upvotes: 1

Related Questions