小工具知识网

当前位置: 首页 > 行业知识 > Flutter布局详解,必知必会,项目实战

行业知识

Flutter布局详解,必知必会,项目实战

2026-11-10 06:36:09 来源:互联网转载

Wrap(

spacing: 10.0,

direction: Axis.horizontal,

alignment: WrapAlignment.start,

children: [

_card(‘关注’),

_card(‘推荐’),

_card(‘新时代’),

_card(‘小视频’),

_card(‘党媒推荐’),

_card(‘中国新唱将’),

_card(‘历史’),

_card(‘视频’),

_card(‘游戏’),

_card(‘头条号’),

_card(‘数码’),

],

)

Widget _card(String title) {

return Card(child: Text(title),);

}

}

运行效果
小结
  • 使用Wrap可以很轻松的实现流式布局效果
  • Wrap支持设置流式布局是纵向显示或者是横向显示
  • 可以使用alignment属性来控制widgets的布局方式
    Flow

    我们一般很少会使用Flow,因为其过于复杂,需要自己实现子widget的位置转换,在很多场景下首先要考虑的是Wrap是否满足需求。Flow主要用于一些需要自定义布局策略或性能要求较高(如动画中)的场景。Flow有如下优点:

    • 性能好;Flow是一个对child尺寸以及位置调整非常高效的控件,Flow用转换矩阵(transformation matrices)在对child进行位置调整的时候进行了优化:在Flow定位过后,如果child的尺寸或者位置发生了变化,在FlowDelegate中的paintChildren()方法中调用context.paintChild 进行重绘,而context.paintChild在重绘时使用了转换矩阵(transformation matrices),并没有实际调整Widget位置。
    • 灵活;由于我们需要自己实现FlowDelegate的paintChildren()方法,所以我们需要自己计算每一个widget的位置,因此,可以自定义布局策略。 缺点:
    • 使用复杂.
    • 不能自适应子widget大小,必须通过指定父容器大小或实现TestFlowDelegate的getSize返回固定大小。
      示例代码

      我们对六个色块进行自定义流式布局:

      Flow(

      delegate: TestFlowDelegate(margin: EdgeInsets.all(10.0)),

      children: [

      new Container(width: 80.0, height:80.0, color: Colors.red,),

      new Container(width: 80.0, height:80.0, color: Colors.green,),

      new Container(width: 80.0, height:80.0, color: Colors.blue,),

      new Container(width: 80.0, height:80.0, color: Colors.yellow,),

      new Container(width: 80.0, height:80.0, color: Colors.brown,),

      new Container(width: 80.0, height:80.0, color: Colors.purple,),

      ],

      )

      实现TestFlowDelegate:

      class TestFlowDelegate extends FlowDelegate {

      EdgeInsets margin = EdgeInsets.zero;

      TestFlowDelegate({this.margin});

      @override

      void paintChildren(FlowPaintingContext context) {

      var x = margin.left;

      var y = margin.top;

      //计算每一个子widget的位置

      for (int i = 0; i

      var w = context.getChildSize(i).width + x + margin.right;

      if (w

      context.paintChild(i,

      transform: new Matrix4.translationValues(

      x, y, 0.0));

      x = w + margin.left;

      } else {

      x = margin.left;

      y += context.getChildSize(i).height + margin.top + margin.bottom;

      //绘制子widget(有优化)

      context.paintChild(i,

      transform: new Matrix4.translationValues(

      x, y, 0.0));

      x += context.getChildSize(i).width + margin.left + margin.right;

      }

      }

      }

      getSize(BoxConstraints constraints){

      //指定Flow的大小

      return Size(double.infinity,200.0);

      }

      @override

      bool shouldRepaint(FlowDelegate oldDelegate) {

      return oldDelegate != this;

      }

      }

      运行效果

      可以看到我们主要的任务就是实现paintChildren,它的主要任务是确定每个子widget位置。由于Flow不能自适应子widget的大小,我们通过在getSize返回一个固定大小来指定Flow的大小,实现起来还是比较麻烦的。

      小结
      • 参数简单,不过需要自己定义delegate
      • delegate一般是为了实现child的绘制,就是位置的摆放,不同情况需要定义不同的delegate
      • 不同的delegate一般会提供实现的几个方法:
      • getConstraintsForChild: 设置每个child的布局约束条件,会覆盖已有的方式
      • getSize:设置控件的尺寸
      • shouldRelayout:表示是否需要重新布局
      • 尽可能的用Wrap,毕竟简单

        层叠布局

        层叠布局和Web中的绝对定位、Android中的Frame布局是相似的,子widget可以根据到父容器四个角的位置来确定本身的位置。绝对定位允许子widget堆叠(按照代码中声明的顺序)。Flutter中使用Stack和Positioned来实现绝对定位,Stack允许子widget堆叠,而Positioned可以给子widget定位(根据Stack的四个角)。

        Stack

        Stack({

        this.alignment = AlignmentDirectional.topStart,

        this.textDirection,

        this.fit = StackFit.loose,

        this.overflow = Overflow.clip,

        List children = const [],

        })

        • alignment:此参数决定如何去对齐没有定位(没有使用Positioned)或部分定位的子widget。所谓部分定位,在这里特指没有在某一个轴上定位:left、right为横轴,top、bottom为纵轴,只要包含某个轴上的一个定位属性就算在该轴上有定位。
        • textDirection:和Row、Wrap的textDirection功能一样,都用于决定alignment对齐的参考系即:textDirection的值为TextDirection.ltr,则alignment的start代表左,end代表右;textDirection的值为TextDirection.rtl,则alignment的start代表右,end代表左。
        • fit:此参数用于决定没有定位的子widget如何去适应Stack的大小。StackFit.loose表示使用子widget的大小,StackFit.expand表示扩伸到Stack的大小。
        • overflow:此属性决定如何显示超出Stack显示空间的子widget,值为Overflow.clip时,超出部分会被剪裁(隐藏),值为Overflow.visible 时则不会。
          下面是我用Stack实现的一个简易的loading

          class Loading extends StatelessWidget {

          /// ProgressIndicator的padding,决定loading的大小

          final EdgeInsets padding = EdgeInsets.all(30.0);

          /// 文字顶部距菊花的底部的距离

          final double margin = 10.0;

          /// 圆角

          final double cornerRadius = 10.0;

          final Widget _child;

          final bool _isLoading;

          final double opacity;

          final Color color;

          final String text;

          Loading({

          Key key,

          @required child,

          @required isLoading,

          this.text,

          this.opacity = 0.3,

          this.color = Colors.grey,

          }) : assert(child != null),

          assert(isLoading != null),

          _child = child,

          _isLoading = isLoading,

          super(key: key);

          @override

          Widget build(BuildContext context) {

          List widgetList = List();

          widgetList.add(_child);

          if (_isLoading) {

          final loading = [

          Opacity(

          opacity: opacity,

          child: ModalBarrier(dismissible: false, color: color),

          ),

          _buildProgressIndicator()

          ];

          widgetList.addAll(loading);

          }

          return Stack(

          children: widgetList,

          );

          }

          Widget _buildProgressIndicator() {

          return Center(

          child: Container(

          padding: padding,

          child: Column(

          mainAxisSize: MainAxisSize.min,

          crossAxisAlignment: CrossAxisAlignment.center,

          children: [

          CupertinoActivityIndicator(),

          Padding(

          padding: EdgeInsets.only(top: margin),

          child: Text(text ?? ‘加载中…’)),

          ],

          ),

          decoration: BoxDecoration(

          borderRadius: BorderRadius.all(Radius.circular(cornerRadius)),

          color: Colors.white),

          ),

          );

          }

          }

          显示效果

          本控件使用Stack封装,你传入的主视图在最下面一层,背景层在中间,最上面一层为菊花和文字loading,用isLoading控制显示

          Positioned

          const Positioned({

          Key key,

          this.left,

          this.top,

          this.right,

          this.bottom,

          this.width,

          this.height,

          @required Widget child,

          })

          left、top 、right、 bottom分别代表离Stack左、上、右、底四边的距离。width和height用于指定定位元素的宽度和高度,注意,此处的width、height 和其它地方的意义稍微有点区别,此处用于配合left、top 、right、 bottom来定位widget,举个例子,在水平方向时,你只能指定left、right、width三个属性中的两个,如指定left和width后,right会自动算出(left+width),如果同时指定三个属性则会报错,垂直方向同理。

          示例代码

          //通过ConstrainedBox来确保Stack占满屏幕

          ConstrainedBox(

          constraints: BoxConstraints.expand(),

          child: Stack(

          alignment:Alignment.center , //指定未定位或部分定位widget的对齐方式

          children: [

          Container(child: Text(“Hello world”,style: TextStyle(color: Colors.white)),

          color: Colors.red,

          ),

          Positioned(

          left: 18.0,

          child: Text(“I am Jack”),

          ),

          Positioned(

          top: 18.0,

          child: Text(“Your friend”),

          )

          ],

          ),

          );

          这里我就分享一份资料,希望可以帮助到大家提升进阶。

          内容包含:Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 这几块的内容。分享给大家,非常适合近期有面试和想在技术道路上继续精进的朋友。

          如果你有需要的话,可以点击Android学习PDF+架构视频+面试文档+源码笔记获取免费领取方式

          喜欢本文的话,不妨给我点个小赞、评论区留言或者转发支持一下呗~

          自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

          深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

          因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

          由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

          如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)

          30545)]

          [外链图片转存中…(img-GikYdPvH-1710967430545)]

          [外链图片转存中…(img-qhcqUMdk-1710967430546)]

          [外链图片转存中…(img-HAS1gqNX-1710967430546)]

          由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

          如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)

          [外链图片转存中…(img-CZafSF45-1710967430547)]

上一篇:苹果连接服务器怎么连不上?

下一篇:MySQL临时表创建出错(OS errno 13 - Permission denied)