【Flutter 專題】61 圖解基本 Button 按鈕小結 (一)

  • 2019 年 10 月 4 日
  • 筆記

Button 在日常中是必不可少的,和尚嘗試過不同類型的 Button,也根據需求自定義過,今天和尚系統的學習一下最基本的 Button

Flutter 中沒有 Button Widget,但提供了很多不同類型的 Child Button Widget;和尚分析源碼整體可分為 RawMaterialButtonIconButton 兩類;

其中 RaisedButton / FlatButton / OutlineButton 繼承自 MaterialButtonMaterialButton 是對 RawMaterialButton 的封裝;而BackButton / CloseButton / PopupMenuButton 繼承自 IconButton;最終 RawMaterialButtonIconButton 都是由 ConstrainedBox 填充繪製;

IconButton 系列

IconButton 系列屬於圖標按鈕,使用相對簡單;其核心是 InkResponse 水波紋效果;

IconButton

源碼分析
const IconButton({      Key key,      this.iconSize = 24.0,   // 圖標大小      this.padding = const EdgeInsets.all(8.0),   // 圖標周圍間距      this.alignment = Alignment.center,          // 圖標位置      @required this.icon,    // 圖標資源      this.color,             // 圖標顏色      this.highlightColor,    // 點擊高亮顏色      this.splashColor,       // 水波紋顏色      this.disabledColor,     // 不可點擊時高亮顏色      @required this.onPressed,      this.tooltip            // 長按提示  })  

分析源碼,其中 icononPressed 是必須要設置的,其餘屬性根據需求而適當調整;

案例嘗試
  1. 和尚首先嘗試最基本的 IconButton;長按會由 tooltip 提醒,點擊為默認主題色;
IconButton(icon: Icon(Icons.android), tooltip: 'IconButton tootip1',      onPressed: () => Toast.show('IconButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM));  
  1. 和尚嘗試其中幾個屬性;其中 icon 顏色為 cyan,點擊高亮背景色為 deepPurple,水波紋顏色為 redAccent;注意當 icon 自身設置顏色時 color 屬性不生效;
IconButton(icon: Icon(Icons.android), tooltip: 'IconButton tootip2',      color: Colors.cyan,      highlightColor: Colors.deepPurple.withOpacity(0.4),      splashColor: Colors.redAccent,      onPressed: () => Toast.show('IconButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM));  
  1. 和尚嘗試不可點擊時,icon 顏色為 disabledColor 設置的 green;同樣若 icon 本身設置了顏色,disabledColor 不生效;注意:onPressed: nullonPressed: ()=> null 不同,前者代表無點擊事件;後者代表有點擊事件,只是點擊無操作;
IconButton(icon: Icon(Icons.android), disabledColor: Colors.green, onPressed: null);  
  1. iconWidget 採用 Icon / Image / ImageIcon 等均可;
IconButton(icon: Image.asset('images/ic_launcher.png'), iconSize: 40.0, onPressed: null);  

BackButton

BackButton 作用非常明確,一般用作返回上一個頁面;

源碼分析
const BackButton({ Key key, this.color })  

分析源碼,BackButton 繼承自 IconButton,只允許設置圖標顏色,圖標樣式 AndroidiOS 不同且不可修改;點擊時會優先判斷 maybePop 是否可以返回上一頁;

案例嘗試
BackButton();  BackButton(color: Colors.green);  

CloseButton

CloseButton 一般用作導航欄關閉按鈕與 BackButton 類似;

源碼分析
const CloseButton({ Key key }) : super(key: key);  

分析源碼,CloseButton 繼承自 IconButton,無需設置任何屬性;點擊時會優先判斷 maybePop 是否可以返回上一頁;

案例嘗試
CloseButton();  

RawMaterialButton 系列

RawMaterialButton

RawMaterialButtonMaterialButton 的基礎,核心是由 MaterialInkWell 等組成;但不可用當前 ThemeButtonTheme 來計算未指定參數的默認值;

源碼分析
const RawMaterialButton({      Key key,      @required this.onPressed,      this.onHighlightChanged,            // 高亮變化的回調      this.textStyle,                     // 文字屬性      this.fillColor,                     // 填充顏色      this.highlightColor,                // 背景高亮顏色      this.splashColor,                   // 水波紋顏色      this.elevation = 2.0,               // 陰影      this.highlightElevation = 8.0,      // 高亮時陰影      this.disabledElevation = 0.0,       // 不可點擊時陰影      this.padding = EdgeInsets.zero,     // 內容周圍邊距      this.constraints = const BoxConstraints(minWidth: 88.0, minHeight: 36.0),   // 默認按鈕尺寸      this.shape = const RoundedRectangleBorder(),    // 形狀樣式      this.animationDuration = kThemeChangeDuration,  // 動畫效果持續時長      this.clipBehavior = Clip.none,                  // 抗鋸齒剪切效果      MaterialTapTargetSize materialTapTargetSize,    // 點擊目標的最小尺寸      this.child,  })  

分析源碼可知,RawMaterialButton 沒有設置寬高的屬性,可根據 padding 或外層依賴 Container 適當調整位置和大小;默認最小尺寸為 88px * 36px

案例嘗試

和尚定義了一個基本的按鈕,並監聽其高亮改變時狀態,與我們常見的按鈕基本一致;

RawMaterialButton(      padding: EdgeInsets.all(20.0),      child: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[        Padding(child: Icon(Icons.android), padding: EdgeInsets.only(right: 10.0)),        Text('RawMaterialButton', style: TextStyle(color: Colors.brown))      ]),      textStyle: TextStyle(color: Colors.pink, fontSize: 18.0),      fillColor: Colors.greenAccent.withOpacity(0.4),      highlightColor: Colors.cyan,      splashColor: Colors.deepPurple.withOpacity(0.4),      onPressed: () => Toast.show('RawMaterialButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM),      onHighlightChanged: (state) => Toast.show('onHighlightChanged -> $state', context, duration: Toast.LENGTH_SHORT, gravity: Toast.CENTER))  

FloatingActionButton

FloatingActionButtonRawMaterialButton 的封裝,主要用於浮動在屏幕內容之上,一般是位於底部左右角或中間;一般一個頁面只有一個;

源碼分析
const FloatingActionButton({      Key key,      this.child,      this.tooltip,                           // 長按提醒      this.foregroundColor,                   // 按鈕上子元素顏色      this.backgroundColor,                   // 背景色      this.heroTag = const _DefaultHeroTag(), // Hero 動畫標籤      this.elevation = 6.0,                   // 陰影      this.highlightElevation = 12.0,         // 高亮時陰影      @required this.onPressed,      this.mini = false,                      // 尺寸大小,分為 mini 和 default      this.shape = const CircleBorder(),      // 樣式形狀      this.clipBehavior = Clip.none,          // 抗鋸齒剪切效果      this.materialTapTargetSize,             // 點擊目標的最小尺寸      this.isExtended = false,                // 是否採用 .extended 方式  })  
案例嘗試
  1. 和尚嘗試一個基本的 FloatingActionButton;長按會有 tooltip 提示;
floatingActionButton: FloatingActionButton(child: Icon(Icons.android), tooltip: 'FloatingActionButton ToolTip',      onPressed: () => Toast.show('FloatingActionButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM));  
  1. foregroundColor 為按鈕上層子元素顏色,若子元素本身設置顏色則不生效;backgroundColor 為按鈕背景色;
foregroundColor: Colors.redAccent.withOpacity(0.7),  backgroundColor: Colors.green.withOpacity(0.4),  
  1. elevation 按鈕默認陰影高度,即 z軸高度;highlightElevation 為點擊高亮時陰影高度;
elevation: 0.0,  highlightElevation: 10.0,  
  1. mini 是否展示成小尺寸模式;materialTapTargetSize 為配置目標的最小點擊尺寸,padded 為默認的 48px * 48pxAndroid 推薦尺寸;shrinkWrap 為縮小到 Material 提供的最小尺寸;
mini: true,  materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,  
  1. shape 為樣式尺寸;clipBehavior 為抗鋸齒效果;
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(14.0))),  clipBehavior: Clip.antiAlias,  
  1. heroTag 動畫標籤,默認的是 FloatingActionButtonAnimator.scaling;且 heroTag 默認是相同的,可以自定義為唯一標籤;和尚設置上一頁面與當前頁面 FloatingActionButtonheroTag 相同;
floatingActionButtonAnimator: MyAnimation(),    heroTag: "aceTag",    class MyAnimation extends FloatingActionButtonAnimator {    double _x, _y;      @override    Offset getOffset({Offset begin, Offset end, double progress}) {      _x = begin.dx + (end.dx - begin.dx) * progress;      _y = begin.dy + (end.dy - begin.dy) * progress;      return Offset(_x * 0.5, _y * 0.9);    }      @override    Animation<double> getRotationAnimation({Animation<double> parent}) {      return Tween<double>(begin: 1.0, end: 1.0).animate(parent);    }      @override    Animation<double> getScaleAnimation({Animation<double> parent}) {      return Tween<double>(begin: 1.0, end: 1.0).animate(parent);    }  }  
  1. FloatingActionButton 提供了 .extended 方式創建代表標籤樣式的,非正方形的按鈕樣式;其餘屬性無差;
floatingActionButton: FloatingActionButton.extended(      onPressed: () => Toast.show('FloatingActionButton.extended', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM),      icon: Icon(Icons.android),      label: Text('Android'));  
  1. 針對特殊的個性化,FloatingActionButton 展示的大小可能會有所不同;和尚嘗試了幾種方式; a. 通過最基本的 RawMaterialButton 實現 FloatingActionButton 樣式,外層添加 Container 約束大小;和尚比較推薦方式一,靈活性更高;
// 方式一  floatingActionButton: Container(      width: 100.0, height: 100.0,      color: Colors.greenAccent.withOpacity(0.4),      child: RawMaterialButton(          shape: CircleBorder(),          elevation: 0.0,          child: Icon(Icons.android),          onPressed: () {}))  

b. 藉助 FittedBox 將按鈕整體放大到 Container 約束範圍內;

// 方式二  floatingActionButton: Container(      width: 100.0, height: 100.0,      child: FittedBox(          child: FloatingActionButton(child: Icon(Icons.android), onPressed: () {})))  

c. SizeBoxFittedBox 約束方式不同,只是整體範圍變大,其內部按鈕按 Material 建議樣式展示;

// 方式三  floatingActionButton: SizedBox(      width: 100.0, height: 100.0,      child: FloatingActionButton(child: Icon(Icons.android), onPressed: () {}))  

d. scaleFittedBox 類似,按比例縮放;

// 方式四  floatingActionButton: Transform.scale(      scale: 1.5,      child: FloatingActionButton(child: Icon(Icons.android), onPressed: () {}))  

Button 涉及的內容較多,擴展性很強,和尚分兩節進行學習嘗試;有些理解可能還不到位,有問題請多多指導!