具有固定底部和可滚动上部的颤振布局

时间:2018-11-15 23:36:34

标签: flutter flutter-layout

我目前正在为类似信息亭的设备编写Flutter应用。该设备将以横向模式安装,底部具有集成的条形码扫描仪。

设备将几乎所有时间都花在单个布局上:

Standard Layout w/o Keyboard

当前,整个主体位于SingleChildScrollView中。当用户在文本输入框中点按时,这可使视图“向上滑动”。然后,当键盘关闭时,视图会向后“滑动”。

With Keyboard Active

我想做的是至少在可见时(当键盘不覆盖它时)将底部的“ Scan Ticket Under”行固定在视图的底部。截至目前,它是一种弹性布局,还没有完全触底。

使用调试画图查看图像:粉红色的框应位于底部,上方的所有内容均应处于可滚动视图中。 View with Debug Box Paint

我开始搞各种各样的选择。我不需要固定位置,因为我们最终还将使解决方案在具有各种屏幕尺寸的iDevice上可用。

这是我目前的脚手架:

Container(
      constraints: BoxConstraints.expand(),
      child: SingleChildScrollView(
        child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            mainAxisSize: MainAxisSize.min,
            children: [
              Padding(
                padding: EdgeInsets.only(top: 50.0, bottom: 20.0),
                child: Text(_message ?? "Welcome!",
                textAlign: TextAlign.center,
                    style:
                        TextStyle(fontSize: 45.0, color: Colors.black)),
              ),
                                  Padding(
                padding: EdgeInsets.all(20.0),
                child: Text("Scan ticket below or Search for your Transaction",
                    style:
                        TextStyle(fontSize: 25.0, color: Colors.black)),
              ),
              Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  mainAxisSize: MainAxisSize.max,
                  children: [
                    Expanded(
                        flex: 4,
                        child: Container(
                            margin: EdgeInsets.symmetric(
                                horizontal: 50.0, vertical: 25.0),
                            color: skidataThemeData.primaryColor,
                            padding: const EdgeInsets.symmetric(
                                horizontal: 25.0, vertical: 25.0),
                            child: Center(
                              child: new TextFormField(
                                //This autofocus works, but during transition from successful plate val to
                                //this page, the keyoard is activated during transition causing an overflow on the
                                //applyValidationScreen.
                                //autofocus: true,
                                style: new TextStyle(
                                    decorationColor:
                                        skidataThemeData.accentColor,
                                    fontSize: 90.0,
                                    color: skidataThemeData.accentColor),
                                textAlign: TextAlign.center,
                                onSaved: (String value) {
                                  this._data.plateNumber = value;
                                },
                                decoration: new InputDecoration(
                                    hintText: "Enter Data",
                                    hintStyle: new TextStyle(
                                        color: Colors.white),
                                    fillColor:
                                        skidataThemeData.accentColor,
                                    contentPadding: EdgeInsets.all(1.0),
                                    border: InputBorder.none),
                                validator: (value) {
                                  if (value.isEmpty) {
                                    return 'Field cannot be blank.';
                                  }
                                },
                                autocorrect: false,
                              ),
                            ))),
                    Expanded(
                        flex: 1,
                        child: Padding(
                          padding: const EdgeInsets.all(25.0),
                          child: RaisedButton(
                            padding: EdgeInsets.all(15.0),
                            color: skidataThemeData.accentColor,
                            onPressed: () async {
                              FocusScope.of(context)
                                  .requestFocus(new FocusNode());
                              setState(() {
                                _message = '';
                              });

                              if (_formKey.currentState.validate()) {
                                // If the form is valid, we want to show a Snackbar
                                Scaffold.of(context).showSnackBar(
                                  new SnackBar(
                                      content: Row(
                                        mainAxisAlignment:
                                            MainAxisAlignment
                                                .spaceBetween,
                                        children: [
                                          new CircularProgressIndicator(
                                            valueColor:
                                                new AlwaysStoppedAnimation<
                                                        Color>(
                                                    skidataThemeData
                                                        .primaryColor),
                                          ),
                                          Text(
                                              'Search for matching record..',
                                              style: new TextStyle(
                                                  color: skidataThemeData
                                                      .primaryColor))
                                        ],
                                      ),
                                      backgroundColor:
                                          skidataThemeData.accentColor,
                                      duration: Duration(seconds: 10)),
                                );
                                await new Future.delayed(
                                    const Duration(milliseconds: 1000));

                                Scaffold.of(context)
                                    .hideCurrentSnackBar();

                                Navigator.push(
                                  context,
                                  new MaterialPageRoute(
                                      builder: (context) =>
                                          new SvalKioskApp()),
                                );
                              } else {
                                setState(() {
                                  _message = "";
                                });
                              }
                            },
                            child: new Text('Search for Record',
                                textAlign: TextAlign.center,
                                style: TextStyle(
                                    fontSize: 25.0, color: Colors.black)),
                          ),
                        ))
                  ]),
              Container(
                color: Colors.pink,
                child: Padding(
                  padding: const EdgeInsets.only(top:40.0),
                  child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
                    Expanded(
                        flex: 1,
                        child: Container(
                          alignment: Alignment.centerRight,
                          padding: EdgeInsets.symmetric(
                              horizontal: 10.0, vertical: 5.0),
                          child: Image.asset(
                            'images/downarrow.png',
                            fit: BoxFit.contain,
                          ),
                        )),
                    Expanded(
                        flex: 5,
                        child: Center(
                            child: Text("Scan Physical Ticket Below!",
                                style: TextStyle(
                                    fontWeight: FontWeight.bold,
                                    fontSize: 45.0)))),
                    Expanded(
                        flex: 1,
                        child: Container(
                          alignment: Alignment.centerLeft,
                          padding: EdgeInsets.symmetric(
                              horizontal: 10.0, vertical: 5.0),
                          child: Image.asset(
                            'images/downarrow.png',
                            fit: BoxFit.contain,
                          ),
                        )),
                  ]),
                ),
              )
            ]), 
      ),
    )

