Flutter 中的按钮

本文转自

简书 - 阿策神奇 - Flutter 61: 图解基本 Button 按钮小结 (一)

简书 - 阿策神奇 - Flutter 62: 图解基本 Button 按钮小结 (二)

本站个人笔记本使用,还请大家移步关注原作者。

此处只作备份,以防止作者删除。

Button 在日常中是必不可少的,小菜尝试过不同类型的 Button,也根据需求自定义过,今天小菜系统的学习一下最基本的 Button

Flutter 中没有 Button Widget,但提供了很多不同类型的 Child Button Widget;小菜分析源码整体可分为 RawMaterialButtonIconButton 两类;

其中 RaisedButton / FlatButton / OutlineButton 继承自 MaterialButtonMaterialButton 是对 RawMaterialButton 的封装;而BackButton / CloseButton / PopupMenuButton 继承自 IconButton;最终 RawMaterialButtonIconButton 都是由 ConstrainedBox 填充绘制;

lixyz 按:按照原作者的说法,BackButton 等是 IconButton 的子类,但是通过查看源码发现 BackButton 是 BackButton extends StatelessWidget,究竟是怎么 回事儿呢?

原因是这样的,看 BackButton 的源码:

  @override
  Widget build(BuildContext context) {
    assert(debugCheckHasMaterialLocalizations(context));
    return IconButton(
      icon: const BackButtonIcon(),
      color: color,
      tooltip: MaterialLocalizations.of(context).backButtonTooltip,
      onPressed: () {
        Navigator.maybePop(context);
      },
    );
  }

可以看到,build 方法 返回的还是一个 IconButton,与其说是 IconButton 的子类,不如说是装饰者模式的一个应用。

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));
    
  2. 小菜尝试其中几个属性;其中 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));
    
  3. 小菜尝试不可点击时,icon 颜色为 disabledColor 设置的 green;同样若 icon 本身设置了颜色,disabledColor 不生效;注意:onPressed: nullonPressed: ()=> null 不同,前者代表无点击事件;后者代表有点击事件,只是点击无操作;

    IconButton(icon: Icon(Icons.android), disabledColor: Colors.green, onPressed: null);
    
  4. 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),
    

  2. elevation 按钮默认阴影高度,即 z 轴高度;highlightElevation 为点击高亮时阴影高度;

    elevation: 0.0,
    highlightElevation: 10.0,
    

  3. mini 是否展示成小尺寸模式;materialTapTargetSize 为配置目标的最小点击尺寸,padded 为默认的 48px \* 48pxAndroid 推荐尺寸;shrinkWrap 为缩小到 Material 提供的最小尺寸;

    mini: true,
    materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
    

  4. shape 为样式尺寸;clipBehavior 为抗锯齿效果;

    shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(14.0))),
    clipBehavior: Clip.antiAlias,
    

  5. 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);
     }
    }
    

  6. 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'));
    

  7. 针对特殊的个性化,FloatingActionButton 展示的大小可能会有所不同;小菜尝试了几种方式;

    1. 通过最基本的 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: () {}))
      

    2. 借助 FittedBox 将按钮整体放大到 Container 约束范围内;

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

    3. SizeBoxFittedBox 约束方式不同,只是整体范围变大,其内部按钮按 Material 建议样式展示;

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

    4. scaleFittedBox 类似,按比例缩放;

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

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)
    

  2. 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))
    

  3. 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)
    

  4. 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),)
    

  5. 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))))
    

  6. 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)
    

  7. 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: () => {})
    

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))
    

  2. 小菜尝试与其他按钮相同的几类按钮属性,使用方式相同;

    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))
    

  3. 以下为 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)
    

  4. 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 高度作为高亮时阴影高度;

建议:

  1. 使用高亮颜色时 highlightElevation 建议设置为 0.0
  2. 若按钮有样式设置,依赖的 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: () => {}))

Copyright© 2020-2022 li-xyz 冀ICP备2022001112号-1