Проверьте, доступно ли подключение к Интернету в приложении Flutter

89

Мне нужно выполнить сетевой вызов. Но перед этим мне нужно проверить, есть ли у устройства подключение к Интернету.

Вот что я сделал до сих пор:

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

Вышеуказанный метод не работает.

Риссмон Суреш
источник

Ответы:

175

Подключения плагинов состояний в его документации , что она предоставляет только информацию , если есть подключение к сети, но если сеть подключена к Интернету

Обратите внимание, что на Android это не гарантирует подключение к Интернету. Например, приложение может иметь доступ к Wi-Fi, но это может быть VPN или Wi-Fi в отеле без доступа.

Ты можешь использовать

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
Я получаю сообщение об ошибке «isNotEmpty не объявлен внутри InternetAddress»
Риссмон Суреш,
2
Можно ли этого добиться в фоновом режиме? Как будто у меня есть очередь задач, ожидающих выполнения и ожидающих выхода в Интернет, но приложение закрыто?
Vidor Vistrom
54
Обратите внимание, что google.com недоступен внутри в Китае, и поэтому пример зависнет, если используется в Китае. Чтобы расширить свою аудиторию, избегайте использования google.com и используйте вместо него example.com. конечный результат = ждать InternetAddress.lookup ('example.com');
otboss
4
Это не работает для меня, if (result.isNotEmpty && result[0].rawAddress.isNotEmpty)возвращает true, когда есть Wi-Fi, но нет подключения к Интернету.
Denn
5
Ах да, совсем забыл об этом! На самом деле я думаю, что могу продолжать использовать await, я могу просто добавить .timeoutпосле lookup().
Мишель Файнштейн,
67

Для всех, кто попадает сюда, я хотел бы добавить к ответу Гюнтера Цохбауэра, это было мое решение для реализации утилиты, чтобы знать, есть ли Интернет или нет, независимо от чего-либо еще.

Отказ от ответственности:

Я новичок как в Dart, так и во Flutter, поэтому, возможно, это не лучший подход, но я хотел бы получить отзывы.


Комбинирование flutter_connectivity и теста подключения Гюнтера Цохбауэра

Мои требования

Я не хотел иметь кучу повторяющегося кода везде, где мне нужно было проверить соединение, и я хотел, чтобы он автоматически обновлял компоненты или что-либо еще, что заботится о соединении всякий раз, когда происходило изменение.

ConnectionStatusSingleton

Сначала мы настраиваем синглтон. Если вы не знакомы с этим шаблоном, в Интернете есть много полезной информации о них. Но суть в том, что вы хотите создать единственный экземпляр класса в течение жизненного цикла приложения и иметь возможность использовать его где угодно.

Этот синглтон перехватывает flutter_connectivityи отслеживает изменения подключения, затем проверяет сетевое соединение, а затем использует StreamControllerдля обновления всего, что имеет значение.

Выглядит это так:

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

Применение

Инициализация

Сначала мы должны убедиться, что мы вызываем инициализацию нашего синглтона. Но только один раз. Это зависит от вас, но я сделал это в своем приложении 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();   
}

В Widgetили в другом месте

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

Надеюсь, кто-то еще найдет это полезным!


Пример репозитория github: https://github.com/dennmat/flutter-connectiontest-example

Переключите режим полета в эмуляторе, чтобы увидеть результат

dennmat
источник
2
Протестировал код, и он работает для меня, мне нужна дополнительная информация, чтобы помочь.
dennmat
3
Ах, хорошо, я это вижу. Итак, еще раз для справки в будущем: ошибка, которую вы публикуете, - это просто попытка редактора открыть файл, в котором, по его мнению, произошла ошибка. Настоящая ошибка должна быть доступна в консоли отладки редактора / панели трассировки стека. Итак, я предполагаю, что runApp возвращает, я предполагал, что он будет работать в течение всей жизни программы. Увидев, что это в основном, удаление здесь действительно не нужно, поэтому просто удалите, connectionStatus.dispose()если вы настраиваете его, main()как указано выше. Обновим пост и ссылку на пример на github.
dennmat
1
Чтобы просто определить, переключается ли Wi-Fi или сотовая связь, вам нужно только соединение с флаттером. Эта оболочка проверяет соединение после переключения. Но не будет предупреждать каждое изменение сети. Если вы используете эмулятор, переключение режима полета - самый простой способ потерять интернет-соединение. Если вы используете реальное устройство, вам нужно убедиться, что вы все еще не подключены к мобильной сети с данными.
dennmat
1
Для этого есть несколько вариантов, вы можете изменить приведенное выше, чтобы использовать таймер для частого тестирования. Или просто часто тестируйте с помощью утилиты Timer. См. Api.dartlang.org/stable/2.1.0/dart-async/Timer-class.html Другой вариант - тестировать соединение перед каждым отправляемым запросом. Хотя похоже, что вы можете искать что-то вроде веб-сокетов. В любом случае удачи
dennmat
2
Разве нам не следует отменить подписку в функции виджета dispose ()? Я вижу, что это сделано в других примерах StreamController, например здесь: stackoverflow.com/questions/44788256/updating-data-in-flutter
Oren
36

