diff --git a/lib/common/blocs/timerbloc/timer_bloc.dart b/lib/common/blocs/timerbloc/timer_bloc.dart new file mode 100644 index 0000000..bc3e37f --- /dev/null +++ b/lib/common/blocs/timerbloc/timer_bloc.dart @@ -0,0 +1,69 @@ +import 'dart:async'; + +import 'package:flutter/cupertino.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:wow_english/common/widgets/timer_ticker.dart'; + +part 'timer_event.dart'; +part 'timer_state.dart'; + +class TimerBloc extends Bloc { + /// 定时器时间 + static const int _duration = 60; + /// 定时器数据流 + final TimerTicker _ticker; + // 流订阅 + StreamSubscription? _tickerSubscription; + TimerBloc({required TimerTicker ticker}) + :_ticker = ticker, + super(const TimerInitial(_duration)) { + on(_onStarted); + on(_onTicked); + on(_onPaused); + on(_onResumed); + on(_onReset); + } + + @override + Future close() { + _tickerSubscription?.cancel(); + return super.close(); + } + + /// 开始计时 + void _onStarted(StartEvent event,Emitter emitter) async { + emitter(RunningState(event.duration)); + _tickerSubscription?.cancel(); + _tickerSubscription = _ticker + .tick(ticks: event.duration) + .listen((event) => add(TickEvent(duration: event))); + } + + /// 计时中,判断是否剩余时间 + void _onTicked(TickEvent event,Emitter emitter) async { + emitter(event.duration>0?RunningState(event.duration):const FinishedState()); + } + + ///暂停,判断当时是否state为PausedState类型,之后暂停订阅,发送暂停state + void _onPaused(PausedEvent event, Emitter emit) async{ + if( state is RunningState) { + _tickerSubscription?.pause(); + emit(PausedState(state.duration)); + } + } + + /// 恢复,我们判断当前state为暂停状态后,进行恢复 + void _onResumed(ResumedEvent event, Emitter emit) async{ + if( state is PausedState) { + _tickerSubscription?.resume(); + emit(RunningState(state.duration)); + } + } + + /// 重置,初始化 + void _onReset(ResetEvent event ,Emitter emit) async{ + _tickerSubscription?.cancel(); + emit(const TimerInitial(_duration)); + } +} diff --git a/lib/common/blocs/timerbloc/timer_event.dart b/lib/common/blocs/timerbloc/timer_event.dart new file mode 100644 index 0000000..68534e1 --- /dev/null +++ b/lib/common/blocs/timerbloc/timer_event.dart @@ -0,0 +1,36 @@ +part of 'timer_bloc.dart'; + +@immutable +abstract class TimerEvent extends Equatable { + const TimerEvent(); + + @override + List get props => []; +} + +/// 开始事件 +class StartEvent extends TimerEvent { + /// 定时器时间 + final int duration; + + const StartEvent({required this.duration}); +} + +/// 定时器事件 +class TickEvent extends TimerEvent { + const TickEvent({required this.duration}); + /// 当前时间 + final int duration; + + @override + List get props => [duration]; +} + +/// 暂停事件 +class PausedEvent extends TimerEvent {} + +/// 恢复状态 +class ResumedEvent extends TimerEvent {} + +/// 重置状态 +class ResetEvent extends TimerEvent {} \ No newline at end of file diff --git a/lib/common/blocs/timerbloc/timer_state.dart b/lib/common/blocs/timerbloc/timer_state.dart new file mode 100644 index 0000000..538badb --- /dev/null +++ b/lib/common/blocs/timerbloc/timer_state.dart @@ -0,0 +1,34 @@ +part of 'timer_bloc.dart'; + +@immutable +abstract class TimerState extends Equatable { + /// 时间 + final int duration; + /// 构造方法 + const TimerState(this.duration); + @override + List get props => [duration]; +} + +class TimerInitial extends TimerState { + const TimerInitial(super.duration); +} + +/// 暂停状态 +class PausedState extends TimerState { + const PausedState(super.duration); +} + +/// 运行状态 +class RunningState extends TimerState { + const RunningState(super.duration); +} + +/// 完成状态 +class FinishedState extends TimerState{ + const FinishedState() : super(0); +} + +class BtnState extends TimerState { + const BtnState(super.duration); +} \ No newline at end of file diff --git a/lib/common/widgets/timer_ticker.dart b/lib/common/widgets/timer_ticker.dart new file mode 100644 index 0000000..c47167c --- /dev/null +++ b/lib/common/widgets/timer_ticker.dart @@ -0,0 +1,8 @@ +///定时器数据源 +class TimerTicker { + const TimerTicker(); + Stream tick({required int ticks}) { + return Stream.periodic(const Duration(seconds: 1), (x) => ticks - x - 1) + .take(ticks); + } +} \ No newline at end of file diff --git a/lib/login/loginpage/bloc/login_bloc.dart b/lib/login/loginpage/bloc/login_bloc.dart index d171698..45184fb 100644 --- a/lib/login/loginpage/bloc/login_bloc.dart +++ b/lib/login/loginpage/bloc/login_bloc.dart @@ -1,7 +1,4 @@ -import 'dart:async'; - import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; part 'login_event.dart'; @@ -35,7 +32,6 @@ class LoginBloc extends Bloc { bool get agreement => _agreement; LoginType get loginType => _loginType; bool get canSendSms => _canSendSms; - int get countDown => _countDown; bool get sendSmsIng => _sendSmsIng; LoginBloc() : super(LoginInitial()) { @@ -45,8 +41,6 @@ class LoginBloc extends Bloc { on(_agreementTypeChange); on(_forgetPassword); on(_checkFiledChange); - on(_countDownChange); - on(_cancelTimer); } @@ -111,16 +105,6 @@ class LoginBloc extends Bloc { } - ///倒计时 - void _countDownChange(CountDownEvent event, Emitter emitter) { - _sendSmsIng = !_sendSmsIng; - emitter(CountDownNumChangeState()); - } - - void _cancelTimer(CancelCountDownEvent event,Emitter emitter) { - - } - ///登陆状态判断 bool _loginStateChange() { if (_agreement) { diff --git a/lib/login/loginpage/login_page.dart b/lib/login/loginpage/login_page.dart index 79a7822..2df802f 100644 --- a/lib/login/loginpage/login_page.dart +++ b/lib/login/loginpage/login_page.dart @@ -5,6 +5,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/common/widgets/textfield_customer_widget.dart'; import 'package:wow_english/login/loginpage/bloc/login_bloc.dart'; +import 'package:wow_english/login/loginpage/time_widget.dart'; import 'package:wow_english/route/route.dart'; class LoginPage extends StatelessWidget { @@ -163,7 +164,7 @@ class LoginPage extends StatelessWidget { ); Widget _buildSmsViewWidget()=> BlocBuilder( - builder: (context,state){ + builder: (context,state) { final bloc = BlocProvider.of(context); return Padding( padding: EdgeInsets.symmetric(horizontal: 135.w), @@ -198,27 +199,8 @@ class LoginPage extends StatelessWidget { controller: bloc.checkNumController, ) ), - GestureDetector( - onTap: () { - if (bloc.canSendSms) { - bloc.add(CountDownEvent()); - } - }, - child: Container( - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage( - bloc.canSendSms?'securitycode'.assetPng:'securitycode_dis'.assetPng - ), - fit: BoxFit.fill - ), - ), - padding: const EdgeInsets.symmetric(horizontal:12.0,vertical: 15.0), - child: Text( - !bloc.sendSmsIng ? '获取验证码':'${bloc.countDown}s后在次获取' - ), - ), - ) + TimerWidget( + canSendSms: bloc.canSendSms) ], ) ], diff --git a/lib/login/loginpage/time_widget.dart b/lib/login/loginpage/time_widget.dart new file mode 100644 index 0000000..99755aa --- /dev/null +++ b/lib/login/loginpage/time_widget.dart @@ -0,0 +1,80 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:wow_english/common/blocs/timerbloc/timer_bloc.dart'; +import 'package:wow_english/common/extension/string_extension.dart'; +import 'package:wow_english/common/widgets/timer_ticker.dart'; + +class TimerWidget extends StatefulWidget { + const TimerWidget({super.key, required this.canSendSms}); + + final bool canSendSms; + + @override + State createState() { + return _TimerWidgetState(); + } +} + +class _TimerWidgetState extends State { + late bool sendSmsIng; + + @override + void initState() { + super.initState(); + sendSmsIng = false; + } + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => TimerBloc(ticker: const TimerTicker()), + child: _buildCountdownWidget(), + ); + } + + Widget _buildCountdownWidget() => BlocBuilder( + buildWhen: (prev, state) => prev.runtimeType != state.runtimeType, + builder: (context,state) { + final bloc = BlocProvider.of(context); + final duration = bloc.state.duration; + final secondsStr = (duration % 60).floor().toString().padLeft(2, '0'); + if (state is TimerInitial) { + sendSmsIng = true; + } else { + sendSmsIng = false; + } + return GestureDetector( + onTap: () { + if (widget.canSendSms && !sendSmsIng) { + bloc.add(ResetEvent()); + bloc.add(StartEvent(duration: bloc.state.duration)); + } + }, + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + widget.canSendSms?'securitycode'.assetPng:'securitycode_dis'.assetPng + ), + fit: BoxFit.fill + ), + ), + padding: const EdgeInsets.symmetric(horizontal:12.0,vertical: 15.0), + child: Row( + children: [ + if (state is TimerInitial)...[ + const Text('获取验证码') + ], + if (state is RunningState)...[ + Text('${secondsStr}s倒计时') + ], + if (state is FinishedState) ...[ + const Text('获取验证码') + ] + ], + ), + ), + ); + }, + ); +} \ No newline at end of file