Flutter狀態(tài)管理Bloc之定時(shí)器示例
本文實(shí)例為大家分享了Flutter狀態(tài)管理Bloc之定時(shí)器的具體代碼,供大家參考,具體內(nèi)容如下
1. 依賴
dependencies: ? flutter_bloc: ^2.1.1 ? equatable: ^1.0.1 ? wave: ^0.0.8
2. Ticker
Ticker 用于產(chǎn)生定時(shí)器的數(shù)據(jù)流。
/// 定時(shí)器數(shù)據(jù)源 class Ticker { ?? ? /// 定時(shí)器數(shù)據(jù)源 ? /// @param ticks 時(shí)間 ? Stream<int> tick({int ticks}){ ? ? ? return Stream.periodic(Duration(seconds: 1), (x) => ticks - x - 1).take(ticks); ? } }
3. TimerBloc
創(chuàng)建 TimerBloc 用于消費(fèi)Ticker, 我們需要?jiǎng)?chuàng)建定時(shí)器狀態(tài),定時(shí)器事件兩個(gè)輔助類。其中定時(shí)器的狀態(tài)有
- Ready(準(zhǔn)備從指定的持續(xù)時(shí)間開始倒計(jì)時(shí))
- Running(從指定持續(xù)時(shí)間開始遞減計(jì)數(shù))
- Paused(在剩余的持續(xù)時(shí)間內(nèi)暫停)
- Finished已完成,剩余持續(xù)時(shí)間為0
import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; ? /// 定時(shí)器狀態(tài) @immutable abstract class TimerState extends Equatable{ ? /// 時(shí)間 ? final int duration; ? ? /// 構(gòu)造方法? ? const TimerState(this.duration); ? ? @override ? List<Object> get props => [this.duration]; } ? /// 準(zhǔn)備狀態(tài) class Ready extends TimerState { ? const Ready(int duration) : super(duration); ? ? @override ? String toString() => 'Ready { duration: $duration }'; } ? /// 暫停狀態(tài) class Paused extends TimerState { ? const Paused(int duration) : super(duration); ?? ? @override ? String toString() => 'Paused { duration: $duration }'; } ? /// 運(yùn)行狀態(tài) class Running extends TimerState { ? const Running(int duration) : super(duration); ?? ? @override ? String toString() => 'Running { duration: $duration }'; } ? /// 完成狀態(tài) class Finished extends TimerState{ ? const Finished() : super(0); }
所有的State都繼承自抽象類TimerState,因?yàn)椴徽撛谀膫€(gè)狀態(tài),我們都需要知道剩余時(shí)間。
4. TimerEvent
我們需要處理的事件有
- Start?(通知TimerBloc定時(shí)器應(yīng)該開始)
- Pause?(通知TimerBloc計(jì)時(shí)器應(yīng)該暫停)
- Resume(通知TimerBloc應(yīng)該恢復(fù)計(jì)時(shí)器)
- Reset?(通知TimerBloc定時(shí)器應(yīng)重置為原始狀態(tài))
- Tick?(通知TimerBloc需要更新剩余時(shí)間)
import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; ? /// 定時(shí)器事件 @immutable abstract class TimerEvent extends Equatable{ ? ? const TimerEvent(); ? ? @override ? List<Object> get props => []; } ? /// 開始時(shí)間 class Start extends TimerEvent { ? /// 定時(shí)器時(shí)間 ? final int duration; ? ? const Start({@required this.duration}); ? ? @override ? String toString() => 'Start { duration: $duration }'; } ? /// 暫停事件 class Paused extends TimerEvent {} ? /// 恢復(fù)狀態(tài) class Resumed extends TimerEvent {} ? /// 重置狀態(tài) class Reset extends TimerEvent {} ? /// 定時(shí)器事件 class Tick extends TimerEvent { ? /// 當(dāng)前時(shí)間 ? final int duration; ? ? const Tick({@required this.duration}); ? ? @override ? List<Object> get props => [this.duration]; ? ? @override ? String toString() => 'Tick { duration: $duration }'; }
5. TimerBloc 實(shí)現(xiàn)
1.初始化狀態(tài)Ready(_duration)
2.創(chuàng)建Ticker對(duì)象, 用戶獲取數(shù)據(jù)流
3.實(shí)現(xiàn)mapEventToState方法
4.當(dāng)event為Start時(shí), 需要開啟數(shù)據(jù)流
5.創(chuàng)建StreamSubscription, 處理流的不同狀態(tài), 并在bloc的close方法中關(guān)閉它
6.當(dāng)event為Tick時(shí), 需要處理數(shù)據(jù)的更新
7.當(dāng)event為Pause時(shí), 需要停止定時(shí)器
8.當(dāng)event為Resume時(shí), 需要重新啟動(dòng)定時(shí)器
9.當(dāng)event為reset時(shí), 需要重置定時(shí)器
import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:flutter/material.dart'; import 'package:state_manage/timer/ticker.dart'; import './bloc.dart'; ? /// 定時(shí)器Bloc class TimerBloc extends Bloc<TimerEvent, TimerState> { ? /// 定時(shí)器時(shí)間 ? final int _duration = 60; ? /// 定時(shí)器數(shù)據(jù)流 ? final Ticker _ticker; ? // 流訂閱 ? StreamSubscription<int> _tickerSubscription; ? ? TimerBloc({@required Ticker ticker}) ? ? ? : assert(ticker != null), ? ? ? ? _ticker = ticker; ? ? /// 初始化狀態(tài) ? @override ? TimerState get initialState => Ready(_duration); ? ? @override ? Stream<TimerState> mapEventToState( ? ? TimerEvent event, ? ) async* { ? ? print('$event'); ? ? if (event is Start) { ? ? ? yield* _mapStartToState(event); ? ? } else if (event is Tick) { ? ? ? yield* _mapTickToState(event); ? ? } else if (event is Pause) { ? ? ? yield* _mapPauseToState(event); ? ? } else if (event is Resume) { ? ? ? yield* _mapResumeToState(event); ? ? } else if (event is Reset) { ? ? ? yield* _mapResetToState(event); ? ? } ? } ? ? @override ? Future<void> close() { ? ? _tickerSubscription?.cancel(); ? ? return super.close(); ? } ? ? /// 處理開始事件 ? Stream<TimerState> _mapStartToState(Start start) async* { ? ? // 運(yùn)行狀態(tài) ? ? yield Running(start.duration); ? ? // 取消訂閱 ? ? _tickerSubscription?.cancel(); ? ? // 創(chuàng)建訂閱 ? ? _tickerSubscription = ? ? ? ? _ticker.tick(ticks: start.duration).listen((duration) { ? ? ? add(Tick(duration: duration)); ? ? }); ? } ? ? /// 處理定時(shí)器事件 ? Stream<TimerState> _mapTickToState(Tick tick) async* { ? ? yield tick.duration > 0 ? Running(tick.duration) : Finished(); ? } ? ? /// 處理暫停事件 ? Stream<TimerState> _mapPauseToState(Pause pause) async* { ? ? if (state is Running) { ? ? ? _tickerSubscription?.pause(); ? ? ? yield Paused(state.duration); ? ? } ? } ? ? /// 處理恢復(fù)狀態(tài) ? Stream<TimerState> _mapResumeToState(Resume resume) async* { ? ? if (state is Paused) { ? ? ? _tickerSubscription?.resume(); ? ? ? yield Running(state.duration); ? ? } ? } ? ? /// 處理重置狀態(tài) ? Stream<TimerState> _mapResetToState(Reset reset) async* { ? ? _tickerSubscription?.cancel(); ? ? yield Ready(_duration); ? } }
6. 界面實(shí)現(xiàn)
實(shí)現(xiàn)定時(shí)器顯示
timer_test.dart
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:state_manage/timer/bloc/bloc.dart'; import 'package:state_manage/timer/ticker.dart'; ? /// 定時(shí)器 class TimerTest extends StatelessWidget { ? @override ? Widget build(BuildContext context) { ? ? return MaterialApp( ? ? ? theme: ThemeData( ? ? ? ? primaryColor: Color.fromRGBO(109, 234, 255, 1), ? ? ? ? accentColor: Color.fromRGBO(72, 74, 126, 1), ? ? ? ? brightness: Brightness.dark, ? ? ? ), ? ? ? title: 'Flutter Timer', ? ? ? home: BlocProvider( ? ? ? ? create: (ctx) => TimerBloc(ticker: Ticker()), ? ? ? ? child: Timer(), ? ? ? ), ? ? ); ? } } ? /// 定時(shí)器頁(yè)面 class Timer extends StatelessWidget{ ? /// 字體樣式 ? static const TextStyle timerTextStyle = TextStyle( ? ? fontSize: 60, ? ? fontWeight: FontWeight.bold ? ); ? ? @override ? Widget build(BuildContext context) { ? ? return Scaffold( ? ? ? appBar: AppBar(title: Text('Flutter Time')), ? ? ? body: Column( ? ? ? ? mainAxisAlignment: MainAxisAlignment.center, ? ? ? ? crossAxisAlignment: CrossAxisAlignment.center, ? ? ? ? children: <Widget>[ ? ? ? ? ? Padding( ? ? ? ? ? ? padding: EdgeInsets.symmetric(vertical: 100.0), ? ? ? ? ? ? child: Center( ? ? ? ? ? ? ? child: BlocBuilder<TimerBloc, TimerState>( ? ? ? ? ? ? ? ? builder: (ctx, state) { ? ? ? ? ? ? ? ? ? // 分鐘格式化 ? ? ? ? ? ? ? ? ? final String minuteStr = ((state.duration / 60) % 60).floor().toString().padLeft(2, '0'); ? ? ? ? ? ? ? ? ? // 秒數(shù)格式化 ? ? ? ? ? ? ? ? ? final String secondStr = (state.duration % 60).floor().toString().padLeft(2, '0'); ? ? ? ? ? ? ? ? ? return Text( ? ? ? ? ? ? ? ? ? ? '$minuteStr : $secondStr', ? ? ? ? ? ? ? ? ? ? style: Timer.timerTextStyle, ? ? ? ? ? ? ? ? ? ); ? ? ? ? ? ? ? ? }, ? ? ? ? ? ? ? ), ? ? ? ? ? ? ), ? ? ? ? ? ) ? ? ? ? ], ? ? ? ), ? ? ); ? } }
添加背景
timer_background.dart
import 'package:flutter/material.dart'; import 'package:wave/config.dart'; import 'package:wave/wave.dart'; ? /// 定時(shí)器背景 class Background extends StatelessWidget { ? @override ? Widget build(BuildContext context) { ? ? return WaveWidget( ? ? ? config: CustomConfig( ? ? ? ? gradients: [ ? ? ? ? ? [ ? ? ? ? ? ? Color.fromRGBO(72, 74, 126, 1), ? ? ? ? ? ? Color.fromRGBO(125, 170, 206, 1), ? ? ? ? ? ? Color.fromRGBO(184, 189, 245, 0.7) ? ? ? ? ? ], ? ? ? ? ? [ ? ? ? ? ? ? Color.fromRGBO(72, 74, 126, 1), ? ? ? ? ? ? Color.fromRGBO(125, 170, 206, 1), ? ? ? ? ? ? Color.fromRGBO(172, 182, 219, 0.7) ? ? ? ? ? ], ? ? ? ? ? [ ? ? ? ? ? ? Color.fromRGBO(72, 73, 126, 1), ? ? ? ? ? ? Color.fromRGBO(125, 170, 206, 1), ? ? ? ? ? ? Color.fromRGBO(190, 238, 246, 0.7) ? ? ? ? ? ] ? ? ? ? ], ? ? ? ? durations: [19440, 10800, 6000], ? ? ? ? heightPercentages: [0.03, 0.01, 0.02], ? ? ? ? gradientBegin: Alignment.bottomCenter, ? ? ? ? gradientEnd: Alignment.topCenter ? ? ? ), ? ? ? size: Size(double.infinity, double.infinity), ? ? ? waveAmplitude: 25, ? ? ? backgroundColor: Colors.blue[50], ? ? ); ? } ?? }
timer_test.dart
?/// 定時(shí)器頁(yè)面 class Timer extends StatelessWidget { ? /// 字體樣式 ? static const TextStyle timerTextStyle = ? ? ? TextStyle(fontSize: 60, fontWeight: FontWeight.bold); ? ? @override ? Widget build(BuildContext context) { ? ? return Scaffold( ? ? ? appBar: AppBar(title: Text('Flutter Time')), ? ? ? body: Stack( ? ? ? ? children: <Widget>[ ? ? ? ? ? Background(), ? ? ? ? ? Column( ? ? ? ? ? ? // ... 省略內(nèi)容 ? ? ? ? ? ) ? ? ? ? ], ? ? ? ), ? ? ); ? } }
添加定時(shí)器動(dòng)作
timer_actions.dart
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:state_manage/timer/bloc/bloc.dart'; ? /// 動(dòng)作 class TimerActions extends StatelessWidget { ? @override ? Widget build(BuildContext context) { ? ? return Row( ? ? ? mainAxisAlignment: MainAxisAlignment.spaceEvenly, ? ? ? children: _mapStateToActionButtons(timerBloc: BlocProvider.of<TimerBloc>(context)), ? ? ); ? } ? ? /// 創(chuàng)建動(dòng)作按鈕 ? /// @param timerBloc 定時(shí)器Bloc ? List<Widget> _mapStateToActionButtons({TimerBloc timerBloc}) { ? ? // 定時(shí)器當(dāng)前狀態(tài) ? ? final TimerState currentState = timerBloc.state; ? ? // 根據(jù)不同狀態(tài)返回不同視圖 ? ? if (currentState is Ready) { ? ? ? return [FloatingActionButton( ? ? ? ? child: Icon(Icons.play_arrow), ? ? ? ? onPressed: () => timerBloc.add(Start(duration: currentState.duration)), ? ? ? )]; ? ? } else if (currentState is Running) { ? ? ? return [ ? ? ? ? FloatingActionButton( ? ? ? ? ? child: Icon(Icons.pause), ? ? ? ? ? onPressed: () => timerBloc.add(Pause()), ? ? ? ? ), ? ? ? ? FloatingActionButton( ? ? ? ? ? child: Icon(Icons.replay), ? ? ? ? ? onPressed: () => timerBloc.add(Reset()), ? ? ? ? ) ? ? ? ]; ? ? } else if (currentState is Paused) { ? ? ? return [ ? ? ? ? FloatingActionButton( ? ? ? ? ? child: Icon(Icons.play_arrow), ? ? ? ? ? onPressed: () => timerBloc.add(Resume()), ? ? ? ? ), ? ? ? ? FloatingActionButton( ? ? ? ? ? child: Icon(Icons.replay), ? ? ? ? ? onPressed: () => timerBloc.add(Reset()), ? ? ? ? ) ? ? ? ]; ? ? } else if (currentState is Finished) { ? ? ? return [ ? ? ? ? FloatingActionButton( ? ? ? ? ? child: Icon(Icons.replay), ? ? ? ? ? onPressed: () => timerBloc.add(Reset()), ? ? ? ? ) ? ? ? ]; ? ? } else { ? ? ? return []; ? ? } ? } }
在界面設(shè)置動(dòng)作
timer_test.dart
/// 定時(shí)器頁(yè)面 class Timer extends StatelessWidget { ? /// 字體樣式 ? static const TextStyle timerTextStyle = ? ? ? TextStyle(fontSize: 60, fontWeight: FontWeight.bold); ? @override ? Widget build(BuildContext context) { ? ? return Scaffold( ? ? ? appBar: AppBar(title: Text('Flutter Timer')), ? ? ? body: Stack( ? ? ? ? children: <Widget>[ ? ? ? ? ? Background(), ? ? ? ? ? Column( ? ? ? ? ? ? mainAxisAlignment: MainAxisAlignment.center, ? ? ? ? ? ? crossAxisAlignment: CrossAxisAlignment.center, ? ? ? ? ? ? children: <Widget>[ ? ? ? ? ? ? ? Padding( ? ? ? ? ? ? ? ? // ... ? ? ? ? ? ? ? ), ? ? ? ? ? ? ? BlocBuilder<TimerBloc, TimerState>( ? ? ? ? ? ? ? ? condition: (previousState, currentState) => currentState.runtimeType != previousState.runtimeType, ? ? ? ? ? ? ? ? builder: (ctx, state) => TimerActions(), ? ? ? ? ? ? ? ) ? ? ? ? ? ? ], ? ? ? ? ? ) ? ? ? ? ], ? ? ? ), ? ? ); ? } }
效果圖
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android自定義View實(shí)現(xiàn)繪制虛線的方法詳解
這篇文章主要給大家介紹了Android自定義View實(shí)現(xiàn)繪制虛線的方法,在繪制過(guò)程中走了一些彎路才實(shí)現(xiàn)了虛線的效果,所以想著總結(jié)分享出來(lái),方便有需要的朋友和自己在需要的時(shí)候參考學(xué)習(xí),下面來(lái)一起看看吧。2017-04-04Android多功能時(shí)鐘開發(fā)案例(實(shí)戰(zhàn)篇)
這篇文章主要為大家詳細(xì)介紹了Android多功能時(shí)鐘開發(fā)案例,開發(fā)了時(shí)鐘、鬧鐘、計(jì)時(shí)器和秒表,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05Android編程記錄ListView標(biāo)記行狀態(tài)的方法
這篇文章主要介紹了Android編程記錄ListView標(biāo)記行狀態(tài)的方法,結(jié)合實(shí)例分析了ListView標(biāo)記的相關(guān)實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11實(shí)例分析Android中HandlerThread線程用法
本篇文章主要給大家介紹了Android HandlerThread使用介紹以及源碼解析,有需要的朋友參考學(xué)習(xí)下吧。2017-12-12Android開發(fā)Jetpack組件Room使用講解
Room是一個(gè)數(shù)據(jù)庫(kù)訪問(wèn)組件; 對(duì)SqLite數(shù)據(jù)庫(kù)做了友好的封裝,使我們?cè)诰幋a的時(shí)候,只需要注重邏輯的部分即可,數(shù)據(jù)庫(kù)就交給Room去流暢的訪問(wèn)即可2022-08-08Android實(shí)現(xiàn)底部圖標(biāo)與Fragment的聯(lián)動(dòng)實(shí)例
本篇文章主要介紹了Android實(shí)現(xiàn)底部圖標(biāo)與Fragment的聯(lián)動(dòng)實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07Android 系統(tǒng)相機(jī)拍照后相片無(wú)法在相冊(cè)中顯示解決辦法
這篇文章主要介紹了Android 系統(tǒng)相機(jī)拍照后相片無(wú)法在相冊(cè)中顯示解決辦法的相關(guān)資料,需要的朋友可以參考下2016-12-12Android本地驗(yàn)證碼的簡(jiǎn)易實(shí)現(xiàn)方法(防止暴力登錄)
驗(yàn)證馬真是無(wú)處不在啊,主要作用是防止惡意暴力破解登錄,這篇文章主要介紹了Android本地驗(yàn)證碼的簡(jiǎn)易實(shí)現(xiàn)方法(防止暴力登錄),需要的朋友可以參考下2017-04-04Android入門之SubMenu的實(shí)現(xiàn)詳解
這篇文章主要為大家詳細(xì)介紹了Android如何實(shí)現(xiàn)SubMenu子菜單的效果,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Android有一定的幫助,感興趣的可以了解一下2022-11-11android 為應(yīng)用程序創(chuàng)建桌面快捷方式技巧分享
手機(jī)裝的軟件過(guò)多,找起來(lái)很不方便,所以在主頁(yè)面有一個(gè)快捷方式的話會(huì)很不錯(cuò)的,本文將介紹如何實(shí)現(xiàn),需要了解跟多的朋友可以參考下2012-12-12