Flutter Widget 之Flow

370 阅读2分钟

你是否曾经需要协调所有行为类似的一列小部件的动画。也许是为了建立一个自定义的悬浮动作按钮菜单?

如果是这样的话可以试试Flow小部件。

首先,在你的应用程序中添加一个Flow部件并给他一个子列表,然后用FlowDelegate类的实现提供委托参数

Flow(
    delegate: MyFlowDelegate(),
    children: <Widget>[
        Icon(icons.add),
        Icon(icons.subtract),
    ],
)

该类实现了paintChildren和shhouldRepaint

class MyFlowDelegate implements FlowDelegate {
    @override
    void paintChildren(FlowPaintingContext context) {}
    
    @override
    bool shouldRepaint(MyFlowDelegate oldDelegate);
}

paintChildren()告诉Flutter在哪里绘制Flow的每个子项,使用Flow绘画上下文类的paintChild方法,这个方法需要两个参数,一个是整数,代表字列表中的索引,一个是变换,代表该字列表的任何布局调整

void paintChildern(FlowPaintingContext context) {
    context.paintChild(int, Matrix4);
}

要把第一个子项放在它的默认位置,使用context.paintChild传递0表示第一个子项并使用Identity Matrix表示没有变换;

要把第二个子项放在第一个的50像素位置下方,再次使用context.paintChild,但这次传递1作为第二个子项的索引并使用一个转换矩阵

到目前为这感觉就像一个使用矩阵而不是位置小部件的堆栈。

void paintChildren(FlowPaintingContext context) {
    context.paintChild(0, Matrix4.identity());
    context.paintChild(1, Matrix4.translationValues(0, 50, 0));
}

但这并不是Flow的亮点,为了给你的小部件增添一些趣味性,给你的Flow委托添加一个动画

现在,你的paintChildren方法已经准备好步入正轨了,遍历你的子项并使用他们的索引以及动画值来创建你自己的自定义效果

class MyFlowDelegate implements FloowDelagate {
    MyFlowDelegate(this.animation) : super(repaint: animation);
    final Animatino<double> animation;
    
    @override
    void paintChildren(FlowPaintingContext context) {
        for(int i; i < context.childContext; i++) {
            final offset = i * animation.value * 50;
            context.paintChild(
                i,
                Matrix4.transitionValues(
                    -offset,
                    -offset,
                    0,
                ),
            );
        }
    }
    
    @override
    bool shouldRepaint(MyFlowDelegate oldDelegate);
}

该动画的性质由你决定。

ezgif.com-gif-maker.gif

但是现在你的应用程序已准备好进行各种创意,比如致辞后那个原始的浮动动作按钮菜单,只是别忘了在你的小部件树中提供那个动画

Flow(
    delegate: MyFlowDelegate(
        myAnimation,
    ),
    children: <Widget>[
        Icon(icons.add),
        Icon(icons.subtract),
    ]
)

最后,实现delegates shouldRepaint方法,用它来告诉Flutter,这里没有什么可看的而事实上是没有变化的。

class MyFlowDelegate implements FlowDelegate {
    MyFlowDelegare(this.animation) : super(repaint: animation);
    final Animation<double> animation;
    
    @override
    void paintChildren(FlowPaintingContext context){}
    
    @override
    bool shouldRepaint(MyFlowDelegate oldDelegate) => animation != oldDelegate.animation;
}

就这样,你的小部件可以进入状态并在屏幕上大放异彩

如果想了解有关Flow 的内容,或者关于Flutter的其他功能,请访问flutter.dev

原文翻译自视频:视频地址