fitness 1.0.1 copy "fitness: ^1.0.1" to clipboard
fitness: ^1.0.1 copied to clipboard

Flutter plugin for reading step count data. Wraps HealthKit on iOS and GoogleFit on Android.

example/lib/main.dart

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:lottie/lottie.dart';
import 'package:fitness/fitness.dart';

import 'extensions/extensions.dart';

void main() {
  runApp(FitnessApplication());
}

enum PermissionStatus {
  granted,
  denied,
}

enum DateFilter {
  daily,
  weekly,
  monthly,
}

class FitnessApplication extends StatefulWidget {
  @override
  _FitnessApplicationState createState() => _FitnessApplicationState();
}

class _FitnessApplicationState extends State<FitnessApplication> {
  PermissionStatus _status = PermissionStatus.denied;
  DateFilter _filter = DateFilter.daily;
  List<DataPoint> _dataPoints = [];

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

  void _onFilterChanged(DateFilter filter) {
    if (!mounted) {
      return;
    }

    setState(() {
      _filter = filter;
    });

    final now = DateTime.now();
    switch (_filter) {
      case DateFilter.daily:
        _read(timeRange: now.daily(), timeUnit: TimeUnit.hours);
        break;
      case DateFilter.weekly:
        _read(timeRange: now.weekly());
        break;
      case DateFilter.monthly:
        _read(timeRange: now.monthly());
        break;
    }
  }

  /// Fitness Usage STARTED
  void _hasPermission() async {
    final result = await Fitness.hasPermission();
    print('[hasPermission]::$result');

    if (!mounted) {
      return;
    }

    setState(() {
      _status = result ? PermissionStatus.granted : PermissionStatus.denied;
    });

    if (_status != PermissionStatus.granted) {
      return;
    }

    _onFilterChanged(DateFilter.daily);
  }

  void _requestPermission() async {
    final result = await Fitness.requestPermission();
    print('[requestPermission]::$result');

    _hasPermission();
  }

  void _revokePermission() async {
    final result = await Fitness.revokePermission();
    print('[revokePermission]::$result');

    _hasPermission();
  }

  void _read({
    required TimeRange timeRange,
    int bucketByTime = 1,
    TimeUnit timeUnit = TimeUnit.days,
  }) async {
    final results = await Fitness.read(
      timeRange: timeRange,
      bucketByTime: bucketByTime,
      timeUnit: timeUnit,
    );
    print('[READ]::$results');

    if (!mounted) {
      return;
    }

    setState(() {
      _dataPoints = results;
    });
  }

  /// Fitness Usage ENDED

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark(),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Fitness Demo'),
        ),
        body: _buildBody(),
        floatingActionButton:
        _status == PermissionStatus.granted && Platform.isAndroid
            ? FloatingActionButton(
          child: Icon(Icons.link_off),
          backgroundColor: Colors.deepOrangeAccent,
          onPressed: _revokePermission,
        )
            : null,
      ),
    );
  }

  Widget _buildBody() {
    switch (_status) {
      case PermissionStatus.granted:
        return _buildDataViewer();
      case PermissionStatus.denied:
      default:
        return _buildPermissionFlowView();
    }
  }

  Widget _buildPermissionFlowView() {
    return Container(
      width: double.infinity,
      child: Column(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          GestureDetector(
            child: Image.asset(
              Platform.isAndroid
                  ? 'assets/ic_google_fit.png'
                  : 'assets/ic_health_kit.png',
              width: 56.0,
              height: 56.0,
            ),
            onTap: _requestPermission,
          ),
          Padding(
            padding: const EdgeInsets.symmetric(
              vertical: 16.0,
            ),
            child: Text(
              '\u{1F446}\n\nHello! Touch it to get started!',
              textAlign: TextAlign.center,
              style: TextStyle(
                fontSize: 16.0,
              ),
            ),
          ),
        ],
        //U+1F446
      ),
    );
  }

  Widget _buildDataViewer() {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.symmetric(
        vertical: 16.0,
        horizontal: 16.0,
      ),
      child: Column(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Row(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              ChoiceChip(
                label: Text('Today'),
                labelStyle: TextStyle(color: Colors.white),
                selected: _filter == DateFilter.daily,
                selectedColor: Colors.deepOrangeAccent,
                onSelected: (_) => _onFilterChanged(DateFilter.daily),
              ),
              SizedBox(width: 8.0),
              ChoiceChip(
                label: Text('This week'),
                labelStyle: TextStyle(color: Colors.white),
                selected: _filter == DateFilter.weekly,
                selectedColor: Colors.deepOrangeAccent,
                onSelected: (_) => _onFilterChanged(DateFilter.weekly),
              ),
              SizedBox(width: 8.0),
              ChoiceChip(
                label: Text('This month'),
                labelStyle: TextStyle(color: Colors.white),
                selected: _filter == DateFilter.monthly,
                selectedColor: Colors.deepOrangeAccent,
                onSelected: (_) => _onFilterChanged(DateFilter.monthly),
              ),
            ],
          ),
          Expanded(
            child: _dataPoints.isNotEmpty
                ? ListView.separated(
              shrinkWrap: true,
              itemCount: _dataPoints.length,
              itemBuilder: (context, index) {
                final dataPoint = _dataPoints[index];

                return ListTile(
                  leading: Image.asset(
                    'assets/ic_shoes.png',
                    width: 24.0,
                    height: 24.0,
                  ),
                  title: Text('${dataPoint.value} steps'),
                  subtitle: Text(
                    '${_format(dataPoint.dateFrom)} - ${_format(
                        dataPoint.dateTo)}',
                  ),
                );
              },
              separatorBuilder: (_, __) {
                return SizedBox(height: 16.0);
              },
            )
                : Column(
              mainAxisSize: MainAxisSize.min,
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Lottie.asset(
                  'assets/walk.json',
                  width: 100.0,
                  fit: BoxFit.fill,
                ),
                Text('How about taking a walk for a while?'),
              ],
            ),
          ),
        ],
      ),
    );
  }

  String _format(DateTime dateTime) {
    switch (_filter) {
      case DateFilter.daily:
        return DateFormat.jm().format(dateTime);
      case DateFilter.weekly:
      case DateFilter.monthly:
        return DateFormat.yMd().format(dateTime);
      default:
        throw Exception('Unsupported Type.');
    }
  }
}
copied to clipboard
28
likes
140
points
54
downloads

Publisher

unverified uploader

Weekly Downloads

2024.09.19 - 2025.04.03

Flutter plugin for reading step count data. Wraps HealthKit on iOS and GoogleFit on Android.

Repository (GitHub)

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on fitness