Sprawdź, czy w aplikacji Flutter jest dostępne połączenie internetowe


89

Mam do wykonania połączenie sieciowe. Ale zanim to zrobię, muszę sprawdzić, czy urządzenie ma połączenie z Internetem.

Oto, co zrobiłem do tej pory:

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

Powyższa metoda nie działa.

Odpowiedzi:


174

Łączność wtyczki stany w jego docs, że stanowi jedynie informację, czy istnieje połączenie sieciowe, ale nie wtedy, gdy sieć jest podłączona do Internetu

Pamiętaj, że w systemie Android nie gwarantuje to połączenia z Internetem. Na przykład aplikacja może mieć dostęp do Wi-Fi, ale może to być VPN lub hotelowe Wi-Fi bez dostępu.

Możesz użyć

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
Otrzymuję błąd „isNotEmpty nie jest zadeklarowana w
adresie

2
Czy można to osiągnąć w tle? Jak mam kolejkę zadań oczekujących na wykonanie i czekających na internet, ale aplikacja jest zamknięta?
Vidor Vistrom

54
Należy pamiętać, że witryna google.com nie jest dostępna w Chinach, dlatego przykład zostanie zawieszony, jeśli będzie używany w Chinach. Aby poszerzyć grono odbiorców, unikaj używania google.com i zamiast tego użyj example.com. wynik końcowy = await InternetAddress.lookup („example.com”);
otboss

4
To nie działa dla mnie if (result.isNotEmpty && result[0].rawAddress.isNotEmpty)zwraca prawdę, gdy jest Wi-Fi, ale nie ma połączenia z Internetem.
Denn

5
O tak, zupełnie o tym zapomniałem! Właściwie myślę, że mogę nadal używać await, mogę po prostu dołączyć .timeoutpo lookup().
Michel Feinstein

67

Dla każdego, kto tu wyląduje, chciałbym dodać do odpowiedzi Güntera Zöchbauera, to było moje rozwiązanie do wdrożenia narzędzia, aby wiedzieć, czy jest internet, czy nie, niezależnie od wszystkiego innego.

Zrzeczenie się:

Jestem nowy zarówno w Dart, jak i Flutter, więc może to nie jest najlepsze podejście, ale chciałbym uzyskać informacje zwrotne.


Połączenie flutter_connectivity i testu połączenia Güntera Zöchbauera

Moje wymagania

Nie chciałem mieć wielu powtarzających się kodów w dowolnym miejscu, w którym potrzebowałem do sprawdzenia połączenia, i chciałem, aby automatycznie aktualizował komponenty lub cokolwiek innego, co dotyczy połączenia, gdy zajdzie zmiana.

ConnectionStatusSingleton

Najpierw ustawiamy Singleton. Jeśli nie jesteś zaznajomiony z tym schematem, w Internecie jest wiele dobrych informacji na ich temat. Ale sednem jest to, że chcesz utworzyć pojedynczą instancję klasy podczas cyklu życia aplikacji i móc jej używać w dowolnym miejscu.

Ten singleton flutter_connectivitypodłącza się do i nasłuchuje zmian łączności, a następnie testuje połączenie sieciowe, a następnie używa StreamControllerdo zaktualizowania wszystkiego, co jest ważne.

To wygląda tak:

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

Stosowanie

Inicjalizacja

Najpierw musimy się upewnić, że wywołujemy inicjalizację naszego singletona. Ale tylko raz. To zależy od ciebie, ale zrobiłem to w mojej aplikacji 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();   
}

W Widgetlub w innym miejscu

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

Mam nadzieję, że ktoś inny uzna to za przydatne!


Przykładowe repozytorium github: https://github.com/dennmat/flutter-connectiontest-example

Przełącz tryb samolotowy w emulatorze, aby zobaczyć wynik


2
Przetestowałem kod i działa dla mnie Potrzebuję więcej informacji, aby pomóc.
dennmat

