Flutter: Flow 九宫格

Flow需要自己实现子widget的位置转换,在很多场景下首先要考虑的是Wrap是否满足需求。Flow主要用于一些需要自定义布局策略或性能要求较高(如动画中)的场景。

Flow有如下优点:

  1. 性能好;Flow是一个对child尺寸以及位置调整非常高效的控件,Flow用转换矩阵(transformation matrices)在对child进行位置调整的时候进行了优化:在Flow定位过后,如果child的尺寸或者位置发生了变化,在FlowDelegate中的paintChildren()方法中调用context.paintChild 进行重绘,而context.paintChild在重绘时使用了转换矩阵(transformation matrices),并没有实际调整Widget位置。
  2. 灵活;由于我们需要自己实现FlowDelegate的paintChildren()方法,所以我们需要自己计算每一个widget的位置,因此,可以自定义布局策略。

缺点:

  1. 使用复杂.
  2. 不能自适应子widget大小,必须通过指定父容器大小或实现TestFlowDelegate的getSize返回固定大小。

Flow

 Flow({
    Key key,
    @required this.delegate,/// delegate 用于布局childWidget
    List<Widget> children = const <Widget>[],/// childWidget集合
  })

FlowDelegate

用于回调Flow大小,以及 计算childWidget 的frame。

///设置Flow的尺寸;
Size getSize(BoxConstraints constraints) => constraints.biggest;

/// 是否需要重新布局。
bool shouldRelayout(covariant FlowDelegate oldDelegate) => false;
///设置每个child的布局约束条件,会覆盖已有的;
BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) => constraints;

/// child的绘制控制代码,可以调整尺寸位置;
void paintChildren(FlowPaintingContext context);
/// 是否需要重绘;
bool shouldRepaint(covariant FlowDelegate oldDelegate);

FlowPaintingContext

包装了一些布局child的必要数据 以及 child布局方法

abstract class FlowPaintingContext {
  /// 获取childWidget可以绘制的最大的范围,这个值取决于Flow的大小
  Size get size;

  /// 获取childWidget的个数
  int get childCount;

  /// 获取第i个childWidget的大小
  Size getChildSize(int i);

  /// 用矩阵Matrix4布局childWidget
  void paintChild(int i, { Matrix4 transform, double opacity = 1.0 });
}

Matrix4

scale:缩放比例
transform: 移动
rotationZ:绕Z轴旋转
rotationX:绕X轴旋转
rotationY:绕Y轴旋转
columns:设置一个新的矩阵
compose:复合平移、旋转、缩放,形成新的状态
copy:复制一个4*4的张量(矩阵)

demo

import 'package:flutter/material.dart';
import 'package:vector_math/vector_math_64.dart' as Vector;

void main() {
  runApp(MaterialApp(
    home: MyApph(),
  ));
}

class MyApph extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("搜索记录"),
        ),
        body: Flow(
          delegate: TestFlowDelegate(margin: EdgeInsets.all(10.0)),
          children: <Widget>[
            new Container(width: 80.0, height: 80.0, color: Colors.red),
            new Container(width: 80.0, height: 80.0, color: Colors.yellow),
            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.lightBlue),
            new Container(width: 80.0, height: 80.0, color: Colors.black),
            new Container(width: 80.0, height: 80.0, color: Colors.blueGrey),
            new Container(width: 80.0, height: 80.0, color: Colors.brown),
            new Container(width: 80.0, height: 80.0, color: Colors.black12),

          ],
        ));
  }
}

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 < context.childCount; i++) {
      var w = context.getChildSize(i).width + x + margin.right;
      if (w < context.size.width) {
        context.paintChild(
          i,
          transform: new Matrix4.compose(Vector.Vector3(x,y,0.0), Vector.Quaternion(0.0,0.0,0.3,0.1), Vector.Vector3(1.0,1.0,1.0))
        );
        x = w + margin.left;
      } else {
        x = margin.left;
        y += context.getChildSize(i).height + margin.top + margin.bottom;
        //绘制子widget(有优化)
        context.paintChild(i,
            transform: Matrix4.translationValues(x, y, 0.0) //位移
            );
        x += context.getChildSize(i).width + margin.left + margin.right;
      }
    }
  }
  
  getSize(BoxConstraints constraints) {
    //指定Flow的大小
    return Size(double.infinity, double.infinity);
  }

  @override
  bool shouldRepaint(FlowDelegate oldDelegate) {
    return oldDelegate != this;
  }
}

Flow
flutter布局-5-Matrix4矩阵变换
Flutter 布局(九)- Flow、Table、Wrap详解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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