Flutter健身锻炼管理插件workout的使用

发布于 1周前 作者 caililin 来自 Flutter

Flutter健身锻炼管理插件workout的使用

workout简介

workout 插件可以帮助你在Flutter应用程序中启动健身锻炼会话,并从Wear OS和Tizen设备获取实时健康数据。此外,它还可以在iOS上启动watchOS应用。

入门指南

Wear OS

目前Health Services for Wear OS处于测试阶段。为了使用此插件,你需要确保满足以下条件:

  • android/app/build.gradle文件中设置minSdkVersion 30
  • 修改android/app/src/main/AndroidManifest.xml文件,添加必要的权限:
<!-- Required for heart rate -->
<uses-permission android:name="android.permission.BODY_SENSORS" />
<!-- Required for calories, steps, distance, speed -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<!-- Required to use location to estimate distance, speed -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Tizen

此插件要求Tizen 4.0或更高版本。需要对tizen/tizen-manifest.xml进行如下修改:

<manifest api-version="4.0" ...>
    <privileges>
        <privilege>http://tizen.org/privilege/healthinfo</privilege>
    </privileges>
    <feature name="http://tizen.org/feature/sensor.heart_rate_monitor">true</feature>
    <feature name="http://tizen.org/feature/sensor.pedometer">true</feature>
</manifest>

iOS

虽然Flutter不能直接运行在watchOS上,但可以通过iOS启动watch应用。要使这一功能正常工作,需确保:

  • 手机和手表应用都具有HealthKit权限。
  • 手表应用启用了Workout Processing后台模式。

支持的数据类型

Feature Wear OS Tizen
心率 Yes Yes
卡路里 Yes Yes
步数 Yes Yes
速度 Yes Yes
距离 Yes Yes

示例代码

以下是完整的示例demo,演示如何使用workout插件创建一个简单的健身锻炼应用:

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:wear_plus/wear_plus.dart';
import 'package:workout/workout.dart';

void main() {
  runApp(Platform.isIOS ? const MyIosApp() : const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final workout = Workout();

  final exerciseType = ExerciseType.walking;
  final features = [
    WorkoutFeature.heartRate,
    WorkoutFeature.calories,
    WorkoutFeature.steps,
    WorkoutFeature.distance,
    WorkoutFeature.speed,
  ];
  final enableGps = true;

  double heartRate = 0;
  double calories = 0;
  double steps = 0;
  double distance = 0;
  double speed = 0;
  bool started = false;

  _MyAppState() {
    workout.stream.listen((event) {
      debugPrint('${event.feature}: ${event.value} (${event.timestamp})');
      switch (event.feature) {
        case WorkoutFeature.unknown:
          return;
        case WorkoutFeature.heartRate:
          setState(() {
            heartRate = event.value;
          });
        case WorkoutFeature.calories:
          setState(() {
            calories = event.value;
          });
        case WorkoutFeature.steps:
          setState(() {
            steps = event.value;
          });
        case WorkoutFeature.distance:
          setState(() {
            distance = event.value;
          });
        case WorkoutFeature.speed:
          setState(() {
            speed = event.value;
          });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: Colors.black),
      home: AmbientMode(
        builder: (context, mode, child) => child!,
        child: Scaffold(
          body: Center(
            child: Column(
              children: [
                const Spacer(),
                Text('Heart rate: $heartRate'),
                Text('Calories: ${calories.toStringAsFixed(2)}'),
                Text('Steps: $steps'),
                Text('Distance: ${distance.toStringAsFixed(2)}'),
                Text('Speed: ${speed.toStringAsFixed(2)}'),
                const Spacer(),
                TextButton(
                  onPressed: toggleExerciseState,
                  child: Text(started ? 'Stop' : 'Start'),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  void toggleExerciseState() async {
    if (started) {
      await workout.stop();
    } else {
      final supportedExerciseTypes = await workout.getSupportedExerciseTypes();
      debugPrint('Supported exercise types: ${supportedExerciseTypes.length}');

      final result = await workout.start(
        exerciseType: exerciseType,
        features: features,
        enableGps: enableGps,
      );

      if (result.unsupportedFeatures.isNotEmpty) {
        debugPrint('Unsupported features: ${result.unsupportedFeatures}');
      } else {
        debugPrint('All requested features supported');
      }
    }

    setState(() => started = !started);
  }
}

class MyIosApp extends StatefulWidget {
  const MyIosApp({super.key});

  @override
  State<StatefulWidget> createState() => _MyIosAppState();
}

class _MyIosAppState extends State<MyIosApp> {
  final workout = Workout();

  var exerciseType = ExerciseType.workout;
  var locationType = WorkoutLocationType.indoor;
  var swimmingLocationType = WorkoutSwimmingLocationType.pool;
  var lapLength = 25.0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: SafeArea(
          child: ListView(
            padding: const EdgeInsets.all(16),
            children: [
              DropdownButton<ExerciseType>(
                value: exerciseType,
                onChanged: (value) => setState(() => exerciseType = value!),
                items: ExerciseType.values
                    .map((e) => DropdownMenuItem(value: e, child: Text(e.name)))
                    .toList(),
              ),
              DropdownButton<WorkoutLocationType>(
                value: locationType,
                onChanged: (value) => setState(() => locationType = value!),
                items: WorkoutLocationType.values
                    .map((e) => DropdownMenuItem(value: e, child: Text(e.name)))
                    .toList(),
              ),
              DropdownButton<WorkoutSwimmingLocationType>(
                value: swimmingLocationType,
                onChanged: (value) =>
                    setState(() => swimmingLocationType = value!),
                items: WorkoutSwimmingLocationType.values
                    .map((e) => DropdownMenuItem(value: e, child: Text(e.name)))
                    .toList(),
              ),
              TextField(
                decoration: const InputDecoration(labelText: 'Lap length'),
                keyboardType: TextInputType.number,
                onChanged: (value) =>
                    setState(() => lapLength = double.parse(value)),
              ),
              ElevatedButton(
                onPressed: start,
                child: const Text('Start Apple Watch app'),
              ),
            ],
          ),
        ),
      ),
    );
  }

  void start() {
    workout.start(
      exerciseType: exerciseType,
      features: [],
      locationType: locationType,
      swimmingLocationType: swimmingLocationType,
      lapLength: lapLength,
    );
  }
}

通过以上代码,你可以创建一个简单的Flutter应用来管理健身锻炼,并从连接的可穿戴设备获取实时健康数据。希望这个例子能帮助你更好地理解和使用workout插件!


更多关于Flutter健身锻炼管理插件workout的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter健身锻炼管理插件workout的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,关于Flutter中的workout插件(需要注意的是,Flutter社区和官方库中并没有一个直接名为workout的广泛认可的标准插件,但我将假设你指的是一个健身锻炼管理相关的自定义或第三方插件,或者是一个概念性的需求),下面是一个如何集成和使用一个假设的健身锻炼管理插件的示例代码案例。这个示例将涵盖初始化插件、创建锻炼计划、记录锻炼数据等核心功能。

1. 添加依赖

首先,假设这个插件在pub.dev上可用,你需要在pubspec.yaml文件中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  workout_manager: ^1.0.0  # 假设插件名为workout_manager,版本号根据实际情况填写

然后运行flutter pub get来安装依赖。

2. 初始化插件

在你的主应用文件(通常是main.dart)中,初始化插件:

import 'package:flutter/material.dart';
import 'package:workout_manager/workout_manager.dart';  // 假设插件的import路径

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  // 初始化插件(如果有初始化方法)
  WorkoutManager.instance.initialize();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fitness Tracker',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomeScreen(),
    );
  }
}

3. 创建锻炼计划

接下来,你可以创建一个页面来添加锻炼计划。假设插件提供了WorkoutExercise类来表示锻炼和单个练习:

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

class CreateWorkoutScreen extends StatefulWidget {
  @override
  _CreateWorkoutScreenState createState() => _CreateWorkoutScreenState();
}

class _CreateWorkoutScreenState extends State<CreateWorkoutScreen> {
  final TextEditingController _workoutNameController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Create Workout'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: <Widget>[
            TextField(
              controller: _workoutNameController,
              decoration: InputDecoration(labelText: 'Workout Name'),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () async {
                // 创建练习(假设Exercise有一个构造函数接受名称和持续时间)
                List<Exercise> exercises = [
                  Exercise('Squats', Duration(minutes: 3, seconds: 0)),
                  Exercise('Push-ups', Duration(minutes: 2, seconds: 0)),
                ];
                
                // 创建锻炼(假设Workout有一个构造函数接受名称和练习列表)
                Workout workout = Workout(_workoutNameController.text, exercises);
                
                // 保存锻炼到数据库或本地存储(根据插件提供的API)
                await WorkoutManager.instance.saveWorkout(workout);
                
                // 返回到前一个屏幕或显示成功消息
                Navigator.pop(context);
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text('Workout created successfully!')),
                );
              },
              child: Text('Create Workout'),
            ),
          ],
        ),
      ),
    );
  }
}