3
Ahh, ok, widzę to. Więc ponownie, dla przyszłego odniesienia, błąd, który publikujesz, to po prostu edytor próbujący otworzyć plik, w którym według niego wystąpił błąd. Rzeczywisty błąd powinien być dostępny w konsoli debugowania / panelu śledzenia stosu edytorów. Więc wydaje mi się, że runApp zwraca, założyłem, że będzie działał przez cały okres życia programu. Biorąc pod uwagę, że jest to w zasadzie, usuwanie nie jest tutaj konieczne, więc po prostu usuń, connectionStatus.dispose()zakładając, że konfigurujesz go w sposób main()podobny do powyższego. Zaktualizuje post i link do przykładu github.
dennmat

1
Aby po prostu wykryć, czy przełączane jest Wi-Fi lub komórkowa, potrzebujesz tylko połączenia trzepoczącego. To opakowanie sprawdza połączenie po dokonaniu przełączenia. Ale nie ostrzega o każdej zmianie sieci. Jeśli używasz emulatora, przełączanie trybu samolotowego jest najłatwiejszym sposobem na utratę połączenia internetowego. Jeśli korzystasz z rzeczywistego urządzenia, musisz się upewnić, że nie masz połączenia z siecią komórkową z danymi.
dennmat

1
Jest na to kilka opcji, możesz zmodyfikować powyższe, aby często używać Timera do testowania. Lub po prostu często testuj za pomocą narzędzia Timer. Zobacz: api.dartlang.org/stable/2.1.0/dart-async/Timer-class.html Inną opcją jest testowanie połączenia przed każdym wysłanym żądaniem. Chociaż wygląda na to, że szukasz czegoś takiego jak websockets. W każdym razie powodzenia
dennmat

2
Czy nie powinniśmy anulować subskrypcji w funkcji dispose () widżetu? Widzę, że jest to zrobione w innych przykładach StreamController, takich jak tutaj: stackoverflow.com/questions/44788256/updating-data-in-flutter
Oren

36

wprowadź opis obrazu tutaj

Pełny przykład demonstrujący słuchacza połączenia internetowego i jego źródła.

Podziękowania dla: connectivity i 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();
}

przez firebase, SDK czy to możliwe?
LOG_TAG

@LOG_TAG Nie musisz do tego używać Firebase.
CopsOnRoad

1
@CopsOnRoad Dziękuję bardzo. oszczędziłeś mi czasu.
Nimisha Ranipa

Map _source = {ConnectivityResult.none: false}; Dlaczego użyłeś tutaj „fałsz”
Faruk AYDIN

@CopsOnRoad Dziękuję! Użyłem tej metody, ale ta metoda daje mi pierwszy raz NoInternetConnection! Dlaczego najpierw podaj mi Brak? To jest mój wydruk debugowania: connectivityResult.none connectivityResult.wifi connectivityResult.wifi.
Faruk AYDIN

19

Za pomocą

dependencies:
  connectivity: ^0.4.2

to, co otrzymaliśmy z zasobów, to

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

Przyszłość jest dla mnie trochę problematyczna, musimy ją wdrażać za każdym razem jak:

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

Aby rozwiązać ten problem, stworzyłem klasę akceptującą funkcję z logicznym parametrem isNetworkPresent, takim jak ten

methodName(bool isNetworkPresent){}

A klasa użytkowa jest

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

I używać narzędzia do sprawdzania łączności

  fetchPrefrence(bool isNetworkPresent) {
    if(isNetworkPresent){

    }else{

    }
  }

użyję tej składni

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

17

Zauważyłem, że samo użycie pakietu łączności nie wystarczyło, aby stwierdzić, czy Internet był dostępny, czy nie. W Androidzie sprawdza tylko, czy jest WIFI lub czy dane mobilne są włączone, nie sprawdza rzeczywistego połączenia internetowego. Podczas moich testów, nawet bez sygnału mobilnego, ConnectivityResult.mobile zwróciłby wartość true.

