????小菜在之前嘗試過 ToggleButtons 按鈕切換容器組,小菜了解到類似的 iOS 風(fēng)格的 CupertinoSegmentedControl 分段控制器;在日常應(yīng)用中使用頻率較高,今天小菜簡(jiǎn)單學(xué)習(xí)一下;
CupertinoSegmentedControl
源碼分析
CupertinoSegmentedControl({
Key key,
@required this.children,
@required this.onValueChanged, // 狀態(tài)變更回調(diào)
this.groupValue, // 當(dāng)前狀態(tài)
this.unselectedColor, // 未選中區(qū)域顏色
this.selectedColor, // 選中區(qū)域顏色
this.borderColor, // 邊框顏色
this.pressedColor, // 點(diǎn)擊時(shí)顏色
this.padding, // 內(nèi)邊距
})
????簡(jiǎn)單分析源碼可得,整個(gè) CupertinoSegmentedControl 控制器屬性很清晰,使用起來也非常簡(jiǎn)單;
const EdgeInsetsGeometry _kHorizontalItemPadding = EdgeInsets.symmetric(horizontal: 16.0);
const double _kMinSegmentedControlHeight = 28.0;
const Duration _kFadeDuration = Duration(milliseconds: 165);
????通過常量可以了解到控制器設(shè)置的默認(rèn)邊距值,最小高度以及點(diǎn)擊時(shí)顏色切換時(shí)長;其中通過 ColorTween 動(dòng)畫方式進(jìn)行背景色切換;
class _SegmentedControlContainerBoxParentData extends ContainerBoxParentData<RenderBox> {
RRect surroundingRect;
}
RRect rChildRect;
if (child == leftChild) {
rChildRect = RRect.fromRectAndCorners(childRect, topLeft: const Radius.circular(3.0), bottomLeft: const Radius.circular(3.0));
} else if (child == rightChild) {
rChildRect = RRect.fromRectAndCorners(childRect, topRight: const Radius.circular(3.0), bottomRight: const Radius.circular(3.0));
} else {
rChildRect = RRect.fromRectAndCorners(childRect);
}
????邊框的繪制繼承了 ContainerBoxParentData,需要設(shè)置 Widget 的最大最小寬高;通過 RRect 雙層圓角矩形繪制邊框,小菜還學(xué)習(xí)了之前未嘗試過的 fromRectAndCorners 繪制部分圓角方式;
????其中多個(gè) Widget 之間的點(diǎn)擊切換 GestureDetector 使用也非常值得學(xué)習(xí);
案例嘗試
????小菜先實(shí)現(xiàn)一個(gè)基本的分段控制器,然后逐步加入各個(gè)屬性進(jìn)行了解;
1. children & onValueChanged
????children 和 onValueChanged 是兩個(gè)必備屬性,分別對(duì)應(yīng)子 Widget 數(shù)組和狀態(tài)變更回調(diào)的監(jiān)聽;onValueChanged 不可為空;
????其中 children 為 LinkedHashMap 類型,每個(gè) key-value 均不可為空;且如果 key 相同,后面的 key-value 對(duì)會(huì)覆蓋之前重復(fù) key 的 key-value 對(duì);key 為 T 范型類型,并未限制具體的類型;children 長度需 >=2;
var mixMap = {
'飛機(jī)': Padding(padding: EdgeInsets.symmetric(vertical: 10.0), child: Icon(Icons.airplanemode_active)),
'公交': Padding(padding: EdgeInsets.symmetric(vertical: 10.0), child: Icon(Icons.directions_bus)),
'騎行': Padding(padding: EdgeInsets.symmetric(vertical: 10.0), child: Icon(Icons.directions_bike))
};
var _currentIndexStr = '飛機(jī)';
_segmentedWid01() => Container(
child: CupertinoSegmentedControl(
children: mixMap,
onValueChanged: (index) {
print('index -> $index');
setState(() => _currentIndexStr = index);
}));

