在 Flutter 中,有各式各樣的按鈕,該怎麼選擇呢?就操作面上來說,就是按了會有反應的東西,就畫面設計上來說,最好的方式就是有圖有真相吧!因此〈Material Components widgets〉這類像是藝廊式的參考頁面,就是最方便的參考管道。
那麼這個主題就這樣了,可以結束了…XD
嗯…也不是這樣,來看一下 API 架構好了,以〈第一個 Flutter 專案〉中看過的 FloatingActionButton 來說,它直接繼承了 StatelessWidget:
Widget > StatelessWidget > FloatingActionButton
然後,在介紹 Flutter 的按鈕元件相關文件上,常會出現的 FlatButton、OutlineButton、RaisedButton,它們都是 MaterialButton 的子類別:
Widget > StatelessWidget > MaterialButton > FlatButton
Widget > StatelessWidget > MaterialButton > OutlineButton
Widget > StatelessWidget > MaterialButton > RaisedButton
如果你看一下它們的原始碼,這些按鈕元件的 build 傳回的 Widget,都是 RawMaterialButton,而它是 StatefulWidget 的子類別:
Widget > StatefulWidget > RawMaterialButton
其實如果你看過 FloatingActionButton 的原始碼,會發現它的 build 中,也會建立 RawMaterialButton 實例,build 傳回的 Widget,子樹中會包含該實例。
RawMaterialButton 顧名思義,包含了許多「按鈕」的基本定義,除了 onPressed、onLongPressed 事件特性之外,也包含了一些顏色設定、陰影、墨水動畫效果等,Flutter 中有許多按鈕,是基於 RawMaterialButton 來設計。
也就是說,如果 Flutter 本身提供的各式按鈕你都不滿意的話,也可以透過 RawMaterialButton 來自訂,例如,最常有人問的是,Text 能不能有 onPressed 事件?透過 RawMaterialButton 就可以!
import 'package:flutter/material.dart';
void main() => runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: TextButton('按我',
onPressed: () => print('被按了XD'),
)
)
),
)
);
class TextButton extends StatelessWidget {
final String text;
final VoidCallback onPressed;
TextButton(this.text, {this.onPressed});
@override
Widget build(BuildContext context) {
return RawMaterialButton(
child: Text(text),
onPressed: onPressed,
);
}
}
來看一下效果:

嗯?最後長按時那個效果是怎麼回事?那是 RawMaterialButton 內部使用了 InkWell 元件做出來的墨水渲染效果,可以藉由 splashColor 來改變顏色,如果想要讓上例中的文字更像個按鈕,可以如下:
import 'package:flutter/material.dart';
void main() => runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: TextButton('按我',
onPressed: () => print('被按了XD'),
)
)
),
)
);
class TextButton extends StatelessWidget {
final String text;
final VoidCallback onPressed;
TextButton(this.text, {this.onPressed});
@override
Widget build(BuildContext context) {
return RawMaterialButton(
child: Text(text),
onPressed: onPressed,
fillColor: Colors.lightGreen,
splashColor: Colors.red,
);
}
}
執行時的效果會是:

方才談到,RawMaterialButton 包含了許多「按鈕」的基本定義,也就是傳統上你認知的…嗯…「按鈕」!如果你想做的是這類的按鈕,可以透過 RawMaterialButton 來自訂,只要它提供的效果你可以接受就好。
然而,也許你想做個沒有「按鈕」概念的按鈕,畢竟 Material Design 嘛!例如,想要有個 onPressed 事件就好,這可以透過 GestureDetector 來達到:
import 'package:flutter/material.dart';
void main() => runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: TextButton('按我',
onPressed: () => print('被按了XD'),
)
)
),
)
);
class TextButton extends StatelessWidget {
final String text;
final VoidCallback onPressed;
TextButton(this.text, {this.onPressed});
@override
Widget build(BuildContext context) {
return GestureDetector(
child: Text(text),
onTap: onPressed == null ? () => {} : onPressed,
);
}
}
這次就不會有顏色、陰影、動畫了:

不過呢!onPressed 其實也是「按鈕」概念下的東西,手機是平面吧!平面怎麼會有 onPressed 這種壓下的概念呢?最多也就是 click 之類的吧!也就是說,既然都用了 GestureDetector,就用手勢上的事件定義就可以了,onPressed 的隱喻是有些多餘。
不過真要細談的話,GestureDetector 也是觸控事件(Pointer Event)的進一步封裝,這要談下去,會涉及事件處理的細節,之後專門談事件時再來說了。

