相对于居中的小部件定位小部件

Position a widget relative to a centered widget

我正在制作登录页面。我希望 email/password 文本字段在页面中居中,按钮在最后一个字段下方 48dp(因此不考虑文本字段居中位置的计算)。

我不知道如何修改我的布局,使按钮不作为当前屏幕截图中显示的居中元素包含在内:

我的代码:

class LoginScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Expanded(flex: 2, child: Container()),
            Expanded(
              flex: 8,
              child: Form(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    RoundedTextField(
                      hintText: 'Email',
                      keyboardType: TextInputType.emailAddress,
                      textInputAction: TextInputAction.next,
                    ),
                    Container(
                      height: 12,
                    ),
                    RoundedTextField.password(
                      hintText: 'Password',
                      textInputAction: TextInputAction.go,
                    ),
                    Container(
                      height: 48,
                    ),
                    PrimaryButton(
                      child: Text('Log In'),
                      onPressed: () => print(null),
                    ),
                  ],
                ),
              ),
            ),
            Expanded(flex: 2, child: Container()),
          ]),
    );
  }
}

我来自 Android 开发世界,在那里我们可以做 constrainTopToBottomOf:@id/passwordField,我不确定 flex 系统中是否真的有等价物。

最简单的解决方案是使用既不显示也不使用的按钮保持对称性:

class LoginScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Expanded(flex: 2, child: Container()),
            Expanded(
              flex: 8,
              child: Form(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Opacity(
                      opacity: 0.0,
                      child: RaisedButton(
                        child: Text(''),
                        onPressed: null,
                      ),
                    ),
                    Container(
                      height: 12,
                    ),
                    TextField(
                      decoration: InputDecoration(hintText: 'Email'),
                      keyboardType: TextInputType.emailAddress,
                      textInputAction: TextInputAction.next,
                    ),
                    Container(
                      height: 12,
                    ),
                    TextField(
                      decoration: InputDecoration(hintText: 'Password'),
                      textInputAction: TextInputAction.go,
                    ),
                    Container(
                      height: 48,
                    ),
                    RaisedButton(
                      child: Text('Log In'),
                      onPressed: () => print(null),
                    ),
                  ],
                ),
              ),
            ),
            Expanded(flex: 2, child: Container()),
          ]),
    );
  }
}

有点骇人听闻,但您可以使用媒体查询设置高度:

double _height = MediaQuery.of(context).size.height * .5;

然后这样做:

body: Column(
        children: <Widget>[

          Container(
            height: _height,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.end,
              children: <Widget>[
                TextField(
                  decoration: InputDecoration(
                    hintText: 'email@email.com',
                    labelText: 'Email',
                  ),
                ),
              ],
            ),
          ),

          Container(
            height: _height,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              children: <Widget>[
                TextField(
                  decoration: InputDecoration(
                    hintText: 'email@email.com',
                    labelText: 'Email',
                  ),
                ),
                Padding(padding: EdgeInsets.only(top: 48.0)),
                Text('some button'),
              ],
            ),
          ),
        ],
      ),

这个用例的常见解决方案是一个带有一些扩展的列:

Column(
  children: [
    Expanded(),
    someContent,
    Expanded(
      child: someOffsetContent,
    ),
),

一个更直观的例子:(紫色是展开的)

Column(
  children: <Widget>[
    Expanded(child: Container(color: Colors.purple)),
    Column(
      children: <Widget>[
        Container(
          height: 42,
          width: double.infinity,
          color: Colors.red,
        ),
        Container(
          height: 42,
          width: double.infinity,
          color: Colors.yellow,
        ),
      ],
    ),
    Expanded(
      child: Container(
        color: Colors.purple,
        alignment: Alignment.topCenter,
        padding: const EdgeInsets.only(top: 48),
        child: Container(
          color: Colors.red,
          height: 42.0,
          width: double.infinity,
        ),
      ),
    )
  ],
)