W przypadku IOS moje testy wykazały, że wtyczka łączności poprawnie wykrywa połączenie internetowe, gdy telefon nie ma sygnału, problem dotyczył tylko Androida.

Rozwiązaniem, które znalazłem, było użycie data_connection_checker pakietu wraz z pakietem łączności. Zapewnia to tylko połączenie internetowe, wysyłając żądania do kilku wiarygodnych adresów, domyślny limit czasu na sprawdzenie wynosi około 10 sekund.

Moja gotowa funkcja isInternet wyglądała trochę tak:

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

if (await DataConnectionChecker().hasConnection)Część jest taka sama dla obu połączeń komórkowych i Wifi i należy prawdopodobnie przeniesione do osobnej funkcji. Nie zrobiłem tego tutaj, aby był bardziej czytelny.

To moja pierwsza odpowiedź na przepełnienie stosu, mam nadzieję, że komuś pomoże.


1
Witamy w stackoverflow. Zastanawiasz się tylko, jaka jest przewaga nad zwykłym używaniem await DataConnectionChecker().hasConnectionw pierwszej kolejności?
herbert

2
Jedynym powodem jest to, że na IOS pakiet łączności może niemal natychmiast powiedzieć, że nie ma połączenia. Gdybym właśnie użył pakietu data_connection_checker, aplikacja na IOS musiałaby zaczekać, aż żądanie HTTP przekroczyło limit czasu, około 10 sekund, zanim zwróci wartość false. W niektórych przypadkach może to być jednak dopuszczalne. Pakiet łączności może również stwierdzić, czy korzystasz z WIFI lub danych mobilnych, których nie muszę tutaj znać, ale może być przydatny.
Abernee

Działa to doskonale z kilkoma modyfikacjami składni w powyższym kodzie. 1. musisz zmienić Future <Bool> na przyszłe <bool>), ponieważ typy są pisane małymi literami. 2. Dodaj średnik (;) dla czwartej ostatniej instrukcji powrotu.
TDM

Dzięki TDM, zredagowałem odpowiedź z twoimi modyfikacjami.
Abernee

6

Stworzyłem pakiet, który (jak sądzę) niezawodnie radzi sobie z tym problemem.

Pakiet na pub.dev

Pakiet w serwisie GitHub

Dyskusja jest bardzo mile widziana. Możesz skorzystać z narzędzia do śledzenia problemów na GitHub.


Nie uważam już, że poniżej jest to niezawodna metoda:


Chcesz coś dodać do odpowiedzi @ Oren : naprawdę powinieneś dodać jeszcze jeden haczyk, który wyłapie wszystkie inne wyjątki (na wszelki wypadek) LUB po prostu całkowicie usuń typ wyjątku i użyj haczyka, który zajmuje się wszystkimi wyjątkami:

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

lub nawet prostsze ...

Przypadek 2:


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

5

Zrobiłem klasę bazową dla stanu widżetu

Użycie zamiast State<LoginPage>używać, BaseState<LoginPage> a następnie po prostu użyj zmiennej boolowskiej isOnline

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

Najpierw dodaj wtyczkę łączności:

dependencies:
  connectivity: ^0.4.3+2

Następnie dodaj klasę 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;
  }
}

I musisz przesłać widżet w swoim stanie w ten sposób

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

2
jak mogę korzystać z tej klasy?
DolDurma

@DolDurma Po prostu dodaj i zaimportuj, a następnie zamiast State <LoginPage> użyj BaseState <LoginPage>, a następnie po prostu użyj zmiennej boolowskiej isOnline
amorenew

z tym kodem nie mogę uzyskać wartości od widget. na przykład: RegisterBloc get _registerBloc => widget.registerBloc;otrzymuję ten błąd, error: The getter 'registerBloc' isn't defined for the class 'StatefulWidget'. (undefined_getter at lib\screens\fragmemt_register\view\register_mobile_number.dart:29)zobacz tę implementację:class _FragmentRegisterMobileNumberState extends BaseState<FragmentRegisterMobileNumber> with SingleTickerProviderStateMixin { RegisterBloc get _registerBloc => widget.registerBloc;
DolDurma

