繁体   English   中英

在 flutter App 中显示从 BLE 设备到 Heart Widget 的值

[英]Show value from BLE device to Heart Widget in flutter App

我想将来自 Esp32 的读数显示到 HomeScreen 上显示的 Heart Widget。 我使用了 flutter_blue package 的示例代码来检查应用程序是否有读数,但是我很难理解代码,因为我是 Flutter 的新手。

我的蓝牙连接屏幕如下所示,其中显示了附近 BLE 设备的列表

在此处输入图像描述

当我按下连接按钮时,它会进入显示服务、描述符和 MTU 大小的屏幕。

在此处输入图像描述

如图所示,最后一个服务正在从 ESP32 获取值,我想在 Characteristic 下显示值以显示在 Heart Rate Widget 中,其中写入 96。 但我不明白我应该如何在这个屏幕上而不是在上面的屏幕上显示特性的值。

在此处输入图像描述

这些也是我在控制台上获得的值,它们是正确的。 我只希望当我按下 ESP32 的连接按钮时,它应该立即连接,而不是进入第二个屏幕,它应该 go 到主屏幕并显示心率小部件内的值

在此处输入图像描述

我的蓝牙扫描设备屏幕代码是:

// Copyright 2017, Paul DeMarco.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'BluetoothConnectBand.dart';
import 'widgets.dart';


class FlutterBlueApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      //backgroundColor:Colors.lightBlue ,
       body: StreamBuilder<BluetoothState>(
          stream: FlutterBlue.instance.state,
          initialData: BluetoothState.unknown,
          builder: (c, snapshot) {
            final state = snapshot.data;
            if (state == BluetoothState.on) {
              return FindDevicesScreen();
            }
            return BluetoothOffScreen(state: state);

          }),
    );
  }
}

class BluetoothOffScreen extends StatelessWidget {
  const BluetoothOffScreen({Key key, this.state}) : super(key: key);

  final BluetoothState state;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.lightBlue,
      appBar:AppBar(
        backgroundColor: Colors.lightBlue,
        elevation: 0,
      leading:IconButton(
        icon: Icon(
          Icons.arrow_back,
          color: Colors.black,
        ),
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) {
                return BluetoothConnectBand();
              },
            ),
          );
        },
      ),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Icon(
              Icons.bluetooth_disabled,
              size: 200.0,
              color: Colors.white54,
            ),
            Text(
              'Bluetooth Adapter is ${state != null ? state.toString().substring(15) : 'not available'}.',
              style: Theme.of(context)
                  .primaryTextTheme
                  .subhead
                  ?.copyWith(color: Colors.white),
            ),
          ],
        ),
      ),
    );
  }
}

class FindDevicesScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    return Scaffold(
      appBar: AppBar(
        backgroundColor: const Color(0xffE5E0A1),
        elevation: 0,
        centerTitle: true,
        title: Text(
          "Connect Band",
          style: TextStyle(
            fontSize: 15.0,
            color: Colors.black,
            fontFamily: 'Montserrat',
            fontWeight: FontWeight.normal,
          ),
        ),
        leading: IconButton(
          icon: Icon(
            Icons.arrow_back,
            color: Colors.black,
          ),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) {
                  return BluetoothConnectBand();
                },
              ),
            );
          },
        ),
      ),
      body: RefreshIndicator(
        onRefresh: () =>
            FlutterBlue.instance.startScan(timeout: Duration(seconds: 4)),
        child: SingleChildScrollView(
          child: Column(
            children: <Widget>[
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Container(
                      height: size.height * 0.4,
                      width: size.width,
                      color: const Color(0xffE5E0A1),
                      child: Image.asset(
                        'assets/images/bluetooth.png',
                      )),
                  Container(
                    width: size.width,
                    padding: EdgeInsets.symmetric(vertical: 20),
                    child: Text(
                      "Scanning Available Devices...",
                      style: TextStyle(
                        fontSize: 15.0,
                        color: Colors.black,
                        fontWeight: FontWeight.w400,
                        fontFamily: 'Montserrat',
                      ),
                      textAlign: TextAlign.center,
                    ),
                  ),
                  //Expanded(child:_buildListViewOfDevices()),
                ],
              ),
              StreamBuilder<List<BluetoothDevice>>(
                stream: Stream.periodic(Duration(seconds: 2))
                    .asyncMap((_) => FlutterBlue.instance.connectedDevices),
                initialData: [],
                builder: (c, snapshot) => Column(
                  children: snapshot.data
                      .map((d) => ListTile(
                    title: Text(d.name,
                      style: TextStyle(
                        fontSize: 15.0,
                        color: Colors.black,
                        fontWeight: FontWeight.w600,
                        fontFamily: 'Montserrat',
                      ),
                    ),
                    subtitle: Text(d.id.toString(),
                      style: TextStyle(
                        fontSize: 13.0,
                        color: Colors.black,
                        fontWeight: FontWeight.w500,
                        fontFamily: 'Montserrat',
                      ),
                    ),
                    trailing: StreamBuilder<BluetoothDeviceState>(
                      stream: d.state,
                      initialData: BluetoothDeviceState.disconnected,
                      builder: (c, snapshot) {
                        if (snapshot.data ==
                            BluetoothDeviceState.connected) {
                          return FlatButton(
                            shape: RoundedRectangleBorder(
                              borderRadius: BorderRadius.circular(28.0),
                            ),
                            child: Text('Connected'),
                            onPressed: () => Navigator.of(context).push(
                                MaterialPageRoute(
                                    builder: (context) =>
                                        DeviceScreen(device: d))),
                          );
                        }
                        return Text(snapshot.data.toString(),
                        style: TextStyle(
                          color: Colors.green,
                        ),);
                      },
                    ),
                  ))
                      .toList(),
                ),
              ),
              StreamBuilder<List<ScanResult>>(
                stream: FlutterBlue.instance.scanResults,
                initialData: [],
                builder: (c, snapshot) => Column(
                  children: snapshot.data
                      .map(
                        (r) => ScanResultTile(
                      result: r,
                      onTap: () => Navigator.of(context)
                          .push(MaterialPageRoute(builder: (context) {
                        r.device.connect();
                        return DeviceScreen(device: r.device);
                      })),
                    ),
                  )
                      .toList(),
                ),
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: StreamBuilder<bool>(
        stream: FlutterBlue.instance.isScanning,
        initialData: false,
        builder: (c, snapshot) {
          if (snapshot.data) {
            return FloatingActionButton(
              child: Icon(Icons.stop),
              onPressed: () => FlutterBlue.instance.stopScan(),
              backgroundColor: Colors.red,
            );
          } else {
            return FloatingActionButton(
                child: Icon(Icons.search),
                onPressed: () => FlutterBlue.instance
                    .startScan(timeout: Duration(seconds: 4)),
            backgroundColor: const Color(0xffE5E0A1),);
          }
        },
      ),
    );
  }
}