2 个答案:

答案 0 :(得分:1)

今天早上我在洗澡时,解决方案使我震惊:

从一列开始,您将其分为两个扩展小部件以获取所需的顶部/底部部分配比。

然后在每个Expanded中,将一个子容器扩展为填充空间。在顶部容器中,将对齐方式设置为topCenter,将底部容器中的对齐方式设置为bottomCenter

在顶部容器中,添加SingleChildScrollView子级。现在,上部是可滚动的,底部是固定的。

  Widget getInputView() {
return Builder(
    builder: (context) => Container(
          constraints: BoxConstraints.expand(),
          child: Column(
            children: <Widget>[
              Expanded(
                  flex: 4,
                  child: Container(
                      alignment: Alignment.topCenter,
                      constraints: BoxConstraints.expand(),
                      child: SingleChildScrollView(
                        child: Form(
                          key: _formKey,
                          child: Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                Padding(
                                  padding: const EdgeInsets.all(10.0),
                                  child: Row(
                                      mainAxisAlignment:
                                          MainAxisAlignment.end,
                                      mainAxisSize: MainAxisSize.max,
                                      children: [
                                        Text("${_kiosk.kioskName}",
                                            textAlign: TextAlign.end)
                                      ]),
                                ),
                                Padding(
                                  padding: EdgeInsets.symmetric(vertical:30.0),
                                  child: Text(
                                      _kiosk.displayMessage ?? "Welcome!",
                                      textAlign: TextAlign.center,
                                      style: TextStyle(
                                          fontSize: 45.0,
                                          color: Colors.black)),
                                ),
                                Padding(
                                  padding: EdgeInsets.all(20.0),
                                  child: Text(
                                      "Scan barcode below or enter search data in box.",
                                      style: TextStyle(
                                          fontSize: 25.0,
                                          color: Colors.black)),
                                ),
                                Row(
                                    mainAxisAlignment:
                                        MainAxisAlignment.center,
                                    mainAxisSize: MainAxisSize.max,
                                    children: [
                                      Expanded(
                                          flex: 4,
                                          child: Container(
                                              margin: EdgeInsets.symmetric(
                                                  horizontal: 50.0,
                                                  vertical: 25.0),
                                              color: themeData
                                                  .primaryColor,
                                              padding: const EdgeInsets
                                                      .symmetric(
                                                  horizontal: 25.0,
                                                  vertical: 25.0),
                                              child: Center(
                                                child: new TextFormField(
                                                  //This autofocus works, but during transition from successful plate val to
                                                  //this page, the keyoard is activated during transition causing an overflow on the
                                                  //applyValidationScreen.
                                                  //autofocus: true,
                                                  style: new TextStyle(
                                                      decorationColor:
                                                          themeData
                                                              .accentColor,
                                                      fontSize: 90.0,
                                                      color:
                                                          themeData
                                                              .accentColor),
                                                  textAlign:
                                                      TextAlign.center,
                                                  onSaved: (String value) {
                                                    this._data.plateNumber =
                                                        value;
                                                  },
                                                  decoration: new InputDecoration(
                                                      hintText:
                                                          "Enter Data",
                                                      hintStyle:
                                                          new TextStyle(
                                                              color: Colors
                                                                  .white),
                                                      fillColor:
                                                          themeData
                                                              .accentColor,
                                                      contentPadding:
                                                          EdgeInsets.all(
                                                              1.0),
                                                      border:
                                                          InputBorder.none),
                                                  validator: (value) {
                                                    if (value.isEmpty) {
                                                      return 'Field cannot be blank.';
                                                    }
                                                  },
                                                  autocorrect: false,
                                                ),
                                              ))),
                                      Expanded(
                                          flex: 1,
                                          child: Padding(
                                            padding:
                                                const EdgeInsets.all(25.0),
                                            child: RaisedButton(
                                              padding: EdgeInsets.all(15.0),
                                              color: themeData
                                                  .accentColor,
                                              onPressed: () async {
                                                FocusScope.of(context)
                                                    .requestFocus(
                                                        new FocusNode());

                                                if (_formKey.currentState
                                                    .validate()) {
                                                  // If the form is valid, we want to show a Snackbar
                                                  Scaffold.of(context)
                                                      .showSnackBar(
                                                    new SnackBar(
                                                        content: Row(
                                                          mainAxisAlignment:
                                                              MainAxisAlignment
                                                                  .spaceBetween,
                                                          children: [
                                                            new CircularProgressIndicator(
                                                              valueColor: new AlwaysStoppedAnimation<
                                                                      Color>(
                                                                  themeData
                                                                      .primaryColor),
                                                            ),
                                                            Text(
                                                                'Search for matching ticket..',
                                                                style: new TextStyle(
                                                                    color: themeData
                                                                        .primaryColor))
                                                          ],
                                                        ),
                                                        backgroundColor:
                                                            themeData
                                                                .accentColor,
                                                        duration: Duration(
                                                            seconds: 10)),
                                                  );
                                                  await new Future.delayed(
                                                      const Duration(
                                                          milliseconds:
                                                              1000));
                                                  PlateMatchResponse resp =
                                                      await this.submit();

                                                  Scaffold.of(context)
                                                      .hideCurrentSnackBar();

                                                  Navigator.push(
                                                    context,
                                                    new MaterialPageRoute(
                                                        builder: (context) =>
                                                            new SvalKioskApp()),
                                                  );
                                                }
                                              },
                                              child: new Text(
                                                  'Search for Data Match',
                                                  textAlign:
                                                      TextAlign.center,
                                                  style: TextStyle(
                                                      fontSize: 25.0,
                                                      color: Colors.black)),
                                            ),
                                          ))
                                    ]),
                              ]),
                        ),
                      ))),
              Expanded(
                  flex: 1,
                  child: Container(
                      padding: EdgeInsets.all(25.0),
                      alignment: Alignment.bottomCenter,
                      constraints: BoxConstraints.expand(),
                      child: Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            Expanded(
                                flex: 1,
                                child: Container(
                                  alignment: Alignment.centerRight,
                                  padding: EdgeInsets.symmetric(
                                      horizontal: 10.0, vertical: 5.0),
                                  child: Image.asset(
                                    'images/downarrow.png',
                                    fit: BoxFit.contain,
                                  ),
                                )),
                            Expanded(
                                flex: 5,
                                child: Center(
                                    child: Text(
                                        "Scan Physical Ticket Below",
                                        style: TextStyle(
                                            fontWeight: FontWeight.bold,
                                            fontSize: 45.0)))),
                            Expanded(
                                flex: 1,
                                child: Container(
                                  alignment: Alignment.centerLeft,
                                  padding: EdgeInsets.symmetric(
                                      horizontal: 10.0, vertical: 5.0),
                                  child: Image.asset(
                                    'images/downarrow.png',
                                    fit: BoxFit.contain,
                                  ),
                                )),
                          ])))
            ],
          ),
        ));

答案 1 :(得分:0)

如何将SingleChildScrollView替换为Column,然后将resizeToAvoidBottomPadding的{​​{1}}属性设置为false,这样就不需要滚动视图了,因为键盘不会强制调整布局大小。