Flutter布局基础
1、Widget
- 万物皆Widget。
1.1、 Flutter的Widget分两类
- 有状态:
StatefulWidget
- 无状态:
StatelessWidget
- 自定义 一个Widget要能够被渲染:需要实现
build方法
!
1.1.2、 为什么Flutter中大量final修饰的属性,const修饰的构造方法(常量对象)。
- 因为Flutter的渲染逻辑,是增量渲染。
Widget结构是树状结构
。 - 想改变屏幕内容就直接改变Widget对象。
- 常量对象的创建效率更高!
1.1.3、Flutter的状态管理
- StatelessWidget 无状态小部件
- StatefulWidget 有状态小部件
- 继承StatefulWidget,用于对外提供接口
- 实现
createState
方法方法
- 实现
- 继承State用来管理状态
- 热重载不改变state数据,只改变界面,数据在state里面
- 通过
setState
实时设置/改变数据(重新build)
initState
:初始化数据
class StateFulDemo extends StatefulWidget { @override _StateFulDemoState createState() => _StateFulDemoState(); } class _StateFulDemoState extends State<StateFulDemo> { int count = 0; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( backgroundColor: Colors.lightGreenAccent, appBar: AppBar( title: Text('statefulDemo'), ), body: Center( child: Chip( label: Text('$count'), ), ), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: (){ setState(() { count += 1; print('count = $count'); }); }, ), ), ); } }
dispose
:页面销毁
- 继承StatefulWidget,用于对外提供接口
1.2、 MaterialApp
(App素材)
- home属性(主页面)
-
需要一个Widget
-
Scaffold
小部件- 带有导航栏(
appBar
)的小部件elevation:0.0
,导航栏下阴影设置为无
- body属性
actions
:导航栏按钮
bottomNavigationBar
:底部tabbar- 默认
偶数item可能控件会变透明
,可以设置type类型修改
- 默认
- 带有导航栏(
-
debugShowCheckedModeBanner
属性。- 是否显示Debug标记(便于我们在调试版本中做操作)
-
floatingActionButton
属性- 悬浮按钮,必须实现
onPressed:
- 悬浮按钮,必须实现
-
title
- 安卓上划至多任务栏的标题
-
theme:主题,可以改变点击的水波纹特效
-
MediaQuery.removePadding()
:消除子部件
系统自带的padding属性(比如ListView自带的预留刘海高度) -
MediaQuery.of(context).size
:获取屏幕尺寸 -
PopupMenuButton(itemBuilder:itemBuilder)
: 悬浮下拉窗口
-
1.3、ListView
- 类似iOS中的TableView
ListView.builder(itemCount,itemBuilder)
itemCount
:当前这个listView总共有多少个item(没有iOS中那种section和row的分别,只有总item数)itemBuilder
:是一个回调函数。function(BuildContext context,int index)
(cell的布局)controller
:滚动页面(可自己创建一个ScrollController给它)shrinkWrap:true
:根据ListView的内容大小来展示
1.4、 Container
小部件
- 类似iOS的UIView。一个空的小部件。很常用
- margin属性
- 外边距,部件距离周围的边距
- margin: EdgeInsets.only(left: 10),距左边10个px
- padding属性
- 内边距,距内部布局向外扩展小部件大小(向外撑大小部件)
- EdgetInsets.all(10)。上下左右往外扩10个px。
- 如果指定了宽、高度又设置了margin、padding,部件按margin、padding布局,再按指定的宽、高度裁剪,只显示具体数值大小的区域,所以部件内部布局可能显示错位
Alignment
:相对位置属性,参数:x 和 y(范围-1到1)
,原点在中间位置
decoration:BoxDecoration()
:设置圆角
1.5、 Image
图片小部件
Image.network(url)
构造函数:从网络上加载一张图片。
1.6、 SizedBox
- 用来占位的小部件,在复杂的布局中很常用。
1.7、Chip
小部件
- 气泡
1.8、CircleAvatar()
- 圆形图像
CircleAvatar(
backgroundImage: NetworkImage('url'),
)
1.9、Text()
- style:
TextStyle()
overflow: TextOverFlow.ellipsis
:超出内容省略号
1.10、RichText()
:富文本
TextSpan(children:)
:包装文本,赋予样式
1.11、PageView()
final PageController _pController = PageController();
List<Widget> _pages = [ChatPage(),NewsPage()];
- controller: _pController,
- children: _pages,
- 将界面数组添加到Widget树中(若是不通过PageView(),普通方法只添加一个界面到Widget树中,则会产生改变,触发增量渲染,界面切换时会重新创建)
onPageChanged: (int index) {setState((){});}
:拖拽界面触发方法physics: NeverScrollableScrollPhysics(),
:禁止拖拽
1.12、TextField()
cursorColor:
:光标颜色- autofocus:true:自动聚焦
- decoration:
InputDecoration()
1.13、Key(Widget的唯一标识)
- 抽象类,有构造方法
- 场景:
- 在StatefulWidget的State中定义一个Widget所需变量Color,创建多个添加到屏幕上,点击某按钮删除1个Widget,此时Widget会删除颜色错乱
- Widget内部实现了一个
canUpdate
方法,判断是否可以复用
(不是指需要重新渲染,这个命名让人容易理解反了) - 分析:
- Widget树与Element树(
context上下文
)一一对应,State存在于Element树中
,如上图所示规则可知,若没有Key标识,则只判断Widget类型是否一致,删除后的n+1号Widget状态被n号的Element复用,所以颜色出错
- Widget树与Element树(
- 为方便理解,
Element树就是为Widget创建的各种参数、数据的载体
,context上下文就是Element在底层返回到表层的另一个名字
1.13.1、LocalKey
(区别哪个Element保留、哪个删除,diff算法核心)
ValueKey
:以值作为参数(数字、字符串)ObjectKey
:以对象作为参数UniqueKey
:创建唯一标识,绝不可能被复用
1.13.2、GlobalKey
(便于访问某个Widget)
- 类似于tag,定义GlobalKey:
- 为Widget的key赋值,这样可以通过这个GlobalKey拿到这个Widget的Context、Widget、State等,也可以调用setState()
2、弹性盒子布局
2.1、Center
- 让子部件在本部件的居中位置显示
2.2、Row
& Column
- 横向布局Row。
- 子部件按照主轴方向(横向)排列。主轴方向从左到右
textDirection: TextDirection.rtl
:特殊内容,可以改变Row的主轴方向
,让子部件从右向左顺序排列
- 纵向布局Column。
- 子部件按照主轴方向(纵向)排列。主轴方向从上到下
- 每一个UI部件都可以看成一个矩形的“盒子”
- 每一个盒子都有外边距Margin和内边距padding.
- 主轴:
MainAxisAlignment
spaceBetween
: 剩下的空间平均分布到小部件之间!spaceAround
: 剩下的空间平均分布到小部件周围!spaceEvenly
:剩下的空间和小部件一起平均分!- start:向主轴开始的方向对齐
- end:向主轴结束的方向对齐
- center:主轴方向居中对齐
- 交叉轴:
CrossAxisAlignment
垂直于主轴方向baseline
:文字底部对齐stretch
:填满交叉轴方向;- center:交叉轴方向居中对齐
- end:向交叉轴结束的方向对齐
- start:向交叉轴开始的方向对齐
2.3、Expanded
填充布局
- 在
主轴方向不会剩下间隙
。将被Expanded包装的部件进行拉伸和压缩- 主轴横向,宽度设置失效
- 主轴纵向,高度设置失效
- 当Text被Expanded包装后,
文字可以自动换行
。这也被称作灵活布局。
2.4、Stack
- Stack是多层布局,它的主轴方向是从内向外
- alignment属性:可以定位。
alignment:Alignment(x,y)
x和y取值- 范围-1.0 到 1.0
- x=0,y=0 为中心
Positioned
小部件- left、top、right、bottom 4个属性定位
- 参数是像素位置
AspectRatio
宽高比小部件- 它的设置影响父布局(相当于父部件的一个宽高比属性,用小部件的形式设置了,所以AspectRatio虽然是个小部件,但不提倡向里写child)
sepectRatio
属性:宽高比。- 当父布局同时有宽度和高度,那么宽高比失效
2.5、手势GestureDetector
- 将添加手势的部件包进GestureDetector中
2.6.1、onVerticalDrugDown:(DragDownDetails details){}
- 开始垂直拖拽按下时
details.globalPosition
:按下时全局坐标
2.6.2、onVerticalDrugUpdate:(DragUpdateDetails details){}
- 垂直拖拽实时状态
RenderBox box = context.findRenderObject();
print(box.globalToLocal(details.globalPosition));
3、实用方法归纳
3.1、RenderBox
- 拿到当前部件的盒子(在哪个widget里使用,得到那个widget)
RenderBox box = context.findRenderObject();
.globalToLocal(position)
:得到全局位置position在box中的相对位置(左上角为原点0,0)//拖拽手势为例,通过全局位置details.globalPosition得到在盒子中的x、y坐标 double x = box.globalToLocal(details.globalPosition).dx; double y = box.globalToLocal(details.globalPosition).dy;
3.2、Timer定时器
.periodic()
:定时时间.cancel()
:取消.isActive()
:是否在用
3.3、沙盒路径
Directory.systemTemp.path
3.4、try、throw、rethrow、(on)catch、finally:异常处理
3.5、flex:权重
- 给widget设置权重值,最后系统根据权重值算出比例进行布局
3.6、Random():随机数
Random().nextInt(255)
个人学习使用,以后会继续丰富内容
转载自:https://juejin.cn/post/7029332912369565704