BLE support in Flutter - swift

Is there any way to check if my device supports BLE through dart code? I am looking for something like this.
switch ([_manager state])
{
case CBCentralManagerStateUnsupported:
state = #"This device does not support Bluetooth Low Energy.";
break;
case CBCentralManagerStateUnauthorized:
state = #"This app is not authorized to use Bluetooth Low Energy.";
break;
case CBCentralManagerStatePoweredOff:
state = #"Bluetooth on this device is currently powered off.";
break;
case CBCentralManagerStateResetting:
state = #"The BLE Manager is resetting; a state update is pending.";
break;
case CBCentralManagerStatePoweredOn:
state = #"Bluetooth LE is turned on and ready for communication.";
break;
case CBCentralManagerStateUnknown:
state = #"The state of the BLE Manager is unknown.";
break;
default:
state = #"The state of the BLE Manager is unknown.";
}

I know this is an old thread, Thought of updating this anyway as it might help someone else.
You can try using FlutterBlue bluetooth plugin, which is a new SDK from Flutter.
FlutterBlue aims to offer the most from both platforms (iOS and Android).
FlutterBlue should work for both Android and IOS platform. The plugin should also help to scan and connect nearby devices.
Here is the sample from the documentation:
[https://github.com/pauldemarco/flutter_blue#obtain-an-instance][1]
Obtain an instance:
FlutterBlue flutterBlue = FlutterBlue.instance;
isAvailable property checks if whether the device supports Bluetooth
Future<bool> get isAvailable =>
_channel.invokeMethod('isAvailable').then<bool>((d) => d);
isOn property Checks if Bluetooth functionality is turned on :
Future<bool> get isOn => _channel.invokeMethod('isOn').then<bool>((d) => d);
Scan for devices :[https://github.com/pauldemarco/flutter_blue#scan-for-devices][2]
// Start scanning
flutterBlue.startScan(timeout: Duration(seconds: 4));
// Listen to scan results
var subscription = flutterBlue.scanResults.listen((scanResult) {
// do something with scan result
device = scanResult.device;
print('${device.name} found! rssi: ${scanResult.rssi}');
});
// Stop scanning
flutterBlue.stopScan();
Connect/disconnect to a device
https://github.com/pauldemarco/flutter_blue#connect-to-a-device
// Connect to the device
await device.connect();
// Disconnect from device
device.disconnect();
Discover services:
https://github.com/pauldemarco/flutter_blue#discover-services
List<BluetoothService> services = await device.discoverServices();
services.forEach((service) {
// do something with service
});
You can scan for low energy devices with ScanMode.lowLatency :
Stream<ScanResult> scan({
ScanMode scanMode = ScanMode.lowLatency,
List<Guid> withServices = const [],
List<Guid> withDevices = const [],
Duration timeout,
}) async* {
var settings = protos.ScanSettings.create()
..androidScanMode = scanMode.value
..serviceUuids.addAll(withServices.map((g) => g.toString()).toList());
if (_isScanning.value == true) {
throw Exception('Another scan is already in progress.');
}
....
You can also read, write the characters and discriptors or do other stuff with the help of FlutterBlue. Hope that helps.
[1]: https://github.com/pauldemarco/flutter_blue#obtain-an-instance
[2]: https://github.com/pauldemarco/flutter_blue#scan-for-devices

Related

Flutter Push Notification using SignalR

I'm using SignalR for push notifications on my Flutter app and that works ok. I get the message from the backend and show notification using flutter_local_notifications. The problem is that the SignalR service would shut down after some time.
How can I make my app stay on in the background? and even start on reboot?
Here's my code:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:isolate_test/Model/UserMessageModel.dart';
import 'package:signalr_core/signalr_core.dart';
import 'EndPointService.dart';
import 'NotificationService.dart';
class SignalRProvider {
static String appName = "NOTIFICATION";
static String? userName = "";
static String deviceName = "android_app";
static List<UserMessageModel> messages = <UserMessageModel>[];
HubConnection connection = HubConnectionBuilder()
.withUrl(
'my_url',
HttpConnectionOptions(
logging: (level, message) => print(message),
))
.withAutomaticReconnect()
.withHubProtocol(JsonHubProtocol())
.build();
Function(bool update)? onMessagesUpdateCallback;
SignalRProvider({
this.onMessagesUpdateCallback,
});
setUsername(String username) {
userName = username;
}
Future initSignalR(BuildContext context) async {
WidgetsFlutterBinding.ensureInitialized();
await NotificationService().init();
connection.on('SignalRUserReceiveMessage', (message) async {
var data = message!.first;
if (data != null) {
UserMessageModel msg = UserMessageModel.fromJson(data);
messages.add(msg);
msg.showNotification();
}
if (onMessagesUpdateCallback != null) {
onMessagesUpdateCallback!(true);
}
});
connection.on('SignalRMonitoringMessage', (message) async {
var data = message!.first;
if (data != null) {
UserMessageModel msg = UserMessageModel.fromJson(data);
messages.add(msg);
msg.showNotification();
}
if (onMessagesUpdateCallback != null) {
onMessagesUpdateCallback!(true);
}
});
connection.on("SignalRReceiveConnectedMessage", (message) async {
await connection.send(methodName: 'SignalRInit', args: [
userName,
appName,
connection.connectionId,
]);
});
connection.on("SignalRReceiveDisconnectedMessage", (message) async {
if (connection.state == HubConnectionState.disconnected) {
connection.start();
}
});
await connection.start();
}
List<UserMessageModel> getMessages() {
return messages;
}
Future deleteMessage(UserMessageModel _msg) async {
if (_msg == null) return;
var response =
await EndPointService().SetupApi("Message", "", []).httpDelete(
HeaderEnum.BasicHeaderEnum,
ResponseEnum.ResponseModelEnum,
jsonEncode(_msg),
);
}
addOrUpdateMessage(UserMessageModel _msg) {
if (_msg == null) return;
if (messages != null) {
var found =
messages.firstWhere((e) => e.user == _msg.user && e.id == _msg.id);
var index =
messages.indexWhere((e) => e.user == _msg.user && e.id == _msg.id);
if (found != null) {
messages[index] = _msg;
} else {
messages.add(_msg);
}
if (onMessagesUpdateCallback != null) {
onMessagesUpdateCallback!(true);
}
}
}
setMessagesUpdateCallback(Function(bool update) func) {
onMessagesUpdateCallback = func;
}
}
SignalR problems
SignalR for Flutter uses web sockets and SSE to receive messages from the SignalR service. If the app was terminated because the user restarted their phone or the OS shut down the app to save battery, these push notifications would not be received by the app.
To overcome this, app developers (and SignalR) have to use FCM on Android, and APNs on iOS (or FCM which will also use APNs on iOS). All other approaches will be more limited because the operating systems do not allow users to keep background processes running the entire time. This was actually allowed years ago, but the operating systems have made these changes to save the user battery - they enforce that all apps go through the same push notification medium - FCM on Android, APNs on iOS.
SignalR for Flutter uses neither FCM nor APNs. At it's current state, SignalR is not well suited for Android or iOS - take a look at the comments with people struggling with similar problems to you on How to use signalr in Android.
Alternative solution
The simplest / easiest way to get started is to use Firebase Cloud Messaging.
On Android, it will be used directly to send messages to devices, and
on iOS, FCM will use APNs to reach devices reliably
Caveat: On Android, there is a more complicated alternative called unifiedpush, but the limitations include showing a notification to the user at all times to handle background notifications.
My analysis: This is all done based on my quick investigation by reading the pubspec.yaml, the GitHub issues on the original repo, the SignalR documentation, and some experience implementing Push Notifications for Flutter.
Disclosure: I just released a push notification library 2 days ago called push which would be well suited to these types of Push Notification packages making the transformation to using FCM on Android and APNs on iOS. However, as an app developer, in most cases, you should use firebase_messaging, not push.
I worked with SignalR but on native Platform(IOS & Android), I made stock app and get realtime price. When app go to background, I will disconnect with SignalR server after 5 second, and when app go to foreground again, I check if app's current state not connect to server SignalR, I'll connect again. I think it not good if your app still connect and receiver data from signalR server in background state.

Flutter get bluetooth name connected device

How to get the name of a connected device in flutter?
I need only the name of the device connected to the smartphone ie: "Toyota car"
P.S. For Andorid if it makes some difference
Could use flutter_blue package
In the example it's shown how to get device name.
Or could simply:
// Start scanning
flutterBlue.startScan(timeout: Duration(seconds: 4));
// Listen to scan results
var subscription = flutterBlue.scanResults.listen((results) {
// do something with scan results
for (ScanResult r in results) {
print('${r.device.name} found! rssi: ${r.rssi}');
}
});
// Stop scanning
flutterBlue.stopScan();

Connectivity plugin "getWifiName()" method to get ssid returns null in flutter for ios 13+

I am trying to get wifi ssid in flutter for ios(13+) with connectivity plugin but result returns null. I have added access wireless information from Xcode but still not working. Can anyone help out please?
Future<void> _updateConnectionStatus(ConnectivityResult result) async{
switch(result){
case ConnectivityResult.wifi:
String wifiName;
try {
if (Platform.isIOS) {
LocationAuthorizationStatus status =
await _connectivity.getLocationServiceAuthorization();
if (status == LocationAuthorizationStatus.notDetermined) {
print('wifiName notDetermined: ');
status = await _connectivity.requestLocationServiceAuthorization();
}
if (status == LocationAuthorizationStatus.authorizedAlways ||
status == LocationAuthorizationStatus.authorizedWhenInUse) {
print('wifiName authorizedWhenInUse: ');
wifiName = await _connectivity.getWifiName();
setState(() {
_ssid = wifiName != null ? wifiName : _ssid;
});
} else {
print('wifiName ,.,.,.,: ');
wifiName = await _connectivity.getWifiName();
}
} else {
LocationAuthorizationStatus status =
await _connectivity.getLocationServiceAuthorization();
// if(status == )
wifiName = await _connectivity.getWifiName();
print('android wifi');
print(wifiName);
}
} on PlatformException catch (e) {
print(e.toString());
wifiName = "Failed to get Wifi Name";
}
setState(() {
_connectionStatus ='result '+ '$result\n'
'Wifi Name: $wifiName\n';
print('_connectionStatus $_connectionStatus');
});
break;
case ConnectivityResult.mobile:
Fluttertoast.showToast(msg: 'Connected to mobile network');
break;
case ConnectivityResult.none:
Fluttertoast.showToast(msg: 'Connected to no network');
setState(() => _connectionStatus = result.toString());
break;
default:
setState(() => _connectionStatus = 'Failed to get connectivity.');
break;
}
}
I have tried with above code from connectivity plugin example. Also there is showing 'As of iOS 13, Apple announced that these APIs will no longer return valid information'. So how to achieve my goal?
In 2020, the flutter team decided to create a new plugin for wifi information, removing these methods from the connectivity plugin.
So check the network_info_plus plugin: the method signatures are just the same.
As on being able to access this on iOS 13+, according to the package's readme:
The CNCopyCurrentNetworkInfo will work for Apps that:
The app uses Core Location, and has the user’s authorization to use
location information.
The app uses the NEHotspotConfiguration API to configure the current
Wi-Fi network.
The app has active VPN configurations installed.
If your app falls into the last two categories, it will work as it is.
If your app doesn't fall into the last two categories, and you still
need to access the wifi information, you should request user's
authorization to use location information.
There is a helper method provided in this plugin to request the
location authorization: requestLocationServiceAuthorization. To
request location authorization, make sure to add the following keys to
your Info.plist file, located in /ios/Runner/Info.plist:
NSLocationAlwaysAndWhenInUseUsageDescription - describe why the app
needs access to the user’s location information all the time
(foreground and background). This is called Privacy - Location Always
and When In Use Usage Description in the visual editor.
NSLocationWhenInUseUsageDescription - describe why the app needs
access to the user’s location information when the app is running in
the foreground. This is called Privacy - Location When In Use Usage
Description in the visual editor.
So basically, from iOS13 on, you have to request the user's permission for location - it actually makes sense.

Detect whether the device is phone, tablet or tv on flutter

I want to know whether the used device is tv or not..I am using flutter_device_type package but it only detects the tablet and consider any other device as phone
You can use the device_info_plus package for detecting Android TV.
The AndroidDeviceInfo class contains a list of all the system's features as documented in the Android PackageManager
For detecting TV, you can use e.g:
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
bool isTV = androidInfo.systemFeatures.contains('android.software.leanback')
By default flutter detects the device based on its minimum size(screens bigger than a minimum width considered as a tablet), something like this code:
String getDeviceType() {
final data = MediaQueryData.fromWindow(WidgetsBinding.instance.window);
return data.size.shortestSide < 600 ? 'phone' :'tablet';
}
In another hand with Java/Kotlin and based on this documentation you can detect if the device is android Tv with this code:
val uiModeManager = getSystemService(UI_MODE_SERVICE) as UiModeManager
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
Log.d(TAG, "Running on a TV Device")
} else {
Log.d(TAG, "Running on a non-TV Device")
}
So the proper option would be using a native code and connecting it to flutter using platform-channels

Flutter IOS beacon not able to scan with Region and UUID settings

I have this problem is that in flutter I notice there is not able to operate or use the traditional bluetooth as there is no any library supporting it. I have tested flutter_blue-master etc. So then I saw that it can behave as beacon. So I have used the codes below. For android I just set
Region(
identifier: 'com.example.myDeviceRegion',)); its able to work. So the same I set in IOS its not able to work? So what is best workaround for blueetooth in flutter? I am using this package flutter_beacon. For the beacon broadcasting I am using this package beacon_broadcast.
initScanBeacon() async {
await flutterBeacon.initializeScanning;
await checkAllRequirements();
if (!authorizationStatusOk ||
!locationServiceEnabled ||
!bluetoothEnabled) {
print('RETURNED, authorizationStatusOk=$authorizationStatusOk, '
'locationServiceEnabled=$locationServiceEnabled, '
'bluetoothEnabled=$bluetoothEnabled');
return;
}
/*final regions = <Region>[
Region(
identifier: 'com.example.myDeviceRegion',
),
];*/
final regions = <Region>[];
regions.add(Region(
identifier: 'com.example.myDeviceRegion',
minor: 100,
major: 1));
if (_streamRanging != null) {
if (_streamRanging.isPaused) {
_streamRanging.resume();
return;
}
}
_streamRanging =
flutterBeacon.monitoring(regions).listen((MonitoringResult result) {
print(result);
if (result != null && mounted) {
print("GOT RESTULT READY");
setState(() {
//_regionBeacons[result.region] = result.region;
_beacons.clear();
print("List value is json"+result.toJson.toString());
_regionBeacons.values.forEach((list) {
print("List value is");
_beacons.addAll(list);
print("after Beacon size now is "+_beacons.length.toString());
});
//_beacons.sort(_compareParameters);
print("Beacon size now is "+_beacons.length.toString());
});
}
});
}
A few things to check:
Make sure your Region definition has a proximityUUID value. I am surprised that it works even on Android without this. On iOS it certainly won't work at all -- iOS requires a beacon proximityUUID be specified up front in order to detect. The value you give for the prximityUUID must exactly match what your beacon is advertising or you won't see it.
Make sure you have gone through all the iOS setup steps here: https://pub.dev/packages/flutter_beacon
Be extra sure that you have granted location permission to your iOS app. You can go to Settings -> Your App Name to check if location permission is granted.
Make sure bluetooth is enabled in settings for the phone
Make sure location is enabled in settings for the phone
https://pub.dev/packages/flutter_beacon
There's an update on GitHub but was yet to push to pub.dev previously.
Do update to 0.5.0.
Always check pub.dev for updates. or github reported issues.