【问题标题】:Flutter flexible layout, vertical overflowFlutter 灵活布局,垂直溢出
【发布时间】:2022-01-05 11:56:52
【问题描述】:

我正在开始我的第一个真实世界的颤振应用程序,到目前为止,我无法解决如何解决我拥有的这个非常基本的用例。

我在网上找到了各种信息,但还没有真正解决它。这个用例对我来说似乎很常见,所以必须有一个很好的标准方法来做到这一点。

我有一个列布局。为了说明这一点,有一个徽标(蓝色)、一个表单(红色)、一个按钮(绿色)和底部的一些自定义导航(黑色)。

代码如下:

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.amber,
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(32.0),
          child: Column(
            children: [
              const Placeholder(
                color: Colors.blue,
                fallbackHeight: 100.0,
              ),
              const Padding(
                padding: EdgeInsets.symmetric(vertical: 32.0),
                child: Placeholder(
                  color: Colors.red,
                  fallbackHeight: 300.0,
                ),
              ),
              const Spacer(),
              Column(
                children: [
                  const Placeholder(
                    color: Colors.green,
                    fallbackHeight: 40.0,
                  ),
                  Container(
                    margin: const EdgeInsets.only(top: 40.0),
                    child: const Placeholder(
                      color: Colors.black,
                      fallbackHeight: 40.0,
                    ),
                  ),
                ],
              )
            ],
          ),
        ),
      ),
    );
  }
}

我在 Pixel 5 模拟器上运行它。尺寸不是 100% 准确,但足以解释我认为的场景。

此屏幕是具有相似屏幕的流程的一部分,因此我想从底部向上布局自定义导航和按钮。 IE。当我导航到下一个屏幕时,我希望按钮和导航位于同一位置。

徽标和顶部的主要内容也是如此。因此我在中间放了一个垫片,它会占据中间的空间。

到问题。我在表格中有一些验证。当按下按钮并中断验证时,表单将在每个字段下方显示一堆消息。我通过将表单占位符的高度增加到 500 来模拟这一点。

所以现在所有组件都不再适合屏幕了,我在底部得到了一些溢出。没什么奇怪的。 在研究解决此问题的最惯用方法时,我发现添加 SingleChildScrollView 是有意义的。

但是添加一个SingleChildScrollView 会破坏代码,因为我有一个Spacer。我理解那部分。既然我们现在说“占用尽可能多的空间,我会让它可滚动”,我们有无限的空间可供使用。同时Spacer 基本上说,“我会尽可能地把所有东西往下推,直到没有空间为止”。所以这也是有道理的。让我们移除垫片。

