Flutter控件--Row、Column和Stack

flutter控件练习demo地址github

一 Row 和 Column

1. 简介

因为 Row 和 Column 都是继承于 Flex,所以他们两个的属性也都是 Flex 的属性

  • Row 是 Flutter 中常用的控件。一个让 children 在水平方向依次排列 。如果 Row 空间 不足的话。 自身不带滚动的。
  • Column 也是 Flutter 中常用的控件。 一个 children 在垂直方向依次排列 。如果 Column 空间 不足的话。 自身不带滚动的。

2. 属性

2.1 mainAxisAlignment (主轴对准方式)

对于 Row 来说 , 水平是主轴。垂直是 交叉轴。 对于 Column 来说, 垂直是主轴。水平是 交叉轴 把 children 放到 主轴 的哪个位置 。 如果要验证这个属性,记住把 mainAxisSize 设置成 MainAxisSize.max ,

取值说明样式图片(Rowd 的demo)
MainAxisAlignment.start(默认值)把 children 放到主轴的头部
MainAxisAlignment.center把 children 放到主轴的中间
MainAxisAlignment.end把 children 放到主轴的尾部
MainAxisAlignment.spaceAround将主轴方向空白区域均分,使得children之间空间相等,但是首尾 childre 的空白部分为一半
MainAxisAlignment.spaceBetween将主轴方向空白区域均分,使得children之间空间相等,但是首尾childre靠近收尾,没有空细逢
MainAxisAlignment.spaceEvenly将主轴方向空白区域均分,使得children之间空间相等,包括首尾childre

2.2 mainAxisSize

也就是来规定自己( Row 或者 Column )的大小。

  • MainAxisSize.min : 主轴方向,包裹住 childre 即可。相当于 android 中的 wrap_content
  • MainAxisSize.max(默认值) : 主轴方向,铺满 ( Row 或者 Column )的父 Widget 的大小。 相当于 android 中的 match_parent

2.3 crossAxisAlignment (交叉轴)跟主轴垂直的一个轴

交叉轴 顾名思义: 就是 跟 主轴 垂直的 一个轴 对于 Row 。交叉轴 是 在垂直。对于 Column,交叉轴 是水平 。下面还是 以 Row 举个例子

取值说明图片demo(Row)
CrossAxisAlignment.start把 children 放到交叉轴的头部
CrossAxisAlignment.end把 children 放到交叉轴的尾部
CrossAxisAlignment.center把 children 放到交叉轴的中间
CrossAxisAlignment.stretch让children填满交叉轴方向无(没有测试出来,控件 找不到了)
CrossAxisAlignment.baseline让children 于 baseline 对齐,如果主轴是垂直的,那么这个值是当作开始 ,设置了此 属性 textBaseline 不能为 null

2.4 textDirection

children 在 主轴 怎样排列。 正方向排列还是反方向排列

Row

  • TextDirection.ltr : 表示在水平方向(主轴)。由左到右 , 左为头 , 右为尾
  • TextDirection.rtl :表示在水平方向(主轴)。由右到左 , 右为头 , 左为尾

Column

  • TextDirection.ltr : 表示在垂直方向(主轴)。由上到下 , 上为头 , 下为尾
  • TextDirection.rtl :表示在垂直方向(主轴)。由下到上 , 下为头 ,上为尾

2.5 verticalDirection

children 在 交叉轴 怎样排列。 正方向排列还是反方向排列

Row

  • VerticalDirection.down : 表示在垂直方向(交叉轴)。由上到下 , 上为头 , 下为尾
  • VerticalDirection.up :表示在垂直方向(交叉轴)。由下到上 , 下为头 , 上为尾

Column

  • VerticalDirection.down : 表示在水平方向(交叉轴)。由左到右 , 左为头 , 右为尾
  • TextDirection.rtl :表示在水平方向(交叉轴)。由右到左 , 右为头 , 左为尾

二 Stack

Flutter-Stack的使用说明

Stack的定义

Stack和Container,Column,Row基本上一样,都是一种存放其他Widget的容器,不同点在于Row、Column、Stash中存放的是一组Widget;Container每次只能放一个WIdget;具体请查看Stack的定义:
 

