Flutter 136: 圖解 CupertinoSegmentedControl 分段控制器

????小菜在之前嘗試過 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

????childrenonValueChanged 是兩個(gè)必備屬性,分別對(duì)應(yīng)子 Widget 數(shù)組和狀態(tài)變更回調(diào)的監(jiān)聽;onValueChanged 不可為空;
????其中 childrenLinkedHashMap 類型,每個(gè) key-value 均不可為空;且如果 key 相同,后面的 key-value 對(duì)會(huì)覆蓋之前重復(fù) keykey-value 對(duì);keyT 范型類型,并未限制具體的類型;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 案例源碼


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

來源: 阿策小和尚

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容