Flutter - Как заполнить оставшееся пространство в Stepper Controls Builder?

На скриншоте я хочу, чтобы кнопки "Вперед" и "Назад" были в нижней части экрана.

Степпер имеет параметр, controlsBuilder это позволяет вам строить макет для элементов управления. Если это просто строка, она находится прямо под контентом.

По-видимому, Степпер является гибкой оберткой. Я не уверен, что это значит. Я думаю, это означает, что Stepper считается гибким объектом, потому что он содержит прокручиваемую область (для содержимого). Прочитав документы, если я правильно понимаю, он говорит, что я не могу использовать Expanded или Column с максимальным размером в mainAxis, потому что степпер по существу является прокручиваемой областью, что означает, что любой RenderBox внутри него имеет неограниченные ограничения.

Итак, каким образом конструктор элементов управления может быть опущен вниз?

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

Я попытался обернуть вышеупомянутую строку в LayoutBuilder, а также другую попытку с использованием SizedBox, установив высоту в MediaQuery.of(context).size.height;, Он толкает его ближе к низу (не так сильно, как мне нравится), но проблема в том, что теперь под элементами управления есть пространство, в результате чего экран прокручивается вниз в пустое пространство.

Полный код:

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

2 ответа

Решение

Я думаю, что вы можете создать свой собственный Stepper, или вы можете попробовать это "взломать":

Создайте две переменные для хранения обратных вызовов:

      VoidCallback _onStepContinue;
      VoidCallback _onStepCancel;

Положить ваши Form внутри Stack:

        Stack(
                children: <Widget>[
                  Form(
                    child: Stepper(

Измените ваш метод createEventControlBuilder:

          Widget _createEventControlBuilder(BuildContext context,
              {VoidCallback onStepContinue, VoidCallback onStepCancel}) {
            _onStepContinue = onStepContinue;
            _onStepCancel = onStepCancel;
            return SizedBox.shrink();
          }

Добавьте ваши собственные кнопки:

      Widget _bottomBar() {
        return Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              FlatButton(
                onPressed: () => _onStepCancel(),
                child: const Text('BACK'),
              ),
              FlatButton(
                onPressed: () => _onStepContinue(),
                child: const Text('NEXT'),
              ),
            ]);
      } 

Вот как твой Stack будет выглядеть так:

    Stack(
                children: <Widget>[
                  Form(
                    child: Stepper(
                    ....
                   ), //Form
                    Align(
                    alignment: Alignment.bottomCenter,
                    child: _bottomBar(),
                    )

Я знаю, что это немного грязно, но вы можете попробовать, в противном случае я рекомендую вам создать свой собственный виджет.

Вы также можете изменить высоту виджетов в содержимом шагов на то же значение и сделать его SingleChildScrollViewесли нужно. например

      Step(content: Container(
      height: MediaQuery.of(context).size.height - 250, //IMPORTANT
      child: SingleChildScrollView(
        child: Column(children: <Widget>[
        ...
),
Другие вопросы по тегам