Controlla se è disponibile una connessione Internet sull'app Flutter


99

Ho una chiamata di rete da eseguire. Ma prima di farlo devo verificare se il dispositivo dispone di connettività Internet.

Questo è quello che ho fatto finora:

  var connectivityResult = new Connectivity().checkConnectivity();// User defined class
    if (connectivityResult == ConnectivityResult.mobile ||
        connectivityResult == ConnectivityResult.wifi) {*/
    this.getData();
    } else {
      neverSatisfied();
    }

Il metodo sopra non funziona.

Risposte:


193

Il plug-in di connettività afferma nei suoi documenti che fornisce informazioni solo se esiste una connessione di rete, ma non se la rete è connessa a Internet

Nota che su Android, questo non garantisce la connessione a Internet. Ad esempio, l'app potrebbe avere accesso Wi-Fi ma potrebbe essere una VPN o un Wi-Fi dell'hotel senza accesso.

Puoi usare

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
Ricevo l'errore "isNotEmpty non è dichiarato all'interno di InternetAddress"
Rissmon Suresh

2
Questo può essere ottenuto in background? Come se avessi una coda di attività in attesa di esecuzione e in attesa di Internet ma l'app è chiusa?
Vidor Vistrom

61
Tieni presente che google.com non è accessibile all'interno in Cina e come tale l'esempio si bloccherà se utilizzato in Cina. Per espandere il tuo pubblico, evita di utilizzare google.com e utilizza invece example.com. risultato finale = attende InternetAddress.lookup ('esempio.com');
otboss

4
Questo non funziona per me if (result.isNotEmpty && result[0].rawAddress.isNotEmpty)restituisce vero quando c'è il wifi ma non la connessione internet.
Denn

5
Oh, sì, me ne ero completamente dimenticato! In realtà penso di poter continuare a usare await, posso solo aggiungere .timeoutdopo lookup().
Michel Feinstein

73

Per chiunque altro arrivi qui, vorrei aggiungere alla risposta di Günter Zöchbauer questa era la mia soluzione per implementare un'utilità per sapere se c'è Internet o meno indipendentemente da qualsiasi altra cosa.

Dichiarazione di non responsabilità:

Sono nuovo sia per Dart che per Flutter, quindi questo potrebbe non essere l'approccio migliore, ma mi piacerebbe ricevere feedback.


Combinazione di flutter_connectivity e test di connessione di Günter Zöchbauer

Le mie esigenze

Non volevo avere un mucchio di codice ripetuto ovunque avevo bisogno di controllare la connessione e volevo che aggiornasse automaticamente i componenti o qualsiasi altra cosa che si preoccupasse della connessione ogni volta che c'era un cambiamento.

ConnectionStatusSingleton

Per prima cosa configuriamo un Singleton. Se non hai familiarità con questo modello, ci sono molte buone informazioni online su di loro. Ma il succo è che vuoi creare una singola istanza di una classe durante il ciclo di vita dell'applicazione ed essere in grado di usarla ovunque.

Questo singleton si aggancia flutter_connectivitye ascolta le modifiche alla connettività, quindi verifica la connessione di rete, quindi utilizza a StreamControllerper aggiornare tutto ciò che interessa.

Assomiglia a questo:

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

Utilizzo

Inizializzazione

Per prima cosa dobbiamo assicurarci di chiamare l'inizializzazione del nostro singleton. Ma solo una volta. Questo dipende da te, ma l'ho fatto nella mia app 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 Widgeto altrove

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) {
        ...
    }
}

Spero che qualcun altro lo trovi utile!


Repo github di esempio: https://github.com/dennmat/flutter-connectiontest-example

Attiva la modalità aereo nell'emulatore per vedere il risultato


2
Ho testato il codice e funziona per me, avrei bisogno di più informazioni per aiutare.
dennmat

3
Ahh, ok lo vedo. Quindi, ancora una volta per riferimento futuro, l'errore che stai postando è solo l'editor che cerca di aprire il file in cui pensa che si sia verificato l'errore. Il vero errore dovrebbe essere disponibile nella console di debug degli editor / pannello di traccia dello stack. Quindi immagino che runApp restituisca che ho pensato che sarebbe stato eseguito per l'intera durata del programma. Visto che questo è nella sostanza, lo smaltimento non è davvero necessario qui, quindi rimuovilo connectionStatus.dispose()supponendo che tu lo stia impostando main()come sopra. Aggiornerà il post e il collegamento all'esempio di GitHub.
dennmat

