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
最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!