flutter_ftms 1.1.1 copy "flutter_ftms: ^1.1.1" to clipboard
flutter_ftms: ^1.1.1 copied to clipboard

A Flutter package that allows you to connect to FTMS (Fitness Machine Service) devices via Bluetooth Low Energy (BLE).

example/lib/main.dart

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
  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
  State<FlutterFTMSApp> createState() => _FlutterFTMSAppState();
}

class _FlutterFTMSAppState extends State<FlutterFTMSApp> {
  @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
  State<ScanPage> createState() => _ScanPageState();
}

class _ScanPageState extends State<ScanPage> {
  @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
  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
  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
  State<MachineFeatureWidget> createState() => _MachineFeatureWidgetState();
}

class _MachineFeatureWidgetState extends State<MachineFeatureWidget> {
  @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(),
        );
      },
    );
  }
}
8
likes
120
pub points
54%
popularity

Publisher

unverified uploader

A Flutter package that allows you to connect to FTMS (Fitness Machine Service) devices via Bluetooth Low Energy (BLE).

Repository (GitHub)
View/report issues

Topics

#ftms #bluetooth

Documentation

API reference

License

BSD-3-Clause (LICENSE)

Dependencies

flutter, flutter_blue_plus, permission_handler

More

Packages that depend on flutter_ftms