[Flutter] Pedometer 라이브러리 까보기

김영진·2021년 9월 9일
2

Flutter Pedometer

목록 보기
1/3
post-custom-banner

목적

만보기 기능을 담은 개인 앱을 만들어보려고 한다.
만보기 기능은 삼성헬스앱과 애플앱(이름모름)에서 제공하거나 플러터에 있는 Pedometer라이브러리로 구현할 수 있는것으로 보인다.
*뇌피셜 : 삼성헬스앱 말고 네이티브에서 자체적으로 측정해서 가져올수도 있지 않을까 싶다.
아무튼 Pedometer를 사용해서 만보기 앱을 구현해보고 싶었는데 걸음수가 이전날의 데이터와 합쳐진 걸음수가 디스플레이되고, 기능이 즉각적으로 작동하지 않아서 개인적으로 마음에 안들었다. 그래서 어쩔수 없이 그렇게 작동하는건지 등등 리서치를 위해 일단 Pedometer 라이브러리를 까보려고한다.

내용

pedometer.dart

import 'dart:async';
// EventChannel을 사용하기 위해서 import
import 'package:flutter/services.dart';
import 'dart:io' show Platform;
// 상태값을 정수로 준것으로 보인다.
// 멈춘상태이면 0, 걷는상태이면 1인것으로 추측됨
const int _stopped = 0, _walking = 1;
class Pedometer {
// 얼마전 네이티브 코드 연동을 위해 MethodChannel을 사용했었는데 나중에 EventChannel과의 차이점도 공부해봐야 할듯 하다.
// 추후 공부예정 1
// 네이티브와 EventChannel로 연결하는 부분임을 알수있다.
  static const EventChannel _stepDetectionChannel =
      const EventChannel('step_detection');
  static const EventChannel _stepCountChannel =
      const EventChannel('step_count');

// PedestrianStatus를 반환하는 StreamController 선언
// 추후 공부예정 2
  static StreamController<PedestrianStatus> _androidPedestrianController =
      StreamController.broadcast();

  /// Returns one step at a time.
  // 한번에 한발자국씩 리턴합니다.
  /// Events come every time a step is detected.
  // 걸음이 탐지될때마다 이벤트가 탐지됩니다.
//  _stepDetection이 recieveBroadcastStream으로 값을 받고, 받은값을 PedestrianStatus에 정의해놓은 네임드 생성자로 정제하여 PedestrianStatus 객체를 리턴하는 스트림으로 선언
// 요약하면 결과값을 PedstrianStatus로 내입맞에 맞게 바꿔서 리턴하는 스트림을 메소드 채널과 연결해서 선언했다고 보면됨
  static Stream<PedestrianStatus> get pedestrianStatusStream {
    Stream<PedestrianStatus> stream = _stepDetectionChannel
        .receiveBroadcastStream()
        .map((event) => PedestrianStatus._(event));
// 만약 안드로이드라면 아래에 정의해놓은 안드로이드 스트림으로 래핑해서 리턴
    if (Platform.isAndroid) return _androidStream(stream);
// IOS는 지금 해당 스트림 리턴
    return stream;
  }

