Search code examples
flutterscreennavigator

Passing data between screen but using extracted dropdown button widget


First of all, here is my condition. I'm trying to pass all the data that were filled in Dropdownbutton and Date & Time picker at the first screen (left picture) to the second screen(right picture). The problem is, I extracted the DropDownButton widget to another class, and I don't understand how to implement it.

First screen Second Screen

Before that, this is the first screen Code :

  class InformationDetail extends StatefulWidget {
  static const String id = 'InformationDetail';
  @override
  _InformationDetailState createState() => _InformationDetailState();
}

class _InformationDetailState extends State<InformationDetail> {
  String addressText, addNotes;
  DateTime selectedDate = DateTime.now();
  TimeOfDay selectedTime = TimeOfDay.now();

  Future<Null> _selectDate(BuildContext context) async {
    final DateTime picked = await showDatePicker(
        context: context,
        initialDate: selectedDate,
        firstDate: DateTime(2015, 8),
        lastDate: DateTime(2101));
    if (picked != null && picked != selectedDate)
      setState(() {
        selectedDate = picked;
      });
  }

  Future<Null> _selectTime(BuildContext context) async {
    final TimeOfDay picked = await showTimePicker(
      context: context,
      initialTime: selectedTime,
    );
    if (picked != null && picked != selectedTime)
      setState(() {
        selectedTime = picked;
      });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
          child: ListView(
        children: <Widget>[
          Container(
            child: Column(
              children: <Widget>[
                Container(
                  margin: EdgeInsets.fromLTRB(25.0, 68.0, 70.0, 26.0),
                  child: Text(
                    'Information Detail',
                    style: TextStyle(fontSize: 35.0),
                  ),
                ),
                Column(
                  // Wrap Column
                  children: <Widget>[
                    Column(
                      children: <Widget>[
                        TitleName(
                          titleText: 'Grooming Type',
                          infoIcon: Icons.info,
                        ),
                        MenuDropDown(
                          dropdownText: 'Grooming Type...',
                          type: "groomingType",
                        ),
                        TitleName(
                          titleText: 'Cat Breeds',
                        ),
                        MenuDropDown(
                          dropdownText: 'Cat Breeds...',
                          type: "catBreeds",
                        ),
                        TitleName(
                          titleText: 'Cat Size',
                          infoIcon: Icons.info,
                        ),
                        MenuDropDown(
                          dropdownText: 'Cat Size...',
                          type: "catSize",
                        ),
                        TitleName(
                          titleText: 'Add-On Services',
                        ),
                        MenuDropDown(
                          dropdownText: 'Add - On Services...',
                          type: "addOnServices",
                        ),
                        TitleName(
                          titleText: 'Reservation Date',
                        ),
                        Row(
                          children: <Widget>[
                            Container(
                              width: 130,
                              height: 30,
                              margin: EdgeInsets.fromLTRB(50.0, 0, 62, 0),
                              child: RaisedButton(
                                color: Colors.white,
                                shape: RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(15.0),
                                ),
                                elevation: 6,
                                child: Text(
                                  'Choose Date',
                                  style: TextStyle(
                                    fontSize: 12.0,
                                  ),
                                ),
                                onPressed: () => _selectDate(context),
                              ),
                            ),
                            Text("${selectedDate.toLocal()}".split(' ')[0]),
                          ],
                        ),
                        TitleName(
                          titleText: 'Reservation Time',
                        ),
                        Row(
                          children: <Widget>[
                            Container(
                              width: 130,
                              height: 30,
                              margin: EdgeInsets.fromLTRB(50.0, 0, 62, 0),
                              decoration: BoxDecoration(),
                              child: RaisedButton(
                                color: Colors.white,
                                child: Text(
                                  'Choose Time',
                                  style: TextStyle(
                                    fontSize: 12.0,
                                  ),
                                ),
                                shape: RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(15.0),
                                ),
                                elevation: 6,
                                onPressed: () => _selectTime(context),
                              ),
                            ),
                            Text("${selectedTime.toString()}".split(' ')[0]),
                          ],
                        ),
                        TitleName(
                          titleText: 'Pick Up Address',
                        ),
                        Container(
                          width: 320,
                          height: 40,
                          child: TextFormField(
                            maxLines: null,
                            minLines: null,
                            expands: true,
                            decoration: InputDecoration(
                              contentPadding:
                                  EdgeInsets.fromLTRB(35.0, 10.0, 0, 10.0),
                              hintText: 'Address Here...',
                              hintStyle: TextStyle(
                                fontSize: 15.0,
                              ),
                              border: OutlineInputBorder(
                                borderRadius: BorderRadius.circular(15.0),
                              ),
                            ),
                            onChanged: (value) {
                              addressText = value;
                            },
                          ),
                        ),
                        TitleName(
                          titleText: 'Additional Notes',
                          infoIcon: Icons.info,
                        ),
                        Container(
                          width: 320,
                          child: TextFormField(
                            maxLines: 4,
                            decoration: InputDecoration(
                              contentPadding:
                                  EdgeInsets.fromLTRB(35.0, 10.0, 0, 10.0),
                              hintText: 'E.g. ',
                              hintStyle: TextStyle(
                                fontSize: 15.0,
                              ),
                              border: OutlineInputBorder(
                                borderRadius: BorderRadius.circular(15.0),
                              ),
                            ),
                            onChanged: (value) {
                              addNotes = value;
                            },
                          ),
                        ),
                        Container(
                          margin: EdgeInsets.fromLTRB(0, 15.0, 0, 0),
                          width: 75.0,
                          decoration: BoxDecoration(
                            color: Colors.white,
                            shape: BoxShape.rectangle,
                            border: Border.all(
                              color: Colors.black,
                            ),
                            borderRadius: BorderRadius.circular(12.0),
                          ),
                          child: IconButton(
                              icon: Icon(Icons.arrow_forward),
                              onPressed: () {
                                Navigator.of(context).push(MaterialPageRoute(
                                    builder: (context) => ConfirmationOrder(
                                          addressText: addressText,
                                          addNotes: addNotes,
                                        )));
                              }),
                        ),
                      ],
                    ),
                  ],
                ),
              ],
            ),
          ),
        ],
      )),
    );
  }
}

