Creating a Dropdown menu inside the bottomsheet in Flutter

3,747

Step 1: LimitedBox need maxHeight
Step 2: function showModalSheet need to pass context
Step 3: createBox , buildMainDropdown and buildSupportingWidget need to pass state for StatefulBuilder

full code

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: DropdownExample(),
    );
  }
}

class DropdownExample extends StatefulWidget {
  @override
  _DropdownExampleState createState() => _DropdownExampleState();
}

class _DropdownExampleState extends State<DropdownExample> {
  String type;
  int optionId;


  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("Dropdown Example")),
        body: Center(
          child: Container(
            height: 600,
            width: 300,
            child: Row(
              children: <Widget>[
                Align(
                    alignment: Alignment.topRight,
                    child: FlatButton.icon(
                      label: Text('Filters'),
                      icon: Icon(Icons.filter_list),
                      // onPressed: showModalSheet(),
                      onPressed: () {
                        showModalSheet(context);
                      },
                    )),
              ],
            ),
          ),
        ),
      ),
    );
  }

  void showModalSheet(BuildContext context) {
    final items = [
      {
        "displayName": "Enter value",
        "type": "string",
      },
      {
        "displayName": "Source",
        "type": "list",
        "data": [
          {"id": 1, "displayId": "MO"},
          {"id": 2, "displayId": "AO"},
          {"id": 3, "displayId": "OffNet"}
        ]
      }
    ];

    showModalBottomSheet<void>(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(10.0),
        ),
        context: context,
        builder: (BuildContext context) {
          return StatefulBuilder(
              builder: (BuildContext context, StateSetter state) {
                return createBox(context, items, state);
              });
        });
  }



  createBox(BuildContext context, List<Map<String,Object>> val,StateSetter state) {
    Widget supporting = buildSupportingWidget(val,state);
    return SingleChildScrollView(
        child: LimitedBox(
            maxHeight: 300,
            child: Column(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  buildMainDropdown(val,state),
                  if (supporting != null) supporting
                ]
            )
        )
    );

  }



  Expanded buildMainDropdown(List<Map<String,Object>> items,StateSetter setState) {
    return Expanded(
      child: DropdownButtonHideUnderline(
        child: DropdownButton(
          value: type,
          hint: Text("Select a type"),
          items: items
              .map((json) => DropdownMenuItem(
              child: Text(json["displayName"]), value: json["type"]))
              .toList(),
          onChanged: (newType) {
            setState(() {
              type = newType;
            });
          },
        ),
      ),
    );
  }

  Widget buildSupportingWidget(List<Map<String,Object>>items, StateSetter setState) {
    if (type == "list") {
      List<Map<String, Object>> options = items[1]["data"];
      return Expanded(
        child: DropdownButtonHideUnderline(
          child: DropdownButton(
            value: optionId,
            hint: Text("Select an entry"),
            items: options
                .map((option) => DropdownMenuItem(
                child: Text(option["displayId"]), value: option["id"]))
                .toList(),
            onChanged: (newId) => setState(() {
              this.optionId = newId;
            }),
          ),
        ),
      );
    } else if (type == "string") {
      return Expanded(child: TextFormField());
    }
    return null;
  }
}

enter image description here

Share:
3,747
Mark12
Author by

Mark12

Updated on December 14, 2022