  /// Transformed stream for the Android platform
  // 안드로이드를 위한 변형스트림
  static Stream<PedestrianStatus> _androidStream(
      Stream<PedestrianStatus> stream) {
    /// Init a timer and a status
// 타이머와 상태를 초기화한다.
    Timer? t;
    int? pedestrianStatus;

    /// Listen for events on the original stream
    // 원래 스트림(위에 정의한 스트림)에서 이벤트를 리슨한다
    /// Transform these events by using the timer
    // 타이머를 사용하여 이벤트를 변환한다.
    // 상단에 정의한 스트림을 파라미터로 받아서 리슨
    stream.listen((dynamic e) {
      /// If an event is received it means the status is 'walking'
      // 만약 이벤트가 들어오면 걷는 상태
      /// If the timer has been started, it should be cancelled
      // 타이머가 시작되었다면 타이머를 취소해야한다. (무슨말이지?)
      /// to prevent sending out additional 'walking' events
      // 추가적인 걷기 이벤트가 보내지는것을 막는다
      // 타이머가 널이 아니면(타이머가 있으면)
      if (t != null) {
      // 타이머는 취소
        t!.cancel();

        /// If a previous status was either not set yet, or was 'stopped'
        // 만약 이전의 상태가 설정되어있지 않거나 멈춤 상태라면
        /// then a 'walking' event should be emitted.
        // 걷기 이벤트가 방출된다.
        if (pedestrianStatus == null || pedestrianStatus == _stopped) {
// 걷기 상태를 스트림으로 보낸다.
_androidPedestrianController.add(PedestrianStatus._(_walking));
// 지역변수의 상태값에 현재 상태를 등록한다.(다음번 조건문에 활용됨)
          pedestrianStatus = _walking;
        }
      }

      /// After receiving an event, start a timer for 2 seconds, after
      // 이벤트를 수신한 후 2초동안 타이머를 시작한다.
      /// which a 'stopped' event is emitted. If it manages to go through,
      // 멈춤 이벤트가 방출된다. 정상적으로 작동된다면.
      /// it is because no events were received for the 2 second duration
      // 2초동안 아무 이벤트가 수신되지 않기 때문이다.
      // 쉽게말해서 그냥 2초동안 이벤트 안들어오면 멈춤상태 등록되는거임
      t = Timer(Duration(seconds: 2), () {
        _androidPedestrianController.add(PedestrianStatus._(_stopped));
        pedestrianStatus = _stopped;
      });
    });

// 안드로이드 전용 컨트롤러 리턴
    return _androidPedestrianController.stream;
  }

  /// Returns the steps taken since last system boot.
  // 마지막으로 시스템이 부트된 이후에 걸음수를 가져온다.
  /// Events may come with a delay.
  // 이벤트는 지연되서 올지도 모른다.
  // 아까 상단에 정의한 이벤트채널에 StepCount객체를 반환하는 게터를 설정
  static Stream<StepCount> get stepCountStream => _stepCountChannel
      .receiveBroadcastStream()
      .map((event) => StepCount._(event));
}

정리하면,

EventChannel _stepDetectionChannel,
EventChannel _stepCountChannel
을 선언,

pedestrianStatusStream 게터 설정(이벤트 채널과 연결됨),

안드로이드 전용 스트림 생성하는 메소드(pedestrianStatusStream래핑)

stepCountStream 게터 설정(이벤트 채널과 연결됨)

/// A DTO for steps taken containing the number of steps taken.
// StepCount Model임
class StepCount {
  late DateTime _timeStamp;
  int _steps = 0;

// 네임드 생성자
  StepCount._(dynamic e) {
    _steps = e as int;
    _timeStamp = DateTime.now();
  }
// 게터
  int get steps => _steps;

  DateTime get timeStamp => _timeStamp;
  
  
  String toString() =>
      'Steps taken: $_steps at ${_timeStamp.toIso8601String()}';

그냥 모델

/// A DTO for steps taken containing a detected step and its corresponding
/// status, i.e. walking, stopped or unknown.
class PedestrianStatus {
  static const _WALKING = 'walking';
  static const _STOPPED = 'stopped';
  static const _UNKNOWN = 'unknown';

  static const Map<int, String> _STATUSES = {
    _stopped: _STOPPED,
    _walking: _WALKING
  };

  late DateTime _timeStamp;
  String _status = _UNKNOWN;

  PedestrianStatus._(dynamic t) {
    int _type = t as int;
    _status = _STATUSES[_type]!;
    _timeStamp = DateTime.now();
  }

  String get status => _status;

  DateTime get timeStamp => _timeStamp;

  
  String toString() => 'Status: $_status at ${_timeStamp.toIso8601String()}';
}

모델

중간정리

이벤트 채널 스트림으로 데이터를 적당히 파싱해서 넘겨주는 소스인듯,
네이티브에서 연결된 이벤트 채널 소스를 한번 봐야할듯 하다.

우선 안드로이드 부터 소스를 보려고 하는데,
다음번 글에서 분석해보자.

profile
2021.05.03) Flutter, BlockChain, Sports, StartUp
post-custom-banner

2개의 댓글

comment-user-thumbnail
2022년 8월 22일

큰 도움이 됐습니다. 감사합니다 👍🏼

1개의 답글