Commit 17a806898123aa4215fd32331797f156ef980868

Authored by liangchengyou
1 parent f138a761

feat:倒计时功能开发

lib/common/blocs/timerbloc/timer_bloc.dart 0 → 100644
  1 +import 'dart:async';
  2 +
  3 +import 'package:flutter/cupertino.dart';
  4 +import 'package:equatable/equatable.dart';
  5 +import 'package:flutter_bloc/flutter_bloc.dart';
  6 +import 'package:wow_english/common/widgets/timer_ticker.dart';
  7 +
  8 +part 'timer_event.dart';
  9 +part 'timer_state.dart';
  10 +
  11 +class TimerBloc extends Bloc<TimerEvent, TimerState> {
  12 + /// 定时器时间
  13 + static const int _duration = 60;
  14 + /// 定时器数据流
  15 + final TimerTicker _ticker;
  16 + // 流订阅
  17 + StreamSubscription<int>? _tickerSubscription;
  18 + TimerBloc({required TimerTicker ticker})
  19 + :_ticker = ticker,
  20 + super(const TimerInitial(_duration)) {
  21 + on<StartEvent>(_onStarted);
  22 + on<TickEvent>(_onTicked);
  23 + on<PausedEvent>(_onPaused);
  24 + on<ResumedEvent>(_onResumed);
  25 + on<ResetEvent>(_onReset);
  26 + }
  27 +
  28 + @override
  29 + Future<void> close() {
  30 + _tickerSubscription?.cancel();
  31 + return super.close();
  32 + }
  33 +
  34 + /// 开始计时
  35 + void _onStarted(StartEvent event,Emitter<TimerState> emitter) async {
  36 + emitter(RunningState(event.duration));
  37 + _tickerSubscription?.cancel();
  38 + _tickerSubscription = _ticker
  39 + .tick(ticks: event.duration)
  40 + .listen((event) => add(TickEvent(duration: event)));
  41 + }
  42 +
  43 + /// 计时中,判断是否剩余时间
  44 + void _onTicked(TickEvent event,Emitter<TimerState> emitter) async {
  45 + emitter(event.duration>0?RunningState(event.duration):const FinishedState());
  46 + }
  47 +
  48 + ///暂停,判断当时是否state为PausedState类型,之后暂停订阅,发送暂停state
  49 + void _onPaused(PausedEvent event, Emitter<TimerState> emit) async{
  50 + if( state is RunningState) {
  51 + _tickerSubscription?.pause();
  52 + emit(PausedState(state.duration));
  53 + }
  54 + }
  55 +
  56 + /// 恢复,我们判断当前state为暂停状态后,进行恢复
  57 + void _onResumed(ResumedEvent event, Emitter<TimerState> emit) async{
  58 + if( state is PausedState) {
  59 + _tickerSubscription?.resume();
  60 + emit(RunningState(state.duration));
  61 + }
  62 + }
  63 +
  64 + /// 重置,初始化
  65 + void _onReset(ResetEvent event ,Emitter<TimerState> emit) async{
  66 + _tickerSubscription?.cancel();
  67 + emit(const TimerInitial(_duration));
  68 + }
  69 +}
... ...
lib/common/blocs/timerbloc/timer_event.dart 0 → 100644
  1 +part of 'timer_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class TimerEvent extends Equatable {
  5 + const TimerEvent();
  6 +
  7 + @override
  8 + List<Object> get props => [];
  9 +}
  10 +
  11 +/// 开始事件
  12 +class StartEvent extends TimerEvent {
  13 + /// 定时器时间
  14 + final int duration;
  15 +
  16 + const StartEvent({required this.duration});
  17 +}
  18 +
  19 +/// 定时器事件
  20 +class TickEvent extends TimerEvent {
  21 + const TickEvent({required this.duration});
  22 + /// 当前时间
  23 + final int duration;
  24 +
  25 + @override
  26 + List<Object> get props => [duration];
  27 +}
  28 +
  29 +/// 暂停事件
  30 +class PausedEvent extends TimerEvent {}
  31 +
  32 +/// 恢复状态
  33 +class ResumedEvent extends TimerEvent {}
  34 +
  35 +/// 重置状态
  36 +class ResetEvent extends TimerEvent {}
