美文网首页Flutter初探
Day07 - Flutter 小部件的布局

Day07 - Flutter 小部件的布局

作者: IIronMan | 来源:发表于2020-05-18 12:48 被阅读0次

    概述

    • 单子布局组件
    • 多子布局组件

    一、单子布局组件

    单子布局组件的含义是其只有一个子组件,可以通过设置一些属性设置该子组件所在的位置信息等。
    比较常用的单子布局组件有:AlignCenterPaddingContainer

    • 1.1、对齐组件

      • 1.1.1、对齐介绍
        看到Align这个词,我们就知道它有我们的 对齐方式 有关。
        在其他端的开发中(iOS,Android,前端)Align通常只是一个属性而已,但是Flutter中Align也是一个组件。
        我们可以通过二进制来看一下Align有一些属性:

        const Align({
           Key key,
           this.alignment: Alignment.center, // 对齐方式,默认居中对齐
           this.widthFactor, // 宽度因子,不设置的情况,会尽可能大
           this.heightFactor, // 高度因子,不设置的情况,会尽可能大
           Widget child // 要布局的子Widget
        })
        

        提示:这里我们特别解释一下 widthFactorheightFactor 作用:

        • 因为子组件在父组件中的对齐方式必须有一个合并,就是父组件得知道自己的范围(宽度和高度);
        • 如果widthFactor和heightFactor不设置,那么替换Align会重置的大(替换为自己所在的父组件);
        • 我们也可以对他们进行设置,尺寸widthFactor设置为2,那么相对于Align的宽度是子组件跨度的2倍;也就是是子组件的 几倍
      • 1.1.2、对齐代码

        class MyHomeBody extends StatelessWidget {
           @override
           Widget build(BuildContext context) {
             return Align(
                child: Icon(Icons.pets, size: 36, color: Colors.red),
                alignment: Alignment.bottomRight,
                widthFactor: 2,
                heightFactor: 2,
             );
           }
        }
        
    • 1.2、中心组件

      • 1.2.1、中心介绍
        实际上 Center组件继承自Align,只是将alignment设置为Alignment.center
        原始码分析:

        class Center extends Align {
           const Center({
              Key key,
              double widthFactor,
              double heightFactor,
              Widget child
           }) : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);
        }
        
      • 1.2.2、代码展示

        class MyHomeBody extends StatelessWidget {
           @override
           Widget build(BuildContext context) {
               return Center(
                   child: Icon(Icons.pets, size: 36, color: Colors.red),
               );
           }
        }
        
    • 1.3、填充组件

      • 1.3.1、填充介绍
        Padding组件在其他端也是一个属性而已,但是在Flutter中是一个小部件,但是Flutter中没有Margin这样一个小部件,这是因为外边距也可以通过Padding来完成。
        填充通常用于设置子窗口小部件到父窗口小部件的边距(您可以称为是父组件的内边距或子窗口小部件的外边距)。
        原始码分析:

        const Padding({
           Key key,
           @requiredthis.padding, // EdgeInsetsGeometry类型(抽象类),使用EdgeInsets
           Widget child,
        })
        
      • 1.3.2、代码展示

        class MyHomeBody extends StatelessWidget {
           @override
           Widget build(BuildContext context) {
              return Padding(
                 padding: EdgeInsets.all(16),
                 child: Text(
                     "莫听穿林打叶声,何妨吟啸且徐行。竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生。",
                     style: TextStyle(
                     color: Colors.redAccent,
                     fontSize: 18
                 ),
               ),
            );
          }
        }
        
    • 1.4、集装箱组件
      Container组件其他其他Android中的View,iOS中的UIView。
      如果你需要一个视图,有一个背景颜色,图像,有固定的尺寸,需要一个边框,圆角等效果,那么就可以使用Container组件。

      • 1.4.1、容器介绍
        Container在开发中被使用的频率是非常高的,特别是我们经常替换其作为容器组件。
        下面我们来看一下Container有哪些属性:

        Container({
          this.alignment,
          this.padding, //容器内补白,属于decoration的装饰范围
          Color color, // 背景色
          Decoration decoration, // 背景装饰
          Decoration foregroundDecoration, //前景装饰
          double width,//容器的宽度
          double height, //容器的高度
          BoxConstraints constraints, //容器大小的限制条件
          this.margin,//容器外补白,不属于decoration的装饰范围
          this.transform, //变换
          this.child,
        })
        

        大多数属性在介绍其他容器时都已经介绍过了,不再重复述,但有两点需要说明:

        • 容器的大小可以通过width,height属性来指定,也可以通过constraints来指定。实际容器内部会根据width,height来生成一个constraints;
        • color和decoration是互斥的,实际上,当指定color时,容器内会自动创建一个装饰;
      • 1.4.2、集装箱代码

        class MyHomeBody extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
              return Center(
                 child: Container(
                    color: Color.fromRGBO(3, 3, 255, .5),
                    width: 100,
                    height: 100,
                    child: Icon(Icons.pets, size: 32, color: Colors.white),
                 ),
              );
          }
        }
        
    • 1.4.3、Box装潢
      Container有一个非常重要的属性decoration:
      他对应的类型是装修类型,但是它是一个抽象类。
      在开发中,我们经常使用它的实现类 BoxDecoration 来进行实例化。
      BoxDecoration常见属性:

      const BoxDecoration({
          this.color, // 颜色,会和Container中的color属性冲突
          this.image, // 背景图片
          this.border, // 边框,对应类型是Border类型,里面每一个边框使用BorderSide
          this.borderRadius, // 圆角效果
          this.boxShadow, // 阴影效果
          this.gradient, // 渐变效果
          this.backgroundBlendMode, // 背景混合
          this.shape = BoxShape.rectangle, // 形变
      })
      

      部分代码:

      class MyHomeBody extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
              return Center(
                child: Container(
                   // color: Color.fromRGBO(3, 3, 255, .5),
                   width: 150,
                   height: 150,
                   child: Icon(Icons.pets, size: 32, color: Colors.white),
                   decoration: BoxDecoration(
                       color: Colors.amber, // 背景颜色
                       border: Border.all(
                          color: Colors.redAccent,
                          width: 3,
                          style: BorderStyle.solid
                       ), // 这里也可以使用Border.all统一设置
                       // top: BorderSide(
                          //  color: Colors.redAccent,
                         //   width: 3,
                         //   style: BorderStyle.solid
                        //  ),
                     borderRadius: BorderRadius.circular(20), // 这里也可以使用.only分别设置
                     boxShadow: [
                          BoxShadow(
                             offset: Offset(5, 5),
                             color: Colors.purple,
                             // 模糊度
                             blurRadius: 5
                          )
                     ],
                    //  shape: BoxShape.circle, // 会和borderRadius冲突
                    gradient: LinearGradient(
                      colors: [
                          Colors.green,
                          Colors.red
                      ]
                   )
                ),
             ),
          );
        }
      }
      
    • 1.4.4、通过Container+BoxDecoration来实现圆角图像

      class HomeContent extends StatelessWidget {
         @override
         Widget build(BuildContext context) {
             return Center(
                child: Container(
                   width: 200,
                   height: 200,
                   decoration: BoxDecoration(
                       borderRadius: BorderRadius.circular(20),
                       image: DecorationImage(
                         image: NetworkImage("https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg"),
                       )
                   ),
                ),
            );
         }
      }
      

    二、多子布局组件

    在开发中,我们经常需要将多个Widget放在一起进行布局,某些水平方向,垂直方向隔开,甚至有时候需要他们进行分解,其中图片上面放一段文字等;
    这个时候我们需要使用多子布局部件(多子布局部件)。
    比较常用的多子布局组件是行,列,堆栈,我们来学习一下他们的使用。

    • 2.1、Flex 组件(很少直接使用)
      实际上,我们即将学习的Row组件和Column组件都继承自Flex组件。

      • Flex组件和行,列属性主要的区别就是多一个方向。
      • 当direction的增量Axis.horizo​​ntal的时候,则是Row。
      • 当direction的增量Axis.vertical的时候,则是Column。

      在学习 RowColumn 之前,我们先学习主轴和交叉轴的概念。
      因为行是一行排布,列是一列排布,那么这些都存在两个方向,并且两个小部件分布的方向应该是对立的。
      它们彼此都有 主轴(MainAxis)和 交叉轴(CrossAxis)的概念:

      • 对于行(Row)来说,主轴:横着(MainAxis)和交叉轴:竖着(CrossAxis)
      • 对于列(Column) 而言,主轴:竖着(主轴)和交叉轴:横着(CrossAxis)分别是下图
    • 2.2、Row行 组件

      • 1>、行介绍
        Row组件用于将所有的子控件排成一行,实际上这种布局应该是放置于Web的Flex布局。如果熟悉Flex布局,会发现非常简单。
        从源码中查看Row的属性:

        Row({
           Key key,
           // 主轴对齐方式
           MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
           // 水平方向尽可能大
           MainAxisSize mainAxisSize = MainAxisSize.max, 
           // 交叉处对齐方式
           CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, 
           // 水平方向子widget的布局顺序(默认为系统当前Locale环境的文本方向(如中文、英语都是从左往右,而阿拉伯语是从右往左))
           TextDirection textDirection, 
           // 表示Row纵轴(垂直)的对齐方向
           VerticalDirection verticalDirection = VerticalDirection.down, 
           // 如果上面是baseline对齐方式,那么选择什么模式(有两种可选)
           TextBaseline textBaseline, 
           List<Widget> children = const <Widget>[],
        })
        

        部分属性详细解析

        • 主轴尺寸:

          • 表示行在主轴(水平)方向占用的空间,MainAxisSize.max 最小是,表示重置多的占用水平方向的空间,此时无论子小物件实际占用多少水平空间,行的宽度始终等于水平方向的最大宽度
          • MainAxisSize.min 表示缩减少的占用水平空间,当子小部件没有占满水平剩余空间,则行的实际宽度等于所有子小部件所占用的水平空间;
            mainAxisAlignment:表示子小部件在行所占用的水平空间内对齐方式
        • mainAxisAlignment:表示子小部件在行所占用的水平空间内对齐方式

          • 如果mainAxisSize变量MainAxisSize.min,则此属性无意义,因为子小部件的宽度等于行的宽度
          • 只有当mainAxisSize的增量MainAxisSize.max时,此属性才赋值
          • MainAxisAlignment.start表示沿textDirection的初始方向对齐,
          • 如textDirection为取值TextDirection.ltr时,则MainAxisAlignment.start表示左对齐,textDirection为取值TextDirection.rtl时表示从右对齐。
          • 而MainAxisAlignment.end和MainAxisAlignment.start正好相反;
          • MainAxisAlignment.center表示居中对齐。

          提示:mainAxisAlignment

          • start: 主轴的开始位置挨个摆放元素
          • end: 主轴结束位置挨个摆放元素
          • center: 主轴的中心点对齐
          • spaceBetween: 左右两边的距离为 0,其他元素之间平分间距
          • spaceAround: 左右两边的距离是其他元素之间的距离的一半
          • spaceEvenly: 所有的间距平分空间
        • crossAxisAlignment:表示子控件在纵轴方向的对齐方式

          • Row的高度等于子Widgets中最高的子元素高度
          • 它的取值和MainAxisAlignment一样(包含start,end,center三个值)
          • 不同的是crossAxisAlignment的参考系是verticalDirection,即verticalDirection转换VerticalDirection.downcrossAxisAlignment.start指顶部对齐,verticalDirection转换VerticalDirection.up时,crossAxisAlignment.start指底部对齐;而crossAxisAlignment.endcrossAxisAlignment.start正好相反;

          提示:crossAxisAlignment

          • start: 交叉轴的起始位置对齐
          • end: 交叉轴的结束为止对齐
          • center: 中心点对齐(没默认值)
          • stretch: 基线对齐(必须有文本的时候才起效果)
          • baseline: 先Row占据交叉轴尽可能大的空间,将左所有的子Widget交叉轴的高度,拉伸到最大
        • 2>、示例代码

          class MyHomeBody extends StatelessWidget {
             @override
             Widget build(BuildContext context) {
                return Row(
                   mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                   crossAxisAlignment: CrossAxisAlignment.end,
                   mainAxisSize: MainAxisSize.max,
                   children: <Widget>[
                      Container(color: Colors.red, width: 60, height: 60),
                      Container(color: Colors.blue, width: 80, height: 80),
                      Container(color: Colors.green, width: 70, height: 70),
                      Container(color: Colors.orange, width: 100, height: 100),
                   ],
                );
             }
          }
          
        • 3>、mainAxisSize
          默认情况下,行会重新覆盖多的宽度,让子小部件在其中进行排布,这是因为mainAxisSize属性替换值是MainAxisSize.max。
          我们来看一下,如果这个值被修改为MainAxisSize.max会什么变化:


          MainAxisSize.min
      • 4>、Expanded 展开式
        如果我们希望红色和黄色的容器Widget不要设置固定的宽度,而是替换剩余的部分,这个时候应该如何处理呢?
        这个时候我们可以使用Expanded来包装Container Widget,并且将其的宽度不设置值,我们可以看到下图多余的空间被红色和黄色的部分分了

        • flex属性,弹性系数,Row会根据两个展开的弹性系数来决定它们剩下多少空间的比例


          截屏2020-05-15 下午5.03.15.png
          class MyHomeBody extends StatelessWidget {
             @override
             Widget build(BuildContext context) {
                return Row(
                   mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                   crossAxisAlignment: CrossAxisAlignment.end,
                   mainAxisSize: MainAxisSize.min,
                   children: <Widget>[
                      Expanded(
                          flex: 1,
                          child: Container(color: Colors.red, height: 60),
                      ),
                      Container(color: Colors.blue, width: 80, height: 80),
                      Container(color: Colors.green, width: 70, height: 70),
                      Expanded(
                          flex: 1,
                          child: Container(color: Colors.orange, height: 100),
                      )
                   ],
               );
             }
          } 
          
    • 2.3、Column 列组件
      列组件用于将所有的子控件排成一列,学会了前面的行后,列只是和行的方向不同而已。

      • 2.3.1>、专栏介绍,我们直接看它的源码:我们发现和Row属性是一致的

        Column({
           Key key,
           MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
           MainAxisSize mainAxisSize = MainAxisSize.max,
           CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
           TextDirection textDirection,
           VerticalDirection verticalDirection = VerticalDirection.down,
           TextBaseline textBaseline,
           List<Widget> children = const <Widget>[],
        })
        
      • 2.3.2>、专栏演练

        class MyHomeBody extends StatelessWidget {
           @override
           Widget build(BuildContext context) {
               return Column(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  crossAxisAlignment: CrossAxisAlignment.end,
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                      Expanded(
                          flex: 1,
                          child: Container(color: Colors.red, width: 60),
                      ),
                      Container(color: Colors.blue, width: 80, height: 80),
                      Container(color: Colors.green, width: 70, height: 70),
                      Expanded(
                          flex: 1,
                          child: Container(color: Colors.orange, width: 100),
                      )
                  ],
               );
           }
        }
        
    • 2.4、堆栈组件
      在开发中,我们多个组件很有可能需要重叠显示,某些在一张图片上显示文字或一个按钮等。
      在Android中可以使用Frame实现,在Web端可以使用绝对定位,在Flutter中我们需要使用布局Stack。

      • 2.4.1>、堆栈介绍
        Stack有一些属性:

        Stack({
           Key key,
           this.alignment = AlignmentDirectional.topStart,
           this.textDirection,
           this.fit = StackFit.loose,
           this.overflow = Overflow.clip,
           List<Widget> children = const <Widget>[],
        })
        
        • 参数解析:
          • 对齐:此参数决定如何去对齐没有定位(没有使用Positioned)或部分定位的子小部件。所谓部分定位,在这里特指没有在某某轴上定位:左,右为横轴,顶部,bottom为纵轴,只要包含某个轴上的一个定位属性就算在该轴上有定位。
          • textDirection:和Row,Wrap的textDirection功能相同,都用于决定对齐对齐的参考系即:textDirection的变量TextDirection.ltr,则alignment的start代表左,end代表右; textDirection的大小TextDirection.rtl,则alignment的start代表右,end代表左。
          • fit:此参数用于决定没有定位的子小部件如何去适应Stack的大小。StackFit.loose表示使用子小部件的大小,StackFit.expand表示扩展伸到Stack的大小。
          • 溢出overflow:此属性决定如何显示超过堆栈显示空间的子部件,变化Overflow.clip时,超出部分会被剪裁(隐藏),转化Overflow.visible时则不会。
      • 2.4.2>、Stack 的 demo
        Stack会经常和Positioned一起来使用,Positioned可以决定组件在Stack中的位置,实现实现Web中的绝对定位效果。

        class MyHomeBody extends StatelessWidget {
            @override
            Widget build(BuildContext context) {
                return Stack(
                    children: <Widget>[
                        Container(
                           color: Colors.purple,
                           width: 300,
                           height: 300,
                        ),
                        Positioned(
                           left: 20,
                           top: 20,
                           child: Icon(Icons.favorite, size: 50, color: Colors.white)
                        ),
                        Positioned(
                          bottom: 20,
                          right: 20,
                          child: Text("你好啊,李银河", style: TextStyle(fontSize: 20, color: Colors.white)),
                        )
                    ],
               );
            }
        }
        

      提示已定位的组件只能在堆栈中使用

    相关文章

      网友评论

        本文标题:Day07 - Flutter 小部件的布局

        本文链接:https://www.haomeiwen.com/subject/cdrnnhtx.html