Flutter入门学习记录【二】

边学边记录,如果你看到了,参考即可,不保证说的完全正确,

官网 layout

拥有多个子元素的布局widget

  1. Flow
    带动画效果的例子

通过decoration参数来处理child的显示位置

            Container(
              height: 100,
              decoration: BoxDecoration(border: Border.all(color: Colors.amberAccent)),
              child: Flow(
                delegate: MyFlowDelegate(),
                children: menuItems.map<Widget>((IconData icon) => flowMenuItem2(icon)).toList(),
              ),
            ),

简单实现

class MyFlowDelegate extends FlowDelegate{
  @override
  void paintChildren(FlowPaintingContext context) {
   for(int i=0;i<context.childCount;i++){
     var matrix4=Matrix4.translationValues(context.getChildSize(i).width*i, 0, 0);
//matrix4 来决定显示的位置,大小,旋转等属性
     context.paintChild(i,transform: matrix4);
   }
  }

  @override
  bool shouldRepaint(FlowDelegate oldDelegate) {
    return false;
  }

}
  1. Row
    类似线性布局的水平方向,
  2. Column
    类似线性布局的垂直方向,
  3. Stack
    类似FrameLayout,参数上篇有介绍
  Stack({
    Key key,
    this.alignment = AlignmentDirectional.topStart,
    this.textDirection,
    this.fit = StackFit.loose,
    this.overflow = Overflow.clip,
    List<Widget> children = const <Widget>[],
  })
  1. IndexedStack
    继承Stack,多了个index参数,而且不是所有的child都显示,只有index那个child才显示,类似android里的ViewAnimator或者ViewSwitcher
  IndexedStack({
    Key key,
    AlignmentGeometry alignment = AlignmentDirectional.topStart,
    TextDirection textDirection,
    StackFit sizing = StackFit.loose,
    this.index = 0,
    List<Widget> children = const <Widget>[],
  })
  1. Table
  Table({
    Key key,
    this.children = const <TableRow>[],
    this.columnWidths,//如果设置了这个,那下边default的就无效了
    this.defaultColumnWidth = const FlexColumnWidth(1.0),//默认的item宽是平分的
    this.textDirection,
    this.border,//边框
    this.defaultVerticalAlignment = TableCellVerticalAlignment.top,//item在垂直方向的重心
    this.textBaseline,
  }) 

测试代码

              Table(
                columnWidths: _getColumnWidth(),
                defaultVerticalAlignment: TableCellVerticalAlignment.middle,
                children: <TableRow>[
                  TableRow(
                      decoration: BoxDecoration(color: Colors.amberAccent),
                      children: [
                        Text(
                          "first line ...",
                          textAlign: TextAlign.center,
                        ),
                        FlatButton(onPressed: () {}, child: Icon(Icons.build))
                      ]),
                  TableRow(
                      decoration: BoxDecoration(color: Colors.redAccent),
                      children: [
                        Text("second line ........",
                            textAlign: TextAlign.center),
                        FlatButton(onPressed: () {}, child: Icon(Icons.build))
                      ]),
                  TableRow(
                      decoration: BoxDecoration(color: Colors.deepPurple),
                      children: [
                        Text("third line ............",
                            textAlign: TextAlign.center),
                        FlatButton(onPressed: () {}, child: Icon(Icons.build))
                      ]),
                ],
              ),

//这里简单就弄下,左边的flex是1,右边的是2,图片可以看出效果
  Map<int, TableColumnWidth> _getColumnWidth() {
    var map = HashMap<int, TableColumnWidth>();
    for (int i = 0; i < 6; i++) {
      map[i] = IntrinsicColumnWidth(flex: i % 2 + 1.0);
    }
    return map;
  }

效果图


image.png

看下几种默认的TableColumnWidth的实现类


image.png

6.1 设置比重的

//Creates a column width based on intrinsic sizing. This sizing algorithm is very expensive.
 const IntrinsicColumnWidth({ double flex })