Stack({
    Key key,
    this.alignment = AlignmentDirectional.topStart,//设置子Widget开始展示的位置,从顶部开始展示
    AlignmentDirectional.topCenter//从顶部中间开始展示
    AlignmentDirectional.topEnd//从顶部结束位置展示
    AlignmentDirectional.centerStart//从中间开始位置开始展示
    AlignmentDirectional.center//从正中间展示
    AlignmentDirectional.centerEnd//从中间结束位置展示
    AlignmentDirectional.bottomStart//从底部开始位置展示
    AlignmentDirectional.bottomCenter//从底部中间位置展示
    AlignmentDirectional.bottomEnd//从底部结束位置展示
    this.textDirection,//设置子widget的左右显示方位
    this.fit = StackFit.loose,//设置没有通过positioned包裹的子widget的size,loose表示,以他子widget最大的size展示
    StackFit.expand//stack的size等于他父widget的size
    this.overflow = Overflow.clip,子widget超出stack时的截取方式,参考Text的溢出截取方式
    List<Widget> children = const <Widget>[],//一组子widgets
})

Stack的使用说明

Stack里面可以存放各种Widget,不过有一个缺点就是所有的子widget都是重叠放在一起的,请看事例代码

Stack(
//        alignment: AlignmentDirectional.center,
    textDirection: TextDirection.rtl,
//        fit: StackFit.passthrough,
    children: <Widget>[
      Container(
        color: Colors.redAccent,
        width: 100.0,
        height: 100.0,
        child: Text('data'),
      ),
  Icon(Icons.settings),
//          Positioned(
//            top: 10,
//            left: 60,
//            child: Icon(Icons.settings),),
      Icon(Icons.opacity),

      Icon(Icons.ondemand_video),
    ],
  )

 为了能够使stack里面的子widget分开展示,需要借助于Positioned这个widget来辅助,可以通过Positioned来设置上下左右,宽高等属性,具体按照需要进行设置

Stack(
//        alignment: AlignmentDirectional.center,
    textDirection: TextDirection.rtl,
//        fit: StackFit.passthrough,
    children: <Widget>[
      Container(
        color: Colors.redAccent,
        width: 100.0,
        height: 100.0,
        child: Text('data'),
      ),
      Positioned(
        top: 10,
        left: 60,
        child: Icon(Icons.settings),),
      Icon(Icons.opacity),

      Icon(Icons.ondemand_video),
    ],
  )

 

取代线性布局 (和Android中的LinearLayout相似,但是我感觉怎么这么像 FrameLayout 呢?),Stack允许子 widget 堆叠, 你可以使用 Positioned 来定位他们相对于Stack的上下左右四条边的位置。Stacks是基于Web开发中的绝度定位(absolute positioning )布局模型设计的。用于将多个childs相对于其框的边缘定位,多用于以简单方式重叠children

2.1 属性

  • alignment: 默认值。AlignmentDirectional.topStart ( AlignmentDirectional(-1.0, -1.0) )。 表示从左上角开始排 children
  • textDirection: 文本方向 , children 的流动方向
  • overflow: 表示 超过的部分是否裁剪掉 Overflow.visible 不剪掉。 Overflow.clip 减掉
  • fit: 让 children 怎样填充 Stack 。
    • StackFit.passthrough 不改变子节点约束 也就是说 children 是多大就是多大
    • StackFit.expand 子节点最大可能的占用空间 ,让 children 的大小 扩大到 Stack 的大小
    • StackFit.loose:放开了子节点宽高的约束,可以让子节点从0到最大尺寸

三 demo图片

demo 代码

import 'package:flutter/material.dart';

class LayoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text("row"),
        centerTitle: true,
      ),
      body: RowDemo(),
    );
  }
}

class RowDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    final _list = <Widget>[
      RaisedButton(
        disabledColor: Colors.red,
        child: Text("儿子1"),
      ),
      Text("儿子2"),
      Text("儿子3"),
      Text("儿子4"),
      Text("儿子5"),
    ];
    return Column(
      children: <Widget>[
        SizedBox(
          height: 30,
        ),
        Container(
          color: Colors.grey,
          child: Row(
            // 主轴(main axis)
            // 把 children 放到 Column 主轴 的哪个位置
            //  end : 尾部, start :头部, center : 中间 ,spaceBetween:在 children 之间均匀地放置 空间 ,spaceAround : 每个 children
            mainAxisAlignment: MainAxisAlignment.start,

            // 此 Row 的宽度。默认是 MainAxisSize.max
            //          MainAxisSize.min 是 包裹 children 的高度 即可  。android 中 相当于 wrap_content
            //          MainAxisSize.max 是 铺满 Row 的父 Widget 的宽度  。android 中 相当于 match_parent
            //  如果设置成 MainAxisSize.min 。 那么 mainAxisAlignment 属性相当于无效。 因为是包裹 children
            mainAxisSize: MainAxisSize.max,
            // 交叉轴(cross axis)
            // 把 children 放到 Column 主轴 的哪个位置
            // end : 尾部, start :头部, center : 中间 ,
            crossAxisAlignment: CrossAxisAlignment.start,
            // children 在主轴 的排列顺序
            textDirection: TextDirection.ltr,
            // children 在 交叉轴 的排列顺序
            verticalDirection: VerticalDirection.down,
            children: _list,
          ),
        ),
        SizedBox(
          height: 30,
        ),
        SizedBox(
            width: 200,
            height: 200,
            child: Stack(
              alignment: AlignmentDirectional.topStart,
//            alignment:   AlignmentDirectional(-1.0, -1.0),
              fit: StackFit.loose,
              overflow: Overflow.visible,
              children: <Widget>[
                Container(
                  color: Colors.black,
                  height: 200,
                  width: 200,
                ),
                Container(
                  color: Colors.deepPurple,
                  height: 100,
                  width: 100,
                ),
                Container(
                  color: Colors.green,
                  height: 50,
                  width: 50,
                ),
              ],
            )),
      ],
    );
  }
}


作者:android大哥
链接:https://juejin.cn/post/6844903849019310093
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1.Column 和Row 是横向关系的对比而已。

同样的,column 中的子widget 不能超过屏幕的剩余空间,不支持上下滚动。如果需要支持滚动则要使用ListView
如果只有一个child,可以考虑用Center或者Align来定位child。
demo

 body: new Column(
        children: <Widget>[
          new Text('eliver features faster'),
          new Text('Craft beautiful UIs'),
          new Expanded(
              child: new FittedBox(
            child: const FlutterLogo(),
          ))
        ],
      ),

效果如下: text,和 图片都是居中显示。

image.png


可以通过设置crossAxisAlignment属性改变对其方式,例如设置 crossAxisAlignmentrossAxisAlignment.start可以让child从左对齐,设置mainAxisSizeMainAxisSize.min可以是column最小化适应子控件。

demo如下

body: new Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          new Text('We move under cover and we move as one'),
          new Text('Through the night, we have ondd ddddddddddd ddddddd ddddddd ddddddddde shot to live another day'),
          new Text('We cannot let a stray gunshot give us away'),
          new Text('We will fight up close, seize the moment and stay in it'),
          new Text('It’s either that or meet the business end of a bayonet'),
          new Text('The code word is ‘Rochambeau,’ dig me?'),
          new Text('Rochambeau!',
              style: DefaultTextStyle
                  .of(context)
                  .style
                  .apply(fontSizeFactor: 0.5)),
        ],
      ),

image.png

2.棘手的问题

  • 2.1当输入的垂直约束是无界限的怎么办?

当一个column嵌套在另一个column或者ListView中,并且这个column有一个或多个Expanded 或Flexible 的子控件时,会出现运行时异常,错误大概是:包含非零的flex 子控件,但是垂直约束是无界限。

刚才上面的异常主要是因为使用Flexible或Expanded意味这对于剩余空间是平均分配的,但是输入的垂直约束是无界限的,这样就意味有无限的剩余空间,显然无法平均分配。

解决这个问题的核心需要确认为什么,Column需要接受无界限的垂直约束。

  • 一个常见的原因是:column被嵌套在另一个column(内嵌的Column并没有使用Expanded或者Flexible)中。当column包裹他的非弹性子孩子(指的非ExpandedFlexible的子控件),column会传递子控件无界限约束因此子控件可以根据控制自己的大小(传递无限制约束通常意味着子控件需要自适应自己的内容)。在这种情况下,

解决方案通常是用Expanded来包裹里面的Column,并且指定它需要占外部的剩余空间,而不是让他占据所需要的任意空间。

  • 另一个原因是 在ListView或其他垂直带可滑动的控件中内嵌Column。这种场景下,确实存在无线的垂直空间(关键是整个垂直列表允许无限空间)。这时通常值得检查下内部的Colunm需要Expanded或者Flexible 子控件的原因:子类所需要实际的大小是多少?

