I have a dialog that uses a Flutter's Stepper
widget like this:
class MyDialog {
....
// STEPS
int _activeCurrentStep = 0;
List<Step> stepList() => [
Step(
state:
_activeCurrentStep <= 1 ? StepState.editing : StepState.complete,
isActive: _activeCurrentStep >= 1,
title: const Text(''),
// title: const Text('Details'),
content: Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
initialValue: widget.task.title,
onSaved: (value) => setState (() => widget.task.title = value!),
validator: (value) => value!.isEmpty ? "Title" : null,
decoration: const InputDecoration(hintText: "Title"),
),
const SizedBox(height: 30,),
Expanded(
child: TextFormField(
textAlignVertical: TextAlignVertical.top,
keyboardType: TextInputType.multiline,
expands: true,
maxLines: null,
initialValue: widget.task.details,
onSaved: (value) => setState (() => widget.task.details = value!),
decoration: const InputDecoration(
hintText: "Enter Task Details",
border: OutlineInputBorder(
borderSide: BorderSide(width: 2)
),
),
),
)
],
),
)),
Step(
state:
_activeCurrentStep <= 1 ? StepState.editing : StepState.complete,
isActive: _activeCurrentStep >= 1,
title: const Text(''),
// title: const Text('Schedule'),
content: Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child:
TextFormField(
enabled: false,
onSaved: (value) => setState (() => widget.task.scheduleDate = _selectedDate.value),
controller: _dateController,
validator: (value) => value!.isEmpty ? "Date Schedule" : null, // NEEDED?
decoration: const InputDecoration(hintText: "Date Schedule"),
),
),
ElevatedButton(
onPressed: () => setState (() => _restorableDatePickerRouteFuture.present()),
child: const Icon(Icons.calendar_month),
)
],
),
Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child:
TextFormField(
enabled: false,
onSaved: (value) => setState (() => widget.task.scheduleTime = _selectedTime.value),
controller: _timeController,
validator: (value) => value!.isEmpty ? "Time Schedule" : null, // NEEDED?
decoration: const InputDecoration(hintText: "Time Schedule"),
),
),
ElevatedButton(
onPressed: () => setState (() => _restorableTimePickerRouteFuture.present()),
child: const Icon(Icons.access_time_outlined),
)
],
),
],
),
)),
Step(
state: StepState.complete,
isActive: _activeCurrentStep >= 2,
// title: const Text('Summary'),
title: const Text(''),
content: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text('Title: ${widget.task.title}'),
Text('Details: ${widget.task.details}'),
Text('Date Schedule: ${widget.task.scheduleDate}'),
Text('Time Schedule: ${widget.task.scheduleTime}'),
],
)
))
];
@override
Widget build(BuildContext context) {
print('rebuild the dialog widget');
return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
return AlertDialog(
insetPadding: const EdgeInsets.all(10),
content: StatefulBuilder(builder: (context, setState) {
print('I loop here');
return Row(children: [
// Expanded(
SizedBox(
width: constraints.maxWidth,
height: constraints.maxHeight * 0.5,
child: Form(
key: widget.formKey,
child: Stepper(
elevation: 0,
margin: const EdgeInsets.all(0),
type: StepperType.horizontal,
currentStep: _activeCurrentStep,
controlsBuilder: (BuildContext context, ControlsDetails controls) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
children: <Widget>[
ElevatedButton(
onPressed: controls.onStepContinue,
child: const Text('NEXT'),
),
if (_activeCurrentStep != 0)
TextButton(
onPressed: controls.onStepCancel,
child: const Text(
'BACK',
style: TextStyle(color: Colors.grey),
),
),
],
),
);
},
steps: stepList(),
onStepContinue: () {
if (_activeCurrentStep < (stepList().length - 1)) {
setState(() {
_activeCurrentStep = 1;
});
}
},
onStepCancel: () {
if (_activeCurrentStep == 0) {
return;
}
setState(() {
_activeCurrentStep -= 1;
});
},
onStepTapped: (int index) {
setState(() {
_activeCurrentStep = index;
});
},
)),
)
]);
}),
title: const Text('Task'),
actions: <Widget>[
TextButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.of(context).pop(null);
},
),
TextButton(
child: const Text('OK'),
onPressed: () {
if (widget.formKey.currentState!.validate()) {
Navigator.of(context).pop(widget.task);
_handleSubmit(widget.task);
}
},
),
],
);
});
}
}
but I can't make it fit in the dialog as I keep on getting errors like:
======== Exception caught by rendering library =====================================================
The following assertion was thrown during performLayout():
RenderFlex children have non-zero flex but incoming height constraints are unbounded.
When a column is in a parent that does not provide a finite height constraint, for example if it is in a vertical scrollable, it will try to shrink-wrap its children along the vertical axis. Setting a flex on a child (e.g. using Expanded) indicates that the child is to expand to fill the remaining space in the vertical direction.
These two directives are mutually exclusive. If a parent is to shrink-wrap its child, the child cannot simultaneously expand to fit its parent.
Consider setting mainAxisSize to MainAxisSize.min and using FlexFit.loose fits for the flexible children (using Flexible rather than Expanded). This will allow the flexible children to size themselves to less than the infinite remaining space they would otherwise be forced to take, and then will cause the RenderFlex to shrink-wrap the children rather than expanding to fit the maximum constraints provided by the parent.
If this message did not help you determine the problem, consider using debugDumpRenderTree():
https://flutter.dev/debugging/#rendering-layer
http://api.flutter.dev/flutter/rendering/debugDumpRenderTree.html
The affected RenderFlex is: RenderFlex#fc1a5 relayoutBoundary=up8 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
parentData: <none> (can use size)
constraints: BoxConstraints(w=363.4, 0.0<=h<=Infinity)
size: MISSING
direction: vertical
mainAxisAlignment: start
mainAxisSize: min
crossAxisAlignment: center
verticalDirection: down
... child 1: _RenderFocusTrapArea#ad907 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
... parentData: offset=Offset(0.0, 0.0); flex=null; fit=null
... constraints: MISSING
... size: MISSING
... child: RenderMouseRegion#f1fe6 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
... parentData: <none>
... constraints: MISSING
... size: MISSING
... behavior: opaque
... listeners: enter, exit
... cursor: SystemMouseCursor(text)
... child: RenderIgnorePointer#14e66 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
... parentData: <none>
... constraints: MISSING
... size: MISSING
... ignoring: false
... ignoringSemantics: implicitly false
... child: RenderSemanticsAnnotations#4b25e NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
... parentData: <none>
... constraints: MISSING
... size: MISSING
... child 2: RenderConstrainedBox#5567c NEEDS-LAYOUT NEEDS-PAINT
... parentData: offset=Offset(0.0, 0.0); flex=null; fit=null
... constraints: MISSING
... size: MISSING
... additionalConstraints: BoxConstraints(0.0<=w<=Infinity, h=30.0)
... child 3: _RenderFocusTrapArea#0ebdd NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
... parentData: offset=Offset(0.0, 0.0); flex=1; fit=FlexFit.tight
... constraints: MISSING
... size: MISSING
... child: RenderMouseRegion#42617 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
... parentData: <none>
... constraints: MISSING
... size: MISSING
... behavior: opaque
... listeners: enter, exit
... cursor: SystemMouseCursor(text)
... child: RenderIgnorePointer#5bd27 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
... parentData: <none>
... constraints: MISSING
... size: MISSING
... ignoring: false
... ignoringSemantics: implicitly false
... child: RenderSemanticsAnnotations#e0955 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
... parentData: <none>
... constraints: MISSING
... size: MISSING
The creator information is set to: Column ← Container ← _EffectiveTickerMode ← TickerMode ← Offstage ← Visibility ← Column ← _AnimatedSize ← AnimatedSize ← RepaintBoundary ← IndexedSemantics ← _SelectionKeepAlive ← ⋯
See also: https://flutter.dev/layout/
If none of the above helps enough to fix this problem, please don't hesitate to file a bug:
https://github.com/flutter/flutter/issues/new?template=2_bug.md
The relevant error-causing widget was:
Column Column:file:///Users/mine/StudioProjects/myapp/lib/screens/todo_list_dialog.dart:156:16
When the exception was thrown, this was the stack:
#0 RenderFlex.performLayout.<anonymous closure> (package:flutter/src/rendering/flex.dart:933:9)
#1 RenderFlex.performLayout (package:flutter/src/rendering/flex.dart:936:6)
#2 RenderObject.layout (package:flutter/src/rendering/object.dart:2135:7)
#3 RenderBox.layout (package:flutter/src/rendering/box.dart:2418:11)
#4 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:120:14)
#5 RenderOffstage.performLayout (package:flutter/src/rendering/proxy_box.dart:3737:13)
#6 RenderObject.layout (package:flutter/src/rendering/object.dart:2135:7)
How can I fix it?
CodePudding user response:
First of all, remove the Row
from the AlertDialog
. and give height and width to SizeBox
.
AlertDialog(
insetPadding: const EdgeInsets.all(10),
content: StatefulBuilder(builder: (context, setState) {
print('I loop here');
return SizedBox( /// <--- Here is my Code
height: 500,
width: 500,
child: Stepper(
// elevation: 0,
margin: const EdgeInsets.all(0),
type: StepperType.horizontal,
currentStep: _activeCurrentStep,
controlsBuilder:
(BuildContext context, ControlsDetails controls) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
children: <Widget>[
ElevatedButton(
onPressed: controls.onStepContinue,
child: const Text('NEXT'),
),
if (_activeCurrentStep != 0)
TextButton(
onPressed: controls.onStepCancel,
child: const Text(
'BACK',
style: TextStyle(color: Colors.grey),
),
),
],
),
);
},
steps: stepList(),
onStepContinue: () {
if (_activeCurrentStep < (stepList().length - 1)) {
setState(() {
_activeCurrentStep = 1;
});
}
},
onStepCancel: () {
if (_activeCurrentStep == 0) {
return;
}
setState(() {
_activeCurrentStep -= 1;
});
},
onStepTapped: (int index) {
setState(() {
_activeCurrentStep = index;
});
},
),
);
}),
title: const Text('Task'),
actions: <Widget>[
TextButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.of(context).pop(null);
},
),
TextButton(
child: const Text('OK'),
onPressed: () {
// if (widget.formKey.currentState!.validate()) {
// Navigator.of(context).pop(widget.task);
// _handleSubmit(widget.task);
// }
},
),
],
);
After Solving the Above issue I encounter the next issue in your 0th index in stepList()
to solve this wrap the children with SingleChildScrollView and SizedBox as code below.
Step(
state: _activeCurrentStep <= 1
? StepState.editing
: StepState.complete,
isActive: _activeCurrentStep >= 1,
title: const Text(''),
// title: const Text('Details'),
content: SingleChildScrollView(. /// <--- Add Single ChildScrollView to prevent overflow
child: SizedBox( /// <--- add SizedBox with height
height: 500,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
initialValue: "widget.task.title",
onSaved: (value) => setState(() {}),
validator: (value) => value!.isEmpty ? "Title" : null,
decoration: const InputDecoration(hintText: "Title"),
),
const SizedBox(
height: 30,
),
Expanded(
child: TextFormField(
textAlignVertical: TextAlignVertical.top,
keyboardType: TextInputType.multiline,
expands: true,
maxLines: null,
initialValue: "widget.task.details",
// onSaved: (value) => setState (() => widget.task.details = value!),
decoration: const InputDecoration(
hintText: "Enter Task Details",
border: OutlineInputBorder(
borderSide: BorderSide(width: 2)),
),
),
)
],
),
),
)),