1
Per rilevare solo se il wifi o il cellulare vengono commutati, è necessaria solo la connettività flutter. Questo wrapper controlla la connessione dopo che si è verificato il passaggio. Ma non avviserà ogni cambiamento di rete. Se stai utilizzando l'emulatore, attivare la modalità aereo è il modo più semplice per perdere la connessione a Internet. Se sei su un dispositivo reale, dovresti assicurarti di non essere ancora connesso a una rete mobile con dati.
dennmat

1
Ci sono alcune opzioni per questo, puoi modificare quanto sopra per usare Timer per testare frequentemente. Oppure prova frequentemente usando l'utilità Timer. Vedi: api.dartlang.org/stable/2.1.0/dart-async/Timer-class.html Un'altra opzione è testare la connessione prima di ogni richiesta inviata. Anche se sembra che tu stia cercando qualcosa come websocket. Comunque buona fortuna
dennmat

3
Non dovremmo annullare l'abbonamento nella funzione dispose () del widget? Vedo che questo è fatto in altri esempi di StreamController come qui: stackoverflow.com/questions/44788256/updating-data-in-flutter
Oren

41

inserisci qui la descrizione dell'immagine

Esempio completo che mostra un ascoltatore della connettività Internet e della sua fonte.

Ringraziamo: connettività e 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();
}

tramite Firebase, SDK è possibile?
LOG_TAG

@LOG_TAG Non è necessario utilizzare Firebase per questo.
CopsOnRoad

1
@CopsOnRoad Grazie mille. mi hai risparmiato tempo.
Nimisha Ranipa

Map _source = {ConnectivityResult.none: false}; Perché hai usato "falso" qui
Faruk AYDIN

@CopsOnRoad Grazie! Ho usato questo metodo, ma questo metodo mi ha dato NoInternetConnection per la prima volta! Perché prima dammi Nessuno? Questa è la mia stampa di debug: connectivityResult.none connectivityResult.wifi connectivityResult.wifi.
Faruk AYDIN

23

Ho scoperto che il solo utilizzo del pacchetto di connettività non era sufficiente per capire se Internet era disponibile o meno. In Android controlla solo se è presente il WIFI o se i dati mobili sono attivati, non controlla l'effettiva connessione Internet. Durante i miei test, anche senza segnale mobile ConnectivityResult.mobile restituirebbe true.

Con IOS i miei test hanno rilevato che il plug-in di connettività rileva correttamente se c'è una connessione Internet quando il telefono non ha segnale, il problema era solo con Android.

La soluzione che ho trovato è stata quella di utilizzare il pacchetto data_connection_checker insieme al pacchetto di connettività. Questo assicura solo che ci sia una connessione Internet effettuando richieste ad alcuni indirizzi affidabili, il timeout predefinito per il controllo è di circa 10 secondi.

La mia funzione isInternet finita sembrava un po 'così:

  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 è la stessa sia per le connessioni mobili che Wi-Fi e probabilmente dovrebbe essere spostata in una funzione separata. Non l'ho fatto qui per renderlo più leggibile.

Questa è la mia prima risposta di Stack Overflow, spero che aiuti qualcuno.


1
Benvenuto in stackoverflow. Mi chiedo solo, qual è il vantaggio rispetto al semplice utilizzo await DataConnectionChecker().hasConnectionin primo luogo?
herbert

2
L'unico motivo è che su IOS il pacchetto di connettività può dire praticamente all'istante che non c'è connessione. Se avessi usato solo il pacchetto data_connection_checker l'app su IOS avrebbe dovuto attendere fino a quando la richiesta http che ha fatto scadere, circa 10 secondi, prima di restituire false. Questo può essere accettabile in alcuni casi però. Il pacchetto di connettività può anche dire se stai usando WIFI o dati mobili che non ho bisogno di sapere qui ma che potrebbe essere utile sapere.
abernee

Funziona perfettamente con poche modifiche alla sintassi nel codice sopra. 1. devi cambiare Future <Bool> in future <bool>), perché i tipi sono minuscoli. 2. Aggiungere il punto e virgola (;) per la quarta ultima istruzione di ritorno.
TDM

Grazie TDM, ho modificato la risposta con le tue modifiche.
abernee

20

Utilizzando

dependencies:
  connectivity: ^0.4.2

quello che abbiamo ottenuto da risorse è

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

Il futuro è un po 'problematico per me, dobbiamo implementarlo ogni volta come:

check().then((intenet) {
      if (intenet != null && intenet) {
        // Internet Present Case
      }
      // No-Internet Case
    });

