Skip to content

层叠与滚动

Stack — 层叠布局

Stack 让子组件像叠卡片一样堆叠,后添加的子组件在上层。适合在背景图上叠加文字、在地图上叠加标注等场景。

基本用法

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Stack 基本用法')),
      body: Center(
        // ─── ★ Stack 层叠布局 ──────────────
        child: Stack(
          children: [
            Container(color: Colors.blue, width: 200, height: 200),  // 底层
            Container(color: Colors.red, width: 100, height: 100),    // 上层
            const Positioned(
              right: 10, top: 10,
              child: Icon(Icons.star, color: Colors.yellow),
            ),
          ],
        ),
        // ─── ☆ Stack 层叠布局 ──────────────
      ),
    );
  }
}

常用属性

属性说明默认值
alignment未定位子组件的对齐方式AlignmentDirectional.topStart
fit未定位子组件的尺寸策略StackFit.loose
clipBehavior超出部分是否裁剪Clip.hardEdge

Positioned — 定位子组件

在 Stack 中使用 Positioned 来精确控制子组件的位置:

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Positioned 定位')),
      body: Stack(
        children: [
          // 底层:背景
          Container(color: Colors.grey[200]),

          // 左上角
          const Positioned(
            left: 16, top: 16,
            child: Text('标题'),
          ),

          // 右下角
          const Positioned(
            right: 16, bottom: 16,
            child: Icon(Icons.arrow_forward),
          ),

          // 居中加载指示器
          Positioned.fill(
            child: Center(child: CircularProgressIndicator()),
          ),
        ],
      ),
    );
  }
}

Positioned 常用属性

属性说明
left距左边距离
top距顶部距离
right距右边距离
bottom距底部距离
width宽度
height高度

注意

  • Positioned 只能在 Stack 中使用
  • 必须设置两个方向的位置(如 left + top,或 left + right + top + bottom)
  • Positioned.fill() 等价于 left: 0, top: 0, right: 0, bottom: 0

常见模式

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Stack 常见模式')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          // 模式一:渐变遮罩 + 文字
          Stack(
            children: [
              Container(
                height: 200,
                color: Colors.blue,
              ),
              Container(
                height: 200,
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    begin: Alignment.topCenter,
                    end: Alignment.bottomCenter,
                    colors: [Colors.transparent, Colors.black54],
                  ),
                ),
              ),
              const Positioned(
                left: 16, bottom: 16,
                child: Text('标题', style: TextStyle(color: Colors.white, fontSize: 24)),
              ),
            ],
          ),
          const SizedBox(height: 32),

          // 模式二:头像 + 角标
          const Text('头像 + 在线角标:'),
          const SizedBox(height: 8),
          SizedBox(
            width: 60, height: 60,
            // ─── ★ Stack 层叠布局 ──────────────
            child: Stack(
              children: [
                const CircleAvatar(
                  backgroundColor: Colors.grey,
                  radius: 30,
                  child: Icon(Icons.person, color: Colors.white),
                ),
                Positioned(
                  right: 0, bottom: 0,
                  child: Container(
                    width: 16, height: 16,
                    decoration: BoxDecoration(
                      color: Colors.green,
                      shape: BoxShape.circle,
                      border: Border.all(color: Colors.white, width: 2),
                    ),
                  ),
                ),
              ],
            ),
            // ─── ☆ Stack 层叠布局 ──────────────
          ),
        ],
      ),
    );
  }
}

ListView — 列表滚动

ListView 是最常用的滚动组件,用于展示垂直或水平方向的列表。

三种构造方式

构造方式适用场景性能
ListView()少量子组件(< 20 个)一般(一次性构建所有子组件)
ListView.builder()大量子组件优(按需构建,懒加载)
ListView.separated()需要分隔线的列表优(按需构建)

基本用法

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ListView 示例')),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: const [
          ListTile(title: Text('项目1')),
          ListTile(title: Text('项目2')),
          ListTile(title: Text('项目3')),
        ],
      ),
    );
  }
}

大量数据推荐使用 ListView.builder

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ListView.builder')),
      body: ListView.builder(
        itemCount: 100,
        itemBuilder: (context, index) {
          return ListTile(title: Text('项目 $index'));
        },
      ),
    );
  }
}

带分隔线:

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ListView.separated')),
      body: ListView.separated(
        itemCount: 20,
        separatorBuilder: (context, index) => const Divider(),
        itemBuilder: (context, index) {
          return ListTile(title: Text('项目 $index'));
        },
      ),
    );
  }
}