Comments

  • Mark12
    Mark12 over 1 year

    I am trying to do something like this , to have a dropdown based on the list contents. My List looks like this,

    [
        {
            id: val,
            displayName: Enter value,
            type: string, 
            value: "any"
        },
        {
            id: si,
            displayName: Source,
            type: list,
            value: [
                MO
            ],
            data: [
                {id: 1, displayId: MO},
                {id: 2, displayId: AO},
                {id: 3, displayId: OffNet}
            ]
         }
     ]
    

    Currently there are 2 entries. display a dropdown containing those options (Enter value and Source) as 2 entries of dropdown

    If Enter value is selected a text box next to it should be displayed, since it has a type of string. If Source option in dropdown is selected another dropdown containing those entries (MO, AO, Offnet) should be present as a dropdown value, since it has a type of list.

    based on the selection of the 1st dropdown a widget to be displayed (either text box or another dropdown) should be chosen.

    I have a code like this, Which will do the needful but here taking the entire page as a container and whenever the option changes a setState called, which rebuilds the build method, but I want to implement the same thing inside the bottomsheet, there I dont know managing the state, i.e once the option in the dropdown changes I want the bottomsheet to get rebuild with data.

    code:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(DropdownExample());
    }
    
    class DropdownExample extends StatefulWidget {
      @override
      _DropdownExampleState createState() => _DropdownExampleState();
    }
    
    class _DropdownExampleState extends State<DropdownExample> {
      String type;
      int optionId;
    
      final items = [
        {
          "displayName": "Enter value",
          "type": "string",
        },
        {
          "displayName": "Source",
          "type": "list",
          "data": [
            {"id": 1, "displayId": "MO"},
            {"id": 2, "displayId": "AO"},
            {"id": 3, "displayId": "OffNet"}
          ]
        }
      ];
    
      @override
      Widget build(BuildContext context) {
        Widget supporting = buildSupportingWidget();
    
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(title: Text("Dropdown Example")),
            body: Center(
              child: Container(
                height: 600,
                width: 300,
                child: Row(
                  children: <Widget>[
                    buildMainDropdown(),
                    if (supporting != null) supporting,
                  ],
                ),
              ),
            ),
          ),
        );
      }
    
      Expanded buildMainDropdown() {
        return Expanded(
          child: DropdownButtonHideUnderline(
            child: DropdownButton(
              value: type,
              hint: Text("Select a type"),
              items: items
                  .map((json) => DropdownMenuItem(
                      child: Text(json["displayName"]), value: json["type"]))
                  .toList(),
              onChanged: (newType) {
                setState(() {
                  type = newType;
                });
              },
            ),
          ),
        );
      }
    
      Widget buildSupportingWidget() {
        if (type == "list") {
          List<Map<String, Object>> options = items[1]["data"];
          return Expanded(
            child: DropdownButtonHideUnderline(
              child: DropdownButton(
                value: optionId,
                hint: Text("Select an entry"),
                items: options
                    .map((option) => DropdownMenuItem(
                        child: Text(option["displayId"]), value: option["id"]))
                    .toList(),
                onChanged: (newId) => setState(() {
                  this.optionId = newId;
                }),
              ),
            ),
          );
        } else if (type == "string") {
          return Expanded(child: TextFormField());
        }
        return null;
      }
    }
    

    the above code working fine ,but I am trying to do is same thing should come inside the bottomsheet with the exact functionality.

    whenever the button "open bottomsheet" is pressed ,A bottom sheet should popup and display the code result as the content of the bottom sheet.

    I have done something like this but its not working

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(DropdownExample());
    }
    
    class DropdownExample extends StatefulWidget {
      @override
      _DropdownExampleState createState() => _DropdownExampleState();
    }
    
    class _DropdownExampleState extends State<DropdownExample> {
      String type;
      int optionId;
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(title: Text("Dropdown Example")),
            body: Center(
              child: Container(
                height: 600,
                width: 300,
                child: Row(
                  children: <Widget>[
                    Align(
                        alignment: Alignment.topRight,
                        child: FlatButton.icon(
                          label: Text('Filters'),
                          icon: Icon(Icons.filter_list),
                           onPressed: showModalSheet(),
                           )),
                  ],
                ),
              ),
            ),
          ),
        );
      }
    
    showModalSheet() {
    
    final items = [
        {
          "displayName": "Enter value",
          "type": "string",
        },
        {
          "displayName": "Source",
          "type": "list",
          "data": [
            {"id": 1, "displayId": "MO"},
            {"id": 2, "displayId": "AO"},
            {"id": 3, "displayId": "OffNet"}
          ]
        }
      ];
    
         showModalBottomSheet<void>(
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(10.0),
            ),
            context: context,
            builder: (BuildContext context) {
              return StatefulBuilder(
                  builder: (BuildContext context, StateSetter state) {
                return createBox(context, items, state);
              });
            });
    
    }
    
    createBox(BuildContext context, List<Map<String,Object>> val,StateSetter state) {
          Widget supporting = buildSupportingWidget(val);
        return SingleChildScrollView(
          child: LimitedBox(
            child: Column(
              mainAxisSize: MainAxisSize.max,
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                   buildMainDropdown(val),
                    if (supporting != null) supporting
              ]
            )
          )
        );
    
    }
    
    
    
      Expanded buildMainDropdown(List<Map<String,Object>> items) {
        return Expanded(
          child: DropdownButtonHideUnderline(
            child: DropdownButton(
              value: type,
              hint: Text("Select a type"),
              items: items
                  .map((json) => DropdownMenuItem(
                      child: Text(json["displayName"]), value: json["type"]))
                  .toList(),
              onChanged: (newType) {
                setState(() {
                  type = newType;
                });
              },
            ),
          ),
        );
      }
    
      Widget buildSupportingWidget(List<Map<String,Object>>items) {
        if (type == "list") {
          List<Map<String, Object>> options = items[1]["data"];
          return Expanded(
            child: DropdownButtonHideUnderline(
              child: DropdownButton(
                value: optionId,
                hint: Text("Select an entry"),
                items: options
                    .map((option) => DropdownMenuItem(
                        child: Text(option["displayId"]), value: option["id"]))
                    .toList(),
                onChanged: (newId) => setState(() {
                  this.optionId = newId;
                }),
              ),
            ),
          );
        } else if (type == "string") {
          return Expanded(child: TextFormField());
        }
        return null;
      }
    }
    

    let me know to changes needed, Thank you