Reputation: 4532
In the screenshot, I want to get the next and back buttons to the bottom of the screen.
The stepper has a parameter, controlsBuilder
that allows you to build out the layout for the controls. If it's just a simple row, It's placed right underneath the content.
Apparently, the Stepper is a flexible wrapper. I'm not sure what that means. I think it means that the Stepper is considered a flex object because it contains a scrollable area (for the content). Having read the docs, if I'm understanding correctly, it says that I cannot use an Expanded
or a Column
with a max size in the mainAxis because the stepper is essentially a scrollable area, meaning any RenderBox inside it has unbounded constraints.
So, what are some ways the controls builder can be pushed down to the bottom?
Widget _createEventControlBuilder(BuildContext context, {VoidCallback onStepContinue, VoidCallback onStepCancel}) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
FlatButton(
onPressed: onStepCancel,
child: const Text('BACK'),
),
FlatButton(
onPressed: onStepContinue,
child: const Text('NEXT'),
),
]
);
}
I did try wrapping the above row in a LayoutBuilder as well as another attempt using a SizedBox, setting the height to MediaQuery.of(context).size.height;
. It does push it near to the bottom (not quite as much as I like), but the problem is that now there is space beneath controls, causing the screen to scroll down into empty space.
Full code:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Create an Event"),
),
body: Form(
key: _eventFormKey,
child: Stepper(
type: StepperType.horizontal,
currentStep: _currentStep,
controlsBuilder: _createEventControlBuilder,
onStepContinue: () {
if (_currentStep + 1 >= MAX_STEPS)
return;
setState(() {
_currentStep += 1;
});
},
onStepCancel: () {
if (_currentStep + 1 >= MAX_STEPS)
return;
setState(() {
_currentStep -= 1;
});
},
steps: <Step>[
Step(
title: Text("Name"),
isActive: 0 == _currentStep,
state: _getStepState(0),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 10.0),
child: Text(
"Give your event a cool name",
style: Theme.of(context).textTheme.title,
),
),
TextFormField(
maxLines: 1,
maxLength: 50,
maxLengthEnforced: true,
decoration: InputDecoration(
hintText: "e.g. Let's eat cheeseburgers!",
),
validator: (value) {
if (value.trim().isEmpty)
return "Event name required.";
},
)
],
)
),
Step(
title: Text("Type"),
isActive: 1 == _currentStep,
state: _getStepState(1),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 10.0),
child: Text(
"Select an event type",
style: Theme.of(context).textTheme.title,
),
),
Container(
margin: EdgeInsets.only(bottom: 10.0),
child: Row(
children: <Widget>[
Expanded(
child: DropdownButton<int>(
items: _stepTwoDropdownItems,
hint: Text("Select event type"),
isExpanded: true,
value: _eventTypeSelectedIndex,
onChanged: (selection) {
setState(() {
_eventTypeSelectedIndex = selection;
});
}),
)
],
)
)
],
)
),
]
),
),
);
}
Upvotes: 7
Views: 11325
Reputation: 96
You can also change the heights of the widgets in the contents of Steps to the same value, and make it a SingleChildScrollView
if needed. e.g
Step(content: Container(
height: MediaQuery.of(context).size.height - 250, //IMPORTANT
child: SingleChildScrollView(
child: Column(children: <Widget>[
...
),
Upvotes: 0
Reputation: 103401
I think you can create your own Stepper
, or you can try this 'hack' :
Create two variables to store the callbacks:
VoidCallback _onStepContinue;
VoidCallback _onStepCancel;
Put your Form
inside a Stack
:
Stack(
children: <Widget>[
Form(
child: Stepper(
Change your createEventControlBuilder method:
Widget _createEventControlBuilder(BuildContext context,
{VoidCallback onStepContinue, VoidCallback onStepCancel}) {
_onStepContinue = onStepContinue;
_onStepCancel = onStepCancel;
return SizedBox.shrink();
}
Add your custom buttoms :
Widget _bottomBar() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
FlatButton(
onPressed: () => _onStepCancel(),
child: const Text('BACK'),
),
FlatButton(
onPressed: () => _onStepContinue(),
child: const Text('NEXT'),
),
]);
}
This is how your Stack
will looks like :
Stack(
children: <Widget>[
Form(
child: Stepper(
....
), //Form
Align(
alignment: Alignment.bottomCenter,
child: _bottomBar(),
)
I know this is a little dirty, but you could try , otherwise I recommend you to create your own Widget.
Upvotes: 11