我正在尝试使用 flutter_Blue 开发“BLE 控制应用程序”。 我添加了一个标签栏,所以我想保持蓝牙状态“连接”。 所以我正在尝试使用 Provider 来设置连接状态,但我遇到了这样的错误。
**======== Exception caught by foundation library ====================================================
The following assertion was thrown while dispatching notifications for BluetoothProvider:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<BluetoothProvider> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: _InheritedProviderScope<BluetoothProvider>
value: Instance of 'BluetoothProvider'
listening to value
The widget which was currently being built when the offending call was made was: Consumer<BluetoothProvider>
dirty
dependencies: [_InheritedProviderScope<BluetoothProvider>]
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:4138:11)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4153:6)
#2 _InheritedProviderScopeElement.markNeedsNotifyDependents (package:provider/src/inherited_provider.dart:531:5)
#3 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:243:25)
#4 BluetoothProvider.startScan (package:flutter_joystick/provider/bluetooth_provider.dart:46:5)
...
The BluetoothProvider sending notification was: Instance of 'BluetoothProvider'**
这是我的蓝牙提供商代码
class BluetoothProvider with ChangeNotifier{
final String SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb";
final String CHARACTERISTIC_UUID="0000ffe1-0000-1000-8000-00805f9b34fb";
final String TARGET_DEVICE_NAME="HMSoft";
FlutterBlue flutterBlue = FlutterBlue.instance;
StreamSubscription<ScanResult> scanSubScription;
BluetoothDevice targetDevice;
BluetoothCharacteristic targetCharacteristic;
BluetoothState bluetoothState;
String connectionText="";
String joystick="";
startScan(){
connectionText="Start Scanning";
scanSubScription = flutterBlue.scan().listen((scanResult){
if(scanResult.device.name==TARGET_DEVICE_NAME){
print("Device Found");
stopScan();
connectionText="Found Target Device";
targetDevice = scanResult.device;
}
}, onDone: () => stopScan());
notifyListeners();
}
stopScan(){
scanSubScription?.cancel();
scanSubScription=null;
notifyListeners();
}
connectToDevice() async{
if(targetDevice==null) return;
connectionText = "Device Connecting";
await targetDevice.connect();
print("Device Connected");
connectionText="Device Connected";
discoverServices();
notifyListeners();
}
disconnectFromDevice(){
if(targetDevice==null) return;
targetDevice.disconnect();
connectionText="Device Disconnected";
notifyListeners();
}
discoverServices() async{
if(targetDevice==null) return;
List<BluetoothService> services = await targetDevice.discoverServices();
services.forEach((service) {
if(service.uuid.toString() == SERVICE_UUID){
service.characteristics.forEach((characteristc) {
if (characteristc.uuid.toString() == CHARACTERISTIC_UUID) {
targetCharacteristic = characteristc;
writeData("Connect Complete!\r\n");
connectionText = "All Ready with ${targetDevice.name}";
}
});
}
}
);
notifyListeners();
}
writeData(String data) async{
if(targetCharacteristic==null) return;
List<int> bytes = utf8.encode(data);
await targetCharacteristic.write(bytes);
notifyListeners();
}
}
有趣的是,蓝牙连接正在进行中,但是控制台窗口不断出现上面写的错误。
Tab Bar第一页是摇杆页面,蓝牙连接出错,摇杆不工作。
这是操纵杆代码
class JoyPad extends StatefulWidget {
@override
_JoyPadState createState() => _JoyPadState();
}
class _JoyPadState extends State<JoyPad> {
BluetoothProvider _bluetoothProvider;
@override
Widget build(BuildContext context) {
_bluetoothProvider = Provider.of<BluetoothProvider>(context,listen:false);
return Consumer<BluetoothProvider>(
builder:(context,provider,child) {
_bluetoothProvider.startScan();
return Scaffold(
appBar: AppBar(
title: Text(_bluetoothProvider.connectionText),
backgroundColor: Colors.indigoAccent,
actions: <Widget>[
IconButton(
icon: Icon(Icons.bluetooth), iconSize: 30,
onPressed: () {
_bluetoothProvider.connectToDevice();
print(_bluetoothProvider.bluetoothState.toString());
},
),
IconButton(
icon: Icon(Icons.bluetooth_disabled), iconSize: 30,
onPressed: () {
_bluetoothProvider.disconnectFromDevice();
print(_bluetoothProvider.bluetoothState.toString());
}),
],
),
body: joystickWidget(),
);
});
}
}
另外,提供者没有“setState”,所以我尝试根据应用栏上的状态变化来显示连接文本,但这是不可能的。 如果您能告诉我如何解决它,我也将不胜感激。
答案 0 :(得分:0)
您实际上遇到此错误是因为您在构建小部件树时尝试重建它。
您在消费者的构建器方法中对 _bluetoothProvider.startScan();
的调用将调用 notifyListeners
方法,该方法实际上会在构建树时尝试重建树,因此将抛出该异常。
为什么?
Consumer 小部件实际上正在监听您的 BluetoothProvider 上的更改;因此,当您在 BluetothProvider
类上调用 notifyListeners 时,Consumer 会尝试重建自己,但这是未经授权的。
一种解决方案是先构建树,然后调用 startScan 方法。
你可以试试这个:
提供商代码
class BluetoothProvider with ChangeNotifier{
final String SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb";
final String CHARACTERISTIC_UUID="0000ffe1-0000-1000-8000-00805f9b34fb";
final String TARGET_DEVICE_NAME="HMSoft";
FlutterBlue flutterBlue = FlutterBlue.instance;
StreamSubscription<ScanResult> scanSubScription;
BluetoothDevice targetDevice;
BluetoothCharacteristic targetCharacteristic;
BluetoothState bluetoothState;
String connectionText="";
String joystick="";
startScan() {
connectionText="Start Scanning";
scanSubScription = flutterBlue.scan().listen((scanResult){
if(scanResult.device.name==TARGET_DEVICE_NAME){
print("Device Found");
stopScan();
connectionText="Found Target Device";
targetDevice = scanResult.device;
}
}, onDone: () => stopScan());
notifyListeners();
}
stopScan() {
scanSubScription?.cancel();
scanSubScription=null;
notifyListeners();
}
connectToDevice() async{
if(targetDevice==null) return;
connectionText = "Device Connecting";
await targetDevice.connect();
print("Device Connected");
connectionText="Device Connected";
discoverServices();
notifyListeners();
}
disconnectFromDevice(){
if(targetDevice==null) return;
targetDevice.disconnect();
connectionText="Device Disconnected";
notifyListeners();
}
discoverServices() async {
if(targetDevice==null) return;
List<BluetoothService> services = await targetDevice.discoverServices();
services.forEach((service) {
if(service.uuid.toString() == SERVICE_UUID){
service.characteristics.forEach((characteristc) {
if (characteristc.uuid.toString() == CHARACTERISTIC_UUID) {
targetCharacteristic = characteristc;
writeData("Connect Complete!\r\n");
connectionText = "All Ready with ${targetDevice.name}";
}
});
}
});
notifyListeners();
}
writeData(String data) async{
if(targetCharacteristic==null) return;
List<int> bytes = utf8.encode(data);
await targetCharacteristic.write(bytes);
notifyListeners();
}
}
小部件代码
class JoyPad extends StatefulWidget {
@override
_JoyPadState createState() => _JoyPadState();
}
class _JoyPadState extends State<JoyPad> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
// The code in this block will be executed after the build method
context.read<BluetoothProvider>().startScan();
});
}
@override
Widget build(BuildContext context) {
return Consumer<BluetoothProvider>(
builder:(context,provider,child) {
return Scaffold(
appBar: AppBar(
title: Text(_bluetoothProvider.connectionText),
backgroundColor: Colors.indigoAccent,
actions: <Widget>[
IconButton(
icon: Icon(Icons.bluetooth), iconSize: 30,
onPressed: () {
_bluetoothProvider.connectToDevice();
print(_bluetoothProvider.bluetoothState.toString());
},
),
IconButton(
icon: Icon(Icons.bluetooth_disabled), iconSize: 30,
onPressed: () {
_bluetoothProvider.disconnectFromDevice();
print(_bluetoothProvider.bluetoothState.toString());
},
),
],
),
body: joystickWidget(),
);
});
}
}
}
context.read<BluetoothProvider>().startScan();
是 Provider.of<BluetoothProvider>(context, listen: false).startScan()
的快捷方式:它基本上做同样的事情。
如果您对我的解决方案有任何疑问,请告诉我。