2. groupValue
????groupValue 對(duì)應(yīng)當(dāng)前選中的狀態(tài),若不設(shè)置該屬性,在控制器切換過程中只可以監(jiān)聽到回調(diào)方法,而不會(huì)實(shí)際進(jìn)行變更;
_segmentedWid02() => Container(
child: CupertinoSegmentedControl(
children: mixMap,
onValueChanged: (index) {
print('index -> $index');
setState(() => _currentIndexStr = index);
},
groupValue: _currentIndexStr));

3. unselectedColor
????unselectedColor 對(duì)應(yīng)未選中切換區(qū)域背景色,默認(rèn)是 CupertinoTheme.primaryContrastingColor;
_segmentedWid03() => Container(
child: CupertinoSegmentedControl(
children: mixMap,
onValueChanged: (index) {
print('index -> $index');
setState(() => _currentIndexStr = index);
},
groupValue: _currentIndexStr,
unselectedColor: Colors.black.withOpacity(0.2)));

4. selectedColor
????selectedColor 對(duì)應(yīng)選中切換區(qū)域背景色,默認(rèn)是 CupertinoTheme.primaryColor;
_segmentedWid04() => Container(
child: CupertinoSegmentedControl(
children: mixMap,
onValueChanged: (index) {
print('index -> $index');
setState(() => _currentIndexStr = index);
},
groupValue: _currentIndexStr,
unselectedColor: Colors.black.withOpacity(0.2),
selectedColor: Colors.deepOrange.withOpacity(0.4)));

5. borderColor
????borderColor 對(duì)應(yīng)邊框色,默認(rèn)是 CupertinoTheme.primaryColor;
_segmentedWid05() => Container(
child: CupertinoSegmentedControl(
children: mixMap,
onValueChanged: (index) {
print('index -> $index');
setState(() => _currentIndexStr = index);
},
groupValue: _currentIndexStr,
unselectedColor: Colors.black.withOpacity(0.2),
selectedColor: Colors.deepOrange.withOpacity(0.4),
borderColor: Colors.deepPurple));

6. pressedColor
????pressedColor 對(duì)點(diǎn)擊選中時(shí)背景色,默認(rèn)是 selectedColor 顏色加 20% 透明度;
_segmentedWid06() => Container(
child: CupertinoSegmentedControl(
children: mixMap,
onValueChanged: (index) {
print('index -> $index');
setState(() => _currentIndexStr = index);
},
groupValue: _currentIndexStr,
unselectedColor: Colors.black.withOpacity(0.2),
selectedColor: Colors.deepOrange.withOpacity(0.4),
borderColor: Colors.deepPurple,
pressedColor: Colors.green.withOpacity(0.4)));

7. padding
????padding 對(duì)應(yīng) CupertinoSegmentedControl 內(nèi)邊距,注意該 padding 是整個(gè)控制器的內(nèi)邊距,而非子 Widget 的內(nèi)邊距,默認(rèn)是居于水平方向,左右 16 距離;
_segmentedWid07() => Container(
child: CupertinoSegmentedControl(
children: mixMap,
onValueChanged: (index) {
print('index -> $index');
setState(() => _currentIndexStr = index);
},
groupValue: _currentIndexStr,
unselectedColor: Colors.black.withOpacity(0.2),
selectedColor: Colors.deepOrange.withOpacity(0.4),
borderColor: Colors.deepPurple,
pressedColor: Colors.green.withOpacity(0.4),
padding: EdgeInsets.all(30.0)));

????CupertinoSegmentedControl 案例源碼
????CupertinoSegmentedControl 在 iOS 設(shè)備上支持點(diǎn)擊和滑動(dòng)切換,但小菜嘗試在 Android 端主要是點(diǎn)擊切換;小菜對(duì)于源碼的閱讀還很淺薄,如有錯(cuò)誤,請(qǐng)多多指導(dǎo)!
來源: 阿策小和尚