Skip to content

Commit 100e7f5

Browse files
authored
Merge pull request #37 from jeroen1602/update_characteristic_properties
Update characteristic properties
2 parents 6336c7c + 3724d1e commit 100e7f5

File tree

6 files changed

+421
-49
lines changed

6 files changed

+421
-49
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
* Added `requestLEScan` to `Bluetooth` and `FlutterWebBluetooth`. You can now scan for devices advertisements without
77
needing to pair with each device. However, a normal pair does need to happen before characteristics can be used.
88
* **Breaking** `BluetoothDevice.gatt` is no longer marked as deprecated, it is still marked as `visibleForTesting`.
9+
* Continuing with the IOS Bluefy browser fix for characteristic properties.
10+
* Added `has` methods for all the fields in characteristic properties.
11+
* Return `false` as default value when a property doesn't exist.
12+
* Added `hasProperties` method to check if there are any properties at all. Use this to test if the properties are
13+
even reliable.
14+
* You can remove any try-catch logic around reading properties if you added those for the mitigation.
915

1016
## 0.0.9
1117

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import 'dart:async';
2+
3+
import 'package:flutter/material.dart';
4+
import 'package:flutter_web_bluetooth/flutter_web_bluetooth.dart';
5+
6+
abstract class Actions {
7+
Future<void> readValue();
8+
9+
Future<void> toggleNotify();
10+
11+
bool get isNotifying;
12+
13+
Future<void> writeValue(BuildContext? context);
14+
}
15+
16+
class CharacteristicActions extends Actions {
17+
CharacteristicActions(BluetoothCharacteristic characteristic)
18+
: _characteristic = characteristic;
19+
20+
final BluetoothCharacteristic _characteristic;
21+
22+
@override
23+
bool get isNotifying => _characteristic.isNotifying;
24+
25+
@override
26+
Future<void> readValue() {
27+
return _characteristic.readValue();
28+
}
29+
30+
@override
31+
Future<void> toggleNotify() {
32+
if (isNotifying) {
33+
return _characteristic.stopNotifications();
34+
} else {
35+
return _characteristic.startNotifications();
36+
}
37+
}
38+
39+
@override
40+
Future<void> writeValue(final BuildContext? context) async {
41+
if (context != null) {
42+
ScaffoldMessenger.maybeOf(context)?.showSnackBar(const SnackBar(
43+
content: Text('Write is supported just not implemented in the example'),
44+
));
45+
}
46+
}
47+
}
48+
49+
///
50+
/// A widget to handle the different actions for a characteristic.
51+
///
52+
/// It reads from the [properties] to see if the actions is supported and
53+
/// then adds it to the list, or it leaves it out.
54+
///
55+
class ActionsWidget extends StatefulWidget {
56+
const ActionsWidget(this.properties, this.actions, {super.key});
57+
58+
final Actions actions;
59+
final BluetoothCharacteristicProperties properties;
60+
61+
@override
62+
State<StatefulWidget> createState() {
63+
return ActionsSate();
64+
}
65+
}
66+
67+
class ActionsSate extends State<ActionsWidget> {
68+
Color? _getErrorColor() {
69+
if (!mounted) {
70+
return null;
71+
}
72+
final theme = Theme.of(context);
73+
return theme.colorScheme.error;
74+
}
75+
76+
Widget? getReadValueAction() {
77+
if (!widget.properties.hasProperties || widget.properties.read) {
78+
return OutlinedButton(
79+
onPressed: handlePressed(widget.actions.readValue),
80+
child: const Text('Read value'));
81+
}
82+
return null;
83+
}
84+
85+
Widget? getNotifyAction() {
86+
if (!widget.properties.hasProperties || widget.properties.notify) {
87+
return OutlinedButton(
88+
onPressed: handlePressed(widget.actions.toggleNotify),
89+
child: Text(widget.actions.isNotifying
90+
? 'Stop notifying'
91+
: 'Start notifying'));
92+
}
93+
return null;
94+
}
95+
96+
Widget? getWriteAction() {
97+
if (!widget.properties.hasProperties || widget.properties.write) {
98+
return OutlinedButton(
99+
onPressed: handlePressedWithContext(widget.actions.writeValue),
100+
child: const Text('Write value'));
101+
}
102+
return null;
103+
}
104+
105+
FutureOr<void> Function() handlePressed(FutureOr<void> Function() method) {
106+
return () async {
107+
try {
108+
await method.call();
109+
} catch (e, s) {
110+
debugPrint("$e\n$s");
111+
if (mounted) {
112+
ScaffoldMessenger.maybeOf(context)?.showSnackBar(SnackBar(
113+
content: Text('Something went wrong: $e'),
114+
backgroundColor: _getErrorColor(),
115+
));
116+
}
117+
}
118+
};
119+
}
120+
121+
FutureOr<void> Function() handlePressedWithContext(
122+
FutureOr<void> Function(BuildContext?) method) {
123+
return () async {
124+
try {
125+
if (mounted) {
126+
await method.call(context);
127+
} else {
128+
await method.call(null);
129+
}
130+
} catch (e, s) {
131+
debugPrint("$e\n$s");
132+
if (mounted) {
133+
ScaffoldMessenger.maybeOf(context)?.showSnackBar(SnackBar(
134+
content: Text('Something went wrong: $e'),
135+
backgroundColor: _getErrorColor(),
136+
));
137+
}
138+
}
139+
};
140+
}
141+
142+
bool _hasValueSet<T>([List<T?> args = const []]) {
143+
for (final arg in args) {
144+
if (arg != null) {
145+
return true;
146+
}
147+
}
148+
return false;
149+
}
150+
151+
@override
152+
Widget build(final BuildContext context) {
153+
final actionReadValue = getReadValueAction();
154+
final actionNotify = getNotifyAction();
155+
final actionWrite = getWriteAction();
156+
157+
final hasAction =
158+
_hasValueSet([actionReadValue, actionNotify, actionWrite]);
159+
160+
if (hasAction) {
161+
return Row(
162+
children: [
163+
if (actionReadValue != null) actionReadValue,
164+
if (actionNotify != null) actionNotify,
165+
if (actionWrite != null) actionWrite,
166+
],
167+
);
168+
} else {
169+
return Row(
170+
children: const [
171+
Text("No actions for this characteristic"),
172+
],
173+
);
174+
}
175+
}
176+
}

