How to disconnect from multiple connected ble devices in Flutter app? - android

I am currently connected to multiple ble devices. Now, I'd like to disconnect from them but I can't.
Here's the code for connecting:
for (String deviceId in deviceIdList) {
try {
await Provider.of<BleDeviceConnector>(context, listen: false)
.connectAndLoad(
deviceId: deviceId, custom: custom, index: index);
} catch (e) {
print(e);
}
}
// In separate file...
class BleDeviceConnector extends ReactiveState<ConnectionStateUpdate> {
BleDeviceConnector({
required FlutterReactiveBle ble,
required Function(String message) logMessage,
}) : _ble = ble,
_logMessage = logMessage;
final FlutterReactiveBle _ble;
final void Function(String message) _logMessage;
#override
Stream<ConnectionStateUpdate> get state => _deviceConnectionController.stream;
final _deviceConnectionController = StreamController<ConnectionStateUpdate>();
late StreamSubscription<ConnectionStateUpdate> _connection;
Future<void> connectAndLoad(
{required String deviceId,
required CustomModel custom,
required int index}) async {
_connection = _ble.connectToDevice(id: deviceId).listen((update) async {
_deviceConnectionController.add(update);
if (update.connectionState == DeviceConnectionState.connected) {
await custom.loadCustomStuff();
}
}, onError: (e) {
print('Connecting to device $deviceId resulted in error $e');
});
}
Future<void> disconnect(String deviceId) async {
try {
print('disconnecting from device: $deviceId');
_logMessage('disconnecting from device: $deviceId');
await _connection.cancel();
} on Exception catch (e, _) {
print("Error disconnecting from a device: $e");
_logMessage("Error disconnecting from a device: $e");
} finally {
// Since [_connection] subscription is terminated, the "disconnected" state cannot be received and propagated
_deviceConnectionController.add(
ConnectionStateUpdate(
deviceId: deviceId,
connectionState: DeviceConnectionState.disconnected,
failure: null,
),
);
print("Disconnected from $deviceId");
}
}
Future<void> dispose() async {
await _deviceConnectionController.close();
}
This results in connecting to all the devices in deviceList, and loading info.
However, when I then try to disconnect from all of connected devices, it only successfully disconnects from the last one that was written to.
Here's what I'm trying:
for (String deviceId in deviceIdList) {
await Provider.of<BleDeviceConnector>(context, listen: false).disconnect(deviceId);
}
When I reached out to the package author (package: flutter_reactive_ble), he said:
"you can create a class that will hold all subscriptions and then you can loop through them and cancel them one by one in order to disconnect"
When I ran this by the discord, the thought was that I should make a list instead of a class (which seemed more along the lines of what I was thinking). Either way, I tried adding the _connection StreamSubscription to a list, but it doesn't even end up getting added to the list (for some reason).
They also mentioned to try and make another _connection variable since that appears to be the only one.
My understanding was that I would get a new instance every time I make a call to **connectAndLoad(...).
Any help would REALLY be appreciated.
Thanks!

Related

firebase notifications stops working after a while (one day or a few days)

I am very frustrated with this problem:(
I am developing an app for android and ios (using capacitor 3) and I am sending notifications to the app via firebase notifications. (capacitor packages: #capacitor-community/fcm and #capacitor/push-notifications).
It works for a while and after one day or a few days that the app is running in background or foreground (and not killed) it stops from working and the app doesn't get notifications(This has happened to me in android device.).
I am sending notifications using topics and i also tried to send the notification through firebase console, but it didn't work.
I am not sure if this means that the registration token has expired because I would think that the capacitor packages are suppose to handle it since they are not talking about this problem.
I did everything from the documentation of capacitor push notifications.
When I watch the logs I can see the next error: Failed to sync topics. Won't retry sync. INVALID_PARAMETERS.
My code in javascript:
import '#capacitor/core';
import { ActionPerformed, PushNotificationSchema, PushNotifications } from '#capacitor/push-notifications'
import { FCM } from '#capacitor-community/fcm';
import { getMessaging, getToken as firebaseGetToken, onMessage, deleteToken, isSupported } from "firebase/messaging";
import { myAxios } from './generic-functions/my-axios';
const platform = window.Capacitor && window.Capacitor.platform;
const topicIos = `${process.env.REACT_APP_TOPIC}_ios`;
const topicAnd = `${process.env.REACT_APP_TOPIC}_and`;
function isCapacitor(): boolean {
//check if we are in a capacitor platform
return window.Capacitor && (window.Capacitor.platform === "android" || window.Capacitor.platform === "ios")
}
export async function InitFCM(destination: string) {
if (!isCapacitor()) {
const isNtfSupported = await isSupported()
if (!isNtfSupported) return
// web notifications
Notification.requestPermission().then(function (permission) {
if (permission === 'granted') {
subscribeTo(destination);
} else {
// Show some error
}
});
const messaging = getMessaging();
onMessage(messaging, (payload) => {
let notification = payload.data;
const notificationOptions: NotificationOptions = {
badge: notification?.largeIco,
body: notification?.body,
icon: notification?.largeIcon
};
const title = notification?.title || "";
// show notification
navigator.serviceWorker
.getRegistrations()
.then((registration) => {
if (notification?.sound) {
const audio = new Audio(`/notifications/${notification?.sound}`)
audio.play()
}
registration[0].showNotification(title, notificationOptions);
});
})
return
}
try {
console.log('Initializing Push Notifications');
// Request permission to use push notifications
// iOS will prompt user and return if they granted permission or not
// Android will just grant without prompting
PushNotifications.requestPermissions().then(result => {
if (result.receive === 'granted') {
// Register with Apple / Google to receive push via APNS/FCM
// PushNotifications.register();
subscribeTo(destination);
} else {
// Show some error
}
});
// Some issue with our setup and push will not work
PushNotifications.addListener('registrationError',
(error: any) => {
console.log('Error on registration: ' + JSON.stringify(error));
}
);
// Show us the notification payload if the app is open on our device
PushNotifications.addListener('pushNotificationReceived',
(notification: PushNotificationSchema) => {
console.log('Push received: ' + JSON.stringify(notification));
}
);
// Method called when tapping on a notification
PushNotifications.addListener('pushNotificationActionPerformed',
(notification: ActionPerformed) => {
console.log('Push action performed: ' + JSON.stringify(notification));
}
);
} catch (e) {
console.log('err in push notifications: ', e);
}
}
async function subscribeTo(destination: string) {
if (!isCapacitor()) {
//subscribe to web topic
const messaging = getMessaging();
firebaseGetToken(messaging, { vapidKey: process.env.REACT_APP_FIREBASE_VAPID_KEY }).then(
async (token) => {
if (token) {
await myAxios.post("/api/notifications/subscribe-to-topic", { token, destination });
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
});
return
}
try {
await PushNotifications.register();
if (platform === "ios") {
//subscribe to ios topic
const resIos = await FCM.subscribeTo({ topic: `${topicIos}_${destination}` });
console.log(`subscribed to ios Topic ${JSON.stringify(resIos)}`);
}
if (platform === "android") {
//subscribe to android topic
const resAnd = await FCM.subscribeTo({ topic: `${topicAnd}_${destination}` });
console.log(`subscribed to android Topic ${JSON.stringify(resAnd)}`);
}
} catch (error) {
console.log(JSON.stringify(error));
}
}
export async function getToken() {
try {
/* const result = */ await FCM.getToken();
// console.log("TOKEN", result.token);
} catch (error) {
console.log(error);
}
}
export async function unsubscribeFrom(destination?: string) {
if (!isCapacitor()) {
const isNtfSupported = await isSupported()
if (!isNtfSupported || !destination) return
const messaging = getMessaging();
//unsubscribe from web topic
firebaseGetToken(messaging, { vapidKey: process.env.REACT_APP_FIREBASE_VAPID_KEY }).then(
async (token) => {
if (token) {
await myAxios.post("/api/notifications/unsubscribe-from-topic", { token, destination });
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
});
return
}
try {
await PushNotifications.removeAllListeners();
if (destination) {
if (platform === "ios") {
//unsubscribe from ios topic
const resIos = await FCM.unsubscribeFrom({ topic: `${topicIos}_${destination}` });
console.log(`unsubscribed from ios topic ${resIos}`);
}
if (platform === "android") {
//unsubscribe from android topic
const resAndroid = await FCM.unsubscribeFrom({ topic: `${topicAnd}_${destination}` });
console.log(`unsubscribed from android topic ${topicAnd}_${destination}: ${resAndroid.message}`);
}
}
} catch (error) {
console.log(error)
}
if (platform === 'android') {
await FCM.deleteInstance();
}
}
Thank you all in advanced!
This is a common issue since Android 7.0. The problem occurs because you make use of data messages. This part of your code onMessage(messaging, (payload) => { tells me that you rely on that. This means that when a message is received, your apps code will handle the delivery even when in the background. It will create a notification to show it on the device and play a sound for example.
Power Management taken too far
Several device manufacturers have improved their power management too far. This results in the following problem: After a few days of inactivity, an app is completely killed by the Android OS. This means that the app is not able to handle incoming messages in the background anymore. Vendors have gone too far. But you can't do anything about that.
What to do?
To solve the problem, you should rely on notification messages. These are messages that are directly delivered to the Android OS, instead of your app. This means that messages do not need background handling of your app. On the server (sending) side it means you have to modify your current message and add notification info to the message that is sent.
The drawback
The drawback of notification messages is that you can't lay your hands on the data that is part of the notification. If you previously filled your app with data from each notification, with notification messages, you get the data only when your app is in the foreground or the notification is clicked. To get all data within your app, you need a server API solution or something else.
To overcome this you can add a NotificationListener to your app. I am not sure how to do this in Capacitor. A native example can be found here: https://github.com/Chagall/notification-listener-service-example. The NotificationListener can listen for notifications delivered to the Android device also in the background. With this solution you can be sure notifications are always delivered and the data is delivered in the background. But maybe, I don't know, this listener is killed too by power management. When you use the NotificationListener, you need a special permission, that must be set via device settings (see the mentioned example).
Conclusion
Change from data messages to notification messages. Provide a different way to get the data of your messages in your app. You can use the NotificationListener but I don't know if that is reliable. The most obvious solution is to introduce a server side API that provides the data to your app. In the new situation the notifications are reliable delivered to the app.

Flutter FCM: Storing Notification Data while the app is in the Background/Closed

I am trying to store data received via FCM inside a class in order to navigate to a specific tab of my app after a user clicks on that notification.
My problem is that, as far as I could find on the web, the MainActivity is stopped when the app is not in the Foreground, and so, when I try and retrieve this data, I am not getting the updated variables. I have checked this using print statements throughout the app.
In order to store the information and use it when the app is brought back up, do I need to create a local database, or is there another way around this problem?
PS: I have a Stream that receives information that the user has clicked on the notification, and it updates the main page, but I cannot retrieve anything else from it, as it itself doesn't receive the json.
Thank you. Also, this was my first question posted here, be gentle if I didn't follow the protocol by the letter.
Sample code below.
Initialization:
FirebaseOption options = FirebaseOptions(
apiKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXx',
appId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
messagingSenderId: 'XXXXXXXXXXXXXX',
projectId: 'XXXXXXXXXXXXx',
);
Future<void> backgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(
options: options,
);
var decoded = await NotificationModelPusherAG.fromJson(message.data);
var encodedMessage = await json.decode(decoded.message);
var decodedMessage = await PusherMessage.fromJson(encodedMessage);
notifications.type = message.data;
FirebaseNotifications.showNotification(decodedMessage.title, decodedMessage.description, message.data);
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final String INSTANCE_ID = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
await PusherBeams.start(INSTANCE_ID);
await Firebase.initializeApp(
options: options,
);
/// Run this funk when a notification is received and app is in BG
FirebaseMessaging.onBackgroundMessage(backgroundHandler);
runApp(MyApp());
}
The class in which the data is stored:
class Notifications {
var tab = 0;
int get returnTab => return tab;
final _notificationUpdateController = BehaviorSubject();
Stream get update => _notificationUpdateController.stream;
shouldUpdate(add) {
_notificationUpdateController.sink.add(add);
}
void set type(messageData) {
if (messageData['type'] == 'xxxxxxx') {
this.tab = 1;
}
}
var notifications = Notifications();
The widget that should update:
StreamBuilder<Object>(
stream: notifications.update,
builder: (context, snapshot) {
if (updateMulti == true) {
print(notifications.returnTab); /// Here it is '0'
return multiScreen;
} else {
return multiScreen;
}
}
);
And the function that updates it:
flutterNotificationPlugin.initialize(
initSettings,
onSelectNotification: onSelectNotification
);
static Future onSelectNotification(String payload) {
print(payload); /// The payload is always null for some reason
print(notifications.returnTab); /// Here it shows '1' as it should
updateMulti = true;
notifications.shouldUpdate(true);
}
I kind of shortened the code a bit, if I missed something important do tell me, and I shall update accordingly.
Thank you again.

How to cache the SSL Handshake and other Network elements in Flutter?

I am developing a flutter application with network activities. To get data, I am connecting to a REST API which is pretty fast. You can see the postman performance of an API call below.
Then, when I execute the same API in flutter, this is what I got.
If you see, the secret is that postman cached the DNS Lookup, TCP Handshake and SSL Handshake. But in flutter the "Connection established" time is high, which I believe it does not do any caching.
Below is my code, connecting to network.
LoadingScreen2Controller
class LoadingScreen2Controller {
Future<void> initProcess(BuildContext context) async {
final FirebaseAuthService firebaseAuthService =
Provider.of<FirebaseAuthService>(context, listen: false);
final RoleService roleService =
Provider.of<RoleService>(context, listen: false);
fireauth.User? user = firebaseAuthService.getUser();
if (user == null) {
Navigator.of(context).pushNamedAndRemoveUntil(
'/login-select', (Route<dynamic> route) => false);
} else {
String authToken = await firebaseAuthService.getUser()!.getIdToken();
await roleService.getAllRoles(authToken); // PAY ATTENTION HERE!
Navigator.of(context)
.pushNamedAndRemoveUntil('/home', (Route<dynamic> route) => false);
}
}
}
RoleService
class RoleService with ChangeNotifier {
NavLinks _navLinks = NavLinks();
late List<Role> _roles;
/// Return roles
List<Role> returnRoles() {
return _roles;
}
/// Get all Roles
Future<void> getAllRoles(String authToken) async {
try {
var data = await http.get(
Uri.parse(_navLinks.getAllRoles()),
headers: {HttpHeaders.authorizationHeader: "Bearer $authToken"},
);
var jsonData =
convert.json.decode(data.body).cast<Map<String, dynamic>>();
_roles = jsonData.map<Role>((json) => new Role.fromJson(json)).toList();
print(_roles);
} catch (error) {
print(error);
throw error;
}
}
}
All of my API calls are from the same URI. This "no caching" behavior is a problem for me because if I make more network calls in a row, it will take more time. For an example, if I make like 5 network calls it will take 7 seconds so on.
How can I cache the DNS Lookup, TCP Handshake and SSL Handshake in flutter?

Ionic BLE ble.write return null on ios

I'm using this plugin inside a ionic + stencil app
https://github.com/don/cordova-plugin-ble-central
I'm send a write command to a characteristic that support WRITE.
If I call ble.write on Android, the callback returns me OK
If I call ble.write on iOS, the callback returns me NULL.
in both cases, I can see on my peripheral device that the command has been sent correctly.
I connect to the device like this:
connectToDevice(device){
var toast = new utils();
let tclass;
let tmessage;
console.log('----connectToDevice----');
BLE.connect(device.id).subscribe(
peripheralData => {
this.device = peripheralData;
let connectBtn = document.querySelector('#connectBtn') as any;
console.log('device connected: ', this.device);
},
error => {
console.log('connect error: ', error);
}
);
}
And then, send the command with this code:
async sendCommandNew(id){
let noteOk;
let noteError;
let data;
data = this.stringToBytes(this.commandToSend);
let timerId = setInterval(() => {
BLE.write(id, "49535343-fe7d-4ae5-8fa9-9fafd205e455", "49535343-1e4d-4bd9-ba61-23c647249616", data).then((res) => {
console.log('res: ', res);
}).catch(() => {
console.log('res no');
}
)
}, 1000);
setTimeout(() => { clearInterval(timerId); console.log('stopped'); }, 5000);
}
This is the characteristic
Inside the plugin issue many people had this problem but no solution provided.

How to check connectivity to a server IP [duplicate]

I have a network call to be executed. But before doing that I need to check whether the device have internet connectivity.
This is what i have done so far:
var connectivityResult = new Connectivity().checkConnectivity();// User defined class
if (connectivityResult == ConnectivityResult.mobile ||
connectivityResult == ConnectivityResult.wifi) {*/
this.getData();
} else {
neverSatisfied();
}
Above method is not working.
The connectivity plugin states in its docs that it only provides information if there is a network connection, but not if the network is connected to the Internet
Note that on Android, this does not guarantee connection to Internet. For instance, the app might have wifi access but it might be a VPN or a hotel WiFi with no access.
You can use
import 'dart:io';
...
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
print('connected');
}
} on SocketException catch (_) {
print('not connected');
}
Update
The connectivity package is deprecated. Use the official Flutter Community connectivity_plus package instead.
For anyone else who lands here I'd like to add on to Günter Zöchbauer's answer this was my solution for implementing a utility to know if there's internet or not regardless of anything else.
Disclaimer:
I'm new to both Dart and Flutter so this may not be the best approach, but would love to get feedback.
Combining flutter_connectivity and Günter Zöchbauer's connection test
My requirements
I didn't want to have a bunch of repeated code anywhere I needed to check the connection and I wanted it to automatically update components or anything else that cared about the connection whenever there was a change.
ConnectionStatusSingleton
First we setup a Singleton. If you're unfamiliar with this pattern there's a lot of good info online about them. But the gist is that you want to make a single instance of a class during the application life cycle and be able to use it anywhere.
This singleton hooks into flutter_connectivity and listens for connectivity changes, then tests the network connection, then uses a StreamController to update anything that cares.
It looks like this:
import 'dart:io'; //InternetAddress utility
import 'dart:async'; //For StreamController/Stream
import 'package:connectivity/connectivity.dart';
class ConnectionStatusSingleton {
//This creates the single instance by calling the `_internal` constructor specified below
static final ConnectionStatusSingleton _singleton = new ConnectionStatusSingleton._internal();
ConnectionStatusSingleton._internal();
//This is what's used to retrieve the instance through the app
static ConnectionStatusSingleton getInstance() => _singleton;
//This tracks the current connection status
bool hasConnection = false;
//This is how we'll allow subscribing to connection changes
StreamController connectionChangeController = new StreamController.broadcast();
//flutter_connectivity
final Connectivity _connectivity = Connectivity();
//Hook into flutter_connectivity's Stream to listen for changes
//And check the connection status out of the gate
void initialize() {
_connectivity.onConnectivityChanged.listen(_connectionChange);
checkConnection();
}
Stream get connectionChange => connectionChangeController.stream;
//A clean up method to close our StreamController
// Because this is meant to exist through the entire application life cycle this isn't
// really an issue
void dispose() {
connectionChangeController.close();
}
//flutter_connectivity's listener
void _connectionChange(ConnectivityResult result) {
checkConnection();
}
//The test to actually see if there is a connection
Future<bool> checkConnection() async {
bool previousConnection = hasConnection;
try {
final result = await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
hasConnection = true;
} else {
hasConnection = false;
}
} on SocketException catch(_) {
hasConnection = false;
}
//The connection status changed send out an update to all listeners
if (previousConnection != hasConnection) {
connectionChangeController.add(hasConnection);
}
return hasConnection;
}
}
Usage
Initialization
First we have to make sure we call the initialize of our singleton. But only once.
This parts up to you but I did it in my app's main():
void main() {
ConnectionStatusSingleton connectionStatus = ConnectionStatusSingleton.getInstance();
connectionStatus.initialize();
runApp(MyApp());
//Call this if initialization is occuring in a scope that will end during app lifecycle
//connectionStatus.dispose();
}
In Widget or elsewhere
import 'dart:async'; //For StreamSubscription
...
class MyWidgetState extends State<MyWidget> {
StreamSubscription _connectionChangeStream;
bool isOffline = false;
#override
initState() {
super.initState();
ConnectionStatusSingleton connectionStatus = ConnectionStatusSingleton.getInstance();
_connectionChangeStream = connectionStatus.connectionChange.listen(connectionChanged);
}
void connectionChanged(dynamic hasConnection) {
setState(() {
isOffline = !hasConnection;
});
}
#override
Widget build(BuildContext ctxt) {
...
}
}
Hope somebody else finds this useful!
Example github repo: https://github.com/dennmat/flutter-connectiontest-example
Toggle airplane mode in the emulator to see the result
Null Safe code:
One time check:
Create this method:
Future<bool> hasNetwork() async {
try {
final result = await InternetAddress.lookup('example.com');
return result.isNotEmpty && result[0].rawAddress.isNotEmpty;
} on SocketException catch (_) {
return false;
}
}
Usage:
bool isOnline = await hasNetwork();
Setting up a listener:
Add the following dependency to your pubspec.yaml file.
connectivity_plus: ^2.0.2
Full code:
void main() => runApp(MaterialApp(home: HomePage()));
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Map _source = {ConnectivityResult.none: false};
final MyConnectivity _connectivity = MyConnectivity.instance;
#override
void initState() {
super.initState();
_connectivity.initialise();
_connectivity.myStream.listen((source) {
setState(() => _source = source);
});
}
#override
Widget build(BuildContext context) {
String string;
switch (_source.keys.toList()[0]) {
case ConnectivityResult.mobile:
string = 'Mobile: Online';
break;
case ConnectivityResult.wifi:
string = 'WiFi: Online';
break;
case ConnectivityResult.none:
default:
string = 'Offline';
}
return Scaffold(
body: Center(child: Text(string)),
);
}
#override
void dispose() {
_connectivity.disposeStream();
super.dispose();
}
}
class MyConnectivity {
MyConnectivity._();
static final _instance = MyConnectivity._();
static MyConnectivity get instance => _instance;
final _connectivity = Connectivity();
final _controller = StreamController.broadcast();
Stream get myStream => _controller.stream;
void initialise() async {
ConnectivityResult result = await _connectivity.checkConnectivity();
_checkStatus(result);
_connectivity.onConnectivityChanged.listen((result) {
_checkStatus(result);
});
}
void _checkStatus(ConnectivityResult result) async {
bool isOnline = false;
try {
final result = await InternetAddress.lookup('example.com');
isOnline = result.isNotEmpty && result[0].rawAddress.isNotEmpty;
} on SocketException catch (_) {
isOnline = false;
}
_controller.sink.add({result: isOnline});
}
void disposeStream() => _controller.close();
}
Screenshot:
Credit to : connectivity_plus and Günter Zöchbauer
I found that just using the connectivity package was not enough to tell if the internet was available or not. In Android it only checks if there is WIFI or if mobile data is turned on, it does not check for an actual internet connection . During my testing, even with no mobile signal ConnectivityResult.mobile would return true.
With IOS my testing found that the connectivity plugin does correctly detect if there is an internet connection when the phone has no signal, the issue was only with Android.
The solution I found was to use the data_connection_checker package along with the connectivity package. This just makes sure there is an internet connection by making requests to a few reliable addresses, the default timeout for the check is around 10 seconds.
My finished isInternet function looked a bit like this:
Future<bool> isInternet() async {
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {
// I am connected to a mobile network, make sure there is actually a net connection.
if (await DataConnectionChecker().hasConnection) {
// Mobile data detected & internet connection confirmed.
return true;
} else {
// Mobile data detected but no internet connection found.
return false;
}
} else if (connectivityResult == ConnectivityResult.wifi) {
// I am connected to a WIFI network, make sure there is actually a net connection.
if (await DataConnectionChecker().hasConnection) {
// Wifi detected & internet connection confirmed.
return true;
} else {
// Wifi detected but no internet connection found.
return false;
}
} else {
// Neither mobile data or WIFI detected, not internet connection found.
return false;
}
}
The if (await DataConnectionChecker().hasConnection) part is the same for both mobile and wifi connections and should probably be moved to a separate function. I've not done that here to leave it more readable.
Using
dependencies:
connectivity: ^0.4.2
what we got from resouces is
import 'package:connectivity/connectivity.dart';
Future<bool> check() async {
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {
return true;
} else if (connectivityResult == ConnectivityResult.wifi) {
return true;
}
return false;
}
Future is little problematic for me, we have to implement it every single time like :
check().then((intenet) {
if (intenet != null && intenet) {
// Internet Present Case
}
// No-Internet Case
});
So to solve this problem i have created a class Which accept a function with boolean isNetworkPresent parameter like this
methodName(bool isNetworkPresent){}
And the Utility Class is
import 'package:connectivity/connectivity.dart';
class NetworkCheck {
Future<bool> check() async {
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {
return true;
} else if (connectivityResult == ConnectivityResult.wifi) {
return true;
}
return false;
}
dynamic checkInternet(Function func) {
check().then((intenet) {
if (intenet != null && intenet) {
func(true);
}
else{
func(false);
}
});
}
}
And to use connectivity-check utilty
fetchPrefrence(bool isNetworkPresent) {
if(isNetworkPresent){
}else{
}
}
i will use this syntax
NetworkCheck networkCheck = new NetworkCheck();
networkCheck.checkInternet(fetchPrefrence)
I've created a package that (I think) deals reliably with this issue.
The package on pub.dev
The package on GitHub
Discussion is very welcome. You can use the issues tracker on GitHub.
I no longer think this below is a reliable method:
Wanna add something to #Oren's answer: you should really add one more catch, which will catch all other exceptions (just to be safe), OR just remove the exception type altogether and use a catch, that deals with all of the exceptions:
Case 1:
try {
await Firestore.instance
.runTransaction((Transaction tx) {})
.timeout(Duration(seconds: 5));
hasConnection = true;
} on PlatformException catch(_) { // May be thrown on Airplane mode
hasConnection = false;
} on TimeoutException catch(_) {
hasConnection = false;
} catch (_) {
hasConnection = false;
}
or even simpler...
Case 2:
try {
await Firestore.instance
.runTransaction((Transaction tx) {})
.timeout(Duration(seconds: 5));
hasConnection = true;
} catch (_) {
hasConnection = false;
}
I made a base class for widget state
Usage instead of State<LoginPage> use BaseState<LoginPage>
then just use the boolean variable isOnline
Text(isOnline ? 'is Online' : 'is Offline')
First, add connectivity plugin:
dependencies:
connectivity: ^0.4.3+2
Then add the BaseState class
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:connectivity/connectivity.dart';
import 'package:flutter/widgets.dart';
/// a base class for any statful widget for checking internet connectivity
abstract class BaseState<T extends StatefulWidget> extends State {
void castStatefulWidget();
final Connectivity _connectivity = Connectivity();
StreamSubscription<ConnectivityResult> _connectivitySubscription;
/// the internet connectivity status
bool isOnline = true;
/// initialize connectivity checking
/// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initConnectivity() async {
// Platform messages may fail, so we use a try/catch PlatformException.
try {
await _connectivity.checkConnectivity();
} on PlatformException catch (e) {
print(e.toString());
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) {
return;
}
await _updateConnectionStatus().then((bool isConnected) => setState(() {
isOnline = isConnected;
}));
}
#override
void initState() {
super.initState();
initConnectivity();
_connectivitySubscription = Connectivity()
.onConnectivityChanged
.listen((ConnectivityResult result) async {
await _updateConnectionStatus().then((bool isConnected) => setState(() {
isOnline = isConnected;
}));
});
}
#override
void dispose() {
_connectivitySubscription.cancel();
super.dispose();
}
Future<bool> _updateConnectionStatus() async {
bool isConnected;
try {
final List<InternetAddress> result =
await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
isConnected = true;
}
} on SocketException catch (_) {
isConnected = false;
return false;
}
return isConnected;
}
}
And you need to cast the widget in your state like this
#override
void castStatefulWidget() {
// ignore: unnecessary_statements
widget is StudentBoardingPage;
}
Well I read almost all post and #dennmat post is most usedful to me. though it did't work for me and it is outdated too. I have update with flutter updated connectivity package(i.e connectivity_plus) and data_connection_checker(to check whether there is actual internet connection for mobile and wifi).
After this post you will be able to listen for internet connection continuosly.
1. Add dependencies
a) connectivity_plus: ^1.0.6
b) data_connection_checker: ^0.3.4
2. Custom class that handle all the connection.
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:data_connection_checker/data_connection_checker.dart';
class ConnectionUtil {
//This creates the single instance by calling the `_internal` constructor specified below
static final ConnectionUtil _singleton = new ConnectionUtil._internal();
ConnectionUtil._internal();
//This is what's used to retrieve the instance through the app
static ConnectionUtil getInstance() => _singleton;
//This tracks the current connection status
bool hasConnection = false;
//This is how we'll allow subscribing to connection changes
StreamController connectionChangeController = StreamController();
//flutter_connectivity
final Connectivity _connectivity = Connectivity();
void initialize() {
_connectivity.onConnectivityChanged.listen(_connectionChange);
}
//flutter_connectivity's listener
void _connectionChange(ConnectivityResult result) {
hasInternetInternetConnection();
}
Stream get connectionChange => connectionChangeController.stream;
Future<bool> hasInternetInternetConnection() async {
bool previousConnection = hasConnection;
var connectivityResult = await (Connectivity().checkConnectivity());
//Check if device is just connect with mobile network or wifi
if (connectivityResult == ConnectivityResult.mobile ||
connectivityResult == ConnectivityResult.wifi) {
//Check there is actual internet connection with a mobile network or wifi
if (await DataConnectionChecker().hasConnection) {
// Network data detected & internet connection confirmed.
hasConnection = true;
} else {
// Network data detected but no internet connection found.
hasConnection = false;
}
}
// device has no mobile network and wifi connection at all
else {
hasConnection = false;
}
// The connection status changed send out an update to all listeners
if (previousConnection != hasConnection) {
connectionChangeController.add(hasConnection);
}
return hasConnection;
}
}
Check for connection in anywhere and listen for change.
#override
initState() {
print('called');
//Create instance
ConnectionUtil connectionStatus = ConnectionUtil.getInstance();
//Initialize
connectionStatus.initialize();
//Listen for connection change
_connectionChangeStream = connectionStatus.connectionChange.listen((event) {
print(event);
});
super.initState();
}
Now check the log while toggle flight mode. you should get log with true and false value.
Note: this will not work in flutter web, if you wish to make it work than use dio or http plugin instead of data_connection_checker.
Example Project can be found here. Thanks
I had an issue with the proposed solutions, using lookup does not always return the expected value.
This is due to DNS caching, the value of the call is cached and intead of doing a proper call on the next try it gives back the cached value. Of course this is an issue here as it means if you lose connectivity and call lookup it could still return the cached value as if you had internet, and conversely, if you reconnect your internet after lookup returned null it will still return null for the duration of the cache, which can be a few minutes, even if you do have internet now.
TL;DR: lookup returning something does not necessarily mean you have internet, and it not returning anything does not necessarily mean you don't have internet. It is not reliable.
I implemented the following solution by taking inspiration from the data_connection_checker plugin:
/// If any of the pings returns true then you have internet (for sure). If none do, you probably don't.
Future<bool> _checkInternetAccess() {
/// We use a mix of IPV4 and IPV6 here in case some networks only accept one of the types.
/// Only tested with an IPV4 only network so far (I don't have access to an IPV6 network).
final List<InternetAddress> dnss = [
InternetAddress('8.8.8.8', type: InternetAddressType.IPv4), // Google
InternetAddress('2001:4860:4860::8888', type: InternetAddressType.IPv6), // Google
InternetAddress('1.1.1.1', type: InternetAddressType.IPv4), // CloudFlare
InternetAddress('2606:4700:4700::1111', type: InternetAddressType.IPv6), // CloudFlare
InternetAddress('208.67.222.222', type: InternetAddressType.IPv4), // OpenDNS
InternetAddress('2620:0:ccc::2', type: InternetAddressType.IPv6), // OpenDNS
InternetAddress('180.76.76.76', type: InternetAddressType.IPv4), // Baidu
InternetAddress('2400:da00::6666', type: InternetAddressType.IPv6), // Baidu
];
final Completer<bool> completer = Completer<bool>();
int callsReturned = 0;
void onCallReturned(bool isAlive) {
if (completer.isCompleted) return;
if (isAlive) {
completer.complete(true);
} else {
callsReturned++;
if (callsReturned >= dnss.length) {
completer.complete(false);
}
}
}
dnss.forEach((dns) => _pingDns(dns).then(onCallReturned));
return completer.future;
}
Future<bool> _pingDns(InternetAddress dnsAddress) async {
const int dnsPort = 53;
const Duration timeout = Duration(seconds: 3);
Socket socket;
try {
socket = await Socket.connect(dnsAddress, dnsPort, timeout: timeout);
socket?.destroy();
return true;
} on SocketException {
socket?.destroy();
}
return false;
}
The call to _checkInternetAccess takes at most a duration of timeout to complete (3 seconds here), and if we can reach any of the DNS it will complete as soon as the first one is reached, without waiting for the others (as reaching one is enough to know you have internet). All the calls to _pingDns are done in parallel.
It seems to work well on an IPV4 network, and when I can't test it on an IPV6 network (I don't have access to one) I think it should still work. It also works on release mode builds, but I yet have to submit my app to Apple to see if they find any issue with this solution.
It should also work in most countries (including China), if it does not work in one you can add a DNS to the list that is accessible from your target country.
Following #dennmatt 's answer, I noticed that InternetAddress.lookup may return successful results even if the internet connection is off - I tested it by connecting from my simulator to my home WiFi, and then disconnecting my router's cable. I think the reason is that the router caches the domain-lookup results so it does not have to query the DNS servers on each lookup request.
Anyways, if you use Firestore like me, you can replace the try-SocketException-catch block with an empty transaction and catch TimeoutExceptions:
try {
await Firestore.instance.runTransaction((Transaction tx) {}).timeout(Duration(seconds: 5));
hasConnection = true;
} on PlatformException catch(_) { // May be thrown on Airplane mode
hasConnection = false;
} on TimeoutException catch(_) {
hasConnection = false;
}
Also, please notice that previousConnection is set before the async intenet-check, so theoretically if checkConnection() is called multiple times in a short time, there could be multiple hasConnection=true in a row or multiple hasConnection=false in a row.
I'm not sure if #dennmatt did it on purpose or not, but in our use-case there were no side effects (setState was only called twice with the same value).
The connectivity: package does not guarantee the actual internet connection
(could be just wifi connection without internet access).
Quote from the documentation:
Note that on Android, this does not guarantee connection to Internet. For instance, the app might have wifi access but it might be a VPN or a hotel WiFi with no access.
If you really need to check the connection to the www Internet the better choice would be
data_connection_checker package
use connectivity_widget: ^0.1.7
add dependencies:
dependencies:
connectivity_widget: ^0.1.7
add code:
ConnectivityWidget(
builder: (context, isOnline) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"${isOnline ? 'Connected' : 'Offline'}",
style: TextStyle(
fontSize: 30,
color: isOnline ? Colors.green : Colors.red),
),
],
),
),
)
OUTPUT:
Here Is My Solution It Checks Internet Connectivity as well as Data Connection I hope You Like it.
First of all add dependencies in your pubsec.yaml
dependencies:
data_connection_checker:
And Here Is The main.dart Of My Solution
import 'dart:async';
import 'package:data_connection_checker/data_connection_checker.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Data Connection Checker",
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
StreamSubscription<DataConnectionStatus> listener;
var Internetstatus = "Unknown";
#override
void initState() {
// TODO: implement initState
super.initState();
// _updateConnectionStatus();
CheckInternet();
}
#override
void dispose() {
// TODO: implement dispose
listener.cancel();
super.dispose();
}
CheckInternet() async {
// Simple check to see if we have internet
print("The statement 'this machine is connected to the Internet' is: ");
print(await DataConnectionChecker().hasConnection);
// returns a bool
// We can also get an enum instead of a bool
print("Current status: ${await DataConnectionChecker().connectionStatus}");
// prints either DataConnectionStatus.connected
// or DataConnectionStatus.disconnected
// This returns the last results from the last call
// to either hasConnection or connectionStatus
print("Last results: ${DataConnectionChecker().lastTryResults}");
// actively listen for status updates
listener = DataConnectionChecker().onStatusChange.listen((status) {
switch (status) {
case DataConnectionStatus.connected:
Internetstatus="Connectd TO THe Internet";
print('Data connection is available.');
setState(() {
});
break;
case DataConnectionStatus.disconnected:
Internetstatus="No Data Connection";
print('You are disconnected from the internet.');
setState(() {
});
break;
}
});
// close listener after 30 seconds, so the program doesn't run forever
// await Future.delayed(Duration(seconds: 30));
// await listener.cancel();
return await await DataConnectionChecker().connectionStatus;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Data Connection Checker"),
),
body: Container(
child: Center(
child: Text("$Internetstatus"),
),
),
);
}
}
I ultimately (though reluctantly) settled on the solution given by #abernee in a previous answer to this question. I always try and use as few external packages in my projects as possible - as I know external packages are the only [ potential ] points of failure in the software I create. So to link to TWO external packages just for a simple implementation like this was not easy for me.
Nevertheless, I took abernee's code and modified it to make it leaner and more sensible. By sensible I mean he consumes the power of the Connectivity package in his function but then wastes it internally by not returning the most valuable outputs from this package ( i.e. the network identification ). So here is the modified version of abernee's solution:
import 'package:connectivity/connectivity.dart';
import 'package:data_connection_checker/data_connection_checker.dart';
// 'McGyver' - the ultimate cool guy (the best helper class any app can ask for).
class McGyver {
static Future<Map<String, dynamic>> checkInternetAccess() async {
//* ////////////////////////////////////////////////////////////////////////////////////////// *//
//* INFO: ONLY TWO return TYPES for Map 'dynamic' value => <bool> and <ConnectivityResult> *//
//* ////////////////////////////////////////////////////////////////////////////////////////// *//
Map<String, dynamic> mapCon;
final String isConn = 'isConnected', netType = 'networkType';
ConnectivityResult conRes = await (Connectivity().checkConnectivity());
switch (conRes) {
case ConnectivityResult.wifi: //* WiFi Network: true !!
if (await DataConnectionChecker().hasConnection) { //* Internet Access: true !!
mapCon = Map.unmodifiable({isConn: true, netType: ConnectivityResult.wifi});
} else {
mapCon = Map.unmodifiable({isConn: false, netType: ConnectivityResult.wifi});
}
break;
case ConnectivityResult.mobile: //* Mobile Network: true !!
if (await DataConnectionChecker().hasConnection) { //* Internet Access: true !!
mapCon = Map.unmodifiable({isConn: true, netType: ConnectivityResult.mobile});
} else {
mapCon = Map.unmodifiable({isConn: false, netType: ConnectivityResult.mobile});
}
break;
case ConnectivityResult.none: //* No Network: true !!
mapCon = Map.unmodifiable({isConn: false, netType: ConnectivityResult.none});
break;
}
return mapCon;
}
}
Then you'd use this static function via a simple call from anywhere in your code as follows:
bool isConn; ConnectivityResult netType;
McGyver.checkInternetAccess().then(
(mapCIA) { //* 'mapCIA' == amalgamation for 'map' from 'CheckInternetAccess' function result.
debugPrint("'mapCIA' Keys: ${mapCIA.keys}");
isConn = mapCIA['isConnected'];
netType = mapCIA['networkType'];
}
);
debugPrint("Internet Access: $isConn | Network Type: $netType");
It's a pity that you have to link to TWO EXTERNAL PACKAGES to get this very basic functionality in your Flutter project - but I guess for now this is the best we have. I actually prefer the Data Connection Checker package over the Connectivity package - but (at the time of posting this) the former was missing that very important network identification feature that I require from the Connectivity package. This is the reason I defaulted onto this approach [ temporarily ].
I have written a package to check the active internet connection and display a widget accordingly.
flutter_no_internet_widget
Example:
InternetWidget(
online: Text('Online'),
offline: Text('Offline),
);
As per the network state, the appropriate widgets will be displayed. If you have an active internet connection an online widget will be displayed.
All the heavyweight is done by the package and all you have to do is provide online and offline widgets. Optionally you can provide a loading widget and lookup URL.
Discussion, PRs or suggestions are welcome.
late answer, but use this package to to check.
Package Name: data_connection_checker
in you pubspec.yuml file:
dependencies:
data_connection_checker: ^0.3.4
create a file called connection.dart or any name you want.
import the package:
import 'package:data_connection_checker/data_connection_checker.dart';
check if there is internet connection or not:
print(await DataConnectionChecker().hasConnection);
I used the data_connection_checker package to check the internet access even if the connection available by wifi or mobile, it works well:
here is the code to check the connection:
bool result = await DataConnectionChecker().hasConnection;
if(result == true) {
print('YAY! Free cute dog pics!');
} else {
print('No internet :( Reason:');
print(DataConnectionChecker().lastTryResults);
}
head over the package if you want more information.
Data Connection Checker Package
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:app_settings/app_settings.dart';
import 'package:connectivity/connectivity.dart';
class InternetConnect extends StatefulWidget {
#override
InternetConnectState createState() => InternetConnectState();
}
class InternetConnectState extends State<InternetConnect> {
ConnectivityResult previous;
bool dialogshown = false;
StreamSubscription connectivitySubscription;
Future<bool> checkinternet() async {
try {
final result = await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
return Future.value(true);
}
} on SocketException catch (_) {
return Future.value(false);
}
}
void checkInternetConnect(BuildContext context) {
connectivitySubscription = Connectivity()
.onConnectivityChanged
.listen((ConnectivityResult connresult) {
if (connresult == ConnectivityResult.none) {
dialogshown = true;
showDialog(
context: context, barrierDismissible: false, child: alertDialog());
} else if (previous == ConnectivityResult.none) {
checkinternet().then((result) {
if (result == true) {
if (dialogshown == true) {
dialogshown = false;
Navigator.pop(context);
}
}
});
}
previous = connresult;
});
}
AlertDialog alertDialog() {
return AlertDialog(
title: Text('ERROR'),
content: Text("No Internet Detected."),
actions: <Widget>[
FlatButton(
// method to exit application programitacally
onPressed: () {
AppSettings.openWIFISettings();
},
child: Text("Settings"),
),
],
);
}
#override
Widget build(BuildContext context) {
return Container();
}
}
and you can use this method in init of any class
#override
void initState() {
// TODO: implement initState
InternetConnectState().checkInternetConnect(context);
super.initState();
}
Based on this answer https://stackoverflow.com/a/68436867/10761151
If you used dart null safety you will get an error,
so you can update the dependencies data_connection_checker: ^0.3.4 to internet_connection_checker: ^0.0.1+2
and you can use this code
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:internet_connection_checker/internet_connection_checker.dart';
class ConnectionUtil {
static final ConnectionUtil _singleton = new ConnectionUtil._internal();
ConnectionUtil._internal();
static ConnectionUtil getInstance() => _singleton;
bool hasConnection = false;
StreamController connectionChangeController = StreamController();
final Connectivity _connectivity = Connectivity();
void initialize() {
_connectivity.onConnectivityChanged.listen(_connectionChange);
}
void _connectionChange(ConnectivityResult result) {
_hasInternetInternetConnection();
}
Stream get connectionChange => connectionChangeController.stream;
Future<bool> _hasInternetInternetConnection() async {
bool previousConnection = hasConnection;
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile || connectivityResult == ConnectivityResult.wifi) {
// this is the different
if (await InternetConnectionChecker().hasConnection) {
hasConnection = true;
} else {
hasConnection = false;
}
} else {
hasConnection = false;
}
if (previousConnection != hasConnection) {
connectionChangeController.add(hasConnection);
}
return hasConnection;
}
}
and on the stateful widget you can implement this code
bool hasInterNetConnection = false;
#override
initState() {
ConnectionUtil connectionStatus = ConnectionUtil.getInstance();
connectionStatus.initialize();
connectionStatus.connectionChange.listen(connectionChanged);
super.initState();
}
void connectionChanged(dynamic hasConnection) {
setState(() {
hasInterNetConnection = hasConnection;
});
}
To check whether you have internet access or not, even if you are connected to wifi, use the below function.
Future<bool> checkInternetStatus() async {
try {
final url = Uri.https('google.com');
var response = await http.get(url);
if (response.statusCode == 200) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
}
also add http: ^0.13.5 dependency
then import 'package:http/http.dart' as http;
Just trying to simplify the code using Connectivity Package in Flutter.
import 'package:connectivity/connectivity.dart';
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {
// I am connected to a mobile network.
} else if (connectivityResult == ConnectivityResult.wifi) {
// I am connected to a wifi network.
} else {
// I am not connected to the internet
}
I having some problem with the accepted answer, but it seems it solve answer for others. I would like a solution that can get a response from the url it uses, so I thought http would be great for that functionality, and for that I found this answer really helpful. How do I check Internet Connectivity using HTTP requests(Flutter/Dart)?
For me I just create a single data in Firebase and use future builder to await for the data. Here, like this, you can check if the connection is too slow so the data will be loading:
FutureBuilder(
future: _getImage(context),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Press button to start.');
case ConnectionState.active:
case ConnectionState.waiting:
return Container(
height:
MediaQuery.of(context).size.height / 1.25,
width:
MediaQuery.of(context).size.width / 1.25,
child: Loading());
case ConnectionState.done:
if (snapshot.hasData) {
return snapshot.data;
} else {
return FlatButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ProfilePage()));
},
child: Icon(
Icons.add_a_photo,
size: 50,
),
);
}
// You can reach your snapshot.data['url'] in here
}
return null;
},
),
You can make use of this package
https://pub.dev/packages/flutter_network_connectivity
Underhood it makes use of NetworkCapabilities on Android and NetworkMonitor on iOS and listens to connectivity changes and pings to check internet availability, you can also configure to lookup internet availability in a periodic interval.
Add to your pubspec.yaml
flutter_network_connectivity: ^0.0.6
Create an Object
FlutterNetworkConnectivity flutterNetworkConnectivity =
FlutterNetworkConnectivity(
isContinousLookUp: true, // optional, false if you cont want continous lookup
lookUpDuration: const Duration(seconds: 5), // optional, to override default lookup duration
lookUpUrl: 'example.com', // optional, to override default lookup url
);
and you can use its methods to check for network connectivity continuously or a call to check the current status
_flutterNetworkConnectivity.getInternetAvailabilityStream().listen((isInternetAvailable) {
// do something
});
and register listener
await _flutterNetworkConnectivity.registerAvailabilityListener();
to check status on call
bool _isNetworkConnectedOnCall = await _flutterNetworkConnectivity.isInternetConnectionAvailable();
final ConnectivityResult result =
await Connectivity().checkConnectivity();
if (result == ConnectivityResult.wifi) {
print('Connected to a Wi-Fi network');
} else if (result == ConnectivityResult.mobile) {
print('Connected to a mobile network');
} else {
print('Not connected to any network');
}
Use the observe_internet_connectivity package.
Available Features
Check to know if a device has internet connection
final hasInternet = await InternetConnectivity().hasInternetConnection;
if (hasInternet) {
//You are connected to the internet
} else {
//"No internet connection
}
Listen to internet connection changes via stream
final subscription =
InternetConnectivity().observeInternetConnection.listen((bool hasInternetAccess) {
if(!hasInternetAccess){
showToast('No Internet Connection');
}
});
await Future.delayed(const Duration(seconds: 10 ));
subscription.cancel();
Use InternetConnectivityListener to listen to internet connectivity changes inside a flutter widget
return InternetConnectivityListener(
connectivityListener: (BuildContext context, bool hasInternetAccess) {
if (hasInternetAccess) {
context.showBanner('You are back Online!', color: Colors.green);
} else {
context.showBanner('No internet connection', color: Colors.red);
}
},
child: Scaffold(
body: Container(),
),
);
Use InternetConnectivityBuilder to build internet connection aware widgets
return InternetConnectivityBuilder(
connectivityBuilder: (BuildContext context, bool hasInternetAccess, Widget? child) {
if(hasInternetAccess) {
return OnlineWidget();
} else {
return OfflineWidget();
}
},
child: ChildWidget(),
);
The connectivity plugin states in its docs that it only provides information if there is a network connection, but not if the network is connected to the Internet. Use the below code and don't forget to use ".timeout()" because you can stuck forever using "await".
import 'dart:io';
Future<bool> isConnected() async {
try {
List<InternetAddress> result = await InternetAddress.lookup('example.com')
.timeout(Duration(seconds: 5));
//
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
return true;
}
//
else {
return false;
}
} on SocketException catch (_) {
return false;
}
}

Categories

Resources