dart - Compruebe si hay una conexión a Internet disponible en la aplicación Flutter
(7)
Tengo una llamada de red para ser ejecutada. Pero antes de hacerlo, necesito verificar si el dispositivo tiene conectividad a Internet.
Esto es lo que he hecho hasta ahora:
var connectivityResult = new Connectivity().checkConnectivity();// User defined class
if (connectivityResult == ConnectivityResult.mobile ||
connectivityResult == ConnectivityResult.wifi) {*/
this.getData();
} else {
neverSatisfied();
}
El método anterior no funciona.
El complemento de connectivity indica en sus documentos que solo proporciona información si hay una conexión de red, pero no si la red está conectada a Internet
Tenga en cuenta que en Android, esto no garantiza la conexión a Internet. Por ejemplo, la aplicación podría tener acceso wifi pero podría ser una VPN o un WiFi del hotel sin acceso.
Puedes usar
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'');
}
He creado un paquete que (creo) trata de manera confiable este problema.
La discusión es muy bienvenida. Puede usar el rastreador de problemas en GitHub.
Ya no creo que esto a continuación sea un método confiable:
Quiero agregar algo a respuesta de : realmente debería agregar una captura más, que capturará todas las demás excepciones (solo para estar seguro), O simplemente elimine el tipo de excepción por completo y use una captura, que se ocupa de todas las excepciones:
Caso 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;
}
o incluso más simple ...
Caso 2:
try {
await Firestore.instance
.runTransaction((Transaction tx) {})
.timeout(Duration(seconds: 5));
hasConnection = true;
} catch (_) {
hasConnection = false;
}
Hice una clase base para el estado del widget
Uso en lugar de
State<LoginPage>
use
BaseState<LoginPage>
luego use la variable booleana isOnline
dependencies:
connectivity: ^0.4.3+2
Primero, agregue el complemento de conectividad:
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 {
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;
}
}
Luego agregue la clase BaseState
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;
}
Para cualquier otra persona que aterrice aquí, me gustaría agregar a la respuesta de Günter Zöchbauer, esta fue mi solución para implementar una utilidad para saber si hay Internet o no, independientemente de cualquier otra cosa.
Renuncia:
Soy nuevo en Dart y Flutter, por lo que este puede no ser el mejor enfoque, pero me encantaría recibir comentarios.
Combinando flutter_connectivity y la prueba de conexión de Günter Zöchbauer
Mis requisitos
No quería tener un montón de código repetido en cualquier lugar donde necesitaba verificar la conexión y quería que actualizara automáticamente los componentes o cualquier otra cosa que se preocupara por la conexión cada vez que había un cambio.
ConnectionStatusSingleton
Primero configuramos un Singleton. Si no está familiarizado con este patrón, hay mucha buena información en línea sobre ellos. Pero lo esencial es que desea crear una sola instancia de una clase durante el ciclo de vida de la aplicación y poder usarla en cualquier lugar.
Este singleton se conecta a
flutter_connectivity
y escucha los cambios de conectividad, luego prueba la conexión de red, luego usa un
StreamController
para actualizar cualquier cosa que le
StreamController
.
Se parece a esto:
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;
}
}
Uso
Inicialización
Primero tenemos que asegurarnos de llamar a la inicialización de nuestro singleton.
Pero solo una vez.
Esto depende de ti, pero lo hice en
main()
mi aplicación:
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();
}
En
Widget
o en otro lugar
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) {
...
}
}
¡Espero que alguien más encuentre esto útil!
Ejemplo de repositorio de github: https://github.com/dennmat/flutter-connectiontest-example
Cambia el modo avión en el emulador para ver el resultado
Siguiendo la
share
@dennmatt, noté que
InternetAddress.lookup
puede devolver resultados exitosos incluso si la conexión a Internet está apagada. Lo probé conectándome desde mi simulador al WiFi de mi casa y luego desconectando el cable de mi enrutador.
Creo que la razón es que el enrutador almacena en caché los resultados de búsqueda de dominio, por lo que no tiene que consultar los servidores DNS en cada solicitud de búsqueda.
De todos modos, si usa Firestore como yo, puede reemplazar el bloque try-SocketException-catch con una transacción vacía y capturar TimeoutExceptions:
Text(isOnline ? ''is Online'' : ''is Offline'')
Además, tenga en cuenta que
previousConnection
se establece antes de la comprobación de intenet asíncrona, por lo que teóricamente si
checkConnection()
se llama varias veces en poco tiempo, podría haber múltiples
hasConnection=true
en una fila o múltiples
hasConnection=false
en una fila.
No estoy seguro de si @dennmatt lo hizo a propósito o no, pero en nuestro caso de uso no hubo efectos secundarios (
setState
solo se llamó dos veces con el mismo valor).
Utilizando
dependencies:
connectivity: ^0.4.2
lo que obtuvimos de los connectivity es
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;
}
El futuro es poco problemático para mí, tenemos que implementarlo cada vez como:
check().then((intenet) {
if (intenet != null && intenet) {
// Internet Present Case
}
// No-Internet Case
});
Entonces, para resolver este problema, he creado una clase que acepta una función con un parámetro boolean isNetworkPresent como este
methodName(bool isNetworkPresent){}
Y la clase de utilidad es
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); } }); } }
Y para utilizar la utilidad de verificación de conectividad
fetchPrefrence(bool isNetworkPresent) {
if(isNetworkPresent){
}else{
}
}
usaré esta sintaxis
NetworkCheck networkCheck = new NetworkCheck();
networkCheck.checkInternet(fetchPrefrence)
Ejemplo completo que demuestra un oyente de la conectividad a internet y su fuente.
Crédito a: connectivity y 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();
}