Flutter布局基础

695 阅读6分钟

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:页面销毁 image.png

1.2、 MaterialApp(App素材)

  • home属性(主页面)
    • 需要一个Widget

    • Scaffold小部件

      • 带有导航栏(appBar)的小部件
        • elevation:0.0,导航栏下阴影设置为无
      • body属性
        • actions:导航栏按钮
      • bottomNavigationBar:底部tabbar
        • 默认偶数item可能控件会变透明,可以设置type类型修改
    • debugShowCheckedModeBanner属性。

      • 是否显示Debug标记(便于我们在调试版本中做操作)
    • floatingActionButton属性

      • 悬浮按钮,必须实现onPressed:
    • title

      • 安卓上划至多任务栏的标题
    • theme:主题,可以改变点击的水波纹特效 image.png

    • MediaQuery.removePadding():消除子部件系统自带的padding属性(比如ListView自带的预留刘海高度) image.png

    • 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给它) image.png
    • 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():设置圆角 image.png

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() image.png

1.13、Key(Widget的唯一标识)

  • 抽象类,有构造方法
  • 场景:
    • StatefulWidget的State中定义一个Widget所需变量Color,创建多个添加到屏幕上,点击某按钮删除1个Widget,此时Widget会删除颜色错乱
  • Widget内部实现了一个canUpdate方法,判断是否可以复用(不是指需要重新渲染,这个命名让人容易理解反了) image.png
  • 分析:
    • Widget树Element树(context上下文)一一对应,State存在于Element树中,如上图所示规则可知,若没有Key标识,则只判断Widget类型是否一致,删除后的n+1号Widget状态被n号的Element复用,所以颜色出错 image.png
  • 为方便理解,Element树就是为Widget创建的各种参数、数据的载体,context上下文就是Element在底层返回到表层的另一个名字
1.13.1、LocalKey(区别哪个Element保留、哪个删除,diff算法核心)
  • ValueKey:以值作为参数(数字、字符串)
  • ObjectKey:以对象作为参数
  • UniqueKey:创建唯一标识,绝不可能被复用
1.13.2、GlobalKey(便于访问某个Widget)
  • 类似于tag,定义GlobalKey: image.png
  • 为Widget的key赋值,这样可以通过这个GlobalKey拿到这个Widget的Context、Widget、State等,也可以调用setState() image.png

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小部件
    • lefttoprightbottom 4个属性定位
    • 参数是像素位置
  • AspectRatio 宽高比小部件
    • 它的设置影响父布局(相当于父部件的一个宽高比属性,用小部件的形式设置了,所以AspectRatio虽然是个小部件,但不提倡向里写child)
    • sepectRatio属性:宽高比。
    • 当父布局同时有宽度和高度,那么宽高比失效

2.5、手势GestureDetector

  • 将添加手势的部件包进GestureDetectorimage.png
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():是否在用 image.png

3.3、沙盒路径

Directory.systemTemp.path

3.4、try、throw、rethrow、(on)catch、finally:异常处理

3.5、flex:权重

  • 给widget设置权重值,最后系统根据权重值算出比例进行布局

3.6、Random():随机数

  • Random().nextInt(255)

个人学习使用,以后会继续丰富内容