Quindi per risolvere questo problema ho creato una classe che accetta una funzione con il parametro booleano isNetworkPresent come questo

methodName(bool isNetworkPresent){}

E la classe di utilità è

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

E per utilizzare l'utilità di controllo della connettività

  fetchPrefrence(bool isNetworkPresent) {
    if(isNetworkPresent){

    }else{

    }
  }

userò questa sintassi

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

6

Ho creato un pacchetto che (credo) affronta in modo affidabile questo problema.

Il pacchetto su pub.dev

Il pacchetto su GitHub

La discussione è molto gradita. Puoi utilizzare il tracker dei problemi su GitHub.


Non penso più che questo di seguito sia un metodo affidabile:


Voglio aggiungere qualcosa alla risposta di @ Oren : dovresti davvero aggiungere un'altra cattura, che catturerà tutte le altre eccezioni (solo per sicurezza), OPPURE rimuovi del tutto il tipo di eccezione e usa una cattura, che si occupa di tutte le eccezioni:

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 anche più semplice ...

Caso 2:


try {
  await Firestore.instance
    .runTransaction((Transaction tx) {})
    .timeout(Duration(seconds: 5));
  hasConnection = true;
} catch (_) {
  hasConnection = false;
}

5

Ho creato una classe base per lo stato del widget

Usage invece di State<LoginPage>usare BaseState<LoginPage> quindi usa semplicemente la variabile booleana isOnline

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

Innanzitutto, aggiungi il plug-in di connettività:

dependencies:
  connectivity: ^0.4.3+2

Quindi aggiungi la classe 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;
  }
}

E devi lanciare il widget nel tuo stato in questo modo

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

2
come posso usare questa classe?
DolDurma

@DolDurma Aggiungilo e importalo, quindi invece di State <LoginPage> usa BaseState <LoginPage> quindi usa semplicemente la variabile booleana isOnline
amorenew

con questo codice non posso ottenere valori da widget. per esempio: RegisterBloc get _registerBloc => widget.registerBloc;ottengo questo errore error: The getter 'registerBloc' isn't defined for the class 'StatefulWidget'. (undefined_getter at lib\screens\fragmemt_register\view\register_mobile_number.dart:29)vedere questa class _FragmentRegisterMobileNumberState extends BaseState<FragmentRegisterMobileNumber> with SingleTickerProviderStateMixin { RegisterBloc get _registerBloc => widget.registerBloc;
implementazione

@DolDurma Non sono sicuro di quale sia il problema senza un campione GitHub perché questa informazione non è sufficiente
rinnovo il

1
per favore controlla questo repository e mostrami come posso usarlo is_onlineper accedere alla console github.com/MahdiPishguy/flutter-connectivity-sample
DolDurma

3

Seguendo la risposta di @dennmatt , ho notato che InternetAddress.lookuppotrebbe restituire risultati positivi anche se la connessione Internet è disattivata: l'ho testata collegandomi dal mio simulatore al WiFi di casa e quindi scollegando il cavo del mio router. Penso che il motivo sia che il router memorizza nella cache i risultati della ricerca del dominio in modo da non dover interrogare i server DNS su ogni richiesta di ricerca.

Ad ogni modo, se usi Firestore come me, puoi sostituire il blocco try-SocketException-catch con una transazione vuota e catturare 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;
}

Inoltre, tieni presente che previousConnectionviene impostato prima del controllo intenet asincrono, quindi in teoria se checkConnection()viene chiamato più volte in breve tempo, potrebbero esserci più hasConnection=truedi una riga o più hasConnection=falsedi una riga. Non sono sicuro che @dennmatt l'abbia fatto apposta o meno, ma nel nostro caso d'uso non c'erano effetti collaterali (è setStatestato chiamato solo due volte con lo stesso valore).


3

La connettività: il pacchetto non garantisce l'effettiva connessione internet (potrebbe essere solo una connessione wifi senza accesso ad internet)

Citazione dalla documentazione:

Nota che su Android, questo non garantisce la connessione a Internet. Ad esempio, l'app potrebbe avere accesso Wi-Fi ma potrebbe essere una VPN o un Wi-Fi dell'hotel senza accesso.

Se hai davvero bisogno di controllare la connessione a Internet www, la scelta migliore sarebbe

pacchetto data_connection_checker


1

Ecco la mia soluzione Controlla la connettività Internet e la connessione dati Spero che ti piaccia.

