fit_kit 1.1.1

  • Readme
  • Changelog
  • Example
  • Installing
  • 93

FitKit () #

pub package

Flutter plugin for reading health and fitness data. Wraps HealthKit on iOS and GoogleFit on Android.

Usage #

To use this plugin, add fit_kit as a dependency in your pubspec.yaml file.

Getting Started #

Android

Enable Fitness API and obtain an OAuth 2.0 client ID.

iOS

Enable HealthKit and add NSHealthShareUsageDescription key to the Info.plist file.

Sample Usage #

If you're using more than one DataType it's advised to call requestPermissions with all the data types once, otherwise iOS HealthKit will ask to approve every permission one by one in separate screens.

import 'package:fit_kit/fit_kit.dart';

void read() async {
  final results = await FitKit.read(
    DataType.HEART_RATE,
    dateFrom: DateTime.now().subtract(Duration(days: 5)),
    dateTo: DateTime.now(),
  );
}

void readLast() async {
  final result = await FitKit.readLast(DataType.HEIGHT);
}

void readAll() async {
  if (await FitKit.requestPermissions(DataType.values)) {
    for (DataType type in DataType.values) {
      final results = await FitKit.read(
        type,
        dateFrom: DateTime.now().subtract(Duration(days: 5)),
        dateTo: DateTime.now(),
      );
    }
  }
}

Supported data types #

These are currently available data types and their corresponding GoogleFit/HealthKit types.

Data TypeAndroid (GoogleFit)iOS (HealthKit)Unit
HEART_RATETYPE_HEART_RATE_BPMheartRatecount/min
STEP_COUNTTYPE_STEP_COUNT_DELTAstepCountcount
HEIGHTTYPE_HEIGHTheightmeter
WEIGHTTYPE_WEIGHTbodyMasskilogram
DISTANCETYPE_DISTANCE_DELTAdistanceWalkingRunningmeter
ENERGYTYPE_CALORIES_EXPENDEDactiveEnergyBurnedkilocalorie
WATERTYPE_HYDRATIONdietaryWater >= iOS 9liter
STAND_TIMENot supportedappleStandTime >= iOS 13minute
EXERCISE_TIMENot supportedappleExerciseTime >= iOS 9.3minute
SLEEPFitnessActivities.SLEEPsleepAnalysisiOS:
0 - inBed
1 - asleep
2 - awake
Android:
72 - SLEEP
109 - SLEEP_LIGHT
110 - SLEEP_DEEP
111 - SLEEP_REM
112 - SLEEP_AWAKE

BE AWARE #

  • Calling await FitKit.read(dataType) without any extra parameters can lead to FAILED BINDER TRANSACTION on Android devices because of the data batch size being too large.

There's some differences on iOS for these methods:

  • FitKit.hasPermissions - false means no, true means user has approved or declined permissions.

    To help prevent possible leaks of sensitive health information, your app cannot determine whether or not a user has granted permission to read data. If you are not given permission, it simply appears as if there is no data of the requested type in the HealthKit store. https://developer.apple.com/documentation/healthkit/hkhealthstore/1614154-authorizationstatus

  • FitKit.revokePermissions - isn't supported by HealthKit, method does nothing.

1.1.1 #

  • Added STAND_TIME, EXERCISE_TIME data types for iOS.
  • Fixed Android requestCode too big.

1.1.0 #

  • Added sleep data type.

1.0.1 #

  • Added userEntered to FitData.

1.0.0 #

  • Added hasPermissions method.
  • Added revokePermissions method.
  • BREAKING CHANGE dateFrom and dateTo are now named parameters.
  • Added named limit parameter.
  • Updated README

0.0.6 #

  • Added data source to FitData.

0.0.5 #

  • Added distance walked, calories burned and water drank data types.

0.0.4 #

  • BREAKING CHANGE Migrated to use AndroidX.

0.0.3 #

  • Added requestPermissions method to ease requesting more than 1 data type.

0.0.2 #

  • Fix multiple addActivityResultListener issue.

0.0.1 #

  • Initial release.

example/lib/main.dart

import 'dart:async';

