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 的基本学习暂且到此,还有一些遗留问题有待研究,如有错误请多多指导!

来源: 阿策小和尚

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容