Flutter状态管理Bloc之定时器
mazaiting 人气:01. 依赖
dependencies: flutter_bloc: ^2.1.1 equatable: ^1.0.1 wave: ^0.0.8
2. Ticker
Ticker 用于产生定时器的数据流。
/// 定时器数据源 class Ticker { /// 定时器数据源 /// @param ticks 时间 Stream<int> tick({int ticks}){ return Stream.periodic(Duration(seconds: 1), (x) => ticks - x - 1).take(ticks); } }
3. TimerBloc
创建 TimerBloc 用于消费Ticker, 我们需要创建定时器状态,定时器事件两个辅助类。其中定时器的状态有
- Ready(准备从指定的持续时间开始倒计时)
- Running(从指定持续时间开始递减计数)
- Paused(在剩余的持续时间内暂停)
- Finished已完成,剩余持续时间为0
import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; /// 定时器状态 @immutable abstract class TimerState extends Equatable{ /// 时间 final int duration; /// 构造方法 const TimerState(this.duration); @override List<Object> get props => [this.duration]; } /// 准备状态 class Ready extends TimerState { const Ready(int duration) : super(duration); @override String toString() => 'Ready { duration: $duration }'; } /// 暂停状态 class Paused extends TimerState { const Paused(int duration) : super(duration); @override String toString() => 'Paused { duration: $duration }'; } /// 运行状态 class Running extends TimerState { const Running(int duration) : super(duration); @override String toString() => 'Running { duration: $duration }'; } /// 完成状态 class Finished extends TimerState{ const Finished() : super(0); }
所有的State都继承自抽象类TimerState,因为不论在哪个状态,我们都需要知道剩余时间。
4. TimerEvent
我们需要处理的事件有
- Start (通知TimerBloc定时器应该开始)
- Pause (通知TimerBloc计时器应该暂停)
- Resume(通知TimerBloc应该恢复计时器)
- Reset (通知TimerBloc定时器应重置为原始状态)
- Tick (通知TimerBloc需要更新剩余时间)
import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; /// 定时器事件 @immutable abstract class TimerEvent extends Equatable{ const TimerEvent(); @override List<Object> get props => []; } /// 开始时间 class Start extends TimerEvent { /// 定时器时间 final int duration; const Start({@required this.duration}); @override String toString() => 'Start { duration: $duration }'; } /// 暂停事件 class Paused extends TimerEvent {} /// 恢复状态 class Resumed extends TimerEvent {} /// 重置状态 class Reset extends TimerEvent {} /// 定时器事件 class Tick extends TimerEvent { /// 当前时间 final int duration; const Tick({@required this.duration}); @override List<Object> get props => [this.duration]; @override String toString() => 'Tick { duration: $duration }'; }
5. TimerBloc 实现
1.初始化状态Ready(_duration)
2.创建Ticker对象, 用户获取数据流
3.实现mapEventToState方法
4.当event为Start时, 需要开启数据流
5.创建StreamSubscription, 处理流的不同状态, 并在bloc的close方法中关闭它
6.当event为Tick时, 需要处理数据的更新
7.当event为Pause时, 需要停止定时器
8.当event为Resume时, 需要重新启动定时器
9.当event为reset时, 需要重置定时器
import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:flutter/material.dart'; import 'package:state_manage/timer/ticker.dart'; import './bloc.dart'; /// 定时器Bloc class TimerBloc extends Bloc<TimerEvent, TimerState> { /// 定时器时间 final int _duration = 60; /// 定时器数据流 final Ticker _ticker; // 流订阅 StreamSubscription<int> _tickerSubscription; TimerBloc({@required Ticker ticker}) : assert(ticker != null), _ticker = ticker; /// 初始化状态 @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* { // 运行状态 yield Running(start.duration); // 取消订阅 _tickerSubscription?.cancel(); // 创建订阅 _tickerSubscription = _ticker.tick(ticks: start.duration).listen((duration) { add(Tick(duration: duration)); }); } /// 处理定时器事件 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); } } /// 处理恢复状态 Stream<TimerState> _mapResumeToState(Resume resume) async* { if (state is Paused) { _tickerSubscription?.resume(); yield Running(state.duration); } } /// 处理重置状态 Stream<TimerState> _mapResetToState(Reset reset) async* { _tickerSubscription?.cancel(); yield Ready(_duration); } }
6. 界面实现
实现定时器显示
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'; /// 定时器 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(), ), ); } } /// 定时器页面 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'); // 秒数格式化 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'; /// 定时器背景 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
/// 定时器页面 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( // ... 省略内容 ) ], ), ); } }
添加定时器动作
timer_actions.dart
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:state_manage/timer/bloc/bloc.dart'; /// 动作 class TimerActions extends StatelessWidget { @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: _mapStateToActionButtons(timerBloc: BlocProvider.of<TimerBloc>(context)), ); } /// 创建动作按钮 /// @param timerBloc 定时器Bloc List<Widget> _mapStateToActionButtons({TimerBloc timerBloc}) { // 定时器当前状态 final TimerState currentState = timerBloc.state; // 根据不同状态返回不同视图 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 []; } } }
在界面设置动作
timer_test.dart
/// 定时器页面 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(), ) ], ) ], ), ); } }
效果图
加载全部内容