введите описание изображения здесь

Полный пример, демонстрирующий слушателя подключения к Интернету и его источник.

Авторы и права : Connectivity и 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();
}
CopsOnRoad
источник
через firebase, SDK это возможно?
LOG_TAG
@LOG_TAG Для этого необязательно использовать Firebase.
CopsOnRoad
1
@CopsOnRoad Большое спасибо. ты сэкономил мне время.
Нимиша Ранипа,
Карта _source = {ConnectivityResult.none: false}; Почему вы использовали здесь «ложь»
Фарук АЙДИН
@CopsOnRoad Спасибо! Я использовал этот метод, но этот метод дает мне впервые NoInternetConnection! Почему сначала мне ничего не нужно? Это моя отладка: connectivityResult.none connectivityResult.wifi connectivityResult.wifi.
Фарук АЙДИН
19

С помощью

dependencies:
  connectivity: ^0.4.2

что мы получили от ресы есть

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

Будущее для меня немного проблематично, мы должны реализовывать его каждый раз, например:

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

Итак, чтобы решить эту проблему, я создал класс, который принимает функцию с логическим параметром isNetworkPresent, подобную этому

methodName(bool isNetworkPresent){}

И класс полезности

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

И использовать утилиту проверки подключения

  fetchPrefrence(bool isNetworkPresent) {
    if(isNetworkPresent){

    }else{

    }
  }

я буду использовать этот синтаксис

NetworkCheck networkCheck = new NetworkCheck();
networkCheck.checkInternet(fetchPrefrence)
Тушар Пандей
источник
17

Я обнаружил, что простого использования пакета подключения было недостаточно, чтобы определить, доступен ли Интернет или нет. В Android он проверяет только наличие Wi-Fi или включение мобильных данных, но не проверяет наличие фактического подключения к Интернету. Во время моего тестирования даже без мобильного сигнала ConnectivityResult.mobile возвращал true.

В IOS мое тестирование показало, что плагин подключения правильно определяет, есть ли подключение к Интернету, когда на телефоне нет сигнала, проблема была только с Android.

Решение, которое я нашел, заключалось в использовании пакета data_connection_checker вместе с пакетом подключения. Это просто обеспечивает подключение к Интернету, отправляя запросы на несколько надежных адресов, тайм-аут по умолчанию для проверки составляет около 10 секунд.

Моя законченная функция isInternet выглядела примерно так:

  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)часть одинакова как для мобильных, так и для Wi-Fi-соединений и, вероятно, должна быть перенесена в отдельную функцию. Я не делал этого здесь, чтобы сделать его более читабельным.

Это мой первый ответ на Stack Overflow, надеюсь, это кому-то поможет.

Abernee
источник
1
Добро пожаловать в stackoverflow. Просто интересно, в чем вообще преимущество перед простым использованием await DataConnectionChecker().hasConnection?
Герберт
2
Единственная причина в том, что в IOS пакет подключения может практически мгновенно сказать, что подключения нет. Если бы я просто использовал пакет data_connection_checker, приложению на IOS пришлось бы ждать, пока истечет время ожидания HTTP-запроса, около 10 секунд, прежде чем вернуть false. Однако в некоторых случаях это может быть приемлемо. Пакет подключения также может сказать, используете ли вы Wi-Fi или мобильные данные, о которых мне не нужно знать здесь, но может быть полезно знать.
abernee
Это отлично работает с небольшими изменениями синтаксиса в приведенном выше коде. 1. вам нужно заменить Future <Bool> на future <bool>), потому что типы строчные. 2. Добавьте точку с запятой (;) в четвертый последний оператор возврата.
TDM
Спасибо TDM, отредактировал ответ с вашими доработками.
abernee
6

Я создал пакет, который (я думаю) надежно решает эту проблему.

Пакет на pub.dev

Пакет на GitHub

