flutter系列之:flutter中可以建索引的棧布局IndexedStack

簡介

之前我們介紹了一個flutter的棧結構的layout組件叫做Stack,通過Stack我們可以將一些widget疊放在其他widget之上,從而可以實現影像的組合功能,也是日常中最常用的一種組件了。今天我們要介紹的組件是Stack的近親,叫做IndexedStack,它有什麼功能呢?一起來看看吧。

IndexedStack簡介

從名字可以看出,IndexedStack是給Stack添加了一個index的功能,事實是否如此呢?我們先來看一下IndexedStack的定義:

class IndexedStack extends Stack 

可以看到IndexedStack繼承自Stack,它實際上是Stack的子類,所以之前介紹的Stack有的功能IndexedStack全都有,並且IndexedStack是對Stack的功能進行了增強。

我們來看下它的構造函數:

  IndexedStack({
    Key? key,
    AlignmentGeometry alignment = AlignmentDirectional.topStart,
    TextDirection? textDirection,
    StackFit sizing = StackFit.loose,
    this.index = 0,
    List<Widget> children = const <Widget>[],
  }) : super(key: key, alignment: alignment, textDirection: textDirection, fit: sizing, children: children);

可以看到和Stack相比,IndexedStack多了一個index參數,但是這個參數並沒有傳入到super的構造函數中去,那麼index到底是在哪裡使用的呢?

別急,IndexedStack還重寫了下面的兩個方法,分別是createRenderObject和updateRenderObject:

  @override
  RenderIndexedStack createRenderObject(BuildContext context) {
    assert(_debugCheckHasDirectionality(context));
    return RenderIndexedStack(
      index: index,
      alignment: alignment,
      textDirection: textDirection ?? Directionality.maybeOf(context),
    );
  }

  @override
  void updateRenderObject(BuildContext context, RenderIndexedStack renderObject) {
    assert(_debugCheckHasDirectionality(context));
    renderObject
      ..index = index
      ..alignment = alignment
      ..textDirection = textDirection ?? Directionality.maybeOf(context);
  }

和Stack相比,IndexedStack在這兩個方法中使用的是RenderIndexedStack,而Stack使用的是RenderStack。

所以雖然IndexedStack繼承自Stack,但是兩者在表現上是有本質區別的。

對於Stack來說,一個widget被放在另外一個widget之上,但是多個widget可以同時展示出來。而對於IndexedStack來說,它只會展示對應index的widget。

RenderIndexedStack也是繼承自RenderStack:

class RenderIndexedStack extends RenderStack 

我們看下它的paintStack方法:

  @override
  void paintStack(PaintingContext context, Offset offset) {
    if (firstChild == null || index == null)
      return;
    final RenderBox child = _childAtIndex();
    final StackParentData childParentData = child.parentData! as StackParentData;
    context.paintChild(child, childParentData.offset + offset);
  }

可以看到在paintStack方法中,只繪製了和index對應的_childAtIndex這個組件,所以如果index不匹配的話,並不會展示出來。

IndexedStack的表現有點像我們常見的tab。

IndexedStack的使用

從上面IndexedStack的構造函數中,我們知道IndexedStack需要傳入一個index屬性和對應的children。

在本例中,我們給IndexedStack傳入一個可變的index屬性,和4個child:

IndexedStack(
          index: _counter,
          children: [
            widgetOne(),
            widgetTwo(),
            widgetThree(),
            widgetFour(),
          ],
        )

_counter是定義在StatefulWidget中的變數。可以通過調用setState方法對index進行修改,從而實現動態切換child的目的。

這裡的child widget很簡單,我們使用了不同大小的SizedBox,SizedBox中設置不同的color來方便觀察切換的效果:

  Widget widgetOne() {
    return SizedBox(
      width: 100,
      height: 100,
      child: Container(
        color: Colors.yellow,
      ),
    );
  }

最後,在Scaffold的floatingActionButton中調用_changeIndex方法實現index的改變,最終的程式碼如下:

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _changeIndex() {
    setState(() {
      _counter = (_counter+1) % 4;
      print(_counter);
    });
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: IndexedStack(
          index: _counter,
          children: [
            widgetOne(),
            widgetTwo(),
            widgetThree(),
            widgetFour(),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _changeIndex,
        tooltip: 'change index',
        child: const Icon(Icons.arrow_back),
      ), 
    );
  }

程式運行之後的效果如下:

通過點擊右下方的按鈕,我們得到了不同的widget。

總結

IndexWidget和tab有點類似,大家可以在需要的時候使用。

本文的例子://github.com/ddean2009/learn-flutter.git

更多內容請參考 www.flydean.com

最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!