美文网首页
Flutter 状态管理 Bloc 之定时器示例

Flutter 状态管理 Bloc 之定时器示例

作者: _凌浩雨 | 来源:发表于2019-12-12 17:24 被阅读0次
    1. 依赖
    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(),
                  )
                ],
              )
            ],
          ),
        );
      }
    }
    
    • 效果图
    效果图.gif

    相关文章

      网友评论

          本文标题:Flutter 状态管理 Bloc 之定时器示例

          本文链接:https://www.haomeiwen.com/subject/izoggctx.html