Flutter 步进器微件不适合对话框

yftpprvb  于 2022-12-30  发布在  Flutter
关注(0)|答案(1)|浏览(126)

我有一个使用Flutter的Stepper小部件的对话框,如下所示:

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);
                    }
                  },
                ),
              ],
            );
      });
  }
}

但我无法使其适合对话框,因为我不断收到以下错误:

======== 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)

我该怎么修呢?

6yt4nkrj

6yt4nkrj1#

首先,从AlertDialog中删除Row,并将高度和宽度指定为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);
              // }
            },
          ),
        ],
      );

在解决了上述问题后,我在stepList()中的第0个索引中遇到了下一个问题,要解决这个问题,请使用SingleChildScrollView和SizedBox Package 子对象,如下所示。

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)),
                ),
              ),
            )
          ],
        ),
      ),
    )),

相关问题