【发布时间】:2021-12-24 05:56:51
【问题描述】:
我敢肯定这个问题以前有人问过,但我什至不知道如何正确表达它。
我正在尝试将蓝牙设备数据导入我的颤振应用程序。我发现的示例要么使用非空安全版本的 dart 代码,要么隐藏所有重要细节。
我正在尝试从头开始构建一个非常简单的原型,以便更好地掌握事物。
我想制作一个基于 notifyListeners() 进行更新的有状态小部件。我的想法是从“noName 蓝牙设备”开始,然后一旦连接了设备,我就可以更新对象并显示连接设备的名称。
我一直遇到同样的障碍,我无法克服它。我想制作一个默认的蓝牙设备,但该设备没有默认构造函数。由于 null 安全,默认设备不能为 null。
谁能帮我解决这个问题。有些事情我知道我从根本上误解了,但我不知道从哪里开始。
下面的代码使 ChangeNotifierProvider 成为我的 BlueTesting 小部件的父级,它应该显示连接的蓝牙设备的详细信息(我还没有编写所有代码)。
BTDevices 类应该更新蓝牙设备对象,并通知应用程序将更新的数据从“默认空设备”显示到新连接的设备。
感谢您的帮助!
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:provider/provider.dart';
void main() => runApp(
ChangeNotifierProvider(create: (_) => BTDevices(), child: BlueTesting()));
class BTDevices extends ChangeNotifier {
BluetoothDevice device = BluetoothDevice();
void setDevice(BluetoothDevice newDevice) {
device = newDevice;
notifyListeners();
}
}
更新:
我尝试更新上面的代码来设置:
BluetoothDevice device = null;
“Null”类型的值不能分配给“BluetoothDevice”类型的变量。 尝试更改变量的类型,或将右侧类型转换为 'BluetoothDevice'.dartinvalid_assignment
这里是关于蓝牙设备定义的信息:
part of flutter_blue;
class BluetoothDevice {
final DeviceIdentifier id;
final String name;
final BluetoothDeviceType type;
BluetoothDevice.fromProto(protos.BluetoothDevice p)
: id = new DeviceIdentifier(p.remoteId),
name = p.name,
type = BluetoothDeviceType.values[p.type.value];
BehaviorSubject<bool> _isDiscoveringServices = BehaviorSubject.seeded(false);
Stream<bool> get isDiscoveringServices => _isDiscoveringServices.stream;
/// Establishes a connection to the Bluetooth Device.
Future<void> connect({
Duration? timeout,
bool autoConnect = true,
}) async {
var request = protos.ConnectRequest.create()
..remoteId = id.toString()
..androidAutoConnect = autoConnect;
Timer? timer;
if (timeout != null) {
timer = Timer(timeout, () {
disconnect();
throw TimeoutException('Failed to connect in time.', timeout);
});
}
await FlutterBlue.instance._channel
.invokeMethod('connect', request.writeToBuffer());
await state.firstWhere((s) => s == BluetoothDeviceState.connected);
timer?.cancel();
return;
}
/// Cancels connection to the Bluetooth Device
Future disconnect() =>
FlutterBlue.instance._channel.invokeMethod('disconnect', id.toString());
BehaviorSubject<List<BluetoothService>> _services =
BehaviorSubject.seeded([]);
/// Discovers services offered by the remote device as well as their characteristics and descriptors
Future<List<BluetoothService>> discoverServices() async {
final s = await state.first;
if (s != BluetoothDeviceState.connected) {
return Future.error(new Exception(
'Cannot discoverServices while device is not connected. State == $s'));
}
var response = FlutterBlue.instance._methodStream
.where((m) => m.method == "DiscoverServicesResult")
.map((m) => m.arguments)
.map((buffer) => new protos.DiscoverServicesResult.fromBuffer(buffer))
.where((p) => p.remoteId == id.toString())
.map((p) => p.services)
.map((s) => s.map((p) => new BluetoothService.fromProto(p)).toList())
.first
.then((list) {
_services.add(list);
_isDiscoveringServices.add(false);
return list;
});
await FlutterBlue.instance._channel
.invokeMethod('discoverServices', id.toString());
_isDiscoveringServices.add(true);
return response;
}
/// Returns a list of Bluetooth GATT services offered by the remote device
/// This function requires that discoverServices has been completed for this device
Stream<List<BluetoothService>> get services async* {
yield await FlutterBlue.instance._channel
.invokeMethod('services', id.toString())
.then((buffer) =>
new protos.DiscoverServicesResult.fromBuffer(buffer).services)
.then((i) => i.map((s) => new BluetoothService.fromProto(s)).toList());
yield* _services.stream;
}
/// The current connection state of the device
Stream<BluetoothDeviceState> get state async* {
yield await FlutterBlue.instance._channel
.invokeMethod('deviceState', id.toString())
.then((buffer) => new protos.DeviceStateResponse.fromBuffer(buffer))
.then((p) => BluetoothDeviceState.values[p.state.value]);
yield* FlutterBlue.instance._methodStream
.where((m) => m.method == "DeviceState")
.map((m) => m.arguments)
.map((buffer) => new protos.DeviceStateResponse.fromBuffer(buffer))
.where((p) => p.remoteId == id.toString())
.map((p) => BluetoothDeviceState.values[p.state.value]);
}
/// The MTU size in bytes
Stream<int> get mtu async* {
yield await FlutterBlue.instance._channel
.invokeMethod('mtu', id.toString())
.then((buffer) => new protos.MtuSizeResponse.fromBuffer(buffer))
.then((p) => p.mtu);
yield* FlutterBlue.instance._methodStream
.where((m) => m.method == "MtuSize")
.map((m) => m.arguments)
.map((buffer) => new protos.MtuSizeResponse.fromBuffer(buffer))
.where((p) => p.remoteId == id.toString())
.map((p) => p.mtu);
}
/// Request to change the MTU Size
/// Throws error if request did not complete successfully
Future<void> requestMtu(int desiredMtu) async {
var request = protos.MtuSizeRequest.create()
..remoteId = id.toString()
..mtu = desiredMtu;
return FlutterBlue.instance._channel
.invokeMethod('requestMtu', request.writeToBuffer());
}
/// Indicates whether the Bluetooth Device can send a write without response
Future<bool> get canSendWriteWithoutResponse =>
new Future.error(new UnimplementedError());
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is BluetoothDevice &&
runtimeType == other.runtimeType &&
id == other.id;
@override
int get hashCode => id.hashCode;
@override
String toString() {
return 'BluetoothDevice{id: $id, name: $name, type: $type, isDiscoveringServices: ${_isDiscoveringServices.value}, _services: ${_services.value}';
}
}
enum BluetoothDeviceType { unknown, classic, le, dual }
enum BluetoothDeviceState { disconnected, connecting, connected, disconnecting }
【问题讨论】:
-
"默认设备不能为 null,因为 null 安全。"空安全并不意味着从不使用
null;这意味着明确识别、跟踪和检查可能是null的变量。使变量可以为空并稍后检查它是否为null没有任何问题。您也可以将device声明为late,前提是您可以从逻辑上保证在调用setDevice之前永远不会访问device,但这通常容易出错,因为调用者无法动态检查late变量是否已初始化。 -
感谢您的信息。我更新了将设备设置为空时的错误。现在就我而言,我想为设备显示一个“默认值”,并且将来我想更新该值。因此,将其设置为后期变量将容易出错。也许正确的方法是将设备声明为迟,然后在显示之前检查它是否不为空,否则不显示。这可能行得通,但我认为这并不理想。
标签: flutter dart android-bluetooth flutter-provider