层叠与滚动
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 |
