和尚繼續嘗試 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,
})
複制
分析源碼可知,RaisedButton 與 FlatButton 基本完全相同,隻是 RaisedButton 多了一些陰影高度的特有屬性,和尚準備同時對兩類 Button 進行嘗試,比較兩者的不同;
案例嘗試
- 和尚首先嘗試最基本的 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)
複制
- 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))
複制
- 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)
複制
- color 為 Button 背景色;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),)
複制
- shape 為 Button 形狀;因按鈕沒有 Material 中 hight 屬性,需要采用 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))))
複制
- 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)
複制
- 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: () => {})
複制
- elevation 為 RaisedButton 所特有的陰影高度;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 屬性;
案例嘗試
- 和尚首先嘗試一個最基本的 OutlineButton;長按無提醒;
OutlineButton(child: Text('OutlineButton'), onPressed: () => Toast.show('OutlineButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM))
複制
- 和尚嘗試與其他按鈕相同的幾類按鈕屬性,使用方式相同;
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))
複制
- 以下為 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)
複制
- 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: () => {}))
複制