当前位置: 首页 > article >正文

Flutter 常见布局模型

Flutter的常见的布局模型有容器(Container)、弹性盒子布局(Flex、Row、Column、Expanded)、流式布局(Wrap、Flow)、层叠布局(Stack、Position)、滚动布局(ListView、GridView)等。
布局模型也都是 widget,很多布局widget都继承自Container,其布局UI树状图如图:
布局

布局容器 Container

创建一个项目:

flutter create buju

lib/main.dart 代码

import 'dart:math' as math;
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: Container(
          height: 50,
          width: 100,
          alignment: Alignment.center,
          decoration: BoxDecoration(
            color: Colors.grey,
            borderRadius: BorderRadius.all(Radius.circular(10.0)),
            border: Border.all(color: Colors.black, width: 2.0),
          ),
          margin: EdgeInsets.fromLTRB(200, 50, 0, 0),
          transform: Matrix4.rotationZ(30 * math.pi / 180),
          child: Text("Container \n布局"),
        ),
      ),
    );
  }
}

从上面代码可以看到 Container 布局有很多属性:
color 设置容器的背景色。
width 设置容器的宽。
height 设置容器的高。
alignment 设置子widget的对齐方式,如居中。
decoration 设置背景装饰,如阴影、背景图等。
foregroundDecoration 设置前景装饰,比如前景色。
margin 设置容器等外边距。
padding 设置容器等内边距。
transform 设置形变,如需旋转、拉长等。
child 设置容器包裹的子Widget。
更多请见文档。

效果图如下:
1

弹性盒子布局

Flex

direction 设置主轴排列方向

Flex 有个重要的属性 direction,用来指定主轴的方向,子元素会按照主轴的方向排列。

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: Flex(
          direction: Axis.horizontal,
          children: <Widget>[
            Container(
              width: 30,
              height: 50,
              color: Colors.red,
            ),
            Container(
              width: 30,
              height: 50,
              color: Colors.green,
            ),
            Container(
              width: 30,
              height: 50,
              color: Colors.grey,
            ),
          ],
        ),
      ),
    );
  }
}

这里主轴方向是horizontal水平方向,效果图如下:
Flex
改成垂直direction: Axis.vertical,后的效果:
Flex

mainAxisAlignment 设置子元素排列方式

另一个重要属性是mainAxisAlignment,用来决定主轴的排列方式。
默认是start:mainAxisAlignment: MainAxisAlignment.start
end:指最后一个元素排在主轴的最后。这里以主轴水平方向排列为例,如下图:
1
center:所有子元素按照主轴方向依次排列,并居中显示在父容器中。如下图:
center
spaceAround:每个子元素之间的间隔相等,第一个元素和最后一个元素距离父容器的边距为孩子之间间距的一半。如下图:
spaceAround
spaceBetween:子元素两端对齐,第一个子元素和最后一个子元素分别位于容器中主轴的起始处和终止处,同时各个子元素之间的间隔相等,如下图:
spaceBetween
spaceEvenly: 各个子元素之间的间隔相等,同时第一个子元素和最后一个子元素距离父容器的边距也为各子元素之间的间隔。
spaceEvenly

mainAxisSize 设置主轴大小

mainAxisSize默认是max,Flex容器占据主轴全部空间。
还可以设置为min,Flex容器尽可能占据少的主轴空间。

direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,

mainAxisSize
这里把Flex容器设置为最小的大小,导致三个子元素挤在了一起。

crossAxisAlignment 设置交叉轴对齐方式

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: Flex(
          direction: Axis.horizontal,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            Container(
              width: 30,
              height: 100,
              color: Colors.red,
            ),
            Container(
              width: 30,
              height: 50,
              color: Colors.green,
            ),
            Container(
              width: 30,
              height: 50,
              color: Colors.grey,
            ),
          ],
        ),
      ),
    );
  }
}

crossAxisAlignment 用来设置元素在交叉轴方向上的对齐方式。
默认值是center,各个子元素在交叉轴上居中对齐。如下图(这里以主轴沿水平方向):
center
end:子元素布局在Flex容器交叉轴方向的尾部。
end

