【Flutter】线性布局&弹性布局&层叠布局

🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Flutter学习
🌠 首发时间:2024年5月25日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾

线性布局

Row水平布局组件

在这里插入图片描述

实现如下效果:

在这里插入图片描述

因为图中有3个差不多的盒子,都是一个盒子里面放一个图标,所以我们将其写成了一个类 IconContainer,以减少代码量

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Container(
      height: double.infinity, //无穷大
      width: double.infinity,
      color: Colors.white,
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          IconContainer(Icons.home, color: Colors.red),
          IconContainer(Icons.search),
          IconContainer(Icons.send, color: Colors.orange),
        ],
      ),
    );
  }
}

class IconContainer extends StatelessWidget {
  Color color; //盒子颜色
  double iconSize; //图标大小
  IconData icon; //图标
  //盒子默认为蓝色,图标大小默认32
  IconContainer(this.icon,
      {super.key, this.color = Colors.blue, this.iconSize = 32});

  
  Widget build(BuildContext context) {
    return Container(
      height: 100,
      width: 100,
      color: color,
      //图标颜色默认为白色
      child: Center(child: Icon(icon, size: iconSize, color: Colors.white)),
    );
  }
}

Column垂直布局组件

在这里插入图片描述

实现如下效果:

在这里插入图片描述

将前面代码中的 Row 改成 Column 即可:

在这里插入图片描述

double.infinity和double.maxFinite

double.infinitydouble.maxFinite 可以让当前元素的 width 或者 height 达到父元素的尺寸

如下可以让Container铺满整个屏幕

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Container(
      height: double.infinity,
      width: double.infinity,
      color: Colors.white,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          IconContainer(Icons.home, color: Colors.red),
          IconContainer(Icons.search),
          IconContainer(Icons.send, color: Colors.orange),
        ],
      ),
    );
  }
}

如下可以让Container的宽度和高度等于父元素的宽度高度

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Container(
      height: 500,
      width: 300,
      color: Colors.red,
      child: Container(
        height: double.maxFinite,
        width: double.infinity,
        color: Colors.yellow,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            IconContainer(Icons.home, color: Colors.red),
            IconContainer(Icons.search),
            IconContainer(Icons.send, color: Colors.orange),
          ],
        ),
      ),
    );
  }
}

在这里插入图片描述

弹性布局

水平弹性布局

Flex 组件可以沿着水平或垂直方向排列子组件,如果你知道主轴方向,使用 RowColumn 会方便一些,因为 RowColumn 都继承自 Flex ,参数基本相同,所以能使用 Flex 的地方基本上都可以使用 RowColumnFlex 本身功能是很强大的,它也可以和 Expanded 组件配合实现弹性布局 。

实现如下效果:

在这里插入图片描述

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Flex(
      direction: Axis.horizontal, //水平方向
      children: [
        //flex: 占用多少位置
        Expanded(flex: 2, child: IconContainer(Icons.home, color: Colors.red)),
        Expanded(flex: 1, child: IconContainer(Icons.search))
      ],
    );
  }
}

class IconContainer extends StatelessWidget {
  Color color; //盒子颜色
  double iconSize; //图标大小
  IconData icon; //图标
  //盒子默认为蓝色,图标大小默认32
  IconContainer(this.icon,
      {super.key, this.color = Colors.blue, this.iconSize = 32});

  
  Widget build(BuildContext context) {
    return Container(
      height: 100,
      width: 100,
      color: color,
      //图标颜色默认为白色
      child: Center(child: Icon(icon, size: iconSize, color: Colors.white)),
    );
  }
}

因为 Row 继承自 Flex ,所以我们将代码中的 Flex 换成 Row,同样是可以的,而且我们还不用设置方向。当我们能确定主轴的方向时,推荐使用 RowColumn

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Row(
      children: [
        //flex: 占用多少位置
        Expanded(flex: 2, child: IconContainer(Icons.home, color: Colors.red)),
        Expanded(flex: 1, child: IconContainer(Icons.search))
      ],
    );
  }
}

垂直弹性布局

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        //flex: 占用多少位置
        Expanded(flex: 2, child: IconContainer(Icons.home, color: Colors.red)),
        Expanded(flex: 1, child: IconContainer(Icons.search))
      ],
    );
  }
}

在这里插入图片描述

使用Row或Column结合Expanded实现案例

