Flutter 62: 圖解基本 Button 按鈕小結 (二)

??????小菜繼續嘗試 Flutter 的基本按鈕;今天小菜學習 MaterialButton 系列相關 Button;該系列以 MaterialButton 為父類,衍生出 RaisedButton 凸起按鈕,FlatButton 扁平按鈕和 OutlineButton 邊框按鈕;可根據不同場景靈活運用;

MaterialButton

源碼分析
const MaterialButton({
    Key key,
    @required this.onPressed,
    this.onHighlightChanged,        // 高亮變化的回調
    this.textTheme,                 // 文字主題
    this.textColor,                 // 文字顏色
    this.disabledTextColor,         // 不可點擊時文字顏色
    this.color,                     // 背景色
    this.disabledColor,             // 不可點擊時背景色
    this.highlightColor,            // 點擊高亮時背景色
    this.splashColor,               // 水波紋顏色
    this.colorBrightness,
    this.elevation,                 // 陰影高度
    this.highlightElevation,        // 高亮時陰影高度
    this.disabledElevation,         // 不可點擊時陰影高度
    this.padding,                   // 內容周圍邊距
    this.shape,                     // 按鈕樣式
    this.clipBehavior = Clip.none,  // 抗鋸齒剪切效果
    this.materialTapTargetSize,     // 點擊目標的最小尺寸
    this.animationDuration,         // 動畫效果持續時長
    this.minWidth,                  // 最小寬度
    this.height,                    // 按鈕高度
    this.child,
 })

??????分析源碼可知,MaterialButton 作為其他 Button 父類,各屬性比較清晰明了,有 hight 屬性可設置 Button 高度,其子類 Button 只可通過 padding 或其他方式調整高度;

案例嘗試

??????小菜測試發現 hight 可以設置 MaterialButton 高度,但 shape 按鈕形狀卻不適用;其父類 RawMaterialButton 卻正常;小菜嘗試網上大神的處理方式是外層依賴 Material 并需要 clip 裁切成 shape 樣式;有待進一步學習;

Material(
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30.0)),
    clipBehavior: Clip.antiAlias,
    child: MaterialButton(
        color: Colors.teal.withOpacity(0.4),
        height: 60.0,
        child: Text('MaterialButton'),
        onPressed: () {}))

RaisedButton / FlatButton

源碼分析
const RaisedButton({
    Key key,
    @required VoidCallback onPressed,
    ValueChanged<bool> onHighlightChanged,
    ButtonTextTheme textTheme,          // 按鈕文字主題
    Color textColor,                    // 子元素顏色
    Color disabledTextColor,            // 不可點擊時子元素顏色
    Color color,                        // 按鈕背景色
    Color disabledColor,                // 不可點擊時按鈕背景色
    Color highlightColor,               // 點擊高亮時按鈕背景色
    Color splashColor,                  // 水波紋顏色
    Brightness colorBrightness,         // 顏色對比度
    double elevation,                   // 陰影高度
    double highlightElevation,          // 高亮時陰影高度
    double disabledElevation,           // 不可點擊時陰影高度
    EdgeInsetsGeometry padding,         // 子元素周圍邊距
    ShapeBorder shape,                  // 按鈕樣式
    Clip clipBehavior = Clip.none,      // 抗鋸齒剪切效果
    MaterialTapTargetSize materialTapTargetSize,
    Duration animationDuration,         // 動畫時長
    Widget child,
})

const FlatButton({
    Key key,
    @required VoidCallback onPressed,
    ValueChanged<bool> onHighlightChanged,
    ButtonTextTheme textTheme,          // 按鈕文字主題
    Color textColor,                    // 子元素顏色
    Color disabledTextColor,            // 不可點擊時子元素顏色
    Color color,                        // 按鈕背景色
    Color disabledColor,                // 不可點擊時按鈕背景色
    Color highlightColor,               // 點擊高亮時按鈕背景色
    Color splashColor,                  // 水波紋顏色
    Brightness colorBrightness,         // 顏色對比度
    EdgeInsetsGeometry padding,         // 子元素周圍邊距
    ShapeBorder shape,                  // 按鈕樣式
    Clip clipBehavior = Clip.none,      // 抗鋸齒剪切效果
    MaterialTapTargetSize materialTapTargetSize,
    @required Widget child,
  })