start:子元素布局在Flex容器交叉轴方向的头部。
start
stretch:子元素填充满交叉轴方向的空间。
stretch

verticalDirection 设置交叉轴布局的对齐方向

这个属性和上面的crossAxisAlignment属性配合使用,用于指定交叉轴布局的对齐方向
默认值是down,表示默认对齐的方向是从上到下,crossAxisAlignment默认start时,三个元素在顶部。
如果将verticalDirection改成up,表示从下到上,crossAxisAlignment还是默认start时,效果图如下:
verticalDirection
Row 和 Column 都继承自Flex,所以Flex都属性同样适用于Row和Column,只是预先指定好了主轴的方向。
Row表示在水平方向上布局子元素,主轴方向是水平方向。
Column表示在垂直方向上布局子元素,主轴方向是垂直方向。
Flex 更多参数见:https://api.flutter-io.cn/flutter/widgets/Flex-class.html

Expanded 按比例缩放

Expanded 布局能让子元素能够按照一定的比例缩放,来填充满父容器在主轴方向上剩余的剩余空间。通常,Expanded 组件通常是作为Flex 组件的子组件使用。

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: Flex(
          direction: Axis.horizontal,
          children: <Widget>[
            Expanded(
              flex: 1,
              child: Container(
                height: 150,
                color: Colors.red,
              ),
            ),
            Expanded(
              flex: 1,
              child: Container(
                height: 150,
                color: Colors.green,
              ),
            ),
            Expanded(
              flex: 1,
              child: Container(
                height: 150,
                color: Colors.blue,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

效果图:
Expanded
如果把 Expanded 的 Flex 属性分别设置为 1,2,3,得到的效果图如下:
Flex

流式布局

在弹性布局中,如果宽度或高度超过屏幕尺寸,会抛出异常:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: Flex(
          direction: Axis.horizontal,
          children: <Widget>[
            Container(
              width: 150,
              height: 100,
              color: Colors.red,
            ),
            Container(
              width: 280,
              height: 50,
              color: Colors.green,
            ),
            Container(
              width: 50,
              height: 50,
              color: Colors.grey,
            ),
          ],
        ),
      ),
    );
  }
}

1

════════ Exception caught by rendering library ═════════════════════════════════
The following assertion was thrown during layout:
A RenderFlex overflowed by 105 pixels on the right.

这是因为弹性和子布局模型是线性的,不会自动换行。

Wrap 自动换行的容器

Wrap支持子元素自动换行,默认主轴水平方向。

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: Wrap(
          direction: Axis.horizontal,
          spacing: 10,
          runAlignment: WrapAlignment.start,
          verticalDirection: VerticalDirection.down,
          runSpacing: 10,
          children: <Widget>[
            Container(
              width: 150,
              height: 150,
              color: Colors.red,
            ),
            Container(
              width: 200,
              height: 50,
              color: Colors.green,
            ),
            Container(
              width: 50,
              height: 50,
              color: Colors.grey,
            ),
            Container(
              width: 200,
              height: 150,
              color: Colors.black,
            ),
          ],
        ),
      ),
    );
  }
}

alignment、crossAxisAlignment、verticalDirection 与 Flex 的同名属性作用相同。
runAlignment 用来设置新一行或新一列的对齐方式。默认值是start,如果主轴方向是水平方向,start就是在交叉轴方向上从顶部开始布局子元素,如下图。end 是在交叉轴方向上从底部开始布局子元素。center 设置居中对齐,子元素会从交叉轴方向上父容器的中心开始布局。
runAlignment

Flow 布局允许用户自定义布局规则,具体参数见:https://api.flutter-io.cn/flutter/widgets/Flow-class.html

层叠布局:绝对定位

Stack 布局类似Web开发中的绝对定位。

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(),
          body: Center(
            child: Stack(
              children: <Widget>[
                Container(
                  width: 150,
                  height: 150,
                  color: Colors.red,
                ),
                Positioned(
                  left: 8.0,
                  right: 8.0,
                  top: 8.0,
                  child: Text('测试'),
                )
              ],
            ),
          )),
    );
  }
}