6.2 固定值

//Creates a column width based on a fixed number of logical pixels.
  const FixedColumnWidth(this.value)

6.3 和table的最大宽的百分比,换句话说,column的宽就是下边这个因子乘以Table的宽

//Creates a column width based on a fraction of the table's constraints' maxWidth.
const FractionColumnWidth(this.value)

6.4
比重是多少,和6.1有点像

const FlexColumnWidth([this.value = 1.0])

6.5
取参数a,b两者算出来的最大值

  const MaxColumnWidth(this.a, this.b);
  /// A lower bound for the width of this column.
  final TableColumnWidth a;

  /// Another lower bound for the width of this column.
  final TableColumnWidth b;

简单总结下:
这玩意是个表格,也就是大家每行的列数数必须一样,每列的宽度也是一样的,你修改了其中一列的宽,其他列也会跟着变的
对齐方式
如果每列都是文字,而高度有不一样,用baseline对齐比较好看点

defaultVerticalAlignment: TableCellVerticalAlignment.baseline,//用这个的话下边的那个必须设置
                  textBaseline: TextBaseline.alphabetic,
  1. Wrap
  Wrap({
    Key key,
    this.direction = Axis.horizontal,
    this.alignment = WrapAlignment.start,
    this.spacing = 0.0,
    this.runAlignment = WrapAlignment.start,
    this.runSpacing = 0.0,
    this.crossAxisAlignment = WrapCrossAlignment.start,
    this.textDirection,
    this.verticalDirection = VerticalDirection.down,
    List<Widget> children = const <Widget>[],
  })

类似android里的Flow,一个挨一个的放,放不下就换行

            Wrap(children: <Widget>[FlatButton(onPressed: (){}, child: Text("测试文字长点。。。。。。。。。。。。。。。。。")),
            Text("第二个文字算了算了是了算了算了是算了算了绿色是老师老实",style: TextStyle(color: Colors.deepPurple,fontSize: 35),)
            ,Text("第三个"),Text("第四个")],),
            ],
image.png
  1. ListBody
    看起来和ListView以及Column/Row很像,啥区别?
    ListBody强制child的宽为它的容器的宽【垂直方向而言】,不可滚动
    ListView宽度为容器宽,需要确定的高度,可以滚动
    Column 宽度为wrap,不可滚动
  ListBody({
    Key key,
    this.mainAxis = Axis.vertical,
    this.reverse = false,
    List<Widget> children = const <Widget>[],
  })
  1. ListView
  ListView({
    Key key,
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
    ScrollController controller,
    bool primary,
    ScrollPhysics physics,
    bool shrinkWrap = false,
    EdgeInsetsGeometry padding,
    this.itemExtent,
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
    bool addSemanticIndexes = true,
    double cacheExtent,
    List<Widget> children = const <Widget>[],
    int semanticChildCount,
    DragStartBehavior dragStartBehavior = DragStartBehavior.start,
  })
  1. CustomMultiChildLayout
    这个需要自己处理child的大小和位置
  CustomMultiChildLayout({
    Key key,
    @required this.delegate,
    List<Widget> children = const <Widget>[],
  })

知识点:
10.1 这个需要确定自身的大小,也就是说自身的大小是确定的
10.2 child有限制,必须是LayoutId 的包裹类,也就是必须有个唯一的id来确定child
10.3 然后delegate里对每个child调用layoutChild处理child的大小,调用postionChild处理child的位置
如下是个简单的例子

class ComplexState extends State<ComplexWidget> {
  @override
  Widget build(BuildContext context) {
    return CustomMultiChildLayout(
      delegate: MyDelegate(),
      children: _createWidget(),
    );
  }

  List<Widget> _createWidget() {
    var list = List<Widget>(5);
    for (int i = 0; i < 5; i++) {
      list[i] = LayoutId(id: i, child: Text("simple ${i + 1}"));
    }
    return list;
  }
}

class MyDelegate extends MultiChildLayoutDelegate {
  @override
  void performLayout(Size size) {
    for (int i = 0; i < 5; i++) {
      if (hasChild(i)) {
        layoutChild(i,
            BoxConstraints.expand(width: 50.0 + i * 10, height: 50.0 + i * 10));
        positionChild(i, Offset(50.0 * i, 50.0 * i));
      }
    }
  }

  @override
  bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) {
    return false;
  }
}

其他
childId是任意类型的数据,唯一就行

Size layoutChild(Object childId, BoxConstraints constraints)

效果图


image.png

拥有单个子元素的布局widget

简单说下,以下效果图的父容器是Column,重心设置是居右的

  1. Container
  Container({
    Key key,
    this.alignment,//对齐方式,Alignment提供了一些默认的,还可以自定义x,y值
    this.padding,
    Color color,//color和decoration二选一存在,都是设置背景的
    Decoration decoration,
    this.foregroundDecoration,
    double width,
    double height,
    BoxConstraints constraints,//可以设置最大最小宽高
    this.margin,
    this.transform,//对容器进行平移拉伸缩放都操作
    this.child,
  })

简单例子

              Container(
                alignment: Alignment.topCenter,
                padding: EdgeInsets.all(20),
                decoration: BoxDecoration(border: Border.all()),
                width: 200,
                height: 200,
                margin: EdgeInsets.all(20),
                transform: Matrix4.skewY(0.2),
                child: Text(
                  "hahahahah",
                  style: TextStyle(
                      color: Colors.redAccent,
                      backgroundColor: Colors.deepPurple),
                ),
              ),
image.png
  1. Padding
    字面意思,child加个padding,感觉更像android里的margin
              Padding(
                padding: EdgeInsets.all(10),
                child: Text(
                  "hahahahah",
                ),
              )
  1. Align
  const Align({
    Key key,
    this.alignment = Alignment.center,
    this.widthFactor,//宽度是child宽度乘以这个factor
    this.heightFactor,//同上
    Widget child,
  })

举例

              Align(
                widthFactor: 1.5,
                heightFactor: 2,
                alignment: Alignment.centerLeft,
                child: Text(
                  "hahahahah",
                  style: TextStyle(
                      color: Colors.redAccent,
                      backgroundColor: Colors.deepPurple),
                ),
              ),
image.png
  1. Center
    就是个Align,alignment参数没了,而这个默认就是center
class Center extends Align {
  /// Creates a widget that centers its child.
  const Center({ Key key, double widthFactor, double heightFactor, Widget child })
    : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);
}
  1. FittedBox
    按自己的大小调整其子widget的大小和位置
  const FittedBox({
    Key key,
    this.fit = BoxFit.contain,
    this.alignment = Alignment.center,
    Widget child,
  }) 

demo如下,图片大小是120*120的

              Container(
                width: 260,
                height: 160,
                decoration: BoxDecoration(border: Border.all()),
                child:  FittedBox(
                  fit: BoxFit.fill,
                  child:Image.asset("images/l.png"),
                ),
              ),
BoxFit.fill

BoxFit说明

enum BoxFit {
  /// Fill the target box by distorting the source's aspect ratio.
  ///
  /// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_fill.png)
  fill,//就是拉伸child铺满parent

  /// As large as possible while still containing the source entirely within the
  /// target box.
  ///
  /// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_contain.png)
  contain,//不变形的前提下拉伸child

  /// As small as possible while still covering the entire target box.
  ///
  /// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_cover.png)
  cover,//不变形的前提,铺满整个parent,所以可能有部分不可见

  /// Make sure the full width of the source is shown, regardless of
  /// whether this means the source overflows the target box vertically.
  ///
  /// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_fitWidth.png)
  fitWidth,//保证child的宽度和parent一样

  /// Make sure the full height of the source is shown, regardless of
  /// whether this means the source overflows the target box horizontally.
  ///
  /// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_fitHeight.png)
  fitHeight,//保证child的高度和parent一样

  /// Align the source within the target box (by default, centering) and discard
  /// any portions of the source that lie outside the box.
  ///
  /// The source image is not resized.
  ///
  /// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_none.png)
  none,//不对child做任何处理,比parent小正常显示,比parent大,那么被裁剪,也就是部分不显示

  /// Align the source within the target box (by default, centering) and, if
  /// necessary, scale the source down to ensure that the source fits within the
  /// box.
  ///
  /// This is the same as `contain` if that would shrink the image, otherwise it
  /// is the same as `none`.
  ///
  /// ![](https://flutter.github.io/assets-for-api-docs/assets/painting/box_fit_scaleDown.png)
  scaleDown,//child比容器大的话,进行缩小,否则原图显示
}

下图是容器比child大的情况


image.png
  1. AspectRatio
    一个widget,试图将子widget的大小指定为某个特定的长宽比
  const AspectRatio({
    Key key,
    @required this.aspectRatio,//宽高比
    Widget child,
  })
  1. ConstrainedBox
    对其子项施加附加约束的widget
  ConstrainedBox({
    Key key,
    @required this.constraints,
    Widget child,
  })
  1. Baseline
    A widget that positions its child according to the child's baseline.
  const Baseline({
    Key key,
    @required this.baseline,
    @required this.baselineType,
    Widget child,
  })

  /// The number of logical pixels from the top of this box at which to position  the child's baseline.
  final double baseline;

demo

              Baseline(
                baseline: 50.0,
                baselineType: TextBaseline.alphabetic,
                child: Text(
                  "Hellogbefpqrey",
                  style: TextStyle(backgroundColor: Colors.deepPurple,fontSize: 35),
                ),
              ),

baseline看上去就像往下偏移了50,如下蓝线的距离


image.png
  1. FractionallySizedBox
    一个widget,它把它的子项放在可用空间的一小部分,子控件的大小按照容器大小的百分比【这里是因子factor】,比如widthFactor是0.5,那么child的宽就是SizedBox宽的一半
  const FractionallySizedBox({
    Key key,
    this.alignment = Alignment.center,
    this.widthFactor,
    this.heightFactor,
    Widget child,
  })

demo

              Container(
                constraints: BoxConstraints.expand(width: 200, height: 100),
                decoration: BoxDecoration(border: Border.all()),
                child: FractionallySizedBox(
                  widthFactor: 0.5,
                  heightFactor: 0.5,
                  child: Container(
                    decoration: BoxDecoration(border: Border.all()),
                    width: 100,
                    height: 100,
                  ),
                ),
              ),

可以看到child本来是宽高100的,结果最后成了宽100,高50的矩形了,也就是父容器宽高乘以factor 0.5


image.png
  1. IntrinsicHeight
    child的宽高是step的倍数,不是的话会增加
  const IntrinsicWidth({ Key key, this.stepWidth, this.stepHeight, Widget child })
/// If non-null, force the child's width to be a multiple of this value.
  final double stepWidth;
  /// If non-null, force the child's height to be a multiple of this value.
  ///
  /// If null or 0.0 the child's height will not be constrained.
  ///
  /// This value must not be negative.
  final double stepHeight;

demo

              IntrinsicWidth(
                stepWidth: 20,
                stepHeight: 20,
                child: Container(
                  decoration: BoxDecoration(border: Border.all()),
                  width: 85,
                  height: 85,
                ),
              )

step是20,child的width是85明显不是20的倍数,所以最终child的宽是100,高也是100

  1. IntrinsicWidth
    和10一样
  2. LimitedBox
    一个当其自身不受约束时才限制其大小的盒子
  const LimitedBox({
    Key key,
    this.maxWidth = double.infinity,
    this.maxHeight = double.infinity,
    Widget child,
  })

demo
上边说过,测试都是在Column里测试的,所以宽度是铺满屏幕的,高度不确定
所以下边的结果child最终的大小不是5050,而是8550
为啥?只有自身不受约束的时候max才生效,这里宽度是有约束的,所以max无效,而高度是无限的,所以约束生效

              LimitedBox(
                maxWidth: 50,
                maxHeight: 50,
                child: Container(
                  decoration: BoxDecoration(border: Border.all()),
                  width: 85,
                  height: 85,
                ),
              )
  1. Offstage
    一个布局widget,可以控制其子widget的显示和隐藏
    offstage 为true不可见,为false可见
  const Offstage({ Key key, this.offstage = true, Widget child })
  1. OverflowBox
    对其子项施加不同约束的widget,它可能允许子项溢出父级
  const OverflowBox({
    Key key,
    this.alignment = Alignment.center,
    this.minWidth,
    this.maxWidth,
    this.minHeight,
    this.maxHeight,
    Widget child,
  })

demo

              Container(
                height: 100,
                decoration: BoxDecoration(border: Border.all()),
                child: OverflowBox(
                  minWidth: 20,
                  minHeight: 20,
                  maxWidth: 50,
                  maxHeight: 150,
                  alignment: Alignment.topCenter,
                  child: Container(
                    decoration: BoxDecoration(border: Border.all()),
                    width: 85,
                    height: 185,
                  ),
                ),
              ),

最终的高度是150,超过了容器本身的高度100


image.png
  1. SizedBox
    一个特定大小的盒子。这个widget强制它的孩子有一个特定的宽度和高度。如果宽度或高度为NULL,则此widget将调整自身大小以匹配该维度中的孩子的大小
 const SizedBox({ Key key, this.width, this.height, Widget child })
  1. SizedOverflowBox
    一个特定大小的widget,但是会将它的原始约束传递给它的孩子,它可能会溢出
  const SizedOverflowBox({
    Key key,
    @required this.size,
    this.alignment = Alignment.center,
    Widget child,
  }) 

demo

              SizedOverflowBox(
                size: Size(100, 50),
                child: Container(
                  decoration: BoxDecoration(border: Border.all()),
                  width: 85,
                  height: 185,
                ),
              ),

红框部分100*50是原本的size大小,可以看到实际显示的是85*185的框框,因为默认的alignment是center,所以实际的child是以红框为中心的


image.png
  1. Transform
    在绘制子widget之前应用转换的widget
  const Transform({
    Key key,
    @required this.transform,
    this.origin,
    this.alignment,
    this.transformHitTests = true,
    Widget child,
  }) 

demo

              Transform(
                transform: Matrix4.translationValues(-100, 0, 0),
                child: Container(
                  decoration: BoxDecoration(border: Border.all()),
                  width: 100,
                  height: 50,
                ),
              ),

简单的左移了100


image.png
  1. CustomSingleChildLayout
    一个自定义的拥有单个子widget的布局widget
  const CustomSingleChildLayout({
    Key key,
    @required this.delegate,
    Widget child,
  })

demo

              CustomSingleChildLayout(
                delegate: SingleX(),
                child: Container(
                  decoration: BoxDecoration(border: Border.all()),
                  width: 100,
                  height: 50,
                ),
              ),

delegate

class SingleX extends SingleChildLayoutDelegate {
  @override
  bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) {
    return false;
  }

//返回容器的大小,对于CustomSingleChildLayout的父容器宽或高是无限的情况下,这里必须返回具体的宽高
  @override
  Size getSize(BoxConstraints constraints) {
    return Size(200.0, 200.0);
  }

//可以添加对child的约束
  @override
  BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
    return BoxConstraints.expand(width: 30,height: 30);
  }

//可以对child进行一些平移
  @override
  Offset getPositionForChild(Size size, Size childSize) {
    return super.getPositionForChild(size, childSize);
  }
}

下边的效果图,上边delegate就返回了个size 50*50


image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269