Flutter未定义功能插件flutter_ftms的使用
Flutter未定义功能插件flutter_ftms的使用
FTMS Flutter 插件
此 Flutter 包允许您通过蓝牙低功耗(BLE)连接到 FTMS(健身机器服务)设备。它提供了扫描可用设备、连接到特定设备以及从设备读取数据和状态信息的功能。
支持的设备
设备 | 实现情况 |
---|---|
跑步机 | ✅ (已实现) |
综合训练器 | ✅ (已实现) |
楼梯机 | ❌ (未实现) |
登山机 | ❌ (未实现) |
划船机 | ✅ (已实现) |
室内自行车 | ✅ (已实现) |
支持的特征
特征 | 函数 | 设备类型 | 描述 | 实现情况 |
---|---|---|---|---|
DeviceDataCharacteristic | useDeviceDataCharacteristic(BluetoothDevice device, void Function(DeviceData) onData) |
跑步机、综合训练器、楼梯机、登山机、划船机和室内自行车 | 报告实时运动数据 | ✅ (已实现) |
MachineFeatureCharacteristic | readMachineFeatureCharacteristic(BluetoothDevice device) |
跑步机、综合训练器、楼梯机、登山机、划船机和室内自行车 | 描述设备支持的功能 | ✅ (已实现) |
MachineStatusCharacteristic | useMachineStatusCharacteristic(BluetoothDevice device, void Function(MachineStatus) onData) |
跑步机、综合训练器、楼梯机、登山机、划船机和室内自行车 | 报告设备状态数据 | ✅ (已实现) |
MachineControlPointCharacteristic | writeMachineControlPointCharacteristic(BluetoothDevice device, MachineControlPoint controlPoint) |
跑步机的可选支持,综合训练器、划船机和室内自行车的强制支持 | 控制设备状态(暂停或继续) | ✅ (已实现) |
安装
要使用 FTMS Flutter 插件,您需要遵循 flutter_blue_plus
的“入门指南”,因为该包基于 flutter_blue_plus
。
你可以在以下链接找到指南:
https://pub.dev/packages/flutter_blue_plus#getting-started
你不需要手动安装 flutter_blue_plus
包,因为它作为 flutter_ftms
的依赖项包含在内。
完成 flutter_blue_plus
设置后,在你的 pubspec.yaml
文件中添加以下依赖项:
dependencies:
flutter_ftms: 1.1.2
然后运行 flutter pub get
来安装包。
使用
导入 flutter_ftms
包并使用 FTMS
类与 FTMS 设备进行交互。
扫描设备
您可以使用 scanForBluetoothDevices()
函数扫描可用的 FTMS 设备。这将启动扫描并返回一个 ScanResult
对象流。
import 'package:flutter_ftms/flutter_ftms.dart';
await FTMS.scanForBluetoothDevices();
Stream<List<ScanResult>> scanResults = FTMS.scanResults;
连接到设备
一旦您获得了一个 BluetoothDevice
对象,就可以使用 connectToFTMSDevice()
函数进行连接。
import 'package:flutter_ftms/flutter_ftms.dart';
BluetoothDevice device = // 获取一个 BluetoothDevice 对象
await FTMS.connectToFTMSDevice(device);
检查设备类型
您可以使用 isBluetoothDeviceFTMSDevice()
函数检查给定的蓝牙设备是否为 FTMS 设备。该函数返回一个布尔值,指示设备是否支持 FTMS 服务。
import 'package:flutter_ftms/flutter_ftms.dart';
BluetoothDevice device = // 获取一个 BluetoothDevice 对象
bool isFTMSDevice = await FTMS.isBluetoothDeviceFTMSDevice(device);
获取设备类型
您可以使用 getDeviceDataType()
函数获取已连接设备的 FTMS 数据类型。该函数返回一个 DeviceDataType
枚举值,指示设备是室内自行车、综合训练器、跑步机还是划船机。
import 'package:flutter_ftms/flutter_ftms.dart';
BluetoothDevice device = // 获取一个 BluetoothDevice 对象
DeviceDataType? dataType = await FTMS.getDeviceDataType(device);
if (dataType != null) {
String deviceTypeString = FTMS.convertDeviceDataTypeToString(dataType);
// 处理设备类型
}
从设备读取数据
您可以使用 useDeviceDataCharacteristic()
函数从 FTMS 设备读取数据。该函数接受一个回调,每当从设备接收到新数据时都会调用该回调。
import 'package:flutter_ftms/flutter_ftms.dart';
BluetoothDevice device = // 获取一个 BluetoothDevice 对象
await FTMS.useDeviceDataCharacteristic(device, (DeviceData data) {
// 处理新数据
});
从设备读取机器特性信息
您可以使用 readMachineFeatureCharacteristic()
函数从 FTMS 设备读取机器特性信息。该函数接受一个 BluetoothDevice
对象,并返回一个 MachineFeature
对象。
import 'package:flutter_ftms/flutter_ftms.dart';
BluetoothDevice device = // 获取一个 BluetoothDevice 对象
MachineFeature? feature = await FTMS.readMachineFeatureCharacteristic(device);
if (feature != null) {
// 处理特征对象
}
从设备读取机器状态信息
您可以使用 useMachineStatusCharacteristic()
函数从 FTMS 设备读取机器状态信息。该函数接受一个回调,每当从设备接收到新的状态信息时都会调用该回调。
import 'package:flutter_ftms/flutter_ftms.dart';
BluetoothDevice device = // 获取一个 BluetoothDevice 对象
await FTMS.useMachineStatusCharacteristic(device, (MachineStatus status) {
// 处理新机器状态
});
写入机器控制点特征
您可以使用 writeMachineControlPointCharacteristic()
函数向 FTMS 设备的机器控制点特征写入信息。该函数接受一个 BluetoothDevice
对象和一个 MachineControlPoint
对象作为参数。
import 'package:flutter_ftms/flutter_ftms.dart';
BluetoothDevice device = // 获取一个 BluetoothDevice 对象
MachineControlPoint controlPoint = // 创建一个 MachineControlPoint 对象
await FTMS.writeMachineControlPointCharacteristic(device, controlPoint);
示例代码
以下是来自官方仓库的示例代码,展示了如何使用 flutter_ftms
插件。
import 'package:flutter/material.dart';
import 'package:flutter_ftms/flutter_ftms.dart';
import 'package:flutter_ftms_example/bloc.dart';
import 'package:flutter_ftms_example/widgets.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter FTMS Example App',
theme: ThemeData(
useMaterial3: true,
),
home: const FlutterFTMSApp(),
);
}
}
class FlutterFTMSApp extends StatefulWidget {
const FlutterFTMSApp({super.key});
[@override](/user/override)
State<FlutterFTMSApp> createState() => _FlutterFTMSAppState();
}
class _FlutterFTMSAppState extends State<FlutterFTMSApp> {
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("flutter_ftms example"),
),
body: const ScanPage(),
);
}
}
class ScanPage extends StatefulWidget {
const ScanPage({super.key});
[@override](/user/override)
State<ScanPage> createState() => _ScanPageState();
}
class _ScanPageState extends State<ScanPage> {
[@override](/user/override)
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(
child: StreamBuilder<bool>(
stream: FTMS.isScanning,
builder: (c, snapshot) =>
scanBluetoothButton(snapshot.data ?? false),
),
),
StreamBuilder<List<ScanResult>>(
stream: FTMS.scanResults,
initialData: const [],
builder: (c, snapshot) => scanResultsToWidget(
(snapshot.data ?? [])
.where((element) => element.device.platformName.isNotEmpty)
.toList(),
context),
),
],
);
}
}
class FTMSPage extends StatefulWidget {
final BluetoothDevice ftmsDevice;
const FTMSPage({super.key, required this.ftmsDevice});
[@override](/user/override)
State<FTMSPage> createState() => _FTMSPageState();
}
class _FTMSPageState extends State<FTMSPage> {
void writeCommand(MachineControlPointOpcodeType opcodeType) async {
MachineControlPoint? controlPoint;
switch (opcodeType) {
case MachineControlPointOpcodeType.requestControl:
controlPoint = MachineControlPoint.requestControl();
break;
case MachineControlPointOpcodeType.reset:
controlPoint = MachineControlPoint.reset();
break;
case MachineControlPointOpcodeType.setTargetSpeed:
controlPoint = MachineControlPoint.setTargetSpeed(speed: 12);
break;
case MachineControlPointOpcodeType.setTargetInclination:
controlPoint = MachineControlPoint.setTargetInclination(inclination: 23);
break;
case MachineControlPointOpcodeType.setTargetResistanceLevel:
controlPoint = MachineControlPoint.setTargetResistanceLevel(resistanceLevel: 3);
break;
case MachineControlPointOpcodeType.setTargetPower:
controlPoint = MachineControlPoint.setTargetPower(power: 34);
break;
case MachineControlPointOpcodeType.setTargetHeartRate:
controlPoint = MachineControlPoint.setTargetHeartRate(heartRate: 45);
break;
case MachineControlPointOpcodeType.startOrResume:
controlPoint = MachineControlPoint.startOrResume();
break;
case MachineControlPointOpcodeType.stopOrPause:
controlPoint = MachineControlPoint.stopOrPause(pause: true);
break;
default:
throw 'MachineControlPointOpcodeType $opcodeType is not implemented in this example';
}
await FTMS.writeMachineControlPointCharacteristic(
widget.ftmsDevice, controlPoint);
}
[@override](/user/override)
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: 1,
length: 3,
child: Scaffold(
appBar: AppBar(
title: Text(
'${widget.ftmsDevice.platformName} (${FTMS.getDeviceDataTypeWithoutConnecting(widget.ftmsDevice)})'),
bottom: const TabBar(
tabs: <Widget>[
Tab(
text: 'Data',
icon: Icon(Icons.data_object),
),
Tab(
text: 'Device Data Features',
icon: Icon(Icons.featured_play_list_outlined),
),
Tab(
text: 'Machine Features',
icon: Icon(Icons.settings),
),
],
),
),
body: TabBarView(
children: <Widget>[
SingleChildScrollView(
child: StreamBuilder<DeviceData?>(
stream: ftmsBloc.ftmsDeviceDataControllerStream,
builder: (c, snapshot) {
if (!snapshot.hasData) {
return Column(
children: [
const Center(child: Text("No FTMSData found!")),
ElevatedButton(
onPressed: () async {
await FTMS.useDeviceDataCharacteristic(
widget.ftmsDevice, (DeviceData data) {
ftmsBloc.ftmsDeviceDataControllerSink.add(data);
});
},
child: const Text("use FTMS"),
),
],
);
}
return Padding(
padding: const EdgeInsets.all(8),
child: Column(
children: [
Text(
FTMS.convertDeviceDataTypeToString(
snapshot.data!.deviceDataType),
textScaler: const TextScaler.linear(4),
style:
TextStyle(color: Theme.of(context).primaryColor),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: snapshot.data!
.getDeviceDataParameterValues()
.map((parameterValue) =>
Text(
parameterValue.toString(),
textScaler: const TextScaler.linear(2),
))
.toList(),
),
],
),
);
},
),
),
SingleChildScrollView(
child: StreamBuilder<DeviceData?>(
stream: ftmsBloc.ftmsDeviceDataControllerStream,
builder: (c, snapshot) {
if (!snapshot.hasData) {
return Column(
children: [
const Center(child: Text("No FTMSData found!")),
ElevatedButton(
onPressed: () async {
await FTMS.useDeviceDataCharacteristic(
widget.ftmsDevice, (DeviceData data) {
ftmsBloc.ftmsDeviceDataControllerSink.add(data);
});
},
child: const Text("use FTMS"),
),
],
);
}
return Column(
children: [
Text(
"Device Data Features",
textScaler: const TextScaler.linear(3),
style: TextStyle(color: Theme.of(context).primaryColor),
),
Column(
children: snapshot.data!
.getDeviceDataFeatures()
.entries
.toList()
.map((entry) =>
Text('${entry.key.name}: ${entry.value}'))
.toList(),
),
],
);
},
),
),
Column(
children: [
MachineFeatureWidget(ftmsDevice: widget.ftmsDevice),
const Divider(
height: 2,
),
SizedBox(
height: 60,
child: ListView(
scrollDirection: Axis.horizontal,
children: MachineControlPointOpcodeType.values
.map(
(MachineControlPointOpcodeType opcodeType) =>
Padding(
padding: const EdgeInsets.all(4),
child: OutlinedButton(
onPressed: () => writeCommand(opcodeType),
child: Text(opcodeType.name),
),
),
)
.toList(),
),
)
],
)
],
),
),
);
}
}
class MachineFeatureWidget extends StatefulWidget {
final BluetoothDevice ftmsDevice;
const MachineFeatureWidget({super.key, required this.ftmsDevice});
[@override](/user/override)
State<MachineFeatureWidget> createState() => _MachineFeatureWidgetState();
}
class _MachineFeatureWidgetState extends State<MachineFeatureWidget> {
[@override](/user/override)
Widget build(BuildContext context) {
return StreamBuilder(
stream: ftmsBloc.ftmsMachineFeaturesControllerStream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Column(
children: [
const Text("No Machine Features found!"),
ElevatedButton(
onPressed: () async {
MachineFeature? machineFeature = await FTMS
.readMachineFeatureCharacteristic(widget.ftmsDevice);
ftmsBloc.ftmsMachineFeaturesControllerSink
.add(machineFeature);
},
child: const Text("get Machine Features")),
],
);
}
return Column(
children: snapshot.data!
.getFeatureFlags()
.entries
.toList()
.where((element) => element.value)
.map((entry) =>
Text('${entry.key.name}: ${entry.value}'))
.toList(),
);
},
);
}
}
更多关于Flutter未定义功能插件flutter_ftms的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html