class DeviceScreen extends StatelessWidget {
  const DeviceScreen({Key key, this.device}) : super(key: key);

  final BluetoothDevice device;

  List<int> _getRandomBytes() {
    final math = Random();
    return [
      math.nextInt(255),
      math.nextInt(255),
      math.nextInt(255),
      math.nextInt(255)
    ];
  }

  List<Widget> _buildServiceTiles(List<BluetoothService> services) {
    return services
        .map(
          (s) => ServiceTile(
        service: s,
        characteristicTiles: s.characteristics
            .map(
              (c) => CharacteristicTile(
            characteristic: c,
            onReadPressed: () => c.read(),
            onWritePressed: () async {
              await c.write(_getRandomBytes(), withoutResponse: true);
              await c.read();
            },
            onNotificationPressed: () async {
              await c.setNotifyValue(!c.isNotifying);
              await c.read();
            },
            descriptorTiles: c.descriptors
                .map(
                  (d) => DescriptorTile(
                descriptor: d,
                onReadPressed: () => d.read(),
                onWritePressed: () => d.write(_getRandomBytes()),
              ),
            )
                .toList(),
          ),
        )
            .toList(),
      ),
    )
        .toList();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(device.name),
        actions: <Widget>[
          StreamBuilder<BluetoothDeviceState>(
            stream: device.state,
            initialData: BluetoothDeviceState.connecting,
            builder: (c, snapshot) {
              VoidCallback onPressed;
              String text;
              switch (snapshot.data) {
                case BluetoothDeviceState.connected:
                  onPressed = () => device.disconnect();
                  text = 'DISCONNECT';
                  break;
                case BluetoothDeviceState.disconnected:
                  onPressed = () => device.connect();
                  text = 'CONNECT';
                  break;
                default:
                  onPressed = null;
                  text = snapshot.data.toString().substring(21).toUpperCase();
                  break;
              }
              return FlatButton(
                  onPressed: onPressed,
                  child: Text(
                    text,
                    style: Theme.of(context)
                        .primaryTextTheme
                        .button
                        ?.copyWith(color: Colors.white),
                  ));
            },
          )
        ],
      ),
      body: SingleChildScrollView(
        child: Column(
          children: <Widget>[
            StreamBuilder<BluetoothDeviceState>(
              stream: device.state,
              initialData: BluetoothDeviceState.connecting,
              builder: (c, snapshot) => ListTile(
                leading: (snapshot.data == BluetoothDeviceState.connected)
                    ? Icon(Icons.bluetooth_connected)
                    : Icon(Icons.bluetooth_disabled),
                title: Text(
                    'Device is ${snapshot.data.toString().split('.')[1]}.'),
                subtitle: Text('${device.id}'),
                trailing: StreamBuilder<bool>(
                  stream: device.isDiscoveringServices,
                  initialData: false,
                  builder: (c, snapshot) => IndexedStack(
                    index: snapshot.data ? 1 : 0,
                    children: <Widget>[
                      IconButton(
                        icon: Icon(Icons.refresh),
                        onPressed: () => device.discoverServices(),
                      ),
                      IconButton(
                        icon: SizedBox(
                          child: CircularProgressIndicator(
                            valueColor: AlwaysStoppedAnimation(Colors.grey),
                          ),
                          width: 18.0,
                          height: 18.0,
                        ),
                        onPressed: null,
                      )
                    ],
                  ),
                ),
              ),
            ),

            StreamBuilder<int>(
              stream: device.mtu,
              initialData: 0,
              builder: (c, snapshot) => ListTile(
                title: Text('MTU Size'),
                subtitle: Text('${snapshot.data} bytes'),
                trailing: IconButton(
                  icon: Icon(Icons.edit),
                  onPressed: () => device.requestMtu(223),
                ),
              ),
            ),
            StreamBuilder<List<BluetoothService>>(
              stream: device.services,
              initialData: [],
              builder: (c, snapshot) {
                return Column(
                  children: _buildServiceTiles(snapshot.data),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

Widgets 的代码与代码示例中编写的代码相同,我只更改了 UI 的主文件。

心率小部件代码:

          Center(
            child: Container(
              height: size.height * 0.190,
              width: size.width * 0.80,
              padding: EdgeInsets.all(11.0),
              child: Card(
                color: const Color(0xffe8e5af),
                elevation: 5,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(20),
                ),
                child: Stack(
                  children: <Widget>[
                    Container(
                        padding: EdgeInsets.symmetric(horizontal: 25),
                        alignment: Alignment.centerLeft,
                        child: const SpinKitPumpingHeart(
                          color: Colors.white,
                        )
                        //Image.asset(
                        //   'assets/images/heart_button.png',
                        // ),
                        ),
                    Container(
                      padding: EdgeInsets.symmetric(vertical: 20),
                      alignment: Alignment.center,
                      width: size.width * 0.80,
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            '96',
                            style: TextStyle(
                              fontFamily: 'SF Pro Display',
                              fontSize: 19,
                              color: const Color(0xffffffff),
                              fontWeight: FontWeight.w500,
                              height: 1.4736842105263157,
                            ),
                            textHeightBehavior: TextHeightBehavior(
                                applyHeightToFirstAscent: false),
                            textAlign: TextAlign.left,
                          ),
                          SizedBox(
                            height: 5,
                          ),
                          Text(
                            'Heart Rate',
                            style: TextStyle(
                              fontFamily: 'SF Pro Display',
                              fontSize: 19,
                              color: Colors.white.withOpacity(0.7),
                              fontWeight: FontWeight.w500,
                              height: 1.2777777777777777,
                            ),
                            textHeightBehavior: TextHeightBehavior(
                                applyHeightToFirstAscent: false),
                            textAlign: TextAlign.left,
                          ),
                        ],
                      ),
                    )
                  ],
                ),
              ),
            ),
          )

解决它的一种方法是使用提供者。 class 1个


    import 'package:flutter_blue/flutter_blue.dart';
    
    class BlueDevice extends ChangeNotifier {
      /// Internal, private state of the Device.
      BluetoothDevice _device;
      BluetoothService _service;
    
      /// Device metrics
      int heartRate;
    
    ...
    
    /// Adds device to model. This is the only way to modify the device from outside.
      void add(BluetoothDevice d) async {
        _device = d;
        await _device.connect();
        _device.discoverServices();
        _device.services.listen((lista) {
          for (int iService = 0; iService < lista.length; iService++) {
            if (lista[iService].uuid.toString().startsWith("YOUR UUID")) {
              _service = lista[iService];
            }
          }
        });
        // This line tells [Model] that it should rebuild the widgets that
        // depend on it.
        notifyListeners();
      }
    
      void read() async {
        List<List> values = [];
        if (_service != null) {
          for (BluetoothCharacteristic c in _service.characteristics) {
            values.add(await c.read());
          }
        }
        heartRate = values[0][0];
        notifyListeners();
      }
    }

需要创建提供者


    class FlutterBlueApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return ChangeNotifierProvider(
            // In this app, BlueDevice is implemented as a ChangeNotifier,
            // which calls for the use of ChangeNotifierProvider.
            create: (context) => BlueDevice(),
            child: Scaffold(...
         

你可以和消费者一起使用它

    Consumer<BlueDevice>(
              builder: (context, blue, child) => Text(blue.heartRate.toString(),
         //'96', THIS WAS THE VALUE
       style: TextStyle(
       fontFamily: 'SF Pro Display',
       fontSize: 19,
       color: const Color(0xffffffff),
       fontWeight: FontWeight.w500,
       height: 1.4736842105263157,
       ),

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM