懂 Vue、React 就懂 Flutter 页面布局 - 下篇

对于开发移动端应用来说,创建一个看起来吸引人、反应又快的用户界面真的超级重要。不过,我们时不时会遇到渲染布局的问题,导致界面出现故障和错误。这些问题的罪魁祸首之一就是使用约束的时候没用对。在Flutter里,约束就像是定义屏幕上小部件大小和位置的积木,要想布局流畅,我们得把它们用得溜溜的。

其实 Flutter 布局和 Web 布局本质上完全不同,在 Flutter 中流传着这样一句话

Constraints go down. Sizes go up. Parent sets position.

具体是什么意思? 我们稍后再来理解

谈谈约束

首先,让我们来理解什么是约束。你可以把约束看作是一种规则,它告诉我们元素应该如何排列和改变大小。

假设你正在设计一个房子的蓝图。房子的每个房间都有一定的大小和形状,这就是约束。你不能把一个大的沙发放到一个小的房间里,因为这违反了房间的大小约束。同样,你也不能把一个高的柜子放到一个低的房间里,因为这违反了房间的高度约束。

在这个例子中,布局是你如何根据房间的约束来放置家具的过程。你可能会根据房间的大小和形状来决定如何放置沙发、桌子和椅子,这就是布局。

在Flutter中,约束和布局的概念类似。你可以把约束看作是元素的"房间",而布局就是你如何根据这些"房间"的大小和形状来放置其他元素。

来看一个最简单的例子

dart 复制代码
Container(
  width: 200.0,
  height: 200.0,
  decoration: BoxDecoration(
    color: Colors.blue,
  ),
  child: Text('Hello Flutter'),
)

这里的宽高 200 就称之为约束, 这意味着容器的宽度和高度将被限制为200.0,"Hello Flutter"文本的最大宽度就不能超过200.0单位

再比如,我们想写一个列表,列表高度固定,宽度不限制,我们也可以通过约束来实现

dart 复制代码
ListView.builder(
  itemCount: 10,
  itemBuilder: (context, index) {
    return ConstrainedBox(
      constraints: BoxConstraints(
        minHeight: 50,
        maxHeight: 50,
      ),
      child: ListTile(
        title: Text('Item $index'),
      ),
    );
  },
)

我们通过 ConstrainedBox 给每一个 ListTile 施加了一个固定的高度约束,但是宽度可以根据屏幕大小进行变化。

除了约束大小,我们也可以约束比例,比如需要创建一个宽高比固定的小部件,比如一个正方形的容器或者一个宽高比为 16:9 的视频播放器。这时,我们可以使用 AspectRatio 小部件。

dart 复制代码
AspectRatio(
  aspectRatio: 1.0, // For a square container
  child: Container(
    color: Colors.blue,
  ),
)

有时候我们的约束是想根据屏幕大小来设置小部件的大小,这时我们可以利用MediaQuery来获取屏幕的大小。

dart 复制代码
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;

Container(
  width: screenWidth,
  height: screenHeight,
  child: FlutterLogo(),
)

获取约束

LayoutBuilder 可以根据父 Widget 提供的约束条件来决定自身的布局。

假设我们有一个需求,当屏幕的宽度大于 600 像素时,我们显示两列的布局,当屏幕的宽度小于600像素时,我们显示一列的布局。

这个需求我们就可以通过 LayoutBuilder 来实现。首先,我们定义一个 LayoutBuilder Widget:

dart 复制代码
LayoutBuilder(
  builder: (BuildContext context, BoxConstraints constraints) {
    if (constraints.maxWidth > 600) {
      return _buildWideContainers();
    } else {
      return _buildNormalContainers();
    }
  },
);

如果屏幕的宽度大于600像素,我们返回 _buildWideContainers 函数,该函数返回的是两列的布局;如果屏幕的宽度小于600像素,我们返回 _buildNormalContainers 函数,该函数返回的是一列的布局。

响应式布局

刚才提到 LayoutBuilder 时我们举例一个响应式布局的例子,没错,在Flutter 中,实现响应式布局有2种方法, 一是使用 LayoutBuilder,另一个是使用媒体查询 MediaQuery.of()

我们这里举一个使用媒体查询的例子, 我们的手机都有一个横屏竖屏转动的功能,有些 APP 在横屏和竖屏时 UI的展示方式是不同的,那我们如何实现这个效果呢? 我们需要使用 MediaQuery 来检测屏幕的方向并以此布局。

思路有了后我们实现一个照片墙,当横屏时候,显示 3 列图片,当竖屏时候显示 2 列图片:

dart 复制代码
@override
Widget build(BuildContext context) {
    return Scaffold(
        body: OrientationBuilder(
            builder: (context, orientation) {
                return GridView.count(
                    // 对于横屏,我们希望有3列;对于竖屏,我们希望有2列
                    crossAxisCount: orientation == Orientation.portrait ? 2 : 3,
                    // ...其余部分
                );
            },
        ),
    );
}

当然我们也不一定要做响应式布局对于横屏竖屏,我们甚至可以设置我们的 APP 只支持横屏或者竖屏

dart 复制代码
void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
  ]);
  runApp(const MyApp());
}

我们调用 SystemChrome.setPreferredOrientations() 来设置设备的首选方向。除了竖直方向(portraitUp),DeviceOrientation 还包括以下值:

  • portraitDown:设备向上边缘朝向用户并向下旋转90度。
  • landscapeLeft:设备向上边缘朝向用户并向左旋转90度。
  • landscapeRight:设备向上边缘朝向用户并向右旋转90度。
相关推荐
小林爱10 分钟前
【Compose multiplatform教程08】【组件】Text组件
android·java·前端·ui·前端框架·kotlin·android studio
跨境商城搭建开发20 分钟前
一个服务器可以搭建几个网站?搭建一个网站的流程介绍
运维·服务器·前端·vue.js·mysql·npm·php
hhzz21 分钟前
vue前端项目中实现电子签名功能(附完整源码)
前端·javascript·vue.js
秋雨凉人心24 分钟前
上传npm包加强
开发语言·前端·javascript·webpack·npm·node.js
NoneCoder1 小时前
CSS系列(37)-- Overscroll Behavior详解
前端·css
Nejosi_念旧1 小时前
使用Webpack构建NPM Library
前端·webpack·npm
前端切圖仔1 小时前
失业,仲裁,都赶上了(二)
前端·javascript·程序员
冰红茶-Tea1 小时前
typescript数据类型(二)
前端·typescript
slongzhang_1 小时前
elementPlus消息组件多按钮案例
前端·javascript·vue.js
会发光的猪。2 小时前
vue中el-select选择框带搜索和输入,根据用户输入的值显示下拉列表
前端·javascript·vue.js·elementui