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.
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!
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.