如何在 flutter 中重复更新倒数计时器
How to update the countdown timer repeatedly in flutter
SCENARIO
此应用程序中有一个倒数计时器,每当我打开该应用程序时,倒数计时器会自动从 5 开始并在 0 停止。
QUESTION
如何update/change 计时器编号达到 0 并自动返回 5 并再次开始(循环)。下面是 dart 和 bloc 代码。
Ticker.dart
class Ticker {
Stream<int> tick({int ticks}) {
return Stream.periodic(Duration(seconds: 1), (x) => ticks - x - 1)
.take(ticks);
}}
TimerBloc.dart
class TimerBloc extends Bloc<TimerEvent, TimerState> {
final Ticker _ticker;
final int _duration = 5;
StreamSubscription<int> _tickerSubscription;
TimerBloc({@required Ticker ticker})
: assert(ticker != null),
_ticker = ticker;
@override
TimerState get initialState => Ready(_duration);
@override
void onTransition(Transition<TimerEvent, TimerState> transition) {
super.onTransition(transition);
print(transition);
}
@override
Stream<TimerState> mapEventToState(
TimerEvent event,
) async* {
if (event is Start) {
yield* _mapStartToState(event);
}else if (event is Tick) {
yield* _mapTickToState(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();
}
}
TimerEvent.dart
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 Tick extends TimerEvent {
final int duration;
const Tick({@required this.duration});
@override
List<Object> get props => [duration];
@override
String toString() => "Tick { duration: $duration }";
}
TimerState.dart
abstract class TimerState extends Equatable {
final int duration;
const TimerState(this.duration);
@override
List<Object> get props => [duration];
}
class Ready extends TimerState {
const Ready(int duration) : super(duration);
@override
String toString() => 'Ready { 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);
}
main.dart
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Timer',
home: BlocProvider(
create: (context) => TimerBloc(ticker: Ticker()),
child: Timer(),
),);}}
class Timer extends StatefulWidget {
static const TextStyle timerTextStyle = TextStyle(
fontSize: 60,
fontWeight: FontWeight.bold,
color: Colors.black
);
@override
_TimerState createState() => _TimerState();
}
class _TimerState extends State<Timer> {
TimerBloc timerBloc;
@override
void initState() {
timerBloc = BlocProvider.of<TimerBloc>(context);
timerBloc.add(Start(duration: 5));
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Flutter Timer')),
body: Stack(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 100.0),
child: Center(
child: BlocBuilder<TimerBloc, TimerState>(
builder: (context, state) {
final String minutesStr = ((state.duration / 60) % 60)
.floor()
.toString()
.padLeft(2, '0');
final String secondsStr = (state.duration % 60)
.floor()
.toString()
.padLeft(2, '0');
return Text(
'$minutesStr:$secondsStr',
style: Timer.timerTextStyle,
);},),),),
BlocBuilder<TimerBloc, TimerState>(
condition: (previousState, state) =>
state.runtimeType != previousState.runtimeType,
builder: (context, state) => Actions(),
),],),],),);}}
class Actions extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: _mapStateToActionButtons(
timerBloc: BlocProvider.of<TimerBloc>(context),
),);}
List<Widget> _mapStateToActionButtons({
TimerBloc timerBloc,
}) {
final TimerState currentState = timerBloc.state;
if (currentState is Ready) {
return [];}}
您可以像这样创建一个永远从 5 计数到 0 的流:
const int ticks = 5;
Stream<int>.periodic(
const Duration(seconds: 1), (x) => ticks - x % (ticks + 1))
.listen((value) => print(value));
您应该可以像这样将它集成到您的代码中 class:
class Ticker {
Stream<int> tick({int ticks}) {
return Stream<int>.periodic(
const Duration(seconds: 1), (x) => ticks - x % (ticks + 1));
}
}
SCENARIO
此应用程序中有一个倒数计时器,每当我打开该应用程序时,倒数计时器会自动从 5 开始并在 0 停止。
QUESTION
如何update/change 计时器编号达到 0 并自动返回 5 并再次开始(循环)。下面是 dart 和 bloc 代码。
Ticker.dart
class Ticker {
Stream<int> tick({int ticks}) {
return Stream.periodic(Duration(seconds: 1), (x) => ticks - x - 1)
.take(ticks);
}}
TimerBloc.dart
class TimerBloc extends Bloc<TimerEvent, TimerState> {
final Ticker _ticker;
final int _duration = 5;
StreamSubscription<int> _tickerSubscription;
TimerBloc({@required Ticker ticker})
: assert(ticker != null),
_ticker = ticker;
@override
TimerState get initialState => Ready(_duration);
@override
void onTransition(Transition<TimerEvent, TimerState> transition) {
super.onTransition(transition);
print(transition);
}
@override
Stream<TimerState> mapEventToState(
TimerEvent event,
) async* {
if (event is Start) {
yield* _mapStartToState(event);
}else if (event is Tick) {
yield* _mapTickToState(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();
}
}
TimerEvent.dart
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 Tick extends TimerEvent {
final int duration;
const Tick({@required this.duration});
@override
List<Object> get props => [duration];
@override
String toString() => "Tick { duration: $duration }";
}
TimerState.dart
abstract class TimerState extends Equatable {
final int duration;
const TimerState(this.duration);
@override
List<Object> get props => [duration];
}
class Ready extends TimerState {
const Ready(int duration) : super(duration);
@override
String toString() => 'Ready { 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);
}
main.dart
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Timer',
home: BlocProvider(
create: (context) => TimerBloc(ticker: Ticker()),
child: Timer(),
),);}}
class Timer extends StatefulWidget {
static const TextStyle timerTextStyle = TextStyle(
fontSize: 60,
fontWeight: FontWeight.bold,
color: Colors.black
);
@override
_TimerState createState() => _TimerState();
}
class _TimerState extends State<Timer> {
TimerBloc timerBloc;
@override
void initState() {
timerBloc = BlocProvider.of<TimerBloc>(context);
timerBloc.add(Start(duration: 5));
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Flutter Timer')),
body: Stack(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 100.0),
child: Center(
child: BlocBuilder<TimerBloc, TimerState>(
builder: (context, state) {
final String minutesStr = ((state.duration / 60) % 60)
.floor()
.toString()
.padLeft(2, '0');
final String secondsStr = (state.duration % 60)
.floor()
.toString()
.padLeft(2, '0');
return Text(
'$minutesStr:$secondsStr',
style: Timer.timerTextStyle,
);},),),),
BlocBuilder<TimerBloc, TimerState>(
condition: (previousState, state) =>
state.runtimeType != previousState.runtimeType,
builder: (context, state) => Actions(),
),],),],),);}}
class Actions extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: _mapStateToActionButtons(
timerBloc: BlocProvider.of<TimerBloc>(context),
),);}
List<Widget> _mapStateToActionButtons({
TimerBloc timerBloc,
}) {
final TimerState currentState = timerBloc.state;
if (currentState is Ready) {
return [];}}
您可以像这样创建一个永远从 5 计数到 0 的流:
const int ticks = 5;
Stream<int>.periodic(
const Duration(seconds: 1), (x) => ticks - x % (ticks + 1))
.listen((value) => print(value));
您应该可以像这样将它集成到您的代码中 class:
class Ticker {
Stream<int> tick({int ticks}) {
return Stream<int>.periodic(
const Duration(seconds: 1), (x) => ticks - x % (ticks + 1));
}
}