典型的解决方案是移除内部的Expanded或者Flexible控件。

2.2.黄黑条纹警告线

当Column里面的内容超出了剩余空间,Column溢出了,并且里面的内容被裁剪了。在debug模式下,在溢出边沿出现黄黑条纹警告线意味这出了异常,并且会打印检测出现溢出的多少信息。
解决的方案通常是采用ListView代替Column,以便于在受限的垂直方向上面可以滚动。

3. Layout布局算法

这一部分主要描述Column在framework中的具体是如何渲染的。可以通过查看BoxConstraints了解具体的装载layout的数据模型。

Column进行布局主要包括6步:
step1> 为每个子节点设置一个空或零Flex因子(例如,那些没有展开的),其中包含无限制的垂直约束和输入的水平约束。如果crossAxisAlignment设置为CrossAxisAlignment.stretch而不是采用紧缩水平约束来匹配输入的最大宽度。
step2> 通过设置的flex因子,将剩余垂直空间分配给具有非零flex因子的孩子(例如 Expanded)例如,某个孩子拥有flex 因子2.0的将会是另一个flex因子为1.0的2倍垂直空间。
step3>使用与步骤1相同的水平约束来布局每个剩余的子节点,但是使用基于步骤2中分配的空间量的垂直约束来代替无限制的垂直约束。具有Flexible.fit属性为FlexFit.tight的子节点被给予严格的约束(即,强制填充分配的空间),具有Flexible.fit属性为FlexFit.look的子节点被给予松散约束(即,不强制填充分配的空间)。
step4>Column的宽度通常是子节点(通常满足输入的子节点的约束)的最大宽度。
step5> Column的高度取决于mainAxisSize属性。如果mainAxisSize设置为MainAxisSize.max,那么column的高度则是输入约束的最大高度。如果MainAxisSize设置为MainAxisSize.min,那么column的高度则是子节点(所输入的约束的目标结点)的高度之和。
step6>通过mainAxisAlignmentcrossAxisAlignment确定子节点的位置。例如,如果mainAxisAlignmentMainAxisAlignment.spaceBetween,则未分配给子节点的任何垂直空间被均匀地分割并放置在子节点之间。



作者:浩林Leon
链接:https://www.jianshu.com/p/47de9174cf0d
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flutter中的TextField是一个常用的输入控件,它允许用户在应用程序中输入文本。 TextField通常用于表单中,可以接收用户输入的文本,比如用户名、密码、电子邮件地址等。 以下是一个基本的TextField示例: ```dart TextField( decoration: InputDecoration( hintText: '请输入文本', ), ); ``` 在上面的示例中,我们使用了decoration属性来设置TextField的外观,其中hintText属性用于设置提示文本。 TextField还有许多其他的属性可以设置,比如maxLength(允许输入的最大长度)、keyboardType(键盘类型)、obscureText(是否隐藏输入内容)等等。 下面是一个更完整的TextField示例: ```dart class MyTextField extends StatefulWidget { @override _MyTextFieldState createState() => _MyTextFieldState(); } class _MyTextFieldState extends State<MyTextField> { final TextEditingController _controller = TextEditingController(); @override Widget build(BuildContext context) { return TextField( controller: _controller, maxLength: 10, keyboardType: TextInputType.text, obscureText: true, decoration: InputDecoration( hintText: '请输入文本', labelText: '文本框', prefixIcon: Icon(Icons.text_fields), suffixIcon: IconButton( icon: Icon(Icons.clear), onPressed: () { setState(() { _controller.clear(); }); }, ), border: OutlineInputBorder(), ), onChanged: (value) { print('输入的文本为:$value'); }, ); } } ``` 在上面的示例中,我们使用了TextEditingController来控制TextField的文本内容,maxLength属性限制了用户输入的最大长度为10,keyboardType属性指定了键盘类型为文本类型,obscureText属性设置为true表示隐藏输入内容。 此外,我们还设置了decoration属性来自定义TextField的外观,包括了输入提示文本、标签文本、前缀图标、后缀图标和边框样式等。 最后,我们通过onChanged回调函数来监听用户输入的文本,并打印出来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值