Below the first picture, there's a button to navigate to the second screen.

And here is the class where I extracted DropDownButton :

    class MenuDropDown extends StatefulWidget {
  final String dropdownText;
  final String type;
  MenuDropDown({this.dropdownText, this.type});
  @override
  _MenuDropDownState createState() => _MenuDropDownState();
}

class _MenuDropDownState extends State<MenuDropDown> {
  String selectedItem;
  List<String> dropdownItems = [];

  List<String> groomingTypeList = ['Basic Grooming', 'Full Grooming'];

  List<String> catBreedsList = [
    'Persia',
    'Anggora',
    'Domestic',
    'Maine Coon',
    'Russian Blue',
    'Slamese',
    'Munchkin',
    'Ragdoll',
    'Scottish Fold',
  ];

  List<String> catSizeList = [
    'Small Size',
    'Medium Size',
    'Large Size',
    'Extra Large Size',
  ];

  List<String> addOnServicesList = [
    'Spa & Massage',
    'Shaving Hair / Styling',
    'Injection Vitamis Skin & Coat',
    'Cleaning Pet House and Environment',
    'Fur Tangled Treatment',
  ];

  List<String> getListBasedOnName(String value) {
    print(value);
    switch (value) {
      case "groomingType":
        return groomingTypeList;

        break;
      case "catBreeds":
        return catBreedsList;
        break;

      case "catSize":
        return catSizeList;
        break;
      case "addOnServices":
        return addOnServicesList;
        break;
    }

    return null;
  }

  @override
  void initState() {
    super.initState();
    print(widget.type);

    dropdownItems = getListBasedOnName(widget.type);
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.fromLTRB(0, 8.0, 0, 10.0),
      child: Container(
        width: 325.0,
        height: 50.0,
        decoration: BoxDecoration(
          boxShadow: [
            BoxShadow(
              color: Colors.black45,
              offset: Offset(2.5, 5.5),
              blurRadius: 5.0,
            )
          ],
          borderRadius: BorderRadius.circular(8),
          color: Colors.white,
        ),
        child: DropdownButtonHideUnderline(
          child: DropdownButton(
              value: selectedItem,
              hint: Padding(
                padding: const EdgeInsets.fromLTRB(22.0, 0, 0, 0),
                child: Text(
                  widget.dropdownText,
                  style: TextStyle(),
                ),
              ),
              items: dropdownItems.map((String value) {
                return new DropdownMenuItem<String>(
                  value: value,
                  child: new Text(value),
                );
              }).toList(),
              onChanged: (value) {
                setState(() {
                  selectedItem = value;
                });
              }),
        ),
      ),
    );
  }
}

I really got confused because the onChanged function in DropDownButton already used. I barely manage to do the normal pass data from the text widget. But from the Dropdownbutton and the date & time picker, I have no idea how to do it.

Is there any way to get the data from the first screen, because at the moment I still haven't learned about state management or Bloc. And the code still messy I haven't done the refactoring yet. I really Hope you can help with the solution, Thank you!


Solution

  • First, for the MenuDropDown, you're going to want to do a sort of extension for the onChanged method. Add a VoidCallback parameter to the widget's constructor like so:

    typedef OnChangeCallback = void Function(dynamic value);
    
    class MenuDropDown extends StatefulWidget {
      final String dropdownText;
      final String type;
      final OnChangeCallback onChanged;
    
      MenuDropDown({this.dropdownText, this.type, this.onChanged});
    
      @override
      _MenuDropDownState createState() => _MenuDropDownState();
    }
    

    and in the state, call that method as a part of DropdownButton's native onChanged callback:

    onChanged: (value) {
      setState(() {
        selectedItem = value;
      });
      widget.onChanged(value);
    }
    

    And in _InformationDetailState you'll store the currently selected item for each input field and pass an onChanged function that updates the respective field for each input:

    String catSize; //Declare at the top of _InformationDetailState
    ...
    MenuDropDown(
      dropdownText: 'Cat Size...',
      type: "catSize",
      onChanged: (value) {
        catSize = value;
      }
    ),
    

    Now to pass the data to the next screen. It's never really absolutely necessary to use any kind of state management in your app and I've found that many people use it unnecessarily. In your case, it's absolutely not necessary for just passing data to a single other widget. You're already passing the addressText and addNotes correctly. Just extend this for each parameter you need to show on the confirmation screen. Alternatively, you could store all of the fields in a single Map instead of having a variable for each field and pass that Map to the confirmation page.