import 'package:fit_kit/fit_kit.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String result = '';
  Map<DataType, List<FitData>> results = Map();
  bool permissions;

  RangeValues _dateRange = RangeValues(1, 8);
  List<DateTime> _dates = List<DateTime>();
  double _limitRange = 0;

  DateTime get _dateFrom => _dates[_dateRange.start.round()];
  DateTime get _dateTo => _dates[_dateRange.end.round()];
  int get _limit => _limitRange == 0.0 ? null : _limitRange.round();

  @override
  void initState() {
    super.initState();

    final now = DateTime.now();
    _dates.add(null);
    for (int i = 7; i >= 0; i--) {
      _dates.add(DateTime(
        now.year,
        now.month,
        now.day,
      ).subtract(Duration(days: i)));
    }
    _dates.add(null);

    hasPermissions();
  }

  Future<void> read() async {
    results.clear();

    try {
      permissions = await FitKit.requestPermissions(DataType.values);
      if (!permissions) {
        result = 'requestPermissions: failed';
      } else {
        for (DataType type in DataType.values) {
          results[type] = await FitKit.read(
            type,
            dateFrom: _dateFrom,
            dateTo: _dateTo,
            limit: _limit,
          );
        }

        result = 'readAll: success';
      }
    } catch (e) {
      result = 'readAll: $e';
    }

    setState(() {});
  }

  Future<void> revokePermissions() async {
    results.clear();

    try {
      await FitKit.revokePermissions();
      permissions = await FitKit.hasPermissions(DataType.values);
      result = 'revokePermissions: success';
    } catch (e) {
      result = 'revokePermissions: $e';
    }

    setState(() {});
  }

  Future<void> hasPermissions() async {
    try {
      permissions = await FitKit.hasPermissions(DataType.values);
    } catch (e) {
      result = 'hasPermissions: $e';
    }

    if (!mounted) return;

    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    final items =
        results.entries.expand((entry) => [entry.key, ...entry.value]).toList();

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('FitKit Example'),
        ),
        body: Container(
          padding: EdgeInsets.symmetric(horizontal: 16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Padding(padding: EdgeInsets.symmetric(vertical: 8)),
              Text(
                  'Date Range: ${_dateToString(_dateFrom)} - ${_dateToString(_dateTo)}'),
              Text('Limit: $_limit'),
              Text('Permissions: $permissions'),
              Text('Result: $result'),
              _buildDateSlider(context),
              _buildLimitSlider(context),
              _buildButtons(context),
              Expanded(
                child: ListView.builder(
                  itemCount: items.length,
                  itemBuilder: (context, index) {
                    final item = items[index];
                    if (item is DataType) {
                      return Padding(
                        padding: EdgeInsets.symmetric(vertical: 8),
                        child: Text(
                          '$item - ${results[item].length}',
                          style: Theme.of(context).textTheme.title,
                        ),
                      );
                    } else if (item is FitData) {
                      return Padding(
                        padding: EdgeInsets.symmetric(
                          vertical: 4,
                          horizontal: 8,
                        ),
                        child: Text(
                          '$item',
                          style: Theme.of(context).textTheme.caption,
                        ),
                      );
                    }

                    return Container();
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  String _dateToString(DateTime dateTime) {
    if (dateTime == null) {
      return 'null';
    }

    return '${dateTime.day}.${dateTime.month}.${dateTime.year}';
  }

  Widget _buildDateSlider(BuildContext context) {
    return Row(
      children: [
        Text('Date Range'),
        Expanded(
          child: RangeSlider(
            values: _dateRange,
            min: 0,
            max: 9,
            divisions: 10,
            onChanged: (values) => setState(() => _dateRange = values),
          ),
        ),
      ],
    );
  }

  Widget _buildLimitSlider(BuildContext context) {
    return Row(
      children: [
        Text('Limit'),
        Expanded(
          child: Slider(
            value: _limitRange,
            min: 0,
            max: 4,
            divisions: 4,
            onChanged: (newValue) => setState(() => _limitRange = newValue),
          ),
        ),
      ],
    );
  }

  Widget _buildButtons(BuildContext context) {
    return Row(
      children: [
        Expanded(
          child: FlatButton(
            color: Theme.of(context).accentColor,
            textColor: Colors.white,
            onPressed: () => read(),
            child: Text('Read'),
          ),
        ),
        Padding(padding: EdgeInsets.symmetric(horizontal: 4)),
        Expanded(
          child: FlatButton(
            color: Theme.of(context).accentColor,
            textColor: Colors.white,
            onPressed: () => revokePermissions(),
            child: Text('Revoke permissions'),
          ),
        ),
      ],
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  fit_kit: ^1.1.1

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:fit_kit/fit_kit.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
87
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
93
Learn more about scoring.

We analyzed this package on Apr 7, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.7.1
  • pana: 0.13.6
  • Flutter: 1.12.13+hotfix.8

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.11 1.14.12
meta 1.1.8
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test