content: import 'dart:async';
  
  import 'package:flutter/foundation.dart';
  
  typedef EventCallback<Data> = Future<dynamic> Function(Data data);
  
  abstract class BaseEvent<EventType, Data> {
    EventType type;
    Data data;
  
    /// Constructor.
    BaseEvent(this.type, this.data);
  
    /// Consume this event.
    @visibleForTesting
    Future<dynamic> consume() async {
      final cb = findCallback(type);
      if (cb == null) {
        return null;
      } else {
        return cb(data);
      }
    }
  
    EventCallback<Data>? findCallback(EventType type);
  }
  
  abstract class BaseEventLoop<EventType, Data> {
    final List<BaseEvent<EventType, Data>> _evts = [];
    Timer? _timer;
  
    List<BaseEvent<EventType, Data>> get evts => _evts;
  
    Future<void> onReady() async {
      // Poll every 100ms.
      _timer = Timer.periodic(Duration(milliseconds: 100), _handleTimer);
    }
  
    /// An Event is about to be consumed.
    Future<void> onPreConsume(BaseEvent<EventType, Data> evt) async {}
    /// An Event was consumed.
    Future<void> onPostConsume(BaseEvent<EventType, Data> evt) async {}
    /// Events are all handled and cleared.
    Future<void> onEventsClear() async {}
    /// Events start to consume.
    Future<void> onEventsStartConsuming() async {}
  
    Future<void> _handleTimer(Timer timer) async {
        if (_evts.isEmpty) {
          return;
        }
        timer.cancel();
        _timer = null;
        // Handle the logic.
        await onEventsStartConsuming();
        while (_evts.isNotEmpty) {
          final evt = _evts.first;
          _evts.remove(evt);
          await onPreConsume(evt);
          await evt.consume();
          await onPostConsume(evt);
        }
        await onEventsClear();
        // Now events are all processed.
        _timer = Timer.periodic(Duration(milliseconds: 100), _handleTimer);
    }
  
    Future<void> close() async {
      _timer?.cancel();
    }
  
    void pushEvent(BaseEvent<EventType, Data> evt) {
      _evts.add(evt);
    }
  
    void clear() {
      _evts.clear();
    }
  }
  