@DolDurma Nie jestem pewien, na czym polega problem bez próbki GitHub, ponieważ te informacje to za mało
amorenew

1
sprawdź to repozytorium i pokaż mi, jak mogę is_onlinezalogować się do konsoli github.com/MahdiPishguy/flutter-connectivity-sample
DolDurma

3

Podążając za odpowiedzią @dennmatt , zauważyłem, że InternetAddress.lookupmoże zwrócić pomyślne wyniki, nawet jeśli połączenie internetowe jest wyłączone - przetestowałem je, łącząc się z moim symulatorem z domowym WiFi, a następnie odłączając kabel routera. Myślę, że powodem jest to, że router zapisuje w pamięci podręcznej wyniki wyszukiwania domeny, więc nie musi wysyłać zapytań do serwerów DNS przy każdym żądaniu wyszukiwania.

W każdym razie, jeśli używasz Firestore tak jak ja, możesz zamienić blok try-SocketException-catch na pustą transakcję i złapać 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;
}

Zwróć również uwagę, że previousConnectionjest to ustawiane przed sprawdzaniem sieci intenet async, więc teoretycznie, jeśli checkConnection()zostanie wywołane wiele razy w krótkim czasie, może być wiele hasConnection=truez rzędu lub wiele hasConnection=falsez rzędu. Nie jestem pewien, czy @dennmatt zrobił to celowo, czy nie, ale w naszym przypadku nie było żadnych skutków ubocznych ( setStatezostał wywołany tylko dwukrotnie z tą samą wartością).


3

Łączność: pakiet nie gwarantuje rzeczywistego połączenia z Internetem (może to być tylko połączenie Wi-Fi bez dostępu do Internetu).

Cytat z dokumentacji:

Pamiętaj, że w systemie Android nie gwarantuje to połączenia z Internetem. Na przykład aplikacja może mieć dostęp do Wi-Fi, ale może to być VPN lub hotelowe Wi-Fi bez dostępu.

Jeśli naprawdę potrzebujesz sprawdzić połączenie z internetem www, lepszym wyborem będzie

pakiet data_connection_checker


1

Oto moje rozwiązanie Sprawdza łączność z Internetem, a także połączenie danych Mam nadzieję, że Ci się spodoba.

Przede wszystkim dodaj zależności w swoim pubsec.yaml
dependencies:        
    data_connection_checker:
A oto main.dart mojego rozwiązania
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

Miałem problem z proponowanymi rozwiązaniami, użycie lookupnie zawsze zwraca oczekiwaną wartość.

Dzieje się tak z powodu buforowania DNS, wartość wywołania jest zapisywana w pamięci podręcznej i nie ma konieczności wykonania prawidłowego wywołania przy następnej próbie, która zwraca wartość z pamięci podręcznej. Oczywiście jest to problem, ponieważ oznacza to, że jeśli utracisz łączność i zadzwonisz lookup, nadal możesz zwrócić wartość z pamięci podręcznej, tak jakbyś miał Internet, i odwrotnie, jeśli ponownie połączysz się z Internetem po lookupzwróceniu wartości null, nadal zwróci wartość null przez czas trwania pamięci podręcznej, co może zająć kilka minut, nawet jeśli masz teraz internet.

TL; DR: lookupzwrócenie czegoś niekoniecznie oznacza, że ​​masz internet, a brak zwrotu niekoniecznie oznacza, że ​​nie masz internetu. To nie jest wiarygodne.

Wdrożyłem następujące rozwiązanie, czerpiąc inspirację z data_connection_checkerwtyczki:

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