实现如下效果:

在这里插入图片描述

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  
  Widget build(BuildContext context) {
    return ListView(
      children: [
        Container(
            width: double.infinity,
            height: 200,
            color: Colors.blue,
            child: Image.network(
                "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/1.jpg",
                fit: BoxFit.cover)),
        const SizedBox(height: 10),
        Row(
          children: [
            Expanded(
                flex: 2,
                child: SizedBox(
                  height: 200,
                  child: Image.network(
                      "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/2.jpg",
                      fit: BoxFit.cover),
                )),
            const SizedBox(width: 10),
            Expanded(
              flex: 1,
              child: SizedBox(
                height: 200,
                child: Column(
                  children: [
                    Expanded(
                      flex: 1,
                      child: SizedBox(
                        width: double.infinity,
                        child: Image.network(
                            "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/3.jpg",
                            fit: BoxFit.cover),
                      ),
                    ),
                    const SizedBox(height: 10),
                    Expanded(
                      flex: 1,
                      child: SizedBox(
                        width: double.infinity,
                        child: Image.network(
                            "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/4.jpg",
                            fit: BoxFit.cover),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
        const SizedBox(height: 10),
        Row(
          children: [
            Expanded(
              flex: 1,
              child: SizedBox(
                height: 200,
                child: Column(children: [
                  Expanded(
                    flex: 1,
                    child: SizedBox(
                      width: double.infinity,
                      child: Image.network(
                          "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/5.jpg",
                          fit: BoxFit.cover),
                    ),
                  ),
                  const SizedBox(height: 10),
                  Expanded(
                    flex: 1,
                    child: SizedBox(
                      width: double.infinity,
                      child: Image.network(
                          "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/6.jpg",
                          fit: BoxFit.cover),
                    ),
                  ),
                ]),
              ),
            ),
            const SizedBox(width: 10),
            Expanded(
              flex: 1,
              child: SizedBox(
                height: 200,
                child: Column(children: [
                  Expanded(
                    flex: 1,
                    child: SizedBox(
                      width: double.infinity,
                      child: Image.network(
                          "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/7.jpg",
                          fit: BoxFit.cover),
                    ),
                  ),
                  const SizedBox(height: 10),
                  Expanded(
                    flex: 1,
                    child: SizedBox(
                      width: double.infinity,
                      child: Image.network(
                          "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/8.jpg",
                          fit: BoxFit.cover),
                    ),
                  ),
                ]),
              ),
            ),
          ],
        ),
      ],
    );
  }
}

层叠布局

Stack组件

Stack 表示堆的意思,我们可以用 Stack 或者 Stack 结合 Align 或者 Stack 结合 Positiond 来实现页面的定位布局

属性说明
alignment配置所有子元素的显示位置
children子组件
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return Center(
      child: Stack(
        alignment: Alignment.topLeft,
        children: [
          Container(
            height: 400,
            width: 300,
            color: Colors.blue,
          ),
          const Text(
            "我是一个文本",
            style: TextStyle(fontSize: 40, color: Colors.white),
          )
        ],
      ),
    );
  }
}

在这里插入图片描述

Align组件

Align 组件可以调整子组件的位置 , Stack 组件中结合 Align 组件也可以控制每个子元素的显示位置

属性说明
alignment配置所有子元素的显示位置
child子组件

Align结合Container的使用

我们先来看一个简单的例子:

FlutterLogoFlutter SDK 提供的一个组件,内容就是 Flutterlog

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return Container(
      height: 300,
      width: 300,
      color: Colors.blue.shade50,
      child: const Align(
        alignment: Alignment.topRight,
        child: FlutterLogo(
          size: 100,
        ),
      ),
    );
  }
}

在这里插入图片描述

Align结合Container的使用

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return Container(
      height: 300,
      width: 300,
      color: Colors.blue.shade50,
      child: const Align(
        alignment: Alignment(2, 0),
        child: FlutterLogo(
          size: 100,
        ),
      ),
    );
  }
}

在这里插入图片描述

Alignment Widget 会以矩形的中心点作为坐标原点,即 Alignment(0, 0) 。 x 、y 的值从 -1 到 1 分别代表矩形左边到右边的距离和顶部到底边的距离,因此 2 个水平(或垂直)单位则等于矩形的宽(或高),如 Alignment(-1, -1) 代表矩形的左侧顶点,而 Alignment(1, 1) 代表右侧底
部终点,而 Alignment(1, -1) 则正是右侧顶点,即 Alignment.topRight。为了使用方便,矩形的原点、四个顶点,以及四条边的终点在 Alignment 类中都已经定义为了静态常量。

Alignment 可以通过其坐标转换公式将其坐标转为子元素的具体偏移坐标:

(Alignment.x*childWidth/2+childWidth/2, Alignment.y*childHeight/2+childHeight/2)

其中 childWidth 为子元素的宽度, childHeight 为子元素高度。

现在我们再看看上面的示例,我们将 Alignment(2, 0) 带入上面公式, ( 2 × 300 / 2 + 300 / 2 , 0 × 300 / 2 + 300 / 2 ) (2 \times 300 / 2 + 300 / 2, 0 \times 300 / 2 + 300 / 2) (2×300/2+300/2,0×300/2+300/2) ,可得 FlutterLogo 的实际偏移坐标正是 (450,150)

Align结合Stack的使用

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        height: 400,
        width: 300,
        color: Colors.blue,
        child: const Stack(
          children: [
            Align(
              alignment: Alignment(1, -0.2),
              child: Icon(Icons.home, size: 40, color: Colors.white),
            ),
            Align(
              alignment: Alignment.center,
              child: Icon(Icons.search, size: 30, color: Colors.white),
            ),
            Align(
              alignment: Alignment.bottomRight,
              child: Icon(Icons.settings_applications,
                  size: 30, color: Colors.white),
            ),
          ],
        ),
      ),
    );
  }
}

