Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ void main() {
ble: _ble,
logMessage: _bleLogger.addToLog,
);
final _serviceDiscoverer = BleServiceDiscoverer(
final _serviceDiscoverer = BleDeviceInteractor(
bleDiscoverServices: _ble.discoverServices,
readCharacteristic: _ble.readCharacteristic,
writeWithResponse: _ble.writeCharacteristicWithResponse,
writeWithOutResponse: _ble.writeCharacteristicWithoutResponse,
subscribeToCharacteristic: _ble.subscribeToCharacteristic,
logMessage: _bleLogger.addToLog,
);
runApp(
Expand Down
82 changes: 80 additions & 2 deletions example/lib/src/ble/ble_device_interactor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,43 @@ import 'dart:async';

import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';

class BleServiceDiscoverer {
BleServiceDiscoverer({
class BleDeviceInteractor {
BleDeviceInteractor({
required Future<List<DiscoveredService>> Function(String deviceId)
bleDiscoverServices,
required Future<List<int>> Function(QualifiedCharacteristic characteristic)
readCharacteristic,
required Future<void> Function(QualifiedCharacteristic characteristic,
{required List<int> value})
writeWithResponse,
required Future<void> Function(QualifiedCharacteristic characteristic,
{required List<int> value})
writeWithOutResponse,
required void Function(String message) logMessage,
required Stream<List<int>?> Function(QualifiedCharacteristic characteristic)
subscribeToCharacteristic,
}) : _bleDiscoverServices = bleDiscoverServices,
_readCharacteristic = readCharacteristic,
_writeWithResponse = writeWithResponse,
_writeWithoutResponse = writeWithOutResponse,
_subScribeToCharacteristic = subscribeToCharacteristic,
_logMessage = logMessage;

final Future<List<DiscoveredService>> Function(String deviceId)
_bleDiscoverServices;

final Future<List<int>> Function(QualifiedCharacteristic characteristic)
_readCharacteristic;

final Future<void> Function(QualifiedCharacteristic characteristic,
{required List<int> value}) _writeWithResponse;

final Future<void> Function(QualifiedCharacteristic characteristic,
{required List<int> value}) _writeWithoutResponse;

final Stream<List<int>?> Function(QualifiedCharacteristic characteristic)
_subScribeToCharacteristic;

final void Function(String message) _logMessage;

Future<List<DiscoveredService>> discoverServices(String deviceId) async {
Expand All @@ -26,4 +52,56 @@ class BleServiceDiscoverer {
throw e;
}
}

Future<List<int>> readCharacteristic(
QualifiedCharacteristic characteristic) async {
try {
final result = await _readCharacteristic(characteristic);

_logMessage('Read ${characteristic.characteristicId}: value = $result');
return result;
} on Error catch (e, s) {
_logMessage(
'Error occured when reading ${characteristic.characteristicId} : $e',
);
print(s);
throw e;
}
}

Future<void> writeCharacterisiticWithResponse(
QualifiedCharacteristic characteristic, List<int> value) async {
try {
_logMessage(
'Write with response value : $value to ${characteristic.characteristicId}');
await _writeWithResponse(characteristic, value: value);
} on Error catch (e, s) {
_logMessage(
'Error occured when writing ${characteristic.characteristicId} : $e',
);
print(s);
throw e;
}
}

Future<void> writeCharacterisiticWithoutResponse(
QualifiedCharacteristic characteristic, List<int> value) async {
try {
await _writeWithoutResponse(characteristic, value: value);
_logMessage(
'Write without response value: $value to ${characteristic.characteristicId}');
} on Error catch (e, s) {
_logMessage(
'Error occured when writing ${characteristic.characteristicId} : $e',
);
print(s);
throw e;
}
}

Stream<List<int>?> subScribeToCharacteristic(
QualifiedCharacteristic characteristic) {
_logMessage('Subscribing to: ${characteristic.characteristicId} ');
return _subScribeToCharacteristic(characteristic);
}
}
2 changes: 1 addition & 1 deletion example/lib/src/ble/ble_logger.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:intl/intl.dart';

class BleLogger {
final List<String> _logMessages = [];
final DateFormat formatter = DateFormat('hh:mm:ss.SSS');
final DateFormat formatter = DateFormat('HH:mm:ss.SSS');

List<String> get messages => _logMessages;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:flutter_reactive_ble_example/src/ble/ble_device_interactor.dart';
import 'package:provider/provider.dart';

class CharacteristicInteractionDialog extends StatelessWidget {
const CharacteristicInteractionDialog({
required this.characteristic,
Key? key,
}) : super(key: key);
final QualifiedCharacteristic characteristic;

@override
Widget build(BuildContext context) {
return Consumer<BleDeviceInteractor>(builder: (context, interactor, _) {
return _CharacteristicInteractionDialog(
characteristic: characteristic,
readCharacteristic: interactor.readCharacteristic,
writeWithResponse: interactor.writeCharacterisiticWithResponse,
writeWithoutResponse: interactor.writeCharacterisiticWithoutResponse,
subscribeToCharacteristic: interactor.subScribeToCharacteristic,
);
});
}
}

class _CharacteristicInteractionDialog extends StatefulWidget {
const _CharacteristicInteractionDialog({
required this.characteristic,
required this.readCharacteristic,
required this.writeWithResponse,
required this.writeWithoutResponse,
required this.subscribeToCharacteristic,
Key? key,
}) : super(key: key);

final QualifiedCharacteristic characteristic;
final Future<List<int>> Function(QualifiedCharacteristic characteristic)
readCharacteristic;
final Future<void> Function(
QualifiedCharacteristic characteristic, List<int> value)
writeWithResponse;

final Stream<List<int>?> Function(QualifiedCharacteristic characteristic)
subscribeToCharacteristic;

final Future<void> Function(
QualifiedCharacteristic characteristic, List<int> value)
writeWithoutResponse;

@override
_CharacteristicInteractionDialogState createState() =>
_CharacteristicInteractionDialogState();
}

class _CharacteristicInteractionDialogState
extends State<_CharacteristicInteractionDialog> {
late String readOutput;
late String writeOutput;
late String subscribeOutput;
late TextEditingController textEditingController;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious, I didn't know about this new keyword late 👍

late StreamSubscription<List<int>?> subscribeStream;

@override
void initState() {
readOutput = '';
writeOutput = '';
subscribeOutput = '';
textEditingController = TextEditingController();
super.initState();
}

@override
void dispose() {
subscribeStream.cancel();
super.dispose();
}

Future<void> subscribeCharacteristic() async {
setState(() {
subscribeOutput = 'Notification set';
});
subscribeStream =
widget.subscribeToCharacteristic(widget.characteristic).listen((event) {
setState(() {
subscribeOutput = event.toString();
});
});
}

Future<void> readCharacteristic() async {
final result = await widget.readCharacteristic(widget.characteristic);
setState(() {
readOutput = result.toString();
});
}

List<int> _parseInput() {
return textEditingController.text
.split(',')
.map(
(value) => int.parse(value),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it clear in UI that this is a decimal number input?

)
.toList();
}

Future<void> writeCharacteristicWithResponse() async {
await widget.writeWithResponse(widget.characteristic, _parseInput());
setState(() {
writeOutput = 'Ok';
});
}

Future<void> writeCharacteristicWithoutResponse() async {
await widget.writeWithoutResponse(widget.characteristic, _parseInput());
setState(() {
writeOutput = 'Done';
});
}

Widget sectionHeader(String text) => Text(
text,
style: TextStyle(fontWeight: FontWeight.bold),
);

List<Widget> get writeSection => [
sectionHeader('Write characteristic'),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextField(
controller: textEditingController,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Value',
),
keyboardType: TextInputType.numberWithOptions(
decimal: true,
signed: false,
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: writeCharacteristicWithResponse,
child: Text('With response'),
),
ElevatedButton(
onPressed: writeCharacteristicWithoutResponse,
child: Text('Without response'),
),
],
),
Padding(
padding: const EdgeInsetsDirectional.only(top: 8.0),
child: Text('Output: $writeOutput'),
),
];

List<Widget> get readSection => [
sectionHeader('Read characteristic'),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: readCharacteristic,
child: Text('Read'),
),
Text('Output: $readOutput'),
],
),
];

List<Widget> get subscribeSection => [
sectionHeader('Subscribe / notify'),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: subscribeCharacteristic,
child: Text('Subscribe'),
),
Text('Output: $subscribeOutput'),
],
),
];

Widget get divider => Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0),
child: Divider(thickness: 2.0),
);

@override
Widget build(BuildContext context) {
return Dialog(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: ListView(
shrinkWrap: true,
children: [
Text(
'Select an operation',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Text(
widget.characteristic.characteristicId.toString(),
),
),
divider,
...readSection,
divider,
...writeSection,
divider,
...subscribeSection,
divider,
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(top: 20.0),
child: ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('close')),
),
)
],
),
),
);
}
}
Loading