Comprueba si hay una conexión a Internet disponible en la aplicación Flutter


89

Tengo que ejecutar una llamada de red. Pero antes de hacer eso, 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.

Respuestas:


175

El complemento de conectividad 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 puede tener acceso wifi, pero puede ser una VPN o la wifi de un 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');
}

2
Recibo el error "isNotEmpty no se declara dentro de InternetAddress"
Rissmon Suresh

2
¿Se puede lograr esto en segundo plano? ¿Como si tuviera una cola de tareas pendientes de ejecución y esperando internet pero la aplicación está cerrada?
Vidor Vistrom

54
Tenga en cuenta que no se puede acceder a google.com dentro de China y, por lo tanto, el ejemplo se colgará si se usa en China. Para expandir su audiencia, evite usar google.com y use example.com en su lugar. resultado final = espera a InternetAddress.lookup ('ejemplo.com');
otboss

4
Esto no funciona para mí, if (result.isNotEmpty && result[0].rawAddress.isNotEmpty)devuelve verdadero cuando hay wifi pero no conexión a Internet.
Denn

5
¡Oh, sí, me olvidé por completo de esto! De hecho, creo que puedo seguir usando await, puedo agregar .timeoutdespués lookup().
Michel Feinstein

67

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.

Descargo de responsabilidad:

Soy nuevo en Dart y Flutter, por lo que puede que este no sea 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 hubiera 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 la esencia 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 flutter_connectivityy escucha los cambios de conectividad, luego prueba la conexión de red y luego usa un StreamControllerpara actualizar cualquier cosa que le importe.

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 usted, pero lo hice en mi aplicación 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();   
}

En Widgeto 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

Alternar el modo avión en el emulador para ver el resultado


2
Probé el código y me está funcionando. Necesito más información para ayudar.
dennmat

3
Ahh, está bien, lo veo. Entonces, nuevamente, para su futura referencia, el error que está publicando es solo el editor que intenta abrir el archivo donde cree que ocurrió el error. El error real debería estar disponible en la consola de depuración de sus editores / panel de seguimiento de pila. Así que supongo que runApp devuelve, asumí que se ejecutaría durante toda la vida útil del programa. Dado que esto es en general, el desecho no es realmente necesario aquí, así que simplemente elimínelo connectionStatus.dispose()asumiendo que lo está configurando de la main()manera anterior. Actualizará la publicación y el enlace al ejemplo de github.
dennmat

1
Para detectar simplemente si se está cambiando wifi o celular, solo necesita conectividad flutter. Esta envoltura verifica la conexión después de que ocurre el cambio. Pero no alertará sobre cada cambio de red. Si está utilizando el emulador, alternar el modo avión es la forma más fácil de perder la conexión a Internet. Si está en un dispositivo real, tendrá que asegurarse de no estar todavía conectado a una red móvil con datos.
dennmat

1
Hay algunas opciones para eso, puede modificar lo anterior para usar Timer para probar con frecuencia. O simplemente pruebe con frecuencia utilizando la utilidad Timer. Ver: api.dartlang.org/stable/2.1.0/dart-async/Timer-class.html Otra opción es probar la conexión antes de cada solicitud que envíe. Aunque parece que está buscando algo como websockets. De todos modos buena suerte
dennmat

2
¿No deberíamos cancelar la suscripción en la función dispose () del widget? Veo que esto se hace en otros ejemplos de StreamController como aquí: stackoverflow.com/questions/44788256/updating-data-in-flutter
Oren

36

ingrese la descripción de la imagen aquí

Ejemplo completo que demuestra a un oyente de la conectividad a Internet y su fuente.

Crédito a: conectividad 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();
}

a través de firebase, SDK, ¿es posible?
LOG_TAG

@LOG_TAG No es necesario que use Firebase para esto.
CopsOnRoad

1
@CopsOnRoad Muchas gracias. me salvaste el tiempo.
Nimisha Ranipa

Map _source = {ConnectivityResult.none: false}; Por qué usó "falso" aquí
Faruk AYDIN

@CopsOnRoad ¡Gracias! Usé este método, ¡pero este método me dio la primera vez que NoInternetConnection! ¿Por qué primero darme Ninguno? Esta es mi impresión de depuración: ConnectivityResult.none ConnectivityResult.wifi ConnectivityResult.wifi.
Faruk AYDIN

19

Utilizando

dependencies:
  connectivity: ^0.4.2

lo que obtuvimos de los recursos 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 un 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 el parámetro booleano 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 usar la utilidad de verificación de conectividad

  fetchPrefrence(bool isNetworkPresent) {
    if(isNetworkPresent){

    }else{

    }
  }

usaré esta sintaxis

NetworkCheck networkCheck = new NetworkCheck();
networkCheck.checkInternet(fetchPrefrence)

17