代码如下:

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.amber,
      body: SafeArea(
        child: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(32.0),
            child: Column(
              children: [
                const Placeholder(
                  color: Colors.blue,
                  fallbackHeight: 100.0,
                ),
                const Padding(
                  padding: EdgeInsets.symmetric(vertical: 32.0),
                  child: Placeholder(
                    color: Colors.red,
                    fallbackHeight: 500.0,
                  ),
                ),
                Column(
                  children: [
                    const Placeholder(
                      color: Colors.green,
                      fallbackHeight: 40.0,
                    ),
                    Container(
                      margin: const EdgeInsets.only(top: 40.0),
                      child: const Placeholder(
                        color: Colors.black,
                        fallbackHeight: 40.0,
                      ),
                    ),
                  ],
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

它按预期工作。我没有收到任何错误,我可以滚动查看按钮和导航。

现在总结一下。当我的表单恢复到原来的高度 300 像素时会发生什么?当然,由于删除了垫片,我不再需要按钮和导航从底部开始的那种行为。它们现在被拉到表格中。当然,这也是有道理的。

我明白为什么所有不同的场景都会如此行事。但是我怎样才能创建一个灵活的布局,其中一些东西从底部定位,一些从顶部定位,但仍能防止溢出?

我是否必须开始计算何时以及是否会发生任何溢出并动态添加滚动视图?我还没有走这条路,因为我希望有一种更具声明性、更标准的方式来处理这个问题。

【问题讨论】:

  • 你到底想要什么
  • 我希望我的解释足够好,但我知道这很难。我希望第一张图像始终为真,直到表单增加高度(由于新内容,错误消息)然后我不想要溢出错误,而是想要一个可滚动的视图。但是我愿意接受任何将布局保持在第一张图像中并且在表单高度增加时不会中断的解决方案。无论什么实现都可以解决这个问题,并且在内容不适合屏幕时仍然有效,这很有趣。
  • 你想要第二个像输出一样滚动的图像,对吗?
  • 仅当内容溢出时。否则我想要第一张图片中的布局。但同样,第一张图片是我想要的布局,这是第一要务。并且我希望防止表单高度增加时发生溢出。

标签: flutter flutter-layout


【解决方案1】:

这应该可以解决问题:

return Scaffold(
      appBar: AppBar(
        title: Text('Demo'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          Expanded(
              child: ListView(children: [
                Text('Logo'),
               SizedBox(height: 1000,), //large placeholder
                Text('bottom')
              ],)),
          ElevatedButton(onPressed: () {}, child: Text('Your button'))
        ],
      ),
    );

所以基本上你开始用你的按钮从底部填满你的屏幕,然后用展开的小部件填满剩余的可用空间,你用像 ListView 这样的可滚动小部件填满(这比 SingleChildscrollView 更好多个小部件作为它的子部件。

将此概念应用于您的代码:

return Scaffold(
      backgroundColor: Colors.amber,
      body: SafeArea(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            Expanded(
              child: SingleChildScrollView(
                child: Padding(
                  padding: const EdgeInsets.all(32.0),
                  child: Column(
                    children: [
                      const Placeholder(
                        color: Colors.blue,
                        fallbackHeight: 100.0,
                      ),
                      const Padding(
                        padding: EdgeInsets.symmetric(vertical: 32.0),
                        child: Placeholder(
                          color: Colors.red,
                          fallbackHeight: 500.0,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
            Column(
              children: [
                const Placeholder(
                  color: Colors.green,
                  fallbackHeight: 40.0,
                ),
                Container(
                  margin: const EdgeInsets.only(top: 40.0),
                  child: const Placeholder(
                    color: Colors.black,
                    fallbackHeight: 40.0,
                  ),
                ),
              ],
            )
          ],
        ),
      ),
    );

【讨论】:

  • 谢谢!这看起来很有希望。我会尝试一下,但如果有任何其他答案,我也会等待其他答案,然后再将任何内容标记为已接受。
【解决方案2】:

试试下面的代码希望它对你有帮助。将您的专栏添加到SingleChildScrollView

Scaffold(
      backgroundColor: Colors.amber,
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(32.0),
          child: SingleChildScrollView(
            child: Column(
              children: [
                const Placeholder(
                  color: Colors.blue,
                  fallbackHeight: 100.0,
                ),
                const Padding(
                  padding: EdgeInsets.symmetric(vertical: 32.0),
                  child: Placeholder(
                    color: Colors.red,
                    fallbackHeight: 900.0,
                  ),
                ),
                SingleChildScrollView(
                  child: Column(
                    children: [
                      const Placeholder(
                        color: Colors.green,
                        fallbackHeight: 40.0,
                      ),
                      Container(
                        margin: const EdgeInsets.only(top: 40.0),
                        child: const Placeholder(
                          color: Colors.black,
                          fallbackHeight: 40.0,
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
      // If you want your bottom widget is constant then used bottomSheet (uncomment below bottomSheet code)
      /*
      bottomSheet: Container(
          height: 40,
          color: Colors.red,
        ),
     */
    );

【讨论】:

  • 不幸的是,当表单具有常规高度(在我的示例中为 300 像素)时,这将无济于事。然后不会保留图像 1 中的布局。它宁愿是图像 3,这是我想要避免的。
  • 您只想滚动前两个小部件,然后从 secode 列中删除 SingleChildScrollView
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-16
  • 2021-05-18
  • 2021-09-27
  • 2012-02-15
  • 2014-10-07
  • 2012-10-25
相关资源
最近更新 更多