这个例子是对Text组件和Container组件的重叠,其中Positioned是定位组件,只能放在Stack中。
Stack

滚动布局

ListView 处理大量数据

当数据量特别大时,上面的布局方式实现起来会非常麻烦,而用滚动布局,直接将数据放入滚动布局模型的 children 数据中即可。

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(),
          body: ListView.builder(
              itemCount: 50,
              itemExtent: 50,
              itemBuilder: (BuildContext context, int index) {
                return Container(
                  height: 50,
                  child: Row(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [Text('索引: ${index + 1}')],
                  ),
                );
              })),
    );
  }
}

1
详细参数见文档:https://api.flutter-io.cn/flutter/widgets/ListView-class.html。

ListView.separated 增加分割线

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(),
          body: ListView.separated(
              itemCount: 50,
              separatorBuilder: (BuildContext context, int ndex) {
                return Divider();
              },
              itemBuilder: (BuildContext context, int index) {
                return SizedBox(
                  height: 50,
                  child: Row(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [Text('索引: ${index + 1}')],
                  ),
                );
              })),
    );
  }
}

1

GridView 构建多行多列表格

GridView 的创建需要一个 delegate 代表,即属性 gridDelegate , Flutter SDK 默认提供了两个 SliverGridDelegate 的子类,分别是: SliverGridDelegateWithFixedCrossAxisCount 和 SliverGridDelegateWithMaxCrossAxisExtent 。

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(),
          body: GridView(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 3, crossAxisSpacing: 8, mainAxisSpacing: 8),
            children: [
              Container(
                width: 50,
                height: 50,
                color: Colors.red,
              ),
              Container(
                width: 50,
                height: 50,
                color: Colors.red,
              ),
              Container(
                width: 50,
                height: 50,
                color: Colors.red,
              ),
              Container(
                width: 50,
                height: 50,
                color: Colors.red,
              ),
              Container(
                width: 50,
                height: 50,
                color: Colors.red,
              ),
              Container(
                width: 50,
                height: 50,
                color: Colors.red,
              ),
            ],
          )),
    );
  }
}

效果图:
1

动态创建 GridView

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: GridView.builder(
            gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
                maxCrossAxisExtent: 8,
                mainAxisSpacing: 8,
                crossAxisSpacing: 8,
                childAspectRatio: 2.0),
            itemBuilder: (BuildContext context, int index) {
              return Container(
                color: Colors.red,
                width: 50,
                height: 50,
              );
            }),
      ),
    );
  }
}

1
maxCrossAxisExtent 设置条目的宽度。
mainAxisSpacing 设置主轴的间隔。
crossAxisSpacing 设置交叉轴间隔。
childAspectRatio 设置子元素的宽高比。

相关链接

https://docs.flutter.cn/ui/layout/


http://www.kler.cn/a/549302.html

相关文章:

  • 【LeetCode】LCR 120. 寻找文件副本
  • 使用 pgvector 实现 PostgreSQL 语义搜索和 RAG:完整指南
  • DeepSeek R1 与 OpenAI O1:机器学习模型的巅峰对决
  • 关于防火墙运维面试题2
  • Unity3D 可视化脚本框架设计详解
  • Linux搭建ESP32开发环境
  • 线性代数中的正交和标准正交向量
  • DeepSeek 的开源优势:为什么选择它而不是其他闭源模型?
  • Spring Boot “约定大于配置”
  • 网络安全的现状如何?
  • Keepalived + Nginx 高可用负载均衡部署
  • 低代码组态软件-BY组态
  • nodejs 038: Js Worker线程消息传递 Worker.postMessage() Worker.postMessage()
  • 图像生成GAN和风格迁移
  • mysql读写分离与proxysql的结合
  • FOT(Forest-of-Thought):增强大型语言模型推理能力的新框架
  • Docker安装Kafka(不依赖ZooKeeper)
  • 【Linux】Ubuntu Linux 系统 ——PHP开发环境
  • Netty的基本架构详解
  • 【第12章:深度学习与伦理、隐私—12.1 AI伦理原则与偏见检测的方法与实践】