RxDart——Dart和Flutter中的響應式程式設計入門

白瑞德發表於2019-08-17

RxDart

今年年初開始嘗試使用Flutter開發android APP,期間遇到了不少的坑,但總算是有驚無險。而在做Android原生開發時,RxAndroid讓程式碼爽到飛起。為了找回那種熟悉的感覺,特意將RxDart引入到專案中。首先介紹一下RxDart是什麼: RxDart基於ReactiveX,由Dart標準庫中Stream擴充套件而成,是一個為Dart語言提供響應式程式設計的庫。

如何使用

新增依賴

首先在pubspec.yaml中新增依賴:

dependencies:
  rxdart: ^0.22.1+1
複製程式碼

範例

接下來我們通過一段程式碼來看一下如何使用RxDart

Observable.just(1).listen(print);
>>>1
複製程式碼

RxDart——Dart和Flutter中的響應式程式設計入門
一行完事兒?這都是什麼啊?彆著急,這只是Dart語法的特性,接下來我們將上面的程式碼進行拆分:

//建立一個被觀察者
var observable = Observable.just(1);
//建立一個觀察者
var observa = (int num) {
    print(num);
};
//通過listen實現訂閱
observable.listen(observa);
複製程式碼

這下就明朗了,作為一個Android開發者,對這段程式碼充滿親切感。簡直和RxJava一抹一樣!!!

建立被觀察者

RxDart提供了很多方法供我們建立Observable,除了上文中的直接使用just工廠函式從單個值中建立,還有以下幾種建立方法:

  • 從一個Stream中建立:Observable(Stream.fromIterable([1, 2, 3, 4, 5])).listen(print);
  • 建立週期性事件:Observable.periodic(Duration(seconds: 1), (x) => x.toString()).listen(print);這段程式碼每隔一秒鐘列印一個整數並加一
  • 從Future中建立:通過一個Future建立的Observable,會先等待Future執行完畢,完後發射資料,這個輸出資料就是Future的執行結果,如果Future沒有任何返回值,那麼輸出null。還可以toStream方法將Future轉換為Stream然後再建立:
Observable.periodic(Duration(seconds: 1), (x) => x.toString()).listen(print);
Future<String> asyncFunction() async {
    return Future.delayed(const Duration(seconds: 10), () => "十秒後的資料");
}
Observable.fromFuture(asyncFunction()).listen(print);
複製程式碼

程式碼輸出如下:

RxDart——Dart和Flutter中的響應式程式設計入門

操作符和資料變換

Rx最爽的是什麼?當然是無處不在的操作符啦!我個人覺得操作符是Rx的靈魂,關於操作符原理大家可以閱讀從原始碼檢視RxJava中的map和flatMap的用法和區別去了解。下面我們通過幾個常用的操作符學習一下RxDart的操作符使用方法:

過濾操作符

過濾操作符就是用來過濾掉Observable發射的一些資料,丟棄這些資料只保留過濾後的資料。

where

點了半天編輯器總是不自動補全Filter

RxDart——Dart和Flutter中的響應式程式設計入門
竟然沒有Filter操作符號了,還好有個where 我們實現一個對奇偶進行過濾的操作:

Observable(Stream.fromIterable([1, 2, 3, 4, 5]))
      .where((num) => num % 2 == 0)
      .listen(print);
>>>2 4
複製程式碼
skip

如果我們想跳過前三個數字,我們可以使用skip操作符:

Observable(Stream.fromIterable([1, 2, 3, 4, 5]))
      .skip(3)
      .listen(print);
>>> 4 5
複製程式碼
take

和skip操作相反,如果我們去掉後面三個資料,只輸出前面兩個資料:

Observable(Stream.fromIterable([1, 2, 3, 4, 5]))
      .take(2)
      .listen(print);
<<<1 2
複製程式碼
distinct

如果我們要過濾掉原始資料裡重複的項:

Observable(Stream.fromIterable([1, 2, 2, 2, 3, 3, 4, 5]))
      .distinctUnique()
      .listen(print);
>>> 1 2 3 4 5
複製程式碼

變換操作符

過濾操作符就是用來過變換Observable發射的一些資料或者Observable本身,將被變換的物件轉換成為我們想要的。

map

首先說一下map,依舊是實現一個基本的資料轉換,將數字翻倍:

Observable(Stream.fromIterable([1, 2, 3, 4, 5]))
    .map((num) => num * 2)
    .listen(print);
<<<2 4 6 8 10
複製程式碼
flatMap

接下來說一下flatMap,取出一個由陣列組成的陣列中的元素:

var list1 = [1, 2, 3];
var list2 = [4, 5, 6];
var list3 = [7, 8, 9];
var listAll = [list1, list2, list3];
Observable(Stream.fromIterable(listAll))
    .flatMap((listItem) => Observable(Stream.fromIterable(listItem)))
    .listen(print);
<<<1 2 3 4 5 6 7 8 9
複製程式碼
concatMap

類似flatMap操作符,區別在於concatMap按次序連線而不是合併那些生成的Observables,然後產生自己的資料序列。 看看下面的程式碼:

var list1 = [1, 2, 3];
  var list2 = [4, 5, 6];
  var list3 = [7, 8, 9];
  var listAll = [list1, list2, list3];
  var changeConcatMap = (List<int> listItem) {
    print("concatMap開始變換了");
    return Observable(Stream.fromIterable(listItem));
  };
  var changeFlatMap = (List<int> listItem) {
    print("FlatMap開始變換了");
    return Observable(Stream.fromIterable(listItem));
  };
  Observable(Stream.fromIterable(listAll))
      .concatMap((listItem) => changeConcatMap(listItem))
      .listen(print);
  Observable(Stream.fromIterable(listAll))
      .flatMap((listItem) => changeFlatMap(listItem))
      .listen(print);
複製程式碼

輸出結果為:

RxDart——Dart和Flutter中的響應式程式設計入門

結合操作符

startWith

在資料序列的開頭插入一條指定的項:

var observable = Observable(Stream.fromIterable([1, 2, 3, 4, 5]));
observable.startWith(9).listen(print);
>>>9 1 2 3 4 5
複製程式碼
merge

使用Merge操作符你可以將多個Observables的輸出合併,就好像它們是一個單個的Observable一樣。但是可能會讓合併的Observables發射的資料交錯

Observable.merge([
    Stream.fromIterable([1, 2]),
    Stream.fromIterable([3, 4])
  ]).listen(print);
>>> 1 3 2 4
複製程式碼
combineLatest

當兩個Observables中的任何一個發射了資料時,使用一個函式結合每個Observable發射的最近資料項,並且基於這個函式的結果發射資料。

var observable1 = Observable.just(1);
var observable2 = Observable.just(2);
Observable.combineLatest([observable1, observable2], (num) => num)
      .listen(print);
>>>[1,2]
複製程式碼
mergeWith

mergeWith將多個被觀察者發射的流合併成一個流。資料按照被髮送的順序傳遞

var observable1 = Observable.just(1);
  var observable2 = Observable.just(5);
  var observable = Observable(Stream.fromIterable([1, 2, 3, 4, 5]));
  observable
      .mergeWith([observable2,observable1])
      .listen(print);
>>> 1 5 1 2 3 4 5
複製程式碼

結語

這只是一片RxDart使用的入門教程的。本文並未深入探討RxDart的實現原理和邏輯,因為這些原理基本和RxJava中的類似。感興趣的可以去關注我的RxJava系列的文章

相關文章