Descubrí que usar el paquete de conectividad no era suficiente para saber si Internet estaba disponible o no. En Android, solo verifica si hay WIFI o si los datos móviles están activados, no verifica una conexión a Internet real. Durante mis pruebas, incluso sin señal móvil, ConnectivityResult.mobile devolvería verdadero.

Con IOS, mis pruebas encontraron que el complemento de conectividad detecta correctamente si hay una conexión a Internet cuando el teléfono no tiene señal, el problema era solo con Android.

La solución que encontré fue usar el data_connection_checker paquete junto con el paquete de conectividad. Esto solo asegura que haya una conexión a Internet al realizar solicitudes a algunas direcciones confiables, el tiempo de espera predeterminado para la verificación es de alrededor de 10 segundos.

Mi función isInternet finalizada se parecía un poco a esto:

  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;
    }
  }

La if (await DataConnectionChecker().hasConnection)parte es la misma para las conexiones móviles y wifi y probablemente debería cambiarse a una función separada. No he hecho eso aquí para dejarlo más legible.

Esta es mi primera respuesta de Stack Overflow, espero que ayude a alguien.


1
Bienvenido a stackoverflow. Me pregunto, ¿cuál es la ventaja sobre el simple uso await DataConnectionChecker().hasConnectionen primer lugar?
herbert

2
La única razón es que en IOS, el paquete de conectividad puede decir casi instantáneamente que no hay conexión. Si acabo de usar el paquete data_connection_checker, la aplicación en IOS tendría que esperar hasta que la solicitud http que realizó se agotara, alrededor de 10 segundos, antes de devolver falso. Sin embargo, esto puede ser aceptable en algunos casos. El paquete de conectividad también puede indicar si está utilizando WIFI o datos móviles que no necesito saber aquí, pero que puede ser útil saber.
Abernee

Esto funciona perfectamente con pocas modificaciones de sintaxis en el código anterior. 1. necesita cambiar Future <Bool> a future <bool>), porque los tipos están en minúsculas. 2. Agregue punto y coma (;) para la cuarta última declaración de retorno.
TDM

Gracias TDM, he editado la respuesta con sus modificaciones.
Abernee

6

He creado un paquete que (creo) trata de manera confiable este problema.

El paquete en pub.dev

El paquete en GitHub

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 @ Oren's respuesta de : realmente debería agregar una captura más, que detectará 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;
}

5

Hice una clase base para el estado del widget

Uso en lugar de State<LoginPage>uso BaseState<LoginPage> , simplemente use la variable booleana isOnline

Text(isOnline ? 'is Online' : 'is Offline')

Primero, agregue el complemento de conectividad:

dependencies:
  connectivity: ^0.4.3+2

Luego agregue la clase BaseState

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;
  }
}

Y necesitas lanzar el widget en tu estado así

@override
  void castStatefulWidget() {
    // ignore: unnecessary_statements
    widget is StudentBoardingPage;
  }

2
¿Cómo puedo usar esta clase?
DolDurma

@DolDurma Simplemente agréguelo e impórtelo, luego en lugar de State <LoginPage> use BaseState <LoginPage> y luego use la variable booleana isOnline
amorenew

con este código no puedo obtener objetos de valor widget. por ejemplo: RegisterBloc get _registerBloc => widget.registerBloc;obtengo este error, error: The getter 'registerBloc' isn't defined for the class 'StatefulWidget'. (undefined_getter at lib\screens\fragmemt_register\view\register_mobile_number.dart:29)vea esta implementación:class _FragmentRegisterMobileNumberState extends BaseState<FragmentRegisterMobileNumber> with SingleTickerProviderStateMixin { RegisterBloc get _registerBloc => widget.registerBloc;
DolDurma

@DolDurma No estoy seguro de cuál es el problema sin una muestra de GitHub porque esta información no es suficiente
amorenew

1
por favor revise este repositorio y muéstreme cómo puedo usar is_onlinepara iniciar sesión en la consola github.com/MahdiPishguy/flutter-connectivity-sample
DolDurma

3

Siguiendo la respuesta de @dennmatt , noté que InternetAddress.lookuppuede arrojar resultados exitosos incluso si la conexión a Internet está apagada; lo probé conectándome desde mi simulador al WiFi de mi hogar y luego desconectando el cable de mi enrutador. Creo que la razón es que el enrutador almacena en caché los resultados de la búsqueda de dominio para no tener 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:

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;
}

Además, tenga en cuenta que previousConnectionse establece antes de la comprobación de intenet asíncrona, por lo que, en teoría, si checkConnection()se llama varias veces en poco tiempo, podría haber múltiples hasConnection=trueen una fila o múltiples hasConnection=falseen 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 ( setStatesolo se llamó dos veces con el mismo valor).


3

La conectividad: el paquete no garantiza la conexión a Internet real (podría ser solo una conexión wifi sin acceso a Internet).

Cita de la documentación:

Tenga en cuenta que en Android, esto no garantiza la conexión a Internet. Por ejemplo, la aplicación puede tener acceso wifi, pero puede ser una VPN o la wifi de un hotel sin acceso.

Si realmente necesita verificar la conexión a Internet www, la mejor opción sería

paquete data_connection_checker


1

Aquí está mi solución. Comprueba la conectividad a Internet y la conexión de datos.

En primer lugar, agregue dependencias en su pubsec.yaml
dependencies:        
    data_connection_checker:
Y aquí está el dart principal de mi solución
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"),
        ),
      ),
    );
  }
}

1

Tuve un problema con las soluciones propuestas, el uso lookupno siempre devuelve el valor esperado.

Esto se debe al almacenamiento en caché de DNS, el valor de la llamada se almacena en caché y, en lugar de realizar una llamada adecuada en el próximo intento, devuelve el valor en caché. Por supuesto, esto es un problema aquí, ya que significa que si pierde la conectividad y lo llama lookup, aún podría devolver el valor en caché como si tuviera Internet y, a la inversa, si vuelve a conectar su Internet después de lookupdevolver un valor nulo, seguirá devolviendo un valor nulo durante la duración del caché, que puede tardar unos minutos, incluso si tiene Internet ahora.

TL; DR: lookupdevolver algo no significa necesariamente que tenga Internet, y no devolver nada no significa necesariamente que no tenga Internet. No es confiable.

Implementé la siguiente solución inspirándome en el data_connection_checkercomplemento:

 /// 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;
  }

La llamada a _checkInternetAccesstarda como máximo una duración de timeouten completarse (3 segundos aquí), y si podemos llegar a alguno de los DNS se completará en cuanto se alcance el primero, sin esperar a los demás (ya que alcanzar uno es suficiente para sabes que tienes internet). Todas las llamadas a _pingDnsse realizan en paralelo.

Parece funcionar bien en una red IPV4, y cuando no puedo probarlo en una red IPV6 (no tengo acceso a una), creo que debería funcionar. También funciona en compilaciones en modo de lanzamiento, pero todavía tengo que enviar mi aplicación a Apple para ver si encuentran algún problema con esta solución.

También debería funcionar en la mayoría de los países (incluida China); si no funciona en uno, puede agregar un DNS a la lista que sea accesible desde su país de destino.


1

Finalmente ( aunque de mala gana ) me decidí por la solución dada por @abernee en una respuesta anterior a esta pregunta. Siempre trato de utilizar la menor cantidad posible de paquetes externos en mis proyectos, ya que sé que los paquetes externos son los únicos puntos [potenciales] de falla en el software que creo. Entonces, vincular a DOS paquetes externos solo para una implementación simple como esta no fue fácil para mí .

Sin embargo, tomé el código de Abernee y lo modifiqué para hacerlo más ágil y sensato. Por sensato me refiero a que consume la potencia del paquete de conectividad en su función, pero luego la desperdicia internamente al no devolver los resultados más valiosos de este paquete (es decir, la identificación de la red). Así que aquí está la versión modificada de la solución de abernee:

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;
  }

}

Luego, usaría esta función estática a través de una simple llamada desde cualquier lugar de su código de la siguiente manera:

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");

Es una lástima que tengas que vincular a DOS PAQUETES EXTERNOS para obtener esta funcionalidad básica en tu proyecto Flutter, pero supongo que por ahora esto es lo mejor que tenemos. De hecho, prefiero el paquete Data Connection Checker sobre el paquete Connectivity , pero (en el momento de publicar esto) al primero le faltaba esa característica muy importante de identificación de red que necesito del paquete Connectivity. Esta es la razón por la que adopté este enfoque [temporalmente].


0

Solo intento simplificar el código usando Connectivity Package en 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
}

Sin embargo, el problema con esto en Android es que solo porque esté conectado a través de wifi o móvil, no significa que esté conectado a Internet.
Megadec

1
@Megadec lamentablemente sí, ese es el único problema :(
devDeejay

0

respuesta tardía, pero use este paquete para verificar. Nombre del paquete: data_connection_checker

en su archivo pubspec.yuml:

dependencies:
    data_connection_checker: ^0.3.4

cree un archivo llamado connection.dart o cualquier nombre que desee. importar el paquete:

import 'package:data_connection_checker/data_connection_checker.dart';

compruebe si hay conexión a internet o no:

print(await DataConnectionChecker().hasConnection);

0

Usé el paquete data_connection_checker para verificar el acceso a Internet, incluso si la conexión está disponible por wifi o móvil, funciona bien: aquí está el código para verificar la conexión:

bool result = await DataConnectionChecker().hasConnection;
if(result == true) {
   print('YAY! Free cute dog pics!');
} else {
   print('No internet :( Reason:');
   print(DataConnectionChecker().lastTryResults);
}

dirígete al paquete si quieres más información. Paquete de comprobación de conexión de datos


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.