日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

flutter中使用流式布局示例詳解_Android

作者:程序那些事 ? 更新時間: 2022-11-14 編程語言

簡介

我們在開發(fā)web應用的時候,有時候為了適應瀏覽器大小的調整,需要動態(tài)對頁面的組件進行位置的調整。這時候就會用到flow layout,也就是流式布局。

同樣的,在flutter中也有流式布局,這個流式布局的名字叫做Flow。事實上,在flutter中,Flow通常是和FlowDelegate一起使用的,FlowDelegate用來設置Flow子組件的大小和位置,通過使用FlowDelegate.paintChildre可以更加高效的進行子widget的重繪操作。今天我們來詳細講解flutter中flow的使用。

Flow和FlowDelegate

先來看下Flow的定義:

class Flow extends MultiChildRenderObjectWidget

Flow繼承自MultiChildRenderObjectWidget,說它里面可以包含多個子widget。

再來看下它的構造函數:

  Flow({
    Key? key,
    required this.delegate,
    List<Widget> children = const <Widget>[],
    this.clipBehavior = Clip.hardEdge,
  }) : assert(delegate != null),
       assert(clipBehavior != null),
       super(key: key, children: RepaintBoundary.wrapAll(children));

可以看到Flow中主要有三個屬性,分別是delegate,children和clipBehavior。

children很好理解了,它就是Flow中的子元素。

clipBehavior是一個Clip類型的變量,表示的是如何對widget進行裁剪。這里的默認值是none。

最后一個非常重要的屬性就是FlowDelegate,FlowDelegate主要用來控制Flow中子widget的位置變換。所以,當我們在Flow中定義好子widget之后,剩下的就是定義FlowDelegate來控制如何展示這些子widget。

FlowDelegate是一個抽象類,所以我們在使用的時候,需要繼承它。

FlowDelegate有幾個非常重要的方法:

 Size getSize(BoxConstraints constraints) => constraints.biggest;

這個方法用來定義Flow的size,對于Flow來說,它的size是和子widget的size是獨立的,Flow的大小通過getSize方法來定義。

接下來是getConstraintsForChild方法:

  BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) => constraints;

getConstraintsForChild用來控制子widget的Constraints。

paintChildren用來控制如何繪制子widget,也是我們必須要實現的方法:

  void paintChildren(FlowPaintingContext context);

FlowDelegate還有兩個方法,分別用來判斷是否需要Relayout和Repaint,這兩個方法的參數都是FlowDelegate:

bool shouldRelayout(covariant FlowDelegate oldDelegate) => false;
bool shouldRepaint(covariant FlowDelegate oldDelegate);

Flow的應用

有了上面的介紹,我們基本上已經了解如何構建Flow了,接下來我們通過一個具體的例子來加深對Flow的理解。

在本例中,我們主要是使用Flow來排列幾個圖標。

首先我們定義一個圖標的數組:

  final List<IconData> buttonItems = <IconData>[
    Icons.home,
    Icons.ac_unit,
    Icons.adb,
    Icons.airplanemode_active,
    Icons.account_box_rounded,
  ];

然后通過每個圖標對應的IconData來構建一個IconButton的widget:

  Widget flowButtonItem(IconData icon) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 10.0),
      child: IconButton(
        icon: Icon(icon,
          size: 50,
            color: Colors.blue
        ),
          onPressed: () {
            buttonAnimation.status == AnimationStatus.completed
                ? buttonAnimation.reverse()
                : buttonAnimation.forward();
          },
      )
    );
  }

這里我們使用的是IconButton,為了在不同IconButton之間留一些空間,我們將IconButton封裝在Padding中。

在onPressed方法中,我們希望能夠處理一些動畫效果。這里的buttonAnimation是一個AnimationController對象:

AnimationController  buttonAnimation = AnimationController(
      duration: const Duration(milliseconds: 250),
      vsync: this,
    );

有了flowButtonItem之后,我們就可以構建Flow了:

  Widget build(BuildContext context) {
    return Flow(
      delegate: FlowButtonDelegate(buttonAnimation: buttonAnimation),
      children:
      buttonItems.map<Widget>((IconData icon) => flowButtonItem(icon)).toList(),
    );
  }

Flow的child就是我們剛剛創(chuàng)建的flowButtonItem,FlowButtonDelegate是我們需要新建的類,因為之前在構建flowButtonItem的時候,我們希望進行一些動畫的繪制,而FlowDelegate又是真正用來控制子Widget繪制的類,所以我們需要將buttonAnimation作為參數傳遞給FlowButtonDelegate。

下面是FlowButtonDelegate的定義:

class FlowButtonDelegate extends FlowDelegate {
  FlowButtonDelegate({required this.buttonAnimation})
      : super(repaint: buttonAnimation);
  final Animation<double> buttonAnimation;
  @override
  bool shouldRepaint(FlowButtonDelegate oldDelegate) {
    return buttonAnimation != oldDelegate.buttonAnimation;
  }
  @override
  void paintChildren(FlowPaintingContext context) {
    double dy = 0.0;
    for (int i = 0; i < context.childCount; ++i) {
      dy = context.getChildSize(i)!.height * i;
      context.paintChild(
        i,
        transform: Matrix4.translationValues(
          0,
          dy * buttonAnimation.value,
          0,
        ),
      );
    }
  }

FlowButtonDelegate繼承自FlowDelegate,并且傳入了buttonAnimation對象。

這里我們根據buttonAnimation是否發(fā)生變化來決定是否進行Repaint。

如果需要進行Repaint,那么就要調用paintChildren的方法。

在paintChildren中,我們根據child自身的height和buttonAnimation的值來進行動畫的繪制。

那么buttonAnimation的值是如何變化的呢?這就要回顧之前我們創(chuàng)建flowButtonItems的onPress方法了。

在onPress方法中,我們調用了buttonAnimation.reverse或者buttonAnimation.forward這兩個方法來修改buttonAnimation的值。

運行之后的效果如下:

初始狀態(tài)下,所有的組件都是在一起的。

當我們點擊上面的圖標的時候,我們可以得到下面的界面:

圖標在動畫中展開了。

總結

Flow是一種比較復雜的layout組件,如果和動畫進行結合使用,可以得到非常完美的效果。

本文的例子:github.com/ddean2009/l…

原文鏈接:https://juejin.cn/post/7146098348250890253

欄目分類
最近更新