Обсуждение очень приветствуется. Вы можете использовать трекер проблем на GitHub.


Я больше не думаю, что это ниже надежный метод:


Хотите что- то добавить к @ Орена ответ: вы действительно должны добавить еще один улов, который будет ловить все другие исключения ( на всякий случай), или просто удалить тип исключения вообще и использовать улов, что сделки со всеми исключениями:

Случай 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;
}

или даже проще ...

Случай 2:


try {
  await Firestore.instance
    .runTransaction((Transaction tx) {})
    .timeout(Duration(seconds: 5));
  hasConnection = true;
} catch (_) {
  hasConnection = false;
}
kristiyan.mitev
источник
5

Я сделал базовый класс для состояния виджета

Использование вместо State<LoginPage>использования, BaseState<LoginPage> тогда просто используйте логическую переменную isOnline

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

Сначала добавьте плагин подключения:

dependencies:
  connectivity: ^0.4.3+2

Затем добавьте класс 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;
  }
}

И вам нужно привести виджет в свое состояние следующим образом

@override
  void castStatefulWidget() {
    // ignore: unnecessary_statements
    widget is StudentBoardingPage;
  }
Amorenew
источник
2
как я могу использовать этот класс?
DolDurma,
@DolDurma Просто добавьте его и импортируйте, а затем вместо State <LoginPage> используйте BaseState <LoginPage>, затем просто используйте логическую переменную isOnline
amorenew
с этим кодом я не могу получить ценности widget. например: RegisterBloc get _registerBloc => widget.registerBloc;я получаю эту ошибку, error: The getter 'registerBloc' isn't defined for the class 'StatefulWidget'. (undefined_getter at lib\screens\fragmemt_register\view\register_mobile_number.dart:29)см. эту реализацию:class _FragmentRegisterMobileNumberState extends BaseState<FragmentRegisterMobileNumber> with SingleTickerProviderStateMixin { RegisterBloc get _registerBloc => widget.registerBloc;
DolDurma
@DolDurma Я не уверен, в чем проблема без образца GitHub, потому что этой информации недостаточно
amorenew
1
пожалуйста, проверьте это репо и покажите мне, как я могу использовать is_onlineдля входа в консоль github.com/MahdiPishguy/flutter-connectivity-sample
DolDurma
3

После @dennmatt «s ответ , я заметил , что InternetAddress.lookupможет вернуться успешные результаты , даже если подключение к Интернету выключен - я проверил это путем подключения моего симулятора к домашней Wi - Fi, а затем отсоединить кабель моего маршрутизатора. Я думаю, причина в том, что маршрутизатор кэширует результаты поиска домена, поэтому ему не нужно запрашивать DNS-серверы при каждом запросе поиска.

В любом случае, если вы используете Firestore, как я, вы можете заменить блок try-SocketException-catch пустой транзакцией и перехватить TimeoutExceptions:

try {
  await Firestore.instance.runTransaction((Transaction tx) {}).timeout(Duration(seconds: 5));
  hasConnection = true;
} on PlatformException catch(_) { // May be thrown on Airplane mode
  hasConnection = false;
} on TimeoutException catch(_) {
  hasConnection = false;
}

Также обратите внимание, что previousConnectionэто установлено до async intenet-check, поэтому теоретически, еслиcheckConnection() вызывается несколько раз за короткое время, их может быть несколько hasConnection=trueв строке или несколько hasConnection=falseв строке. Я не уверен, сделал ли @dennmatt это намеренно или нет, но в нашем случае побочных эффектов не было (вызывалась setStateтолько дважды с одним и тем же значением).

Орен
источник
3

Пакет подключения: не гарантирует фактическое подключение к Интернету (может быть просто подключение к Wi-Fi без доступа в Интернет).

Цитата из документации:

Обратите внимание, что на Android это не гарантирует подключение к Интернету. Например, приложение может иметь доступ к Wi-Fi, но это может быть VPN или Wi-Fi в отеле без доступа.

Если вам действительно нужно проверить подключение к Интернету www, лучшим выбором будет

пакет data_connection_checker

Андрей
источник
1

Вот мое решение. Он проверяет подключение к Интернету, а также подключение к данным. Надеюсь, вам понравится.

Прежде всего добавьте зависимости в свой pubsec.yaml
dependencies:        
    data_connection_checker:
И вот главный дарт моего решения
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

У меня возникла проблема с предлагаемыми решениями, использование lookupне всегда возвращает ожидаемое значение.