Prima di tutto aggiungi le dipendenze nel tuo pubsec.yaml
dependencies:        
    data_connection_checker:
Ed ecco il main.dart della mia soluzione
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

Ho avuto un problema con le soluzioni proposte, l'utilizzo lookupnon sempre restituisce il valore atteso.

Ciò è dovuto alla memorizzazione nella cache DNS, il valore della chiamata viene memorizzato nella cache e invece di eseguire una chiamata corretta al tentativo successivo restituisce il valore memorizzato nella cache. Ovviamente questo è un problema qui in quanto significa che se si perde la connettività e si chiama lookuppotrebbe comunque restituire il valore memorizzato nella cache come se si avesse Internet e, viceversa, se si ricollega Internet dopo aver lookuprestituito null, restituirà comunque null per la durata del cache, che può durare alcuni minuti, anche se ora hai Internet.

TL; DR: lookuprestituire qualcosa non significa necessariamente che hai Internet e non restituire nulla non significa necessariamente che non hai Internet. Non è affidabile.

Ho implementato la seguente soluzione ispirandomi al data_connection_checkerplugin:

 /// 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 chiamata a _checkInternetAccessrichiede al massimo una durata di timeoutcompletamento (3 secondi qui), e se riusciamo a raggiungere uno qualsiasi dei DNS si completerà non appena verrà raggiunto il primo, senza attendere gli altri (poiché raggiungerne uno è sufficiente per sai di avere internet). Tutte le chiamate a _pingDnsvengono effettuate in parallelo.

Sembra funzionare bene su una rete IPV4 e quando non riesco a testarlo su una rete IPV6 (non ho accesso a una rete) penso che dovrebbe funzionare ancora. Funziona anche su build in modalità di rilascio, ma devo ancora inviare la mia app ad Apple per vedere se trovano problemi con questa soluzione.

Dovrebbe funzionare anche nella maggior parte dei paesi (compresa la Cina), se non funziona in uno puoi aggiungere un DNS all'elenco accessibile dal tuo paese di destinazione.


1

Alla fine ( anche se con riluttanza ) ho deciso la soluzione data da @abernee in una precedente risposta a questa domanda. Cerco sempre di utilizzare il minor numero possibile di pacchetti esterni nei miei progetti, poiché so che i pacchetti esterni sono gli unici [potenziali] punti di errore nel software che creo. Quindi collegare a DUE pacchetti esterni solo per una semplice implementazione come questa non è stato facile per me .

Tuttavia, ho preso il codice di abernee e l'ho modificato per renderlo più snello e sensato. Per sensato intendo che consuma la potenza del pacchetto Connectivity nella sua funzione, ma poi la spreca internamente non restituendo gli output più preziosi da questo pacchetto (cioè l'identificazione di rete). Quindi ecco la versione modificata della soluzione di 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;
  }

}

Quindi useresti questa funzione statica tramite una semplice chiamata da qualsiasi punto del codice come segue:

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

È un peccato che tu debba collegarti a DUE PACCHETTI ESTERNI per ottenere questa funzionalità di base nel tuo progetto Flutter, ma immagino che per ora questo sia il meglio che abbiamo. In realtà preferisco il pacchetto Data Connection Checker rispetto al pacchetto Connectivity , ma (al momento della pubblicazione di questo) il primo mancava di quella caratteristica di identificazione di rete molto importante che richiedo dal pacchetto Connectivity. Questo è il motivo per cui ho scelto questo approccio [temporaneamente].


0

Sto solo cercando di semplificare il codice utilizzando il pacchetto di connettività 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
}

Il problema con questo su Android però è che solo perché sei connesso tramite Wi-Fi o cellulare, non significa che sei connesso a Internet.
Megadec

1
@Megadec purtroppo sì, questo è l'unico problema :(
devDeejay

0

risposta in ritardo, ma usa questo pacchetto per controllare. Nome pacchetto: data_connection_checker

nel tuo file pubspec.yuml:

dependencies:
    data_connection_checker: ^0.3.4

crea un file chiamato connection.dart o qualsiasi nome tu voglia. importa il pacchetto:

import 'package:data_connection_checker/data_connection_checker.dart';

controlla se c'è la connessione internet o meno:

print(await DataConnectionChecker().hasConnection);

0

Ho utilizzato il pacchetto data_connection_checker per verificare l'accesso a internet anche se la connessione disponibile tramite wifi o mobile, funziona bene: ecco il codice per verificare la connessione:

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

testa sul pacchetto se vuoi maggiori informazioni. Pacchetto controllo connessione dati


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.