0 37 \ No newline at end of file
... ...
lib/common/blocs/timerbloc/timer_state.dart 0 → 100644
  1 +part of 'timer_bloc.dart';
  2 +
  3 +@immutable
  4 +abstract class TimerState extends Equatable {
  5 + /// 时间
  6 + final int duration;
  7 + /// 构造方法
  8 + const TimerState(this.duration);
  9 + @override
  10 + List<Object> get props => [duration];
  11 +}
  12 +
  13 +class TimerInitial extends TimerState {
  14 + const TimerInitial(super.duration);
  15 +}
  16 +
  17 +/// 暂停状态
  18 +class PausedState extends TimerState {
  19 + const PausedState(super.duration);
  20 +}
  21 +
  22 +/// 运行状态
  23 +class RunningState extends TimerState {
  24 + const RunningState(super.duration);
  25 +}
  26 +
  27 +/// 完成状态
  28 +class FinishedState extends TimerState{
  29 + const FinishedState() : super(0);
  30 +}
  31 +
  32 +class BtnState extends TimerState {
  33 + const BtnState(super.duration);
  34 +}
0 35 \ No newline at end of file
... ...
lib/common/widgets/timer_ticker.dart 0 → 100644
  1 +///定时器数据源
  2 +class TimerTicker {
  3 + const TimerTicker();
  4 + Stream<int> tick({required int ticks}) {
  5 + return Stream.periodic(const Duration(seconds: 1), (x) => ticks - x - 1)
  6 + .take(ticks);
  7 + }
  8 +}