Это связано с кешированием DNS, значение вызова кэшируется и, если при следующей попытке выполнить правильный вызов, он возвращает кешированное значение. Конечно, это проблема здесь, так как это означает, что если вы потеряете подключение и вызовете lookupего, он все равно может вернуть кешированное значение, как если бы у вас был Интернет, и, наоборот, если вы повторно подключите свой Интернет после lookupвозврата null, он все равно будет возвращать null на время кеш, что может занять несколько минут, даже если у вас сейчас есть Интернет.

TL; DR: lookupвозврат чего-либо не обязательно означает, что у вас есть Интернет, и отсутствие возврата не обязательно означает, что у вас нет Интернета. Это ненадежно.

Я реализовал следующее решение, вдохновившись data_connection_checkerплагином:

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

Призыв к _checkInternetAccess занимает самое большее время timeout(3 секунды здесь), и если мы сможем достичь любого из DNS, он завершится, как только будет достигнут первый, без ожидания других (так как достижения одного достаточно, чтобы знаю, что у вас есть интернет). Все вызовы _pingDnsвыполняются параллельно.

Кажется, он хорошо работает в сети IPV4, и когда я не могу протестировать его в сети IPV6 (у меня нет доступа к ней), я думаю, что он все равно должен работать. Он также работает в сборках в режиме выпуска, но мне еще нужно отправить свое приложение в Apple, чтобы узнать, обнаружат ли они какие-либо проблемы с этим решением.

Он также должен работать в большинстве стран (включая Китай), если он не работает в одной из них, вы можете добавить DNS в список, доступный из вашей целевой страны.

Квентин
источник
1

В конце концов ( хотя и неохотно ) я остановился на решении, данном @abernee в предыдущем ответе на этот вопрос. Я всегда стараюсь использовать как можно меньше внешних пакетов в своих проектах - поскольку я знаю, что внешние пакеты - единственные [потенциальные] точки отказа в создаваемом мной программном обеспечении. Поэтому для меня было непросто создать ссылку на ДВА внешних пакета только для такой простой реализации .

Тем не менее, я взял код Аберни и изменил его, чтобы сделать его более компактным и разумным. Под разумным я подразумеваю, что он потребляет мощность пакета Connectivity в своей функции, но затем растрачивает ее внутри, не возвращая наиболее ценные выходные данные из этого пакета (то есть идентификацию сети). Итак, вот модифицированная версия решения 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;
  }

}

Затем вы можете использовать эту статическую функцию с помощью простого вызова из любого места вашего кода следующим образом:

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

Жалко, что вам нужно установить ссылку на ДВА ВНЕШНИХ ПАКЕТА, чтобы получить эту базовую функциональность в вашем проекте Flutter, но я думаю, что на данный момент это лучшее, что у нас есть. На самом деле я предпочитаю пакет Data Connection Checker пакету Connectivity , но (на момент публикации) в первом отсутствовала эта очень важная функция сетевой идентификации, которая мне нужна от пакета Connectivity. Это причина того, что я отказался от этого подхода [временно].

СилСур
источник
0

Просто пытаюсь упростить код с помощью пакета подключения во 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
}
devDeejay
источник
Проблема с этим на Android заключается в том, что то, что вы подключены через Wi-Fi или мобильный телефон, не означает, что вы подключены к Интернету.
Megadec,
1
@Megadec, к сожалению, да, это единственная проблема :(
devDeejay,
0

поздний ответ, но используйте этот пакет, чтобы проверить. Имя пакета: data_connection_checker

в вашем файле pubspec.yuml:

dependencies:
    data_connection_checker: ^0.3.4

создайте файл с именем connection.dart или любым другим именем. импортировать пакет:

import 'package:data_connection_checker/data_connection_checker.dart';

проверьте, есть ли подключение к интернету:

print(await DataConnectionChecker().hasConnection);
хекмат
источник
0

Я использовал пакет data_connection_checker для проверки доступа в Интернет, даже если соединение доступно через Wi-Fi или мобильное устройство, оно работает хорошо: вот код для проверки соединения:

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

ознакомьтесь с пакетом, если вам нужна дополнительная информация. Пакет проверки подключения к данным

Мухамад Хайдар Джавад
источник
0

У меня возникла проблема с принятым ответом, но, похоже, это решает ответ для других. Мне нужно решение, которое может получать ответ от используемого URL-адреса, поэтому я подумал, что http отлично подойдет для этой функции, и для этого я нашел этот ответ действительно полезным. Как проверить подключение к Интернету с помощью HTTP-запросов (Flutter / Dart)?

Дэвид Б.
источник