4. 显示锻炼计划

最后,你可以创建一个页面来显示已保存的锻炼计划:

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

class WorkoutListScreen extends StatefulWidget {
  @override
  _WorkoutListScreenState createState() => _WorkoutListScreenState();
}

class _WorkoutListScreenState extends State<WorkoutListScreen> {
  List<Workout> _workouts = [];

  @override
  void initState() {
    super.initState();
    // 从数据库或本地存储加载锻炼计划
    _loadWorkouts();
  }

  Future<void> _loadWorkouts() async {
    _workouts = await WorkoutManager.instance.loadWorkouts();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('My Workouts'),
      ),
      body: _workouts.isEmpty
          ? Center(child: Text('No workouts found.'))
          : ListView.builder(
              itemCount: _workouts.length,
              itemBuilder: (context, index) {
                Workout workout = _workouts[index];
                return ListTile(
                  title: Text(workout.name),
                  subtitle: Text('${workout.exercises.length} exercises'),
                  trailing: Icon(Icons.arrow_forward),
                  onTap: () {
                    // 导航到锻炼详情页面
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) => WorkoutDetailScreen(workout: workout),
                      ),
                    );
                  },
                );
              },
            ),
    );
  }
}

class WorkoutDetailScreen extends StatelessWidget {
  final Workout workout;

  WorkoutDetailScreen({required this.workout});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(workout.name),
      ),
      body: ListView.builder(
        itemCount: workout.exercises.length,
        itemBuilder: (context, index) {
          Exercise exercise = workout.exercises[index];
          return ListTile(
            title: Text(exercise.name),
            subtitle: Text('Duration: ${exercise.duration}'),
          );
        },
      ),
    );
  }
}

总结

以上代码示例展示了如何在Flutter应用中集成和使用一个假设的健身锻炼管理插件。请注意,实际的插件API可能会有所不同,因此你需要参考插件的官方文档来调整代码。如果插件不存在,你可能需要自己实现这些功能,包括数据模型、持久化存储和用户界面。

回到顶部