在这里插入图片描述

Positioned组件

Stack 组件中结合 Positioned 组件也可以控制每个子元素的显示位置

属性说明
top子元素距离顶部的距离
bottom子元素距离底部的距离
left子元素距离左侧距离
right子元素距离右侧距离
child子组件
width组件的高度(注意:宽度和高度必须是固定值,不能使用 double.infinity
height子组件的高度
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        height: 400,
        width: 300,
        color: Colors.blue,
        child: const Stack(
          children: [
            Positioned(
              left: 10,
              child: Icon(Icons.home, size: 40, color: Colors.white),
            ),
            Positioned(
              bottom: 0,
              left: 100,
              child: Icon(Icons.search, size: 30, color: Colors.white),
            ),
            Positioned(
              right: 0,
              child: Icon(Icons.settings_applications,
                  size: 30, color: Colors.white),
            ),
          ],
        ),
      ),
    );
  }
}

在这里插入图片描述

MediaQuery获取屏幕宽度和高度

前面说到 Positioned 组件的高度和宽度不能使用 double.infinity,在这种情况下,如果我们可以在组件的 build 方法中可以通过 MediaQuery.of(context).size; 来设置

final size = MediaQuery.of(context).size;
final width = size.width;
final height = size.height;

Stack结合Positioned固定导航案例

实现如下效果:

在这里插入图片描述

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;

    return Stack(children: [
      ListView(
        padding: const EdgeInsets.only(top: 45),
        children: const [
          ListTile(
            title: Text("我是标题1"),
          ),
          ListTile(
            title: Text("我是标题2"),
          ),
          ListTile(
            title: Text("我是标题3"),
          ),
          ListTile(
            title: Text("我是标题4"),
          ),
          ListTile(
            title: Text("我是标题5"),
          ),
          ListTile(
            title: Text("我是标题6"),
          ),
          ListTile(
            title: Text("我是标题7"),
          ),
          ListTile(
            title: Text("我是标题8"),
          ),
          ListTile(
            title: Text("我是标题9"),
          ),
          ListTile(
            title: Text("我是标题10"),
          ),
          ListTile(
            title: Text("我是标题11"),
          ),
          ListTile(
            title: Text("我是标题12"),
          ),
          ListTile(
            title: Text("我是标题13"),
          ),
          ListTile(
            title: Text("我是标题14"),
          ),
          ListTile(
            title: Text("我是标题15"),
          ),
        ],
      ),
      Positioned(
        top: 0,
        left: 0,
        height: 40,
        width: size.width,
        child: Container(
          alignment: Alignment.center,
          color: Colors.blue,
          child: const Text(
            "二级导航",
            style: TextStyle(color: Colors.white),
          ),
        ),
      ),
    ]);
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序喵正在路上

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值