小菜在之前嘗試過 ToggleButtons 按鈕切換容器組,小菜瞭解到類似的 iOS 風格的 CupertinoSegmentedControl 分段控制器;在日常應用中使用頻率較高,今天小菜簡單學習一下;
CupertinoSegmentedControl
源碼分析
CupertinoSegmentedControl({
Key key,
@required this.children,
@required this.onValueChanged, // 狀態變更回調
this.groupValue, // 當前狀態
this.unselectedColor, // 未選中區域顏色
this.selectedColor, // 選中區域顏色
this.borderColor, // 邊框顏色
this.pressedColor, // 點擊時顏色
this.padding, // 內邊距
})
簡單分析源碼可得,整個 CupertinoSegmentedControl 控制器屬性很清晰,使用起來也非常簡單;
const EdgeInsetsGeometry _kHorizontalItemPadding = EdgeInsets.symmetric(horizontal: 16.0);
const double _kMinSegmentedControlHeight = 28.0;
const Duration _kFadeDuration = Duration(milliseconds: 165);
通過常量可以瞭解到控制器設置的默認邊距值,最小高度以及點擊時顏色切換時長;其中通過 ColorTween 動畫方式進行背景色切換;
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,需要設置 Widget 的最大最小寬高;通過 RRect 雙層圓角矩形繪製邊框,小菜還學習了之前未嘗試過的 fromRectAndCorners 繪製部分圓角方式;
其中多個 Widget 之間的點擊切換 GestureDetector 使用也非常值得學習;
案例嘗試
小菜先實現一個基本的分段控制器,然後逐步加入各個屬性進行了解;
1. children & onValueChanged
children 和 onValueChanged 是兩個必備屬性,分別對應子 Widget 數組和狀態變更回調的監聽;onValueChanged 不可爲空;
其中 children 爲 LinkedHashMap 類型,每個 key-value 均不可爲空;且如果 key 相同,後面的 key-value 對會覆蓋之前重複 key 的 key-value 對;key 爲 T 範型類型,並未限制具體的類型;children 長度需 >=2;
var mixMap = {
'飛機': 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 = '飛機';
_segmentedWid01() => Container(
child: CupertinoSegmentedControl(
children: mixMap,
onValueChanged: (index) {
print('index -> $index');
setState(() => _currentIndexStr = index);
}));
2. groupValue
groupValue 對應當前選中的狀態,若不設置該屬性,在控制器切換過程中只可以監聽到回調方法,而不會實際進行變更;
_segmentedWid02() => Container(
child: CupertinoSegmentedControl(
children: mixMap,
onValueChanged: (index) {
print('index -> $index');
setState(() => _currentIndexStr = index);
},
groupValue: _currentIndexStr));
3. unselectedColor
unselectedColor 對應未選中切換區域背景色,默認是 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 對應選中切換區域背景色,默認是 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 對應邊框色,默認是 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 對點擊選中時背景色,默認是 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 對應 CupertinoSegmentedControl 內邊距,注意該 padding 是整個控制器的內邊距,而非子 Widget 的內邊距,默認是居於水平方向,左右 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 設備上支持點擊和滑動切換,但小菜嘗試在 Android 端主要是點擊切換;小菜對於源碼的閱讀還很淺薄,如有錯誤,請多多指導!
來源: 阿策小和尚