Giới thiệu Stream
Lập trình bất đồng bộ là một thuật ngữ phổ biến trong lập trình. Với ngôn ngữ lập trình Dart, chúng ta đã quen với Future class cung cấp một tính toán không được hoàn thành ngay lập tức và sẽ thông báo kết quả khi sẵn sàng, việc này chỉ thực hiện đúng 1 lần. Thay vào đó Stream là một luồng các sự kiện bất đồng bộ và cho phép chúng ta lắng nghe các sự kiện này khi chúng được bắn ra từ luồng.
A stream is a sequence of asynchronous events.
Phân loại Stream
- Có 2 loại Stream
- Single subscription streams
- Broadcast streams
Hiểu cơ bản là Single subscription streams chỉ có thể lắng nghe 1 lần duy nhất. (Nếu có sự kiện lắng nghe từ một nơi nào khác hoặc lần thứ 2 trở lên thì sẽ báo lỗi). Trái lại Broadcast streams có thể lắng nghe bất kỳ đâu.
123456789101112131415161718<span class="token comment">//Sử dụng controller</span><span class="token comment">//**Single subscription streams</span><span class="token class-name">StreamController</span> streamController <span class="token operator">=</span> <span class="token class-name">StreamController</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">Stream</span> stream <span class="token operator">=</span> streamController<span class="token punctuation">.</span>stream<span class="token punctuation">;</span><span class="token comment">//Broadcast streams-</span><span class="token class-name">StreamController</span> broadcastStreamController <span class="token operator">=</span> <span class="token class-name">StreamController</span><span class="token punctuation">.</span><span class="token function">broadcast</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">Stream</span> stream <span class="token operator">=</span> broadcastStreamController <span class="token punctuation">.</span>stream<span class="token punctuation">;</span><span class="token comment">//Sử dụng async*</span><span class="token class-name">Stream</span><span class="token generics"><span class="token punctuation"><</span>int<span class="token punctuation">></span></span> <span class="token function">countStream</span><span class="token punctuation">(</span>int to<span class="token punctuation">)</span> <span class="token keyword">async*</span> <span class="token punctuation">{</span><span class="token keyword">for</span> <span class="token punctuation">(</span>int i <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator"><=</span> to<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">yield</span> i<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">//yield -> bắn ra một event</span><span class="token comment">//yield* -> bắn ra 1 stream**</span>
Các phương thức
- Sức mạnh của Stream không chỉ giúp việc lập trình bất đồng bộ dễ dàng mà nó còn có các bộ method hổ trợ rất mạnh mẽ như filter, transform…
- Xêm thêm tại https://dart.dev/tutorials/language/streams
- Thư viện support: extension cho stream Rxdart
- Tham khảo https://reactivex.io/ để tìm hiểu về Reactivex/Functional reactive programming để tìm hiểu về cơ chế, cách hoạt động, cách sử dụng (support rất nhiều ngôn ngữ)
Có một câu hỏi nhỏ: Vậy điểm khác biệt giữa 2 loại Stream này là gì?
Tại sao nên sử dụng Stream
- Stream là một thư viện/api core của Dart.
- → Dễ dàng tạo ra các Plugin (Tách nhỏ repo)
- → Nắm vững công nghệ
- Stream phù hợp với phong cách *làm mới giao diện (declarative) của Flutter. (***so với cách Imperative ở Native) và mô hình MVVM.
- Giảm phụ thuộc vào các thư viện của các bên thứ 3.
- → Tránh lỗi khi thay đổi version
- → Giảm app size
- Một số thư viện như bloc, getx có cơ chế phụ thuộc vào Stream.
Xây dựng giao diện bằng StreamBuilder
StreamBuilder lắng nghe sự thay đổi của Stream và làm mới lại giao diện.
Để sử dụng StreamBuilder cần gọi
1 2 3 4 5 6 7 | <span class="token keyword">const</span> <span class="token class-name">StreamBuilder</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token class-name">Key</span><span class="token operator">?</span> key<span class="token punctuation">,</span> <span class="token class-name">Stream</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span><span class="token punctuation">></span></span><span class="token operator">?</span> stream<span class="token punctuation">,</span> <span class="token class-name">T</span><span class="token operator">?</span> initialData<span class="token punctuation">,</span> required <span class="token class-name">AsyncWidgetBuilder</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span><span class="token punctuation">></span></span> builder<span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> |
Ý nghĩa Parameters:
T? initialData: giá trị mặc định, nếu không truyền vào thì coi như chưa nhận được dữ liệu từ Stream
Stream<T>? stream: truyền Stream cần lắng nghe và được xử lý ở hàm builder
required AsyncWidgetBuilder<T> builder: Xây dựng giao diện được thiết lập ở đây
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | builder<span class="token punctuation">:</span> <span class="token punctuation">(</span> <span class="token class-name">BuildContext</span> context<span class="token punctuation">,</span> <span class="token class-name">AsyncSnapshot</span><span class="token generics"><span class="token punctuation"><</span>int<span class="token punctuation">></span></span> snapshot<span class="token punctuation">,</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//return Widget here;</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">AsyncSnapshot</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span><span class="token punctuation">></span></span> <span class="token punctuation">{</span> <span class="token comment">/// Creates an [AsyncSnapshot] with the specified [connectionState],</span> <span class="token comment">/// and optionally either [data] or [error] with an optional [stackTrace]</span> <span class="token comment">/// (but not both data and error).</span> <span class="token keyword">const</span> <span class="token class-name">AsyncSnapshot</span><span class="token punctuation">.</span><span class="token function">_</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>connectionState<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>data<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>error<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>stackTrace<span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">assert</span><span class="token punctuation">(</span>connectionState <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">assert</span><span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>data <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&&</span> error <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">assert</span><span class="token punctuation">(</span>stackTrace <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> error <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Khi xây dựng UI cần chú ý tới thành phần snapshot trong builder
- Kiểm tra ConnectionState xem tình trạng kết nối với Stream
- snapshot.hasError và snapshot.error: Kiểm tra có lỗi và lấy lỗi. (Xử dụng
addError
để bắn ra sự kiện lỗi) - snapshot.hasData và snapshot.data: Kiểm tra có dữ liệu và lấy dữ liệu. (Xử dụng
add
để bắn ra dữ liệu)
Các trạng thái của ConnectionState
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <span class="token comment">/// The state of connection to an asynchronous computation.</span> <span class="token comment">///</span> <span class="token comment">/// The usual flow of state is as follows:</span> <span class="token comment">///</span> <span class="token comment">/// 1. [none], maybe with some initial data.</span> <span class="token comment">/// 2. [waiting], indicating that the asynchronous operation has begun,</span> <span class="token comment">/// typically with the data being null.</span> <span class="token comment">/// 3. [active], with data being non-null, and possible changing over time.</span> <span class="token comment">/// 4. [done], with data being non-null.</span> <span class="token comment">///</span> <span class="token comment">/// See also:</span> <span class="token comment">///</span> <span class="token comment">/// * [AsyncSnapshot], which augments a connection state with information</span> <span class="token comment">/// received from the asynchronous computation.</span> <span class="token keyword">enum</span> <span class="token class-name">ConnectionState</span> <span class="token punctuation">{</span> <span class="token comment">/// Not currently connected to any asynchronous computation.</span> <span class="token comment">///</span> <span class="token comment">/// For example, a [FutureBuilder] whose [FutureBuilder.future] is null.</span> none<span class="token punctuation">,</span> <span class="token comment">/// Connected to an asynchronous computation and awaiting interaction.</span> waiting<span class="token punctuation">,</span> <span class="token comment">/// Connected to an active asynchronous computation.</span> <span class="token comment">///</span> <span class="token comment">/// For example, a [Stream] that has returned at least one value, but is not</span> <span class="token comment">/// yet done.</span> active<span class="token punctuation">,</span> <span class="token comment">/// Connected to a terminated asynchronous computation.</span> done<span class="token punctuation">,</span> <span class="token punctuation">}</span> |
- Khi không truyền Stream vào StreamBuilder (có nghĩa Stream là null) → none
- Khi truyền Stream (initialData có thể bằng null hoặc không) và chưa add sự kiện vào → waiting
- Khi truyền Stream và add sự kiện → active
- Khi truyền Stream và close() → done
Ví dụ về xử lý giao diện:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token class-name">StreamBuilder</span><span class="token generics"><span class="token punctuation"><</span>int<span class="token punctuation">></span></span><span class="token punctuation">(</span> stream<span class="token punctuation">:</span> stream<span class="token punctuation">,</span> builder<span class="token punctuation">:</span> <span class="token punctuation">(</span> <span class="token class-name">BuildContext</span> context<span class="token punctuation">,</span> <span class="token class-name">AsyncSnapshot</span><span class="token generics"><span class="token punctuation"><</span>int<span class="token punctuation">></span></span> snapshot<span class="token punctuation">,</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>snapshot<span class="token punctuation">.</span>connectionState <span class="token operator">==</span> <span class="token class-name">ConnectionState</span><span class="token punctuation">.</span>waiting<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token class-name">CircularProgressIndicator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>snapshot<span class="token punctuation">.</span>connectionState <span class="token operator">==</span> <span class="token class-name">ConnectionState</span><span class="token punctuation">.</span>active <span class="token operator">||</span> snapshot<span class="token punctuation">.</span>connectionState <span class="token operator">==</span> <span class="token class-name">ConnectionState</span><span class="token punctuation">.</span>done<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>snapshot<span class="token punctuation">.</span>hasError<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token operator">*</span><span class="token comment">//<-- Kiểm tra có lỗi**</span> <span class="token keyword">return</span> <span class="token keyword">const</span> <span class="token class-name">Text</span><span class="token punctuation">(</span><span class="token string">'Error'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>snapshot<span class="token punctuation">.</span>hasData<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token operator">*</span><span class="token comment">//<-- Kiểm tra có data**</span> <span class="token keyword">return</span> <span class="token class-name">Text</span><span class="token punctuation">(</span> snapshot<span class="token punctuation">.</span>data<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">*</span><span class="token operator">*</span><span class="token comment">//<-- Lấy data**</span> style<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token class-name">TextStyle</span><span class="token punctuation">(</span>color<span class="token punctuation">:</span> <span class="token class-name">Colors</span><span class="token punctuation">.</span>red<span class="token punctuation">,</span> fontSize<span class="token punctuation">:</span> <span class="token number">40</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">const</span> <span class="token class-name">Text</span><span class="token punctuation">(</span><span class="token string">'Empty data'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token class-name">Text</span><span class="token punctuation">(</span><span class="token string">'State: ${snapshot.connectionState}'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> |
[Demo] Xây dựng ứng dụng đếm ngược thời gian bằng Stream
Demo bên dưới không xử lý các trạng thái lỗi
Các tính năng cơ bản:
Bắt đầu – Tạm dừng – Tiếp tục – Làm mới
UI Bắt đầu
UI khi đã Bắt đầu
Các bước thực hiện
Các khai báo và các hàm được viết trong State của StatefulWidget
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">class</span> <span class="token class-name">CountDownCustomCubitPage</span> <span class="token keyword">extends</span> <span class="token class-name">StatefulWidget</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token class-name">CountDownCustomCubitPage</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token class-name">Key</span><span class="token operator">?</span> key<span class="token punctuation">,</span> required <span class="token keyword">this</span><span class="token punctuation">.</span>seconds<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">super</span><span class="token punctuation">(</span>key<span class="token punctuation">:</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">final</span> int seconds<span class="token punctuation">;</span> <span class="token metadata symbol">@override</span> <span class="token class-name">State</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">CountDownCustomCubitPage</span><span class="token punctuation">></span></span> <span class="token function">createState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token class-name">CountDownCustomCubitPageState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">CountDownCustomCubitPageState</span> <span class="token keyword">extends</span> <span class="token class-name">State</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">CountDownCustomCubitPage</span><span class="token punctuation">></span></span> <span class="token punctuation">{</span> <span class="token comment">/* Code here */</span> <span class="token punctuation">}</span> |
- Tạo StreamController để quản lý luồng dữ liệu
1 2 3 4 | <span class="token keyword">final</span> <span class="token class-name">StreamController</span><span class="token generics"><span class="token punctuation"><</span>int<span class="token punctuation">></span></span> _timeStreamController <span class="token operator">=</span> <span class="token class-name">StreamController</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Stream</span><span class="token generics"><span class="token punctuation"><</span>int<span class="token punctuation">></span></span> <span class="token keyword">get</span> _timeStream <span class="token operator">=</span><span class="token operator">></span> _timeStreamController<span class="token punctuation">.</span>stream<span class="token punctuation">;</span> |
- Tạo StreamSubscription để quản lý việc đếm ngược
1 2 3 4 | _timeSubscription<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">pause</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//tạm dừng</span> _timeSubscription<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">resume</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//tiếp tục</span> _timeSubscription<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//huỷ bỏ</span> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <span class="token class-name">StreamSubscription</span><span class="token operator">?</span> _timeSubscription<span class="token punctuation">;</span> <span class="token keyword">void</span> <span class="token function">_onStart</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _timeSubscription <span class="token operator">=</span> <span class="token class-name">Stream</span><span class="token punctuation">.</span><span class="token function">periodic</span><span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token class-name">Duration</span><span class="token punctuation">(</span>seconds<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>computationCount<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> _start <span class="token operator">-</span> computationCount<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span> <span class="token punctuation">(</span>event<span class="token punctuation">)</span> <span class="token punctuation">{</span> _timeStreamController<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>event<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>event <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">_onFinish</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//nhớ _timeSubscription?.dispose(); ở dispose()</span> <span class="token keyword">void</span> <span class="token function">_onResume</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>_timeSubscription<span class="token operator">?</span><span class="token punctuation">.</span>isPaused <span class="token operator">?</span><span class="token operator">?</span> <span class="token boolean">false</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _timeSubscription<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">resume</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">_onPause</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>_timeSubscription<span class="token operator">?</span><span class="token punctuation">.</span>isPaused <span class="token operator">?</span><span class="token operator">?</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _timeSubscription<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">pause</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">_onFinish</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _timeSubscription<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> _timeSubscription <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">_onReset</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _timeSubscription<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> _timeSubscription <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> _timeStreamController<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>_start<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Stream.periodic(**const** Duration(seconds: 1), (computationCount) => **_start** - computationCount)
tạo một Stream trả về giá trị định kỳ sau 1 giây.
**_timeStreamController**.add(event);
add thời gian mới vào Stream
- Hiển thị dữ liệu lên giao diện
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <span class="token class-name">StreamBuilder</span><span class="token generics"><span class="token punctuation"><</span>int<span class="token punctuation">></span></span><span class="token punctuation">(</span> initialData<span class="token punctuation">:</span> _start<span class="token punctuation">,</span> stream<span class="token punctuation">:</span> _timeStream<span class="token punctuation">,</span> builder<span class="token punctuation">:</span> <span class="token punctuation">(</span>context<span class="token punctuation">,</span> snapshot<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>snapshot<span class="token punctuation">.</span>hasData<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">final</span> int time <span class="token operator">=</span> snapshot<span class="token punctuation">.</span>data<span class="token operator">!</span><span class="token punctuation">;</span> <span class="token keyword">var</span> separateWidget <span class="token operator">=</span> <span class="token class-name">Padding</span><span class="token punctuation">(</span> padding<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token class-name">EdgeInsets</span><span class="token punctuation">.</span><span class="token function">symmetric</span><span class="token punctuation">(</span>horizontal<span class="token punctuation">:</span> <span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">,</span> child<span class="token punctuation">:</span> <span class="token class-name">Text</span><span class="token punctuation">(</span> <span class="token string">':'</span><span class="token punctuation">,</span> style<span class="token punctuation">:</span> <span class="token class-name">Theme</span><span class="token punctuation">.</span><span class="token function">of</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">.</span>textTheme<span class="token punctuation">.</span>headline2<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">copyWith</span><span class="token punctuation">(</span> fontFamily<span class="token punctuation">:</span> <span class="token string">'BlackOpsOne'</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> textAlign<span class="token punctuation">:</span> <span class="token class-name">TextAlign</span><span class="token punctuation">.</span>center<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token class-name">Row</span><span class="token punctuation">(</span> mainAxisSize<span class="token punctuation">:</span> <span class="token class-name">MainAxisSize</span><span class="token punctuation">.</span>min<span class="token punctuation">,</span> crossAxisAlignment<span class="token punctuation">:</span> <span class="token class-name">CrossAxisAlignment</span><span class="token punctuation">.</span>center<span class="token punctuation">,</span> children<span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token function">_TextWidget</span><span class="token punctuation">(</span> number<span class="token punctuation">:</span> time<span class="token punctuation">.</span>hour<span class="token punctuation">.</span>tens<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">_TextWidget</span><span class="token punctuation">(</span> number<span class="token punctuation">:</span> time<span class="token punctuation">.</span>hour<span class="token punctuation">.</span>ones<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> separateWidget<span class="token punctuation">,</span> <span class="token function">_TextWidget</span><span class="token punctuation">(</span> number<span class="token punctuation">:</span> time<span class="token punctuation">.</span>minute<span class="token punctuation">.</span>tens<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">_TextWidget</span><span class="token punctuation">(</span> number<span class="token punctuation">:</span> time<span class="token punctuation">.</span>minute<span class="token punctuation">.</span>ones<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> separateWidget<span class="token punctuation">,</span> <span class="token function">_TextWidget</span><span class="token punctuation">(</span> number<span class="token punctuation">:</span> time<span class="token punctuation">.</span>second<span class="token punctuation">.</span>tens<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">_TextWidget</span><span class="token punctuation">(</span> number<span class="token punctuation">:</span> time<span class="token punctuation">.</span>second<span class="token punctuation">.</span>ones<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token keyword">const</span> <span class="token class-name">SizedBox</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span> |
- Extension lấy thông tin thời gian từ kiểu int
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <span class="token keyword">extension</span> <span class="token class-name">IntToTime</span> <span class="token keyword">on</span> int <span class="token punctuation">{</span> <span class="token comment">///lấy thông tin giờ</span> int <span class="token keyword">get</span> hour <span class="token operator">=</span><span class="token operator">></span> <span class="token function">_getHour</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> int <span class="token function">_getHour</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">/</span> <span class="token number">3600</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// return Duration(seconds: this).inHours;</span> <span class="token punctuation">}</span> <span class="token comment">///lấy thông tin giờ</span> int <span class="token keyword">get</span> minute <span class="token operator">=</span><span class="token operator">></span> <span class="token function">_getMinute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> int <span class="token function">_getMinute</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">/</span> <span class="token number">60</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">60</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">///lấy thông tin giây</span> int <span class="token keyword">get</span> second <span class="token operator">=</span><span class="token operator">></span> <span class="token function">_getSecond</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> int <span class="token function">_getSecond</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token operator">%</span> <span class="token number">60</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">///format hiển thị số ở hàng chục</span> int <span class="token keyword">get</span> tens <span class="token operator">=</span><span class="token operator">></span> <span class="token function">_getTens</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> int <span class="token function">_getTens</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">>=</span> <span class="token number">10</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">-</span> <span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">%</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">round</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">///format hiển thị số ở hàng đơn vị</span> int <span class="token keyword">get</span> ones <span class="token operator">=</span><span class="token operator">></span> <span class="token function">_getOnes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> int <span class="token function">_getOnes</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token operator">%</span> <span class="token number">10</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Nhằm
giúp cho các công việc không thực hiện lại công việc nó đang thực hiện
thực hiện gọi các function thông qua streamController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <span class="token metadata symbol">@override</span> <span class="token keyword">void</span> <span class="token function">initState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">initState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">///việc quản lý các sự kiện bằng stream ở đây</span> <span class="token comment">///giúp cho các công việc không thực hiện lại công việc nó đang thực hiện</span> <span class="token comment">///bằng hàm distinct()</span> _functionSubscription <span class="token operator">=</span> _functionController<span class="token punctuation">.</span>stream<span class="token punctuation">.</span><span class="token function">distinct</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token punctuation">(</span>event<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">switch</span> <span class="token punctuation">(</span>event<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> <span class="token class-name">CountDownEvent</span><span class="token punctuation">.</span>start<span class="token punctuation">:</span> <span class="token function">_onStart</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> <span class="token class-name">CountDownEvent</span><span class="token punctuation">.</span>pause<span class="token punctuation">:</span> <span class="token function">_onPause</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> <span class="token class-name">CountDownEvent</span><span class="token punctuation">.</span>resume<span class="token punctuation">:</span> <span class="token function">_onResume</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> <span class="token class-name">CountDownEvent</span><span class="token punctuation">.</span>reset<span class="token punctuation">:</span> <span class="token function">_onReset</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//ví dụ</span> <span class="token class-name">Button</span><span class="token punctuation">(</span> onTap<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _functionController<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token class-name">CountDownEvent</span><span class="token punctuation">.</span>resume<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> title<span class="token punctuation">:</span> <span class="token string">'Resume'</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> |
Full code →
[Demo] Tự tạo flutter_bloc theo style Cubit bằng Stream
- Nhằm tăng tính đọc hiểu dữ liệu và phân chia mã nguồn giữa giao diện và logic chúng ta cần tách biệt xử lý ở các mà
Bước 1: Tạo abstract class để định nghĩa các biến và hàm cơ bản của bloc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token keyword">abstract</span> <span class="token keyword">class</span> <span class="token class-name">CustomCubit</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span><span class="token punctuation">></span></span> <span class="token punctuation">{</span> <span class="token class-name">CustomCubit</span><span class="token punctuation">(</span><span class="token class-name">T</span> initValue<span class="token punctuation">)</span> <span class="token punctuation">{</span> _streamCtrl <span class="token operator">=</span> <span class="token class-name">StreamController</span><span class="token punctuation">.</span><span class="token function">broadcast</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>initValue<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//nếu dùng thêm thư viện rxdart thì xài seed để init value</span> <span class="token punctuation">}</span> late <span class="token class-name">StreamController</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span><span class="token punctuation">></span></span> _streamCtrl<span class="token punctuation">;</span> <span class="token class-name">Stream</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span><span class="token punctuation">></span></span> <span class="token keyword">get</span> stream <span class="token operator">=</span><span class="token operator">></span> _streamCtrl<span class="token punctuation">.</span>stream<span class="token punctuation">;</span> <span class="token keyword">void</span> <span class="token function">emit</span><span class="token punctuation">(</span><span class="token class-name">T</span> state<span class="token punctuation">)</span> <span class="token punctuation">{</span> _streamCtrl<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _streamCtrl<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
**_streamCtrl** = StreamController.broadcast()..add(initValue);
stream không thể nhận được event này bới stream.listen được gọi sau khi hàm hàm constructor chạy.
Có thể tự initValue ở StreamBuilder hoặc dùng thư viện RxDart có hổ trợ chức năng này
Bước 2:
- Tạo TimerCubit
- Mang hết khai báo và hàm từ vào trong TimerCubit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | <span class="token keyword">class</span> <span class="token class-name">TimerCubit</span> <span class="token keyword">extends</span> <span class="token class-name">CustomCubit</span><span class="token generics"><span class="token punctuation"><</span>int<span class="token punctuation">></span></span> <span class="token punctuation">{</span> <span class="token class-name">TimerCubit</span><span class="token punctuation">(</span>int initValue<span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">super</span><span class="token punctuation">(</span>initValue<span class="token punctuation">)</span> <span class="token punctuation">{</span> startTime <span class="token operator">=</span> initValue<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token class-name">StreamSubscription</span><span class="token operator">?</span> _subscription<span class="token punctuation">;</span> late <span class="token class-name">StreamSubscription</span> _controlSubscription<span class="token punctuation">;</span> <span class="token keyword">final</span> <span class="token class-name">StreamController</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">CountDownEvent</span><span class="token punctuation">></span></span> _timerController <span class="token operator">=</span> <span class="token class-name">StreamController</span><span class="token punctuation">.</span><span class="token function">broadcast</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Stream</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">CountDownEvent</span><span class="token punctuation">></span></span> <span class="token keyword">get</span> timerControllerStream <span class="token operator">=</span><span class="token operator">></span> _timerController<span class="token punctuation">.</span>stream<span class="token punctuation">;</span> int startTime <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token metadata symbol">@override</span> <span class="token keyword">void</span> <span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _subscription<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> _controlSubscription<span class="token punctuation">.</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> _timerController<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _controlSubscription <span class="token operator">=</span> _timerController<span class="token punctuation">.</span>stream<span class="token punctuation">.</span><span class="token function">distinct</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token punctuation">(</span>event<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">switch</span> <span class="token punctuation">(</span>event<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> <span class="token class-name">CountDownEvent</span><span class="token punctuation">.</span>start<span class="token punctuation">:</span> <span class="token function">_onStart</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> <span class="token class-name">CountDownEvent</span><span class="token punctuation">.</span>pause<span class="token punctuation">:</span> <span class="token function">_onPause</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> <span class="token class-name">CountDownEvent</span><span class="token punctuation">.</span>resume<span class="token punctuation">:</span> <span class="token function">_onResume</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token keyword">case</span> <span class="token class-name">CountDownEvent</span><span class="token punctuation">.</span>reset<span class="token punctuation">:</span> <span class="token function">_onReset</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">break</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">_setTime</span><span class="token punctuation">(</span>int time<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">emit</span><span class="token punctuation">(</span>time<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">_onStart</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>_subscription <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">_onReset</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> _subscription <span class="token operator">=</span> <span class="token class-name">Stream</span><span class="token punctuation">.</span><span class="token function">periodic</span><span class="token punctuation">(</span><span class="token keyword">const</span> <span class="token class-name">Duration</span><span class="token punctuation">(</span>seconds<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>computationCount<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> startTime <span class="token operator">-</span> computationCount<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span> <span class="token punctuation">(</span>time<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">_setTime</span><span class="token punctuation">(</span>time<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>time <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">_onFinish</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">_onResume</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>_subscription<span class="token operator">?</span><span class="token punctuation">.</span>isPaused <span class="token operator">?</span><span class="token operator">?</span> <span class="token boolean">false</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _subscription<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">resume</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">_onPause</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>_subscription<span class="token operator">?</span><span class="token punctuation">.</span>isPaused <span class="token operator">?</span><span class="token operator">?</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _subscription<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">pause</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">_onFinish</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _subscription<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> _subscription <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">_onReset</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _subscription<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> _subscription <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">timerController</span><span class="token punctuation">(</span><span class="token class-name">CountDownEvent</span> event<span class="token punctuation">)</span> <span class="token punctuation">{</span> _timerController<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>event<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Bước 3: Tạo BlocProvider
- Việc tạo BlocProvider để quản lý instance của bloc bằng context nếu không muốn làm theo cách này có thể tạo biến toàn cục để quán lý riêng.
- BlocProvider được ứng dùng từ InheritedWidget
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <span class="token comment">// final timerCubit = CustomBlocProvider.of<TimerCubit>(context);</span> <span class="token keyword">class</span> <span class="token class-name">CustomBlocProvider</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span> <span class="token keyword">extends</span> <span class="token class-name">CustomCubit</span><span class="token punctuation">></span></span> <span class="token keyword">extends</span> <span class="token class-name">InheritedWidget</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token class-name">CustomBlocProvider</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">.</span>key<span class="token punctuation">,</span> required <span class="token keyword">this</span><span class="token punctuation">.</span>bloc<span class="token punctuation">,</span> required <span class="token keyword">super</span><span class="token punctuation">.</span>child<span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">final</span> <span class="token class-name">T</span> bloc<span class="token punctuation">;</span> <span class="token keyword">static</span> <span class="token class-name">CustomBlocProvider</span><span class="token operator">?</span> maybeOf<span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span> <span class="token keyword">extends</span> <span class="token class-name">CustomCubit</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token class-name">BuildContext</span> context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> context<span class="token punctuation">.</span>dependOnInheritedWidgetOfExactType<span class="token generics"><span class="token punctuation"><</span><span class="token class-name">CustomBlocProvider</span><span class="token punctuation"><</span><span class="token class-name">T</span><span class="token punctuation">></span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">static</span> <span class="token class-name">T</span> of<span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span> <span class="token keyword">extends</span> <span class="token class-name">CustomCubit</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token class-name">BuildContext</span> context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">final</span> <span class="token class-name">CustomBlocProvider</span><span class="token operator">?</span> result <span class="token operator">=</span> maybeOf<span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span><span class="token punctuation">></span></span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">assert</span><span class="token punctuation">(</span>result <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token string">'No BlocProvider found in context'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> result<span class="token operator">!</span><span class="token punctuation">.</span>bloc <span class="token operator">as</span> <span class="token class-name">T</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token metadata symbol">@override</span> bool <span class="token function">updateShouldNotify</span><span class="token punctuation">(</span><span class="token class-name">CustomBlocProvider</span> oldWidget<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> bloc <span class="token operator">!=</span> oldWidget<span class="token punctuation">.</span>bloc<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">final</span> timerCubit <span class="token operator">=</span> context<span class="token punctuation">.</span>read<span class="token generics"><span class="token punctuation"><</span><span class="token class-name">TimerCubit</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">extension</span> <span class="token class-name">ReadCustomBlocProviderOfContext</span> <span class="token keyword">on</span> <span class="token class-name">BuildContext</span> <span class="token punctuation">{</span> <span class="token class-name">T</span> read<span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span> <span class="token keyword">extends</span> <span class="token class-name">CustomCubit</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token class-name">CustomBlocProvider</span><span class="token punctuation">.</span>of<span class="token generics"><span class="token punctuation"><</span><span class="token class-name">T</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Bước 4: Khai báo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <span class="token keyword">class</span> <span class="token class-name">CountDownCustomCubitPage</span> <span class="token keyword">extends</span> <span class="token class-name">StatefulWidget</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token class-name">CountDownCustomCubitPage</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token class-name">Key</span><span class="token operator">?</span> key<span class="token punctuation">,</span> required <span class="token keyword">this</span><span class="token punctuation">.</span>seconds<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">super</span><span class="token punctuation">(</span>key<span class="token punctuation">:</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">final</span> int seconds<span class="token punctuation">;</span> <span class="token metadata symbol">@override</span> <span class="token class-name">State</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">CountDownCustomCubitPage</span><span class="token punctuation">></span></span> <span class="token function">createState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token class-name">CountDownCustomCubitPageState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">CountDownCustomCubitPageState</span> <span class="token keyword">extends</span> <span class="token class-name">State</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">CountDownCustomCubitPage</span><span class="token punctuation">></span></span> <span class="token punctuation">{</span> late <span class="token class-name">TimerCubit</span> _timerCubit<span class="token punctuation">;</span> <span class="token metadata symbol">@override</span> <span class="token keyword">void</span> <span class="token function">initState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">initState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> _timerCubit <span class="token operator">=</span> <span class="token class-name">TimerCubit</span><span class="token punctuation">(</span>widget<span class="token punctuation">.</span>seconds<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token metadata symbol">@override</span> <span class="token keyword">void</span> <span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> _timerCubit<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token metadata symbol">@override</span> <span class="token class-name">Widget</span> <span class="token function">build</span><span class="token punctuation">(</span><span class="token class-name">BuildContext</span> context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token class-name">CustomBlocProvider</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">TimerCubit</span><span class="token punctuation">></span></span><span class="token punctuation">(</span> bloc<span class="token punctuation">:</span> _timerCubit<span class="token punctuation">,</span> child<span class="token punctuation">:</span> <span class="token class-name">Scaffold</span><span class="token punctuation">(</span> appBar<span class="token punctuation">:</span> <span class="token class-name">AppBar</span><span class="token punctuation">(</span>title<span class="token punctuation">:</span> <span class="token class-name">Text</span><span class="token punctuation">(</span><span class="token string">"Timer test"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> body<span class="token punctuation">:</span> <span class="token class-name">Center</span><span class="token punctuation">(</span> child<span class="token punctuation">:</span> <span class="token class-name">SingleChildScrollView</span><span class="token punctuation">(</span> child<span class="token punctuation">:</span> <span class="token class-name">Column</span><span class="token punctuation">(</span> children<span class="token punctuation">:</span> <span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Widget</span><span class="token punctuation">></span></span><span class="token punctuation">[</span> <span class="token function">_Content</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">/*Some Code*/</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | <span class="token keyword">class</span> _Content <span class="token keyword">extends</span> <span class="token class-name">StatelessWidget</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token function">_Content</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token class-name">Key</span><span class="token operator">?</span> key<span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">super</span><span class="token punctuation">(</span>key<span class="token punctuation">:</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token metadata symbol">@override</span> <span class="token class-name">Widget</span> <span class="token function">build</span><span class="token punctuation">(</span><span class="token class-name">BuildContext</span> context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">final</span> timerCubit <span class="token operator">=</span> context<span class="token punctuation">.</span>read<span class="token generics"><span class="token punctuation"><</span><span class="token class-name">TimerCubit</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// final timerCubit = CustomBlocProvider.of<TimerCubit>(context);</span> <span class="token keyword">return</span> <span class="token class-name">StreamBuilder</span><span class="token generics"><span class="token punctuation"><</span>int<span class="token punctuation">></span></span><span class="token punctuation">(</span> initialData<span class="token punctuation">:</span> timerCubit<span class="token punctuation">.</span>startTime<span class="token punctuation">,</span> stream<span class="token punctuation">:</span> timerCubit<span class="token punctuation">.</span>stream<span class="token punctuation">,</span> builder<span class="token punctuation">:</span> <span class="token punctuation">(</span>context<span class="token punctuation">,</span> snapshot<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>snapshot<span class="token punctuation">.</span>hasData<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">final</span> int time <span class="token operator">=</span> snapshot<span class="token punctuation">.</span>data<span class="token operator">!</span><span class="token punctuation">;</span> <span class="token keyword">var</span> separateWidget <span class="token operator">=</span> <span class="token class-name">Padding</span><span class="token punctuation">(</span> padding<span class="token punctuation">:</span> <span class="token keyword">const</span> <span class="token class-name">EdgeInsets</span><span class="token punctuation">.</span><span class="token function">symmetric</span><span class="token punctuation">(</span>horizontal<span class="token punctuation">:</span> <span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">,</span> child<span class="token punctuation">:</span> <span class="token class-name">Text</span><span class="token punctuation">(</span> <span class="token string">':'</span><span class="token punctuation">,</span> style<span class="token punctuation">:</span> <span class="token class-name">Theme</span><span class="token punctuation">.</span><span class="token function">of</span><span class="token punctuation">(</span>context<span class="token punctuation">)</span><span class="token punctuation">.</span>textTheme<span class="token punctuation">.</span>headline2<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">copyWith</span><span class="token punctuation">(</span> fontFamily<span class="token punctuation">:</span> <span class="token string">'BlackOpsOne'</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> textAlign<span class="token punctuation">:</span> <span class="token class-name">TextAlign</span><span class="token punctuation">.</span>center<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token class-name">FittedBox</span><span class="token punctuation">(</span> child<span class="token punctuation">:</span> <span class="token class-name">InkWell</span><span class="token punctuation">(</span> onTap<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> child<span class="token punctuation">:</span> <span class="token class-name">Row</span><span class="token punctuation">(</span> mainAxisSize<span class="token punctuation">:</span> <span class="token class-name">MainAxisSize</span><span class="token punctuation">.</span>min<span class="token punctuation">,</span> crossAxisAlignment<span class="token punctuation">:</span> <span class="token class-name">CrossAxisAlignment</span><span class="token punctuation">.</span>center<span class="token punctuation">,</span> children<span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token function">_TextWidget</span><span class="token punctuation">(</span> number<span class="token punctuation">:</span> time<span class="token punctuation">.</span>hour<span class="token punctuation">.</span>tens<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">_TextWidget</span><span class="token punctuation">(</span> number<span class="token punctuation">:</span> time<span class="token punctuation">.</span>hour<span class="token punctuation">.</span>ones<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> separateWidget<span class="token punctuation">,</span> <span class="token function">_TextWidget</span><span class="token punctuation">(</span> number<span class="token punctuation">:</span> time<span class="token punctuation">.</span>minute<span class="token punctuation">.</span>tens<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">_TextWidget</span><span class="token punctuation">(</span> number<span class="token punctuation">:</span> time<span class="token punctuation">.</span>minute<span class="token punctuation">.</span>ones<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> separateWidget<span class="token punctuation">,</span> <span class="token function">_TextWidget</span><span class="token punctuation">(</span> number<span class="token punctuation">:</span> time<span class="token punctuation">.</span>second<span class="token punctuation">.</span>tens<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">_TextWidget</span><span class="token punctuation">(</span> number<span class="token punctuation">:</span> time<span class="token punctuation">.</span>second<span class="token punctuation">.</span>ones<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token keyword">const</span> <span class="token class-name">SizedBox</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Full code →
Các tính năng có thể bổ xung
- Có thể custom _Content thành BlocBuilder
- Thêm các tính năng như buildWhen, listenner
- MultiBlocProvider
Tham khảo
https://dart.dev/tutorials/language/streams
https://medium.flutterdevs.com/exploring-streambuilder-in-flutter-5958381bca67