Flutter 系列教程:布局基础 (下) - Stack 绝对定位和 Expanded 弹性布局

在上篇教程中,我们学习了如何使用 Container, Row, 和 Column 来创建有序的线性布局。现在,我们将学习另外两种强大的布局工具,它们将让你的布局能力提升到一个全新的维度。

学习目标

  • 掌握 Stack Widget,实现组件的堆叠效果。
  • 学会使用 Positioned Widget 在 Stack 中进行精确定位。
  • 理解 ExpandedFlexible 的作用,在 RowColumn 中实现弹性布局。
  • 学会使用 flex 属性按比例分配空间。

1. StackPositioned:层叠与绝对定位

你可以将 Stack Widget 想象成一叠扑克牌。它的 children 列表中的第一个 Widget 在最底层,最后一个 Widget 在最顶层。这使得我们可以轻松地实现组件之间的覆盖和堆叠效果。

Stack 核心属性

  • children: 一个 Widget 列表,是需要被堆叠的子组件。
  • alignment: 控制那些没有被Positioned包裹 的子组件的对齐方式。默认值是 Alignment.topLeft (左上角)。

Positioned Widget:Stack 的灵魂伴侣

Stack 本身只是简单地将组件堆叠在一起,但它的真正威力来自于与 Positioned Widget 的结合。Positioned 只能用在 Stackchildren ,它允许你相对于 Stack 的四个角来精确定位一个子组件。

  • 核心属性 : top, bottom, left, right
    • Positioned(top: 10, left: 10, child: ...): 将子组件定位在距离 Stack 顶部 10 像素、左侧 10 像素的位置。
    • 如果你同时设置了对立的属性,比如 left: 10right: 10,那么 Positioned 会拉伸其子组件以填满这两个边界之间的空间。

代码示例:创建一个用户头像卡片

这个例子将展示如何在一个背景图片上层叠用户的名字和状态。

less 复制代码
import 'package.flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Stack & Positioned Demo')),
        body: Center(
          child: Container(
            width: 300,
            height: 200,
            color: Colors.grey,
            child: Stack(
              // alignment: Alignment.center, // 对齐所有非 Positioned 的子组件
              children: <Widget>[
                // 底层:背景图片
                Container(
                  decoration: const BoxDecoration(
                    image: DecorationImage(
                      image: NetworkImage('https://picsum.photos/300/200'),
                      fit: BoxFit.cover,
                    ),
                  ),
                ),

                // 顶层 1: 定位在左下角的用户名称
                Positioned(
                  bottom: 10,
                  left: 10,
                  child: Container(
                    padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                    color: Colors.black.withOpacity(0.5),
                    child: const Text(
                      'Flutter Pro',
                      style: TextStyle(color: Colors.white, fontSize: 18),
                    ),
                  ),
                ),

                // 顶层 2: 定位在右上角的状态图标
                const Positioned(
                  top: 10,
                  right: 10,
                  child: Icon(
                    Icons.check_circle,
                    color: Colors.greenAccent,
                    size: 30,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

ddxx


2. ExpandedFlexible:弹性布局

在上一篇中,我们学习了 RowColumn。但有一个常见问题:如果 Row 中的子组件宽度总和超过了屏幕宽度,Flutter 就会报错并显示一个黄黑相间的"溢出"警告。

ExpandedFlexible 就是为了解决这个问题而生的。它们只能用在 Row, Column, 或 Flexchildren,作用是让其子组件"瓜分"主轴上的剩余空间。

Expanded:强势的填充者

Expanded Widget 会强制 其子组件填满所有可用的剩余空间。如果 RowColumn 中有多个 Expanded 组件,它们会根据 flex 属性按比例分配空间。

  • 核心属性 flex : 一个整数,代表分配空间的"权重"或"比例"。默认值为 1。
    • 如果有两个 Expanded,一个 flex: 2,一个 flex: 1,那么前者会占据剩余空间的 2/3,后者占据 1/3。

Flexible:灵活的适应者

FlexibleExpanded 类似,但它不强制其子组件填满空间。它允许子组件在可用空间内拥有自己的尺寸,但不能超过可用空间(从而避免溢出)。

  • ExpandedFlexible 的一个常用特例,它等价于 Flexible(fit: FlexFit.tight)
  • Flexible 默认的 fitFlexFit.loose,表示"尽可能大,但不要超过剩余空间"。

新手建议 :在绝大多数情况下,你需要的都是 Expanded。当你希望一个组件在有足够空间时保持自身大小,在空间不足时进行收缩,才需要考虑使用 Flexible

代码示例:构建一个按比例划分的导航栏

这个例子将创建一个 Row,其中包含一个固定宽度的图标和两个按比例分配空间的文本按钮。

less 复制代码
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Expanded Demo')),
        body: Column(
          children: [
            // --- 示例 1: 平均分配空间 ---
            Container(
              height: 100,
              color: Colors.blue,
              child: Row(
                children: [
                  Expanded(child: Container(color: Colors.red)),
                  Expanded(child: Container(color: Colors.green)),
                  Expanded(child: Container(color: Colors.yellow)),
                ],
              ),
            ),
            
            const SizedBox(height: 20),

            // --- 示例 2: 按比例分配空间 (2:3:1) ---
            Container(
              height: 100,
              color: Colors.teal,
              child: Row(
                children: [
                  Expanded(
                    flex: 2, // 占据 2/6 的空间
                    child: Container(color: Colors.red),
                  ),
                  Expanded(
                    flex: 3, // 占据 3/6 的空间
                    child: Container(color: Colors.green),
                  ),
                  Expanded(
                    flex: 1, // 占据 1/6 的空间
                    child: Container(color: Colors.yellow),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

动手试试 :修改上面代码中 flex 的值,观察布局是如何相应变化的。你也可以尝试将 Expanded 替换为 Flexible,看看效果有何不同。

总结

至此,我们已经掌握了 Flutter 中最核心的几大布局 Widget:

  • 线性布局 : Row (水平) 和 Column (垂直),用于有序排列。
  • 层叠布局 : StackPositioned,用于实现组件的堆叠和绝对定位。
  • 弹性布局 : ExpandedFlexible,用于在 RowColumn 中按比例分配空间。

熟练运用这些组件,你就可以像搭积木一样,自由地组合出任何你想要的复杂界面。它们是你构建所有 Flutter 应用的坚实基础。

在接下来的教程中,我们将学习如何构建可滚动的列表视图 (ListView) 和网格视图 (GridView),这对于展示大量数据至关重要。我们下篇教程见!

相关推荐
穿花云烛展2 小时前
实习日记6(select选择的超出问题)
前端
前端搞毛开发工程师2 小时前
Ubuntu 系统 Docker 安装避坑指南
前端·后端
伊织code2 小时前
Uvicorn - Python ASGI Web 服务器
服务器·前端·python·uvicorn·asgi
Cache技术分享2 小时前
201. Java 异常 - 如何抛出异常
前端·javascript·后端
Mintopia3 小时前
🌍 cesium-kit —— 快速实现动态标点与交互的 Cesium 工具库
前端·three.js·cesium
Mike_jia3 小时前
RustFS:国产高性能对象存储新标杆,全面替代MinIO的优雅方案
前端
2301_781392523 小时前
提高前端开发效率的利器:VUE常用组件及应用
前端·javascript·vue.js
golang学习记3 小时前
VS Code 2025 最强“黑科技”特性:效率直接拉满!
前端
Mintopia4 小时前
📘 领域适配 AIGC:垂直行业 Web 应用的微调技术实践
前端·aigc·ai编程