dependencies:
flutter_bloc: ^2.1.1
equatable: ^1.0.1
wave: ^0.0.8
Ticker 用于产生定时器的数据流。
/// 定时器数据源
class Ticker {
/// 定时器数据源
/// @param ticks 时间
Stream tick({int ticks}){
return Stream.periodic(Duration(seconds: 1), (x) => ticks - x - 1).take(ticks);
}
}
创建 TimerBloc 用于消费Ticker, 我们需要创建定时器状态,定时器事件两个辅助类。其中定时器的状态有
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
所有的State都继承自抽象类TimerState,因为不论在哪个状态,我们都需要知道剩余时间。
我们需要处理的事件有
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
/// 定时器事件
@immutable
abstract class TimerEvent extends Equatable{
const TimerEvent();
@override
List
Ready(_duration)
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 {
/// 定时器时间
final int _duration = 60;
/// 定时器数据流
final Ticker _ticker;
// 流订阅
StreamSubscription _tickerSubscription;
TimerBloc({@required Ticker ticker})
: assert(ticker != null),
_ticker = ticker;
/// 初始化状态
@override
TimerState get initialState => Ready(_duration);
@override
Stream 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 close() {
_tickerSubscription?.cancel();
return super.close();
}
/// 处理开始事件
Stream _mapStartToState(Start start) async* {
// 运行状态
yield Running(start.duration);
// 取消订阅
_tickerSubscription?.cancel();
// 创建订阅
_tickerSubscription =
_ticker.tick(ticks: start.duration).listen((duration) {
add(Tick(duration: duration));
});
}
/// 处理定时器事件
Stream _mapTickToState(Tick tick) async* {
yield tick.duration > 0 ? Running(tick.duration) : Finished();
}
/// 处理暂停事件
Stream _mapPauseToState(Pause pause) async* {
if (state is Running) {
_tickerSubscription?.pause();
yield Paused(state.duration);
}
}
/// 处理恢复状态
Stream _mapResumeToState(Resume resume) async* {
if (state is Paused) {
_tickerSubscription?.resume();
yield Running(state.duration);
}
}
/// 处理重置状态
Stream _mapResetToState(Reset reset) async* {
_tickerSubscription?.cancel();
yield Ready(_duration);
}
}
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: [
Padding(
padding: EdgeInsets.symmetric(vertical: 100.0),
child: Center(
child: BlocBuilder(
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: [
Background(),
Column(
// ... 省略内容
)
],
),
);
}
}
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(context)),
);
}
/// 创建动作按钮
/// @param timerBloc 定时器Bloc
List _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 [];
}
}
}
/// 定时器页面
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: [
Background(),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
// ...
),
BlocBuilder(
condition: (previousState, currentState) => currentState.runtimeType != previousState.runtimeType,
builder: (ctx, state) => TimerActions(),
)
],
)
],
),
);
}
}