Wywołanie do _checkInternetAccesstrwa co najwyżej timeout(3 sekundy tutaj), a jeśli uda nam się dotrzeć do któregokolwiek z DNS, zakończy się, gdy tylko zostanie osiągnięty pierwszy, bez czekania na pozostałe (ponieważ dotarcie do jednego wystarczy, aby wiem, że masz internet). Wszystkie wywołania _pingDnssą wykonywane równolegle.

Wygląda na to, że działa dobrze w sieci IPV4, a kiedy nie mogę go przetestować w sieci IPV6 (nie mam do niej dostępu), myślę, że powinien nadal działać. Działa również w kompilacjach w trybie wydania, ale muszę jeszcze przesłać moją aplikację do Apple, aby sprawdzić, czy znajdą jakiś problem z tym rozwiązaniem.

Powinien również działać w większości krajów (w tym w Chinach), jeśli nie działa w jednym, możesz dodać DNS do listy, która jest dostępna z kraju docelowego.


1

Ostatecznie ( choć niechętnie ) zdecydowałem się na rozwiązanie podane przez @abernee w poprzedniej odpowiedzi na to pytanie. W moich projektach zawsze staram się używać jak najmniejszej liczby pakietów zewnętrznych - wiem, że pakiety zewnętrzne to jedyne [potencjalne] punkty awarii w tworzonym przeze mnie oprogramowaniu. Tak więc połączenie z DWOMA zewnętrznymi pakietami tylko dla prostej implementacji, takiej jak ta, nie było dla mnie łatwe .

Niemniej jednak wziąłem kod abernee i zmodyfikowałem go, aby był odchudzony i bardziej sensowny. Rozsądnie rozumiem, że w swojej funkcji zużywa on moc pakietu Connectivity, ale potem marnuje ją wewnętrznie, nie zwracając najcenniejszych wyników z tego pakietu (tj. Identyfikacji sieci). Oto zmodyfikowana wersja rozwiązania 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;
  }

}

Następnie użyjesz tej statycznej funkcji poprzez proste wywołanie z dowolnego miejsca w kodzie w następujący sposób:

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

Szkoda, że ​​musisz połączyć się z DWOMA PAKIETAMI ZEWNĘTRZNYMI, aby uzyskać tę bardzo podstawową funkcjonalność w swoim projekcie Flutter - ale myślę, że na razie jest to najlepsze, co mamy. Właściwie wolę pakiet Data Connection Checker niż pakiet Connectivity - ale (w momencie publikowania tego) w pierwszym brakowało tej bardzo ważnej funkcji identyfikacji sieci , której wymagam z pakietu Connectivity. To jest powód, dla którego zdecydowałem się [tymczasowo] na to podejście.


0

Po prostu próbuję uprościć kod za pomocą pakietu Connectivity Package w 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
}

Problem z tym na Androidzie polega jednak na tym, że tylko dlatego, że jesteś połączony przez Wi-Fi lub telefon komórkowy, nie oznacza to, że masz połączenie z Internetem.
Megadec

1
@Megadec niestety tak, to jedyny problem :(
devDeejay

0

późna odpowiedź, ale użyj tego pakietu, aby sprawdzić. Nazwa pakietu: data_connection_checker

w twoim pliku pubspec.yuml:

dependencies:
    data_connection_checker: ^0.3.4

utwórz plik o nazwie connection.dart lub dowolną inną nazwę. zaimportuj paczkę:

import 'package:data_connection_checker/data_connection_checker.dart';

sprawdź, czy jest połączenie internetowe, czy nie:

print(await DataConnectionChecker().hasConnection);

0

Użyłem pakietu data_connection_checker, aby sprawdzić dostęp do Internetu, nawet jeśli połączenie jest dostępne przez Wi-Fi lub telefon komórkowy, działa dobrze: oto kod do sprawdzenia połączenia:

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

przejdź do pakietu, jeśli chcesz uzyskać więcej informacji. Pakiet narzędzia do sprawdzania połączenia danych


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.