example/lib/widgets/characteristic_widget.dart

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'dart:typed_data';
33

44
import 'package:flutter/material.dart';
55
import 'package:flutter_web_bluetooth/flutter_web_bluetooth.dart';
6+
import 'package:flutter_web_bluetooth_example/widgets/characteristic_actions.dart';
67

78
class CharacteristicWidget extends StatefulWidget {
89
CharacteristicWidget({required this.characteristic, Key? key})
@@ -49,27 +50,8 @@ class CharacteristicWidgetState extends State<CharacteristicWidget> {
4950
}),
5051
Padding(
5152
padding: const EdgeInsets.all(8.0),
52-
child: Row(
53-
children: [
54-
OutlinedButton(
55-
onPressed: () async {
56-
await widget.characteristic.readValue();
57-
},
58-
child: const Text('Get value')),
59-
OutlinedButton(
60-
onPressed: () async {
61-
if (widget.characteristic.isNotifying) {
62-
await widget.characteristic.stopNotifications();
63-
} else {
64-
await widget.characteristic.startNotifications();
65-
}
66-
setState(() {});
67-
},
68-
child: Text(widget.characteristic.isNotifying
69-
? 'Stop notifying'
70-
: 'Start notifying'))
71-
],
72-
),
53+
child: ActionsWidget(widget.characteristic.properties,
54+
CharacteristicActions(widget.characteristic)),
7355
),
7456
],
7557
);

lib/src/bluetooth_characteristic.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,22 @@ class BluetoothCharacteristic {
7474
/// the properties of what the [BluetoothCharacteristic] does and doesn't
7575
/// support.
7676
///
77+
/// See [hasProperties] or [BluetoothCharacteristicProperties.hasProperties]
78+
/// to see if the current browser has properties implemented
79+
///
7780
BluetoothCharacteristicProperties get properties => _properties;
7881

82+
///
83+
/// Check to see if [properties] has any properties at all. Some browsers
84+
/// don't have this feature implemented yet.
85+
///
86+
/// If this is `false` then reading any of the properties will also return
87+
/// `false`. It will not throw any errors.
88+
///
89+
bool get hasProperties {
90+
return properties.hasProperties;
91+
}
92+
7993
///
8094
/// Return a [BluetoothDescriptor] for this characteristic.
8195
///

lib/src/bluetooth_characteristic_properties.dart

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ class BluetoothCharacteristicProperties {
2727
/// sends out a broadcast beacon.
2828
bool get broadcast => _properties.broadcast;
2929

30+
///
31+
/// Check to see if the current implementation has the [broadcast] field.
32+
///
33+
/// This may return `false` on the bluefy browser.
34+
///
35+
bool get hasBroadcast => _properties.hasBroadcast;
36+
3037
///
3138
/// Check to see if read is available in this characteristic.
3239
///
@@ -36,6 +43,13 @@ class BluetoothCharacteristicProperties {
3643
/// See: [BluetoothCharacteristic.readValue].
3744
bool get read => _properties.read;
3845

46+
///
47+
/// Check to see if the current implementation has the [read] field.
48+
///
49+
/// This may return `false` on the bluefy browser.
50+
///
51+
bool get hasRead => _properties.hasRead;
52+
3953
///
4054
/// Check to see if write without response is available in this
4155
/// characteristic.
@@ -48,6 +62,13 @@ class BluetoothCharacteristicProperties {
4862
/// [BluetoothCharacteristic.writeValueWithResponse], [write].
4963
bool get writeWithoutResponse => _properties.writeWithoutResponse;
5064

65+
///
66+
/// Check to see if the current implementation has the [writeWithoutResponse] field.
67+
///
68+
/// This may return `false` on the bluefy browser.
69+
///
70+
bool get hasWriteWithoutResponse => _properties.hasWritableAuxiliaries;
71+
5172
///
5273
/// Check to see if write is available in this characteristic.
5374
///
@@ -60,6 +81,15 @@ class BluetoothCharacteristicProperties {
6081
/// [BluetoothCharacteristic.writeValueWithResponse], [writeWithoutResponse].
6182
bool get write => _properties.write;
6283

84+
///
85+
/// Check to see if the current implementation has the [write] field.
86+
///
87+
/// Will return `false` if the field doesn't exist.
88+
///
89+
/// This may return `false` on the bluefy browser.
90+
///
91+
bool get hasWrite => _properties.hasWrite;
92+
6393
///
6494
/// Check to see if notify is available in this characteristic. If this
6595
/// is `true` then you can use [BluetoothCharacteristic.startNotifications].
@@ -68,24 +98,69 @@ class BluetoothCharacteristicProperties {
6898
/// See: [BluetoothCharacteristic.startNotifications].
6999
bool get notify => _properties.notify;
70100

101+
///
102+
/// Check to see if the current implementation has the [notify] field.
103+
///
104+
/// This may return `false` on the bluefy browser.
105+
///
106+
bool get hasNotify => _properties.hasNotify;
107+
71108
///
72109
/// Check to see if indicate is available in this characteristic.
73110
///
74111
bool get indicate => _properties.indicate;
75112

113+
///
114+
/// Check to see if the current implementation has the [indicate] field.
115+
///
116+
/// This may return `false` on the bluefy browser.
117+
///
118+
bool get hasIndicate => _properties.hasIndicate;
119+
76120
///
77121
/// Check to see if authenticated signed writes is available in this
78122
/// characteristic.
79123
///
80124
bool get authenticatedSignedWrites => _properties.authenticatedSignedWrites;
81125

126+
///
127+
/// Check to see if the current implementation has the [authenticatedSignedWrites] field.
128+
///
129+
/// This may return `false` on the bluefy browser.
130+
///
131+
bool get hasAuthenticatedSignedWrites =>
132+
_properties.hasAuthenticatedSignedWrites;
133+
82134
///
83135
/// Check to see if reliable write is available in this characteristic.
84136
///
85137
bool get reliableWrite => _properties.reliableWrite;
86138

139+
///
140+
/// Check to see if the current implementation has the [reliableWrite] field.
141+
///
142+
/// This may return `false` on the bluefy browser.
143+
///
144+
bool get hasReliableWrite => _properties.hasReliableWrite;
145+
87146
///
88147
/// Check to see if writable auxiliaries is available in this characteristic.
89148
///
90149
bool get writableAuxiliaries => _properties.writableAuxiliaries;
150+
151+
///
152+
/// Check to see if the current implementation has the [writableAuxiliaries] field.
153+
///
154+
/// This may return `false` on the bluefy browser.
155+
///
156+
bool get hasWritableAuxiliaries => _properties.hasWritableAuxiliaries;
157+
158+
///
159+
/// Check to see if there is this characteristic has any property at all.
160+
///
161+
/// If this returns `false` then the implementation of the browser probably
162+
/// hasn't implemented this and the values returned from the fields will
163+
/// always be `false` thus not being reliable.
164+
///
165+
bool get hasProperties => _properties.hasProperties;
91166
}

0 commit comments

Comments
 (0)