问题
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.
回答1:
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('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
print('connected');
}
} on SocketException catch (_) {
print('not connected');
}
回答2:
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
回答3:
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)
回答4:
Full example demonstrating a listener of the internet connectivity and its source.
Credit to : connectivity and Günter Zöchbauer
import 'dart:async';
import 'dart:io';
import 'package:connectivity/connectivity.dart';
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: HomePage()));
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Map _source = {ConnectivityResult.none: false};
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.none:
string = "Offline";
break;
case ConnectivityResult.mobile:
string = "Mobile: Online";
break;
case ConnectivityResult.wifi:
string = "WiFi: Online";
}
return Scaffold(
appBar: AppBar(title: Text("Internet")),
body: Center(child: Text("$string", style: TextStyle(fontSize: 36))),
);
}
@override
void dispose() {
_connectivity.disposeStream();
super.dispose();
}
}
class MyConnectivity {
MyConnectivity._internal();
static final MyConnectivity _instance = MyConnectivity._internal();
static MyConnectivity get instance => _instance;
Connectivity connectivity = Connectivity();
StreamController 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');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
isOnline = true;
} else
isOnline = false;
} on SocketException catch (_) {
isOnline = false;
}
controller.sink.add({result: isOnline});
}
void disposeStream() => controller.close();
}
回答5:
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;
}
回答6:
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).
回答7:
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;
}
回答8:
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
来源:https://stackoverflow.com/questions/49648022/check-whether-there-is-an-internet-connection-available-on-flutter-app