Flutter未定义功能插件flutter_ftms的使用

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

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

1 回复

更多关于Flutter未定义功能插件flutter_ftms的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,当你遇到未定义功能的插件(如flutter_ftms),通常意味着这个插件可能不存在、未正确安装、或者缺少必要的实现。由于flutter_ftms并不是一个广为人知的官方或广泛使用的Flutter插件,因此这里我将提供一个假设性的代码案例,展示如何在Flutter中集成和使用一个自定义插件的基本步骤。请注意,这里的代码是为了演示目的而编写的,flutter_ftms的具体实现细节可能会有所不同。

步骤 1: 创建并发布自定义插件(假设你是插件的开发者)

首先,你需要创建一个Flutter插件。这里是一个简化的例子,展示如何创建一个简单的Flutter插件,它有一个方法someFunction

1.1 创建插件项目

使用以下命令创建一个新的Flutter插件项目:

flutter create --template=plugin flutter_ftms

1.2 实现插件功能

flutter_ftms/android/src/main/kotlin/.../FlutterFtmsPlugin.kt(对于Android)和flutter_ftms/ios/Classes/FlutterFtmsPlugin.swift(对于iOS)中实现你的插件逻辑。例如:

Android (Kotlin):

package com.example.flutter_ftms

import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import android.app.Activity

class FlutterFtmsPlugin: FlutterPlugin, ActivityAware, MethodCallHandler {
  private var channel: MethodChannel? = null
  private var activity: Activity? = null

  override fun onAttachedToEngine(binding: FlutterPluginBinding, binaryMessenger: BinaryMessenger) {
    channel = MethodChannel(binaryMessenger, "flutter_ftms")
    channel?.setMethodCallHandler(this)
  }

  override fun onMethodCall(call: MethodCall, result: Result) {
    if (call.method == "someFunction") {
      // 实现你的功能
      result.success("Hello from Flutter Ftms Plugin!")
    } else {
      result.notImplemented()
    }
  }

  override fun onDetachedFromEngine(binding: FlutterPluginBinding) {
    channel?.setMethodCallHandler(null)
    channel = null
  }

  override fun onAttachedToActivity(binding: ActivityPluginBinding) {
    activity = binding.activity
  }

  override fun onDetachedFromActivityForConfigChanges() {
    activity = null
  }

  override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
    activity = binding.activity
  }

  override fun onDetachedFromActivity() {
    activity = null
  }
}

iOS (Swift):

import Flutter

public class FlutterFtmsPlugin: NSObject, FlutterPlugin {
  public static func register(with registrar: FlutterRegistrar) {
    let channel = FlutterMethodChannel(name: "flutter_ftms", binaryMessenger: registrar.messenger())
    let instance = FlutterFtmsPlugin()
    registrar.addMethodCallDelegate(instance, channel: channel)
  }

  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
    if call.method == "someFunction" {
      result("Hello from Flutter Ftms Plugin!")
    } else {
      result(FlutterMethodNotImplementedError(methodName: call.method))
    }
  }
}

1.3 发布插件

将你的插件发布到pub.dev或其他Flutter插件仓库,以便其他开发者可以使用。

步骤 2: 在Flutter应用中使用插件

假设flutter_ftms插件已经发布并可用,你可以在你的Flutter应用中添加并使用它。

2.1 添加依赖

pubspec.yaml文件中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_ftms: ^x.y.z  # 替换为实际的版本号

2.2 使用插件

在你的Dart代码中导入并使用插件:

import 'package:flutter/material.dart';
import 'package:flutter_ftms/flutter_ftms.dart';  // 假设插件提供了这样的导入路径

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Ftms Plugin Demo'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              try {
                final String result = await FlutterFtms.someFunction();
                print(result);  // 应该打印 "Hello from Flutter Ftms Plugin!"
              } catch (e) {
                print('Error: $e');
              }
            },
            child: Text('Call Some Function'),
          ),
        ),
      ),
    );
  }
}

注意:上面的Dart代码假设flutter_ftms插件提供了一个静态方法someFunction。实际使用时,你需要根据插件的文档来调整代码。

由于flutter_ftms可能并不是一个真实存在的插件,上述代码提供了一个框架,展示了如何创建、发布和使用一个自定义Flutter插件的基本流程。如果你确实在寻找一个特定的功能插件,并且它不存在,你可能需要自己实现它,或者寻找其他替代方案。

回到顶部