0 9 \ No newline at end of file
... ...
lib/login/loginpage/bloc/login_bloc.dart
1   -import 'dart:async';
2   -
3 1 import 'package:flutter/cupertino.dart';
4   -import 'package:flutter/foundation.dart';
5 2 import 'package:flutter_bloc/flutter_bloc.dart';
6 3  
7 4 part 'login_event.dart';
... ... @@ -35,7 +32,6 @@ class LoginBloc extends Bloc&lt;LoginEvent, LoginState&gt; {
35 32 bool get agreement => _agreement;
36 33 LoginType get loginType => _loginType;
37 34 bool get canSendSms => _canSendSms;
38   - int get countDown => _countDown;
39 35 bool get sendSmsIng => _sendSmsIng;
40 36  
41 37 LoginBloc() : super(LoginInitial()) {
... ... @@ -45,8 +41,6 @@ class LoginBloc extends Bloc&lt;LoginEvent, LoginState&gt; {
45 41 on<AgreementChangeEvent>(_agreementTypeChange);
46 42 on<ForgetPasswordEvent>(_forgetPassword);
47 43 on<CheckFieldChangeEvent>(_checkFiledChange);
48   - on<CountDownEvent>(_countDownChange);
49   - on<CancelCountDownEvent>(_cancelTimer);
50 44 }
51 45  
52 46  
... ... @@ -111,16 +105,6 @@ class LoginBloc extends Bloc&lt;LoginEvent, LoginState&gt; {
111 105  
112 106 }
113 107  
114   - ///倒计时
115   - void _countDownChange(CountDownEvent event, Emitter<LoginState> emitter) {
116   - _sendSmsIng = !_sendSmsIng;
117   - emitter(CountDownNumChangeState());
118   - }
119   -
120   - void _cancelTimer(CancelCountDownEvent event,Emitter<LoginState> emitter) {
121   -
122   - }
123   -
124 108 ///登陆状态判断
125 109 bool _loginStateChange() {
126 110 if (_agreement) {
... ...
lib/login/loginpage/login_page.dart
... ... @@ -5,6 +5,7 @@ import &#39;package:flutter_screenutil/flutter_screenutil.dart&#39;;
5 5 import 'package:wow_english/common/extension/string_extension.dart';
6 6 import 'package:wow_english/common/widgets/textfield_customer_widget.dart';
7 7 import 'package:wow_english/login/loginpage/bloc/login_bloc.dart';
  8 +import 'package:wow_english/login/loginpage/time_widget.dart';
8 9 import 'package:wow_english/route/route.dart';
9 10  
10 11 class LoginPage extends StatelessWidget {
... ... @@ -163,7 +164,7 @@ class LoginPage extends StatelessWidget {
163 164 );
164 165  
165 166 Widget _buildSmsViewWidget()=> BlocBuilder<LoginBloc,LoginState>(
166   - builder: (context,state){
  167 + builder: (context,state) {
167 168 final bloc = BlocProvider.of<LoginBloc>(context);
168 169 return Padding(
169 170 padding: EdgeInsets.symmetric(horizontal: 135.w),
... ... @@ -198,27 +199,8 @@ class LoginPage extends StatelessWidget {
198 199 controller: bloc.checkNumController,
199 200 )
200 201 ),
201   - GestureDetector(
202   - onTap: () {
203   - if (bloc.canSendSms) {
204   - bloc.add(CountDownEvent());
205   - }
206   - },
207   - child: Container(
208   - decoration: BoxDecoration(
209   - image: DecorationImage(
210   - image: AssetImage(
211   - bloc.canSendSms?'securitycode'.assetPng:'securitycode_dis'.assetPng
212   - ),
213   - fit: BoxFit.fill
214   - ),
215   - ),
216   - padding: const EdgeInsets.symmetric(horizontal:12.0,vertical: 15.0),
217   - child: Text(
218   - !bloc.sendSmsIng ? '获取验证码':'${bloc.countDown}s后在次获取'
219   - ),
220   - ),
221   - )
  202 + TimerWidget(
  203 + canSendSms: bloc.canSendSms)
222 204 ],
223 205 )
224 206 ],
... ...
lib/login/loginpage/time_widget.dart 0 → 100644
  1 +import 'package:flutter/cupertino.dart';
  2 +import 'package:flutter_bloc/flutter_bloc.dart';
  3 +import 'package:wow_english/common/blocs/timerbloc/timer_bloc.dart';
  4 +import 'package:wow_english/common/extension/string_extension.dart';
  5 +import 'package:wow_english/common/widgets/timer_ticker.dart';
  6 +
  7 +class TimerWidget extends StatefulWidget {
  8 + const TimerWidget({super.key, required this.canSendSms});
  9 +
  10 + final bool canSendSms;
  11 +
  12 + @override
  13 + State<StatefulWidget> createState() {
  14 + return _TimerWidgetState();
  15 + }
  16 +}
  17 +
  18 +class _TimerWidgetState extends State<TimerWidget> {
  19 + late bool sendSmsIng;
  20 +
  21 + @override
  22 + void initState() {
  23 + super.initState();
  24 + sendSmsIng = false;
  25 + }
  26 +
  27 + @override
  28 + Widget build(BuildContext context) {
  29 + return BlocProvider(
  30 + create: (_) => TimerBloc(ticker: const TimerTicker()),
  31 + child: _buildCountdownWidget(),
  32 + );
  33 + }
  34 +
  35 + Widget _buildCountdownWidget() => BlocBuilder<TimerBloc,TimerState>(
  36 + buildWhen: (prev, state) => prev.runtimeType != state.runtimeType,
  37 + builder: (context,state) {
  38 + final bloc = BlocProvider.of<TimerBloc>(context);
  39 + final duration = bloc.state.duration;
  40 + final secondsStr = (duration % 60).floor().toString().padLeft(2, '0');
  41 + if (state is TimerInitial) {
  42 + sendSmsIng = true;
  43 + } else {
  44 + sendSmsIng = false;
  45 + }
  46 + return GestureDetector(
  47 + onTap: () {
  48 + if (widget.canSendSms && !sendSmsIng) {
  49 + bloc.add(ResetEvent());
  50 + bloc.add(StartEvent(duration: bloc.state.duration));
  51 + }
  52 + },
  53 + child: Container(
  54 + decoration: BoxDecoration(
  55 + image: DecorationImage(
  56 + image: AssetImage(
  57 + widget.canSendSms?'securitycode'.assetPng:'securitycode_dis'.assetPng
  58 + ),
  59 + fit: BoxFit.fill
  60 + ),
  61 + ),
  62 + padding: const EdgeInsets.symmetric(horizontal:12.0,vertical: 15.0),
  63 + child: Row(
  64 + children: [
  65 + if (state is TimerInitial)...[
  66 + const Text('获取验证码')
  67 + ],
  68 + if (state is RunningState)...[
  69 + Text('${secondsStr}s倒计时')
  70 + ],
  71 + if (state is FinishedState) ...[
  72 + const Text('获取验证码')
  73 + ]
  74 + ],
  75 + ),
  76 + ),
  77 + );
  78 + },
  79 + );
  80 +}
0 81 \ No newline at end of file
... ...