常用属性

属性说明默认值
scrollDirection滚动方向Axis.vertical
reverse是否反向false
padding内边距null
physics滚动物理效果平台自适应
shrinkWrap是否根据子组件大小确定自身大小false
controller滚动控制器null

水平滚动列表

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('水平列表')),
      body: SizedBox(
        height: 120,
        child: ListView.builder(
          scrollDirection: Axis.horizontal,
          itemCount: 10,
          itemBuilder: (context, index) {
            return Container(
              width: 120,
              margin: const EdgeInsets.only(right: 8),
              color: Colors.blue[100 * (index % 9 + 1)],
              child: Center(child: Text('Item $index')),
            );
          },
        ),
      ),
    );
  }
}

ScrollController

ScrollController 监听滚动位置、控制滚动:

dart
class MyListPage extends StatefulWidget {
  @override
  State<MyListPage> createState() => _MyListPageState();
}

class _MyListPageState extends State<MyListPage> {
  final _controller = ScrollController();
  bool _showBackTop = false;

  @override
  void initState() {
    super.initState();
    _controller.addListener(() {
      setState(() => _showBackTop = _controller.offset > 500);
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _scrollToTop() {
    _controller.animateTo(
      0,
      duration: Duration(milliseconds: 300),
      curve: Curves.easeOut,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView.builder(
        controller: _controller,
        itemCount: 100,
        itemBuilder: (_, i) => ListTile(title: Text('Item $i')),
      ),
      floatingActionButton: _showBackTop
          ? FloatingActionButton(
              onPressed: _scrollToTop,
              child: Icon(Icons.arrow_upward),
            )
          : null,
    );
  }
}

滚动物理效果

平台默认效果Physics
Android到顶/底有光晕效果ClampingScrollPhysics
iOS到顶/底有回弹效果BouncingScrollPhysics

手动指定:

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('滚动物理效果')),
      body: ListView(
        physics: const BouncingScrollPhysics(),  // 强制 iOS 风格回弹
        children: const [
          ListTile(title: Text('项目1')),
          ListTile(title: Text('项目2')),
        ],
      ),
    );
  }
}

GridView — 网格滚动

GridView 用于展示二维网格布局,如相册、商品列表等。

三种构造方式

构造方式适用场景
GridView.count()固定列数
GridView.extent()固定子组件最大宽度
GridView.builder()大量数据,按需构建

基本用法

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('GridView 示例')),
      body: GridView.count(
        crossAxisCount: 3,
        mainAxisSpacing: 8,
        crossAxisSpacing: 8,
        childAspectRatio: 1.0,
        padding: const EdgeInsets.all(8),
        children: List.generate(
          12,
          (i) => Container(
            color: Colors.blue[100 * (i % 9 + 1)],
            child: Center(child: Text('$i')),
          ),
        ),
      ),
    );
  }
}

大量数据使用 GridView.builder

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('GridView.builder')),
      body: GridView.builder(
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          mainAxisSpacing: 8,
          crossAxisSpacing: 8,
          childAspectRatio: 0.8,
        ),
        padding: const EdgeInsets.all(8),
        itemCount: 50,
        itemBuilder: (context, index) {
          return Card(child: Center(child: Text('Item $index')));
        },
      ),
    );
  }
}

常用属性

属性说明
crossAxisCount列数
mainAxisSpacing主轴间距
crossAxisSpacing交叉轴间距
childAspectRatio子组件宽高比(宽/高)

SingleChildScrollView

当内容可能超出屏幕但不需要列表的懒加载特性时,使用 SingleChildScrollView

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('表单页面')),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: const [
            TextField(decoration: InputDecoration(labelText: '用户名')),
            SizedBox(height: 16),
            TextField(decoration: InputDecoration(labelText: '密码'), obscureText: true),
            SizedBox(height: 32),
            Text('更多表单内容...'),
          ],
        ),
      ),
    );
  }
}

选择指南

  • 内容少,可能溢出 → SingleChildScrollView
  • 数据量大,列表项多 → ListView.builder
  • 网格布局 → GridView

滚动组件速查

需求组件
垂直列表ListView
水平列表ListView(scrollDirection: Axis.horizontal)
带分隔线的列表ListView.separated
网格布局GridView.count / GridView.builder
简单可滚动区域SingleChildScrollView
监听/控制滚动ScrollController

下一步

基于 Flutter 官方文档整理