Flutter事件監聽

一. 事件監聽

在大前端的開發中,必然存在各類各樣和用戶交互的狀況:好比手指點擊、手指滑動、雙擊、長按等等。html

全部內容首發於公衆號:coderwhy前端

在Flutter中,手勢有兩個不一樣的層次:vue

  • 第一層:原始指針事件(Pointer Events):描述了屏幕上由觸摸板、鼠標、指示筆等觸發的指針的位置和移動。
  • 第二層:手勢識別(Gesture Detector):這個是在原始事件上的一種封裝。
    • 好比咱們要監聽用戶長按,若是本身封裝原始事件咱們須要監遵從用戶按下到擡起的時間來判斷是不是一次長按事件;
    • 好比咱們須要監聽用戶雙擊事件,咱們須要本身封裝監聽用戶兩次按下擡起的時間間隔;
    • 幸運的是各個平臺幾乎都對它們進行了封裝,而Flutter中的手勢識別就是對原始指針事件的封裝;
    • 包括哪些手勢呢?好比點擊、雙擊、長按、拖動等

2.1. 指針事件Pointer

Pointer 表明的是人機界面交互的原始數據。一共有四種指針事件:web

Pointer的原理是什麼呢?算法

  • 在指針落下時,框架作了一個 hit test 的操做,肯定與屏幕發生接觸的位置上有哪些Widget以及分發給最內部的組件去響應;api

  • 事件會沿着最內部的組件向組件樹的根冒泡分發;數據結構

  • 而且不存在用於取消或者中止指針事件進一步分發的機制;app

原始指針事件使用Listener來監聽:框架

class HomeContent extends StatelessWidget {
 @override  Widget build(BuildContext context) {  return Center(  child: Listener(  child: Container(  width: 200,  height: 200,  color: Colors.red,  ),  onPointerDown: (event) => print("手指按下:$event"),  onPointerMove: (event) => print("手指移動:$event"),  onPointerUp: (event) => print("手指擡起:$event"),  ),  );  } } 複製代碼
image-20190925154947943
image-20190925154947943

2.2. 手勢識別Gesture

Gesture是對一系列Pointer的封裝,官方建議開發中儘量使用Gesture,而不是Pointerless

建議使用Gesture
建議使用Gesture

Gesture分層很是多的種類:

點擊

  • onTapDown:用戶發生手指按下的操做
  • onTapUp:用戶發生手指擡起的操做
  • onTap:用戶點擊事件完成
  • onTapCancel:事件按下過程當中被取消

雙擊:

  • onDoubleTap:快速點擊了兩次

長按:

  • onLongPress:在屏幕上保持了一段時間

縱向拖拽:

  • onVerticalDragStart:指針和屏幕產生接觸並可能開始縱向移動;
  • onVerticalDragUpdate:指針和屏幕產生接觸,在縱向上發生移動並保持移動;
  • onVerticalDragEnd:指針和屏幕產生接觸結束;

橫線拖拽:

  • onHorizontalDragStart:指針和屏幕產生接觸並可能開始橫向移動;
  • onHorizontalDragUpdate:指針和屏幕產生接觸,在橫向上發生移動並保持移動;
  • onHorizontalDragEnd:指針和屏幕產生接觸結束;

移動:

  • onPanStart:指針和屏幕產生接觸並可能開始橫向移動或者縱向移動。若是設置了 onHorizontalDragStart 或者 onVerticalDragStart,該回調方法會引起崩潰;
  • onPanUpdate:指針和屏幕產生接觸,在橫向或者縱向上發生移動並保持移動。若是設置了 onHorizontalDragUpdate 或者 onVerticalDragUpdate,該回調方法會引起崩潰。
  • onPanEnd:指針先前和屏幕產生了接觸,而且以特定速度移動,此後再也不在屏幕接觸上發生移動。若是設置了 onHorizontalDragEnd 或者 onVerticalDragEnd,該回調方法會引起崩潰。

從Widget的層面來監聽手勢,咱們須要使用:GestureDetector

  • 固然,咱們也可使用RaisedButton、FlatButton、InkWell等來監聽手勢
  • globalPosition用於獲取相對於屏幕的位置信息
  • localPosition用於獲取相對於當前Widget的位置信息
class HYHomePage extends StatelessWidget {
 @override  Widget build(BuildContext context) {  return Scaffold(  appBar: AppBar(  title: Text("手勢測試"),  ),  body: GestureDetector(  child: Container(  width: 200,  height: 200,  color: Colors.red,  ),  onTap: () {   },  onTapDown: (detail) {  print(detail.globalPosition);  print(detail.localPosition);  },  onTapUp: (detail) {  print(detail.globalPosition);  print(detail.localPosition);  }  ),  );  } }  複製代碼
image-20200317172326395
image-20200317172326395

二. 跨組件事件

在組件之間若是有事件須要傳遞,一方面能夠一層層來傳遞,另外一方面咱們也可使用一個EventBus工具來完成。

其實EventBus在Vue、React中都是一種很是常見的跨組件通訊的方式:

  • EventBus至關因而一種訂閱者模式,經過一個全局的對象來管理;
  • 這個EventBus咱們能夠本身實現,也可使用第三方的EventBus;

這裏咱們直接選擇第三方的EventBus:

dependencies:
 event_bus: ^1.1.1 複製代碼

第一:咱們須要定義一個但願在組件之間傳遞的對象:

  • 咱們能夠稱之爲一個時間對象,也能夠是咱們平時開發中用的模型對象(model)
class UserInfo {
 String nickname;  int level;   UserInfo(this.nickname, this.level); } 複製代碼

第二:建立一個全局的EventBus對象

final eventBus = EventBus();
複製代碼

第三:在某個Widget中,發出事件:

class HYButton extends StatelessWidget {
 @override  Widget build(BuildContext context) {  return RaisedButton(  child: Text("HYButton"),  onPressed: () {  final info = UserInfo("why", 18);  eventBus.fire(info);  },  );  } } 複製代碼

第四:在某個Widget中,監聽事件

class HYText extends StatefulWidget {
 @override  _HYTextState createState() => _HYTextState(); }  class _HYTextState extends State<HYText> {  String message = "Hello Coderwhy";   @override  void initState() {  super.initState();   eventBus.on<UserInfo>().listen((data) {  setState(() {  message = "${data.nickname}-${data.level}";  });  });  }   @override  Widget build(BuildContext context) {  return Text(message, style: TextStyle(fontSize: 30),);  } } 複製代碼

備註:全部內容首發於公衆號,以後除了Flutter也會更新其餘技術文章,TypeScript、React、Node、uniapp、mpvue、數據結構與算法等等,也會更新一些本身的學習心得等,歡迎你們關注

公衆號
公衆號
相關文章
相關標籤/搜索