《Flutter UI》
[!tip]-前言
本人近期在自学flutter,文章纯手敲,自己有Obsidian整理,内容太多先更新一部分,后续会继续更新。
接受各位佬批评,可自行修改错误或补充。
鼓励佬友共建,每次可赏10 ldc。
本文章如有问题,请联系我。
Flutter UI
Flutter widget 采用受React启发的现代框架构建。核心思想是用 widget 构建 UI。 Widget 根据当前配置和状态描述其视图应有的外观。当 widget 的状态改变时,widget 会重建其描述,框架将其与先前的描述进行 diff,以确定底层渲染树从一种状态过渡到另一种状态所需的最小变更。
[!info] Flutter官方文档
1. Flutter
1.1 Flutter 是什么
Flutter 是 Google 开发的开源 UI 框架,使用 Dart 语言编写。它的核心设计理念是一切皆 Widget。不同于其他跨平台框架依赖原生组件渲染,Flutter 自带高性能的渲染引擎 Skia(Impeller 在 iOS 上替代 Skia),直接在屏幕上绘制像素,从而实现真正的跨平台一致性。
Flutter 的目标平台覆盖极广:iOS、Android、Web、Windows、macOS、Linux。一套代码可以在六个平台上运行,这是它最吸引人的地方。
1.2 三层架构
| 层级 | 组成 | 职责 |
|---|---|---|
| Framework | Dart 编写的 Widget 层、渲染层、动画层、手势层 | 开发者直接交互的 API 层 |
| Engine | C/C++ 编写的 Skia/Impeller、Dart VM、文本渲染 | 负责渲染、线程管理、平台通信 |
| Embedder | 平台特定的代码(Java/Kotlin、Swift/Objective-C、C++) | 将 Engine 嵌入到各个操作系统中 |
1.3 热重载与热重启
Flutter 的开发体验很大程度上归功于热重载(Hot Reload)。它借助 Dart 的 JIT 编译器,在代码修改后毫秒级地将变更注入到运行中的应用,同时保持应用状态。
# 在终端中按 r 执行热重载(保留状态)
# 按 R 执行热重启(重新初始化状态)
# 按 q 退出
热重载的局限
热重载不会重新执行
main()和initState()。如果你修改了全局变量初始化、枚举定义、类的静态字段,或者遇到编译错误,热重载会失败,需要使用热重启。
1.4 项目结构
my_app/
├── android/ # Android 原生工程
├── ios/ # iOS 原生工程
├── lib/ # Dart 代码(核心开发目录)
│ └── main.dart # 应用入口
├── test/ # 单元测试和 Widget 测试
├── pubspec.yaml # 依赖管理和资源配置
└── assets/ # 图片、字体等资源
1.5 pubspec.yaml 详解
name: demo
description: "A new Flutter project."
publish_to: 'none'
version: 0.1.0+1
environment:
sdk: ^3.12.2
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
flutter:
uses-material-design: true
2. Widget 基础
2.1 Widget 了解
在 Flutter 中,Widget 是构建 UI 的基本单位。按钮是 Widget,布局是 Widget,手势是 Widget,甚至是应用本身也是 Widget。Widget 描述的是 UI 的配置信息(即"长什么样"),而不是可变的 UI 实例。
Widget 的核心特点:
- 不可变(immutable):Widget 的字段都是
final的 - 轻量级:Widget 本身不直接绘制,只是配置描述
- 可嵌套:通过组合多个 Widget 构建复杂 UI
2.1.1 启动
void main() {
runApp(const MainApp());
}
runApp() 函数接收给定的 Widget 并将其设为 widget 树的根。
2.1.2 Material 组件
Flutter 提供多种 widget,帮助你构建符合 Material Design 的应用。 Material 应用以MaterialApp widget 开头,它在应用根节点构建多种实用 widget,包括Navigator——管理以字符串标识的 widget 栈,即「路由」。
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: Text('Hello World!'),
),
),
);
}
}
MainApp 是 根 widget,因为它是传入 runApp 的 widget。在这个 widget 内部,有一个 build 方法,它返回名为 MaterialApp 的另一个 widget。
MaterialApp
- title:用来展示窗口标题内容。
- theme:用来展示整个应用的主题。
- home:用来展示窗口的主题。
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'My First App',
theme: ThemeData(scaffoldBackgroundColor: Colors.deepPurple),
home: Scaffold(
body: Text("hello world"),
),
);
2.1.2 Scaffold 组件
Scaffold 提供标准、灵活配置的页面骨架。
| 属性 | 作用 |
|---|---|
| appBar | 页面顶部的应用栏,常用于显示标题、导航按钮和操作菜单 |
| body | 页面主题内容,可以放置任何其他组件 |
| bottomNavigationBar | 底部导航栏,方便用户在不同核心页面切换 |
| backgroundColor | 设置整个Scaffold 的背景色 |
| flotingActionButtom | 悬浮操作按钮,常用于触发页面主要动作 |
| … | 其他 |
home: Scaffold(
appBar: AppBar(
title: Text('appbar'),
centerTitle: true,
),
body: Text("body"),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Search',
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
},
child: Icon(Icons.add),
),
),
2.1.3 两种核心 Widget 类型
| 组件名称 | 类型 | 描述 |
|---|---|---|
| StatelessWidget | 无状态组件 | StatelessWidget 一旦构建完成内部内容就不会再改变。它适用于纯展示型 UI,如文本、图标、静态图片等。 |
| StatefulWidget | 有状态组件 | StatefulWidget 由两部分组成:Widget 本身(不可变)和 State(可变)。当状态变化时,调用 setState() 触发重建。 |
(1)StatelessWidget 生命周期 |
StatelessWidget非常简单:构造函数 →build()。没有额外的生命周期回调。- 当组件被创建或父组件状态变化导致需要重新构建时,
build()会被调用。
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Text('Hello World!'),
),
),
);
}
}
总是使用 const 构造函数
当
StatelessWidget的所有字段都是编译期常量时,使用const构造函数可以让 Flutter 在重建时复用同一个 Widget 实例,大幅提升性能。Flutter 官方推荐==尽可能使用 const==。
(2)StatefulWidget 生命周期
| 生命周期阶段 | 函数 | 描述 | 使用场景 |
|---|---|---|---|
| 创建阶段 | createState() | widget初始化调用,创建state对象 | |
| 创建阶段 | initState() | state对象插入widget树立刻执行,仅执行一次 | 订阅流、初始化动画控制器、获取初始数据 |
| 创建阶段 | didChangeDependencies() | initState()后立刻执行,当所依赖的InheritedsWidget更新时调用,可能多次 | 主题、本地化 |
| 构建阶段 | build() | 构建UI,初始化或更新后多次调用 | |
| 更新阶段 | didUpdateWidget() | 父组件传入新配置时调用,用于比较新旧配置 | 父 Widget 重建时,传入新配置 |
| 销毁阶段 | deactivate() | 当state对象从widget树暂时移除时调用 | |
| 销毁阶段 | dispose() | 当state对象从widget树永久移除时调用,释放资源,仅执行一次 | 取消订阅、释放动画控制器、关闭资源 |
// 第一个类继承StatefulWidget,主要接受和定义最终参数,核心作用创建State对象
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
// 第二个类继承State<第一个类类名>,负责管理可变数据和业务逻辑,并实现build方法来渲染视图。
class _CounterState extends State<Counter> {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _increment,
child: Text('Count: $_count'),
);
}
}
生命周期函数
补充
2.2 布局组件
Flutter 布局的核心机制是 widget。在 Flutter 中,几乎所有东西都是 widget — 甚至布局模型都是 widget。
所有布局 widget 都具有以下任一项:
- 一个
child属性,如果它们只包含一个子项 — 例如Center和Container - 一个
children属性,如果它们包含多个子项 — 例如Row、Column、ListView和Stack
2.2.1 布局组件
Container
组合容器,整合尺寸(
width/height)、装饰(decoration/color)、边距(padding/margin)、对齐(alignment)、变换(transform);
decoration属性:装饰器,可实现复杂背景效果,不能与color同时使用;
与
SizeBox区别:SizeBox只能设置尺寸,无其他属性。
特性:
不设宽高时,会尽可能占满父空间;
padding内部留白,margin外部留白;
有
child才会渲染内部内容。
body: Center(
child: Container(
width: 200, // 宽
height: 100, // 高
padding: EdgeInsets.all(10), // 内边距
margin: EdgeInsets.symmetric(vertical: 20), // 外边距
decoration: BoxDecoration(
// 背景装饰
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
boxShadow: [BoxShadow(color: Colors.grey, blurRadius: 4)],
),
alignment: Alignment.center, // 内部子控件对齐
transform: Matrix4.rotationZ(0.1), // 旋转/平移
child: Text("Container"),
),
),
Center
- 将子组件在父容器空间内水平居中和垂直居中;
Center = Align(alignment: Alignment.center),简化版居中;- 不能设置宽高,会尽可能占满父空间;
body: Center(
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
boxShadow: [BoxShadow(color: Colors.grey, blurRadius: 4)],
),
alignment: Alignment.center,
child: Text("center"),
),
),
Align
- 精确控制子控件在父容器内的位置,可设置宽高因子。
body: Container(
width: 300,
height: 200,
color: Colors.grey,
child: Align(
alignment: Alignment.bottomRight, // 右下角
widthFactor: 0.5, // 自身宽度=父宽度*0.5
heightFactor: 0.5, // 自身高度=父高度*0.5
child: Text("右下角文字"),
),
),
Padding
- 控制子控件的内边距
body: Container(
color: Colors.amber,
child: Padding(
padding: EdgeInsets.all(20),
child: Container(
color: Colors.blue,
),
),
),
Column & Row
Column
- 线性布局,垂直排列子控件
| 属性 | 描述 |
|---|---|
mainAxisAlignment | 控制子控件在主轴(垂直方向)的排列方式,顶部对齐、居中。均匀分布 |
crossAxisAlignment | 控制子控件在交叉轴(水平方向)的排列方式,左/右对齐、拉伸填满 |
mainAxisSize | 决定Column在垂直方向上的尺寸策略。沾满所有空间(max),仅仅包裹子控件内容(min) |
children | 垂直排列子控件列表 |
body: Container(
width: double.infinity,
height: double.infinity,
color: Colors.amber,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Container(
color: Colors.blue,
width: 200,
height: 200,
alignment: Alignment.center,
child: Text('111'),
),
Container(
width: 200,
height: 200,
alignment: Alignment.center,
color: Colors.green,
child: Text('222'),
),
],
),
),
Row
- 线性布局,水平排列子控件
| 属性 | 描述 |
|---|---|
mainAxisAlignment | 控制子控件在主轴(水平方向)的排列方式,顶部对齐、居中。均匀分布 |
crossAxisAlignment | 控制子控件在交叉轴(垂直方向)的排列方式,左/右对齐、拉伸填满 |
mainAxisSize | 决定Row在水平方向上的尺寸策略。沾满所有空间(max),仅仅包裹子控件内容(min) |
children | 垂直排列子控件列表 |
body: Container(
width: double.infinity,
height: double.infinity,
color: Colors.amber,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Container(
color: Colors.blue,
width: 200,
height: 200,
alignment: Alignment.center,
child: Text('111'),
),
Container(
width: 200,
height: 200,
alignment: Alignment.center,
color: Colors.green,
child: Text('222'),
),
],
),
),
Flex & Expended
Flex
- 弹性布局,允许沿着一个主轴(水平或垂直)排列子控件,灵活控制子控件在主轴上的尺寸比例和空间分配。相当于是
Row和Column结合
| 属性 | 描述 |
|---|---|
direction | 主轴方向Axis.horizontal水平,`Axis.vertical垂直 |
mainAxisAlignment | 控制子控件在主轴(水平/垂直方向)的排列方式 |
crossAxisAlignment | 控制子控件在交叉轴(垂直/水平方向)的排列方式 |
mainAxisSize | 决定Row在水平方向上的尺寸策略。沾满所有空间(max),仅仅包裹子控件内容(min) |
children | 垂直排列子控件列表 |
| Expended |
- Expended/Flexible 作为
Flex的子控件通过flex属性来分配Flex组件的空间。
| 属性 | 描述 |
|---|---|
flex | 分配子控件在Flex组件的空间 |
child | 子控件 |
body: Container(
width: double.infinity,
height: double.infinity,
color: Colors.amber,
child: Flex(
direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
flex: 2,
child: Container(
alignment: Alignment.center,
color: Colors.red,
width: 200,
height: 200,
child: Text('111'),
),
),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.center,
color: Colors.green,
width: 200,
height: 200,
child: Text('222'),
),
),
],
),
),
Wrap
- 流式布局,当子控件在主轴上排列不下时,它会自动换行(或换列)。
- flex/row/column的内容超出不会自动换行
| 属性 | 描述 |
|---|---|
direction | 主轴方向Axis.horizontal水平,`Axis.vertical垂直 |
spacing | 控制子控件在主轴的间距 |
runSpacing | 控制子控件在交叉轴的间距 |
alignment | 控制子控件在主轴的对齐方式 |
runAlignment | 控制子控件在交叉轴的对齐方式 |
body: Container(
width: double.infinity,
height: double.infinity,
color: Colors.amber,
child: Wrap(
direction: Axis.horizontal,
spacing: 20,
runSpacing: 20,
alignment: WrapAlignment.spaceBetween,
runAlignment: WrapAlignment.center,
children: [
Container(
alignment: Alignment.center,
color: Colors.red,
width: 200,
height: 200,
child: Text('111'),
),
Container(
alignment: Alignment.center,
width: 200,
height: 200,
color: Colors.green,
child: Text('222'),
),
],
),
),
Stack & Positioned
Stack
- 层叠布局,允许将多个子控件按Z轴方向进行叠加排序。
| 属性 | 描述 |
|---|---|
alignment | 控制非定位的子控件在Stack内的对齐方式,默认左上角 |
fit | 控制非定位的子控件如何适应Stack的尺寸 |
clipBehavor | 控制非定位的子控件在Stack超出部分的裁剪方式 |
children | 需要叠层子控件列表 |
| Positioned |
- 层叠布局,必须作为
Stack的子控件,
| 属性 | 描述 |
|---|---|
top/bottom/left/right | 控制子控件在Stack的位置 |
child | 子控件 |
body: Container(
width: double.infinity,
height: double.infinity,
color: Colors.amber,
child: Stack(
alignment: Alignment.center,
children: [
Container(
alignment: Alignment.center,
color: Colors.red,
width: 200,
height: 200,
),
Positioned(
top: 10,
left: 10,
child: Container(
height: 100,
width: 100,
color: Colors.green
),
),
Positioned(
bottom: 10,
right: 10,
child: Container(
height: 100,
width: 100,
color: Colors.blue
),
),
],
),
),
2.2.2 滚动 & 列表 & 网格
| widget | 特征 | 使用场景 |
|---|---|---|
SingleChildScrollView | 让单个子控件可以用滚动,所有内容一次性渲染 | 长表单,设置页、内容不固定但是总量不多的页面 |
ListView | 线性列表,通过builder可实现懒加载,性能优异 | 聊天记录、新闻、常见单列滚动数据列表 |
GridView | 网格列表,支持懒加载,可固定列数 | 图片墙、商品网格、应用图标列表 |
CustomScrollView | 复杂布局方案,通过组合多个Sliver组件实现滚动 | 电商首页、社交app个人主页多个滚动紧密联动 |
PageView | 整页滚动,支持横向和纵向滚动 | 应用引导页、图片轮播、书籍翻页 |
SingleChildScrollView
- 让单个子控件可以用滚动,所有内容一次性渲染
| 属性 | 描述 |
|---|---|
controller | 控制滚动,绑定一个ScrollController对象给controller对象,通过animateTo/jumpTo方法实现控制滚动 |
scrollDirection | 滚动方向,Axis.horizontal水平滚动,Axis.vertical垂直滚动 |
class _TestAppState extends State<TestApp> {
ScrollController _scrollController = ScrollController();
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Stack(
children: [
SingleChildScrollView(
controller: _scrollController,
child: Column(
children: List.generate(
10,
(index) => Container(
height: 200,
width: double.infinity,
margin: EdgeInsets.all(10),
color: Colors.red,
alignment: Alignment.center,
child: Text('$index'),
),
),
),
),
Positioned(
top: 10,
right: 10,
child: GestureDetector(
onTap: () {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 500),
curve: Curves.ease,
);
},
child: Container(
height: 100,
width: 100,
alignment: Alignment.center,
color: Colors.blue,
child: Text('去底部'),
),
),
),
Positioned(
bottom: 10,
right: 10,
child: GestureDetector(
onTap: () {
_scrollController.animateTo(
0,
duration: const Duration(milliseconds: 500),
curve: Curves.ease,
);
},
child: Container(
height: 100,
width: 100,
alignment: Alignment.center,
color: Colors.blue,
child: Text('去顶部'),
),
),
),
],
),
),
);
}
}
ListView
- 线性列表,通过builder可实现懒加载,性能优异
- 提供多种构造函数,如默认构造函数、
ListView.builder、ListView.separated - 采用按需渲染懒加载,只构建当前可见区域的列表项,极大提高长列表性能
| 属性 | 描述 |
|---|---|
controller | 控制滚动,绑定一个ScrollController对象给controller对象,通过animateTo/jumpTo方法实现控制滚动 |
scrollDirection | 滚动方向,Axis.horizontal水平滚动,Axis.vertical垂直滚动 |
itemCount | itemCount控制列表长度 |
itemBuilder | itemBuilder回调函数按需构建列表项 |
separatorbuilder | 需在ListView.separated使用,为列表提供分割线 |
// ListView.builder
class _TestAppState extends State<TestApp> {
final ScrollController _scrollController = ScrollController();
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Stack(
children: [
ListView.builder(
controller: _scrollController,
itemCount: 10,
itemBuilder: (context, index) => Container(
height: 200,
width: double.infinity,
margin: EdgeInsets.all(10),
color: Colors.red,
alignment: Alignment.center,
child: Text('$index'),
),
),
],
),
),
);
}
}
// ListView.separated
class _TestAppState extends State<TestApp> {
final ScrollController _scrollController = ScrollController();
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Stack(
children: [
ListView.separated(
controller: _scrollController,
itemCount: 10,
separatorBuilder: (context, index) => Container(
height: 10,
width: double.infinity,
color: Colors.grey,
),
itemBuilder: (context, index) => Container(
height: 200,
width: double.infinity,
color: Colors.red,
margin: EdgeInsets.all(10),
alignment: Alignment.center,
child: Text('$index'),
),
),
],
),
),
);
}
}
GridView
- 二维网格列表,支持懒加载,可固定列数
- 提供多种构造函数,
GridView.count(固定列数的网格布局)、GridView.extent(固定子项最大宽度/高度的网格布局)、GridView.builder(网格数量巨大或动态生成、需接收girdDelegate布局委托属性),默认构造函数使用起来复杂,很少用。
| 属性 | 描述 |
|---|---|
scrollDirection | 滚动方向,Axis.horizontal水平滚动,Axis.vertical垂直滚动 |
controller | 控制滚动,绑定一个ScrollController对象给controller对象,通过animateTo/jumpTo方法实现控制滚动 |
mainAxisSpacing | 主轴间距 |
crossAxisSpacing | 交叉轴间距 |
crossAxisCount | 需在GridView.count下使用,固定列数 |
maxCrossAxisExtent | 需在GridView.extent中使用,固定子项最大宽度/高度 |
girdDelegate | crossAxisCount固定列数、mainAxisSpacing主轴间距、crossAxisSpacing交叉轴间距 |
// GridView.count
body: GridView.count(
crossAxisCount: 4,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
children: List.generate(
20,
(index) => Container(
color: Colors.green,
alignment: Alignment.center,
child: Text('$index'),
),
),
),
// GridView.extent
body: GridView.extent(
scrollDirection: Axis.horizontal,
maxCrossAxisExtent: 200,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
children: List.generate(
20,
(index) => Container(
color: Colors.green,
alignment: Alignment.center,
child: Text('$index'),
),
),
),
// GridView.builder
body: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
itemCount: 100,
itemBuilder: (context, index) {
return Container(
color: Colors.green,
alignment: Alignment.center,
child: Text('$index'),
);
},
),
CustomScrollView
- 复杂布局方案,组合多个滚动组件(列表、网格),通过组合多个
Sliver组件实现滚动 sliver是描述可滚动视图内的一部分内容的子控件,它是滚动视图的切片。
| 属性 | 描述 |
|---|---|
controller | 控制滚动,绑定一个ScrollController对象给controller对象,通过animateTo/jumpTo方法实现控制滚动 |
scrollDirection | 滚动方向,Axis.horizontal水平滚动,Axis.vertical垂直滚动 |
slivers | 用于接受Sliver组件列表。 |
slivers组件对应关系
| 子控件名称 | 对应组件 | 作用 |
|---|---|---|
SliverAppBar | AppBar | 可折叠、悬浮、固定头部,嵌套在CustomScrollView |
SliverPadding | Padding | 给 Sliver 子组件增加内边距(不能直接包裹普通 Widget) |
SliverToBoxAdapter | 无直接对等组件 | 将普通 RenderBox Widget(Container/Text 等)转为 Sliver,混合布局必备 |
SlievrPersistentHeader | 无直接对等组件 | 持久吸顶头部,滚动时可以固定在顶部(自定义伸缩头部核心) |
SliverList | ListView | 列表懒加载,对应ListView.builder逻辑 |
SliverGird | GridView | 网格布局,对应GridView.builder |
class _TestAppState extends State<TestApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(title: Text("123"), pinned: true),
SliverToBoxAdapter(
child: Container(height: 200.0, color: Colors.red),
),
SliverList.builder(
itemCount: 10,
itemBuilder: (context, index) => Container(
height: 100,
width: double.infinity,
color:Colors.blue,
margin: EdgeInsets.all(10),
child: Text("第"'$index'"个"),
),
),
],
),
),
);
}
}
PageView
- 整页滚动,支持横向和纵向滚动,实现分页滚动视图。
- 提供多种构造函数,默认构造函数、
PageView.bulider(懒加载)
| 属性 | 描述 |
|---|---|
controller | 控制滚动,绑定一个ScrollController对象给controller对象,通过animateTo/jumpTo方法实现控制滚动 |
scrollDirection | 滚动方向,Axis.horizontal水平滚动,Axis.vertical垂直滚动 |
class _TestAppState extends State<TestApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Container(
height: 200,
color: Colors.blue,
child: PageView.builder(
scrollDirection: Axis.horizontal,
itemCount: 10,
itemBuilder: (BuildContext context, int index) => Container(
margin: EdgeInsets.all(10.0),
color: Colors.red,
alignment: Alignment.center,
child: Text('第${(index+1)}页'),
),
),
),
),
);
}
}
[!danger] ^-^启动
-
qdd28 08-04 22:461楼万物皆widget,套又又套去,大概就是这个意思。
- ↑VLESS Reality 最近突然变慢,已排除服务器资源问题,求帮忙定位原因(AWS 三节点)
- ↑MINIMAX-H3本地运行报错【RuntimeError】求助
- ↑[求职]27届3DGS双非硕毕业,求佬救命
- ↑我感觉gemini3.5pro应该是下周了
- ↑把 AtomCode 白嫖的 Token 转成 OpenAI 格式,无缝接入任意 IDE
- 📍 《Flutter UI》
- ↓【真相大白】所有PC端腾讯游戏,需要挂梯子才能启动 【codex使用途中,帮我设置了环境变量代理】抠鼻,感谢佬友们的解答,谢谢
- ↓到底什么样的家宽节点可以支付最近的0刀 plus?
- ↓今天飞了么?闲来无事想弄个手表端应用。
- ↓基于cot和juice的gpt5.6sol检测方式,从此告别掺假![附实战案例]
- ↓gemini是不是最近甲越来越厚了