??????分析源碼可知,RaisedButtonFlatButton 基本完全相同,只是 RaisedButton 多了一些陰影高度的特有屬性,小菜準備同時對兩類 Button 進行嘗試,比較兩者的不同;

案例嘗試
  1. 小菜首先嘗試最基本的 RaisedButton / FlatButton 可點擊和不可點擊樣式;
// 可點擊
RaisedButton(child: Text('RaisedButton'), onPressed: () => Toast.show('RaisedButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))
FlatButton(child: Text('FlatButton'), onPressed: () => Toast.show('FlatButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))

// 不可點擊
RaisedButton(child: Text('RaisedButton'), onPressed: null)
FlatButton(child: Text('FlatButton'), onPressed: null)
  1. ButtonTextTheme 為默認子元素主題,可以設置基本的三種主題樣式:nomal 對應 [ThemeData.brightness]primary 對應 [ThemeData.primaryColor]accent 對應 [ThemeData.accentColor];展示效果比較明顯;
RaisedButton(child: Text('R.nomal'), textTheme: ButtonTextTheme.normal, onPressed: () => Toast.show('RaisedButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))
RaisedButton(child: Text('R.primary'), textTheme: ButtonTextTheme.primary, onPressed: () => Toast.show('RaisedButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))
RaisedButton(child: Text('R.accent'), textTheme: ButtonTextTheme.accent, onPressed: () => Toast.show('RaisedButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))

FlatButton(child: Text('F.nomal'), textTheme: ButtonTextTheme.normal, onPressed: () => Toast.show('FlatButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))
FlatButton(child: Text('F.primary'), textTheme: ButtonTextTheme.primary, onPressed: () => Toast.show('FlatButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))
FlatButton(child: Text('F.accent'), textTheme: ButtonTextTheme.accent, onPressed: () => Toast.show('FlatButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))
    
OutlineButton(child: Text('O.nomal'), textTheme: ButtonTextTheme.normal, onPressed: () => Toast.show('OutlineButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))
OutlineButton(child: Text('O.primary'), textTheme: ButtonTextTheme.primary, onPressed: () => Toast.show('OutlineButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))
OutlineButton(child: Text('O.accent'), textTheme: ButtonTextTheme.accent, onPressed: () => Toast.show('OutlineButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))
  1. textColor 為子 Widget 中元素顏色,不僅為文字顏色;disabledTextColor 為不可點擊時子 Widget 元素顏色;splashColor 為點擊時水波紋顏色;
// 可點擊
RaisedButton(child: Row(mainAxisSize: MainAxisSize.min,
        children: <Widget>[ Padding(child: Icon(Icons.ac_unit), padding: EdgeInsets.only(right: 10.0)), Text('RaisedButton') ]),
    textColor: Colors.deepPurple, onPressed: () => {})
FlatButton(child: Row(mainAxisSize: MainAxisSize.min,
        children: <Widget>[ Padding(child: Icon(Icons.ac_unit), padding: EdgeInsets.only(right: 10.0)), Text('FlatButton') ]),
    textColor: Colors.deepPurple, onPressed: () => {})
    
// 不可點擊
RaisedButton(child: Row(mainAxisSize: MainAxisSize.min,
        children: <Widget>[ Padding(child: Icon(Icons.ac_unit), padding: EdgeInsets.only(right: 10.0)), Text('RaisedButton') ]),
    textColor: Colors.deepPurple, onPressed: null)
FlatButton(child: Row(mainAxisSize: MainAxisSize.min,
        children: <Widget>[ Padding(child: Icon(Icons.ac_unit), padding: EdgeInsets.only(right: 10.0)), Text('FlatButton') ]),
    textColor: Colors.deepPurple, onPressed: null)
  1. colorButton 背景色;highlightColor 為點擊時高亮背景色;disabledColor 為不可點擊時背景色;
// 可點擊
RaisedButton(child: Text('RaisedButton'), onPressed: () => {},
    color: Colors.green.withOpacity(0.4), highlightColor: Colors.purple.withOpacity(0.4),
    splashColor: Colors.yellow.withOpacity(0.7))
FlatButton(child: Text('FlatButton'), onPressed: () => {},
    color: Colors.green.withOpacity(0.4), highlightColor: Colors.purple.withOpacity(0.4),
    splashColor: Colors.yellow.withOpacity(0.7))
    
// 不可點擊
RaisedButton(child: Text('RaisedButton'), onPressed: null, disabledColor: Colors.red.withOpacity(0.4))
FlatButton(child: Text('FlatButton'), onPressed: null, disabledColor: Colors.red.withOpacity(0.4),)
  1. shapeButton 形狀;因按鈕沒有 Materialhight 屬性,需要采用 padding 或外層依賴其他 Widget 調整按鈕大小;
RaisedButton(child: Text('RaisedButton'), onPressed: () => {}
    padding: EdgeInsets.all(16.0),
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(30.0))))
FlatButton(child: Text('FlatButton'), onPressed: () => {}
    padding: EdgeInsets.all(16.0),
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(30.0))))
  1. colorBrightness 代表顏色對比度,一般分為 light / dark 兩種;一般時深色的背景需要淺色的文字對比,淺色的背景需要深色的文字對比;
// 可點擊
RaisedButton(child: Text('R.light'), colorBrightness: Brightness.light, onPressed: () => {})
RaisedButton(child: Text('R.dark'), colorBrightness: Brightness.dark, onPressed: () => {})
FlatButton(child: Text('F.light'), colorBrightness: Brightness.light, onPressed: () => {})
FlatButton(child: Text('F.dark'), colorBrightness: Brightness.dark, onPressed: () => {})

// 不可點擊
RaisedButton(child: Text('R.light'), colorBrightness: Brightness.light, onPressed: null)
RaisedButton(child: Text('R.dark'), colorBrightness: Brightness.dark, onPressed: null)
FlatButton(child: Text('F.light'), colorBrightness: Brightness.light, onPressed: null)
FlatButton(child: Text('F.dark'), colorBrightness: Brightness.dark, onPressed: null)
  1. RaisedButton / FlatButton 均提供了 .icon 帶圖標的簡單方式,icon / label 兩個屬性是必須屬性;注意,.icon 方式中 RaisedButton 沒有 padding 屬性;
RaisedButton.icon(icon: Icon(Icons.ac_unit), label: Text('RaisedButton'),
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(30.0))), onPressed: () => {})
FlatButton.icon(icon: Icon(Icons.ac_unit), label: Text('FlatButton'),
    padding: EdgeInsets.all(16.0),
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(30.0))), onPressed: () => {})
  1. elevationRaisedButton 所特有的陰影高度;highlightElevation 為高亮時陰影高度;disabledColor 為不可點擊時陰影高度;
RaisedButton(child: Text('陰影'), elevation: 20.0, onPressed: () => {})
RaisedButton(child: Text('陰影'), elevation: 0.0, highlightElevation: 20.0, onPressed: () => {})
RaisedButton(child: Text('陰影'), disabledElevation: 20.0, onPressed: null)

OutlineButton

源碼分析
const OutlineButton({
    Key key,
    @required VoidCallback onPressed,
    ButtonTextTheme textTheme,          // 按鈕文字主題
    Color textColor,                    // 文字顏色
    Color disabledTextColor,            // 不可點擊時文字顏色
    Color color,                        // 按鈕背景色
    Color highlightColor,               // 高亮時顏色
    Color splashColor,                  // 水波紋顏色
    double highlightElevation,          // 高亮時陰影高度
    this.borderSide,                    // 邊框樣式
    this.disabledBorderColor,           // 不可點擊時邊框顏色
    this.highlightedBorderColor,        // 高亮時邊框顏色
    EdgeInsetsGeometry padding,         // 內容周圍邊距
    ShapeBorder shape,                  // 按鈕樣式
    Clip clipBehavior = Clip.none,      // 抗鋸齒剪切效果
    Widget child,
})

??????分析源碼可知,OutlineButton 與其他兩種按鈕略有不同,強調邊框的樣式屬性且無長按的 tooltip 屬性;

案例嘗試
  1. 小菜首先嘗試一個最基本的 OutlineButton;長按無提醒;
OutlineButton(child: Text('OutlineButton'), onPressed: () => Toast.show('OutlineButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))
  1. 小菜嘗試與其他按鈕相同的幾類按鈕屬性,使用方式相同;
OutlineButton(
    child: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
      Padding(child: Icon(Icons.ac_unit), padding: EdgeInsets.only(right: 10.0)),
      Text('OutlineButton')
    ]),
    textColor: Colors.pink,
    disabledTextColor: Colors.green,
    padding: EdgeInsets.all(20.0),
    color: Colors.blueAccent.withOpacity(0.2),
    highlightColor: Colors.amber,
    borderSide: BorderSide(width: 4.0),
    highlightedBorderColor: Colors.brown,
    disabledBorderColor: Colors.greenAccent,
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(50.0))),
    clipBehavior: Clip.none,
    onPressed: () => Toast.show('OutlineButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))
  1. 以下為 OutlineButton 特有屬性:borderSide 代表邊框樣式;disabledBorderColor 代表不可點擊時邊框顏色;highlightedBorderColor 代表高亮時邊框顏色;其中 borderSide 可以設置邊框顏色寬度及樣式(solid / none);
OutlineButton(child: Text('OutlineButton'),
    borderSide: BorderSide(width: 4.0, color: Colors.deepPurple, style: BorderStyle.solid),
    highlightedBorderColor: Colors.teal,
    onPressed: () => Toast.show('OutlineButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))

OutlineButton(child: Text('OutlineButton'),
    borderSide: BorderSide(width: 4.0, color: Colors.deepPurple, style: BorderStyle.solid),
    disabledBorderColor: Colors.redAccent,
    onPressed: null)
  1. OutlineButton 還提供了 .icon 帶圖標的簡單方式,icon / label 兩個屬性是必須屬性;
OutlineButton.icon(
    icon: Icon(Icons.ac_unit),
    label: Text('OutlineButton'),
    textColor: Colors.pink,
    disabledTextColor: Colors.green,
    padding: EdgeInsets.all(20.0),
    color: Colors.blueAccent.withOpacity(0.2),
    highlightColor: Colors.amber,
    borderSide: BorderSide(width: 4.0),
    highlightedBorderColor: Colors.brown,
    disabledBorderColor: Colors.greenAccent,
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(50.0))),
    clipBehavior: Clip.none,
    onPressed: () => Toast.show('OutlineButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))

擴展

1. textColor 的作用?

??????小菜原來以為按鈕的子元素是 Widget,可自由設置各類效果,單獨的 textColor 是否會略顯多余;可實際并非如此,子元素設置顏色等之后 textColor 不生效;但 textColor 與主題相關;小菜以 OutlineButton 為例,一目了然;

// Text 設置顏色
OutlineButton(
    child: Text('OutlineButton', style: TextStyle(color: Colors.deepPurple)),
    onPressed: () => Toast.show('OutlineButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))
// textColor 設置顏色 
OutlineButton(
    child: Text('OutlineButton'), textColor: Colors.deepPurple,
    onPressed: () => Toast.show('OutlineButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))

2. 陰影如何改顏色?

??????使用 RaisedButton 時會自帶陰影效果,陰影的高度和高亮時的陰影高度均可自由設置;但是陰影的顏色應該如何處理呢,官方暫未提供陰影效果屬性;小菜嘗試了網上大神的方式,RaisedButton 外層依賴帶模糊陰影效果的 Container;小菜借鑒并稍微調整一下,解決方案并非最佳,僅作嘗試;

??????初始時定義一個默認的高度 height 作為陰影高度,監聽按鈕的 onHighlightChanged 方法更改 height 高度作為高亮時陰影高度;

建議:
a. 使用高亮顏色時 highlightElevation 建議設置為 0.0
b. 若按鈕有樣式設置,依賴的 Container 也要設置相同的 shape 樣式;

var height = 5.0;
Container(
    decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(30.0),
        boxShadow: <BoxShadow>[
          BoxShadow(
              color: Colors.red.withOpacity(0.2),
              blurRadius: hight,
              offset: Offset(0, hight))
        ]),
    child: RaisedButton(
        child: Text('RaisedButton'),
        padding: EdgeInsets.symmetric(vertical: 15.0),
        highlightElevation: 0.0,
        onHighlightChanged: (state) {
          setState(() {
            hight = (state) ? 20.0 : 5.0;
          });
        },
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30.0)),
        onPressed: () => {}))

??????小菜對 Button 的基本學習暫且到此,還有一些遺留問題有待研究,如有錯誤請多多指導!

來源: 阿策小和尚

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容