Перевірте, чи доступне підключення до Інтернету в програмі 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"
Rissmon Suresh

2
Чи можна цього досягти у фоновому режимі? Як я маю чергу завдань, які очікують на виконання та чекають Інтернету, але програма закрита?
Відор Вістром

54
Зверніть увагу, що google.com недоступний у Китаї, тому приклад буде висіти, якщо він використовується в Китаї. Щоб розширити свою аудиторію, уникайте використання google.com, а замість цього використовуйте example.com. остаточний результат = очікуйте InternetAddress.lookup ('example.com');
otboss

4
Це не працює для мене, це if (result.isNotEmpty && result[0].rawAddress.isNotEmpty)повертає істину, коли є Wi-Fi, але немає підключення до Інтернету.
Денн,

5
О, так, я зовсім забув про це! Насправді я думаю, що можу продовжувати користуватися await, я можу просто додати .timeoutпісля lookup().
Мішель Файнштейн

67

Для тих, хто приземляється тут, я хотів би додати відповідь Гюнтера Цохбауера, що це було моє рішення для впровадження утиліти, щоб дізнатися, чи є Інтернет чи ні, незалежно від чогось іншого.

Застереження:

Я новачок як у Дарті, так і у Флаттері, тому, можливо, це не найкращий підхід, але я хотів би отримати відгук.


Поєднуючи flutter_connectivity та тест підключення Гюнтера Цехбауера

Мої вимоги

Я не хотів мати купу повторюваних кодів у будь-якому місці, де мені потрібно було перевірити підключення, і я хотів, щоб він автоматично оновлював компоненти або що-небудь інше, що піклувалося про підключення, коли відбувалися зміни.

ConnectionStatusSingleton

Спочатку ми налаштовуємо Singleton. Якщо ви не знайомі з цією схемою, в Інтернеті є багато корисної інформації про них. Але суть полягає в тому, що ви хочете створити один екземпляр класу протягом життєвого циклу програми і мати можливість використовувати його де завгодно.

Цей синглтон flutter_connectivityпідключається і прослуховує зміни підключення, потім перевіряє мережеве підключення, а потім використовує a 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

Увімкніть режим літака в емуляторі, щоб побачити результат


2
Перевірив код, і він працює для мене. Мені знадобиться додаткова інформація, щоб допомогти.
dennmat

3
Ааа, добре, я це бачу. Тож знову ж таки для вашого подальшого посилання помилка, яку ви публікуєте, - це просто редактор, який намагається відкрити файл там, де, на його думку, сталася помилка. Справжня помилка повинна бути доступна на панелі консолі / панелі трасування вашої редакції. Тому, напевно, runApp повертається, я припускав, що він працюватиме протягом усього життя програм. Вбачаючи, що це в основному, утилізація тут насправді не потрібна, тому просто видаліть, connectionStatus.dispose()припустивши, що ви налаштовуєте це так само, main()як вище. Оновить публікацію та посилання на приклад github.
dennmat

1
Щоб просто виявити, чи перемикається Wi-Fi або стільниковий зв'язок, вам потрібно лише мережеве підключення. Ця обгортка перевіряє з'єднання після перемикання. Але не буде попереджати про кожну зміну мережі. Якщо ви використовуєте емулятор, перемикання режиму польоту - це найпростіший спосіб втратити з’єднання з Інтернетом. Якщо ви перебуваєте на реальному пристрої, вам доведеться переконатися, що ви все ще не підключені до мобільної мережі з даними.
dennmat

1
Для цього є кілька варіантів, ви можете змінити наведене вище, щоб використовувати Таймер для частого тестування. Або просто часто тестуйте за допомогою утиліти Таймер. Див. Api.dartlang.org/stable/2.1.0/dart-async/Timer-class.html Іншим варіантом є тестування з’єднання перед кожним надсиланим запитом. Хоча здається, що ви можете шукати щось на зразок веб-розеток. У будь-якому разі удачі
dennmat

2
Чи не слід скасувати передплату у функції віджета dispose ()? Я бачу, що це робиться в інших прикладах StreamController, як тут: stackoverflow.com/questions/44788256/updating-data-in-flutter
Орен

36

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

Повний приклад демонстрації слухачем підключення до Інтернету та його джерела.

Заслуга : зв’язок та Гюнтер Цехбауер

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

через Firebase, SDK це можливо?
LOG_TAG

@LOG_TAG Для цього не потрібно використовувати Firebase.
CopsOnRoad

1
@CopsOnRoad Щиро дякую. ти врятував мій час.
Німіша Раніпа

Карта _ джерело = {ConnectivityResult.none: false}; Чому ви тут використали "false"
Фарук АЙДІН

@CopsOnRoad Дякую! Я використовував цей метод, але цей метод дає мені перший раз NoInternetConnection! Чому спочатку дайте мені None? Це мій налагоджувальний друк: 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 він лише перевіряє, чи є WIFI, чи ввімкнено мобільні дані, не перевіряє справжнє підключення до Інтернету. Під час мого тестування, навіть без мобільного сигналу, ConnectivityResult.mobile повернувся б істинним.

З моїм тестуванням 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, і, мабуть, її слід перенести на окрему функцію. Я не робив цього тут, щоб зробити це більш читабельним.

Це моя перша відповідь на переповнення стека, сподіваюся, це комусь допоможе.


1
Ласкаво просимо до stackoverflow. Просто цікаво, яка перевага await DataConnectionChecker().hasConnectionв першу чергу перед простим використанням ?
Герберт

2
Єдина причина полягає в тому, що на IOS пакет підключення може миттєво сказати, що з’єднання відсутнє. Якби я просто використовував пакет data_connection_checker, програмі на IOS довелося б почекати, поки не закінчиться час очікування http-запиту, який він зробив, приблизно 10 секунд, перш ніж повернути false. Однак це може бути прийнятним у деяких випадках. Пакет підключення також може визначити, чи використовуєте ви WIFI або мобільні дані, про які мені тут не потрібно знати, але може бути корисно знати.
abernee

Це чудово працює з невеликою кількістю модифікацій синтаксису у наведеному вище коді. 1. вам потрібно змінити Future <Bool> на future <bool>), оскільки типи мають нижчий регістр. 2. Додайте крапку з комою (;) для 4-го останнього оператора повернення.
TDM

Дякую TDM, я відредагував відповідь із вашими змінами.
abernee

6

Я створив пакет, який (я думаю) надійно вирішує цю проблему.

Пакет на pub.dev

Пакет на GitHub

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


Я більше не думаю, що це нижче є надійним методом:


Хочу додати щось до @ Oren's : ви дійсно повинні додати ще один улов, який буде вловлювати всі інші винятки (щоб перестрахуватися), АБО просто видалити тип виключення взагалі і використовувати улов, який має справу з усіма винятками:

Випадок 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;
}

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

2
як я можу використовувати цей клас?
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 Я не впевнений, у чому проблема без зразка GitHub, тому що цієї інформації недостатньо
amorenew

1
будь ласка, перевірте це репо і покажіть мені, як я можу використовувати is_onlineдля входу в консоль github.com/MahdiPishguy/flutter-connectivity-sample
DolDurma

3

Після @dennmatt «сек відповіді , я помітив , що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встановлено перед асинхронною перевіркою intenet, тому теоретично, якщо checkConnection()за короткий час викликається кілька разів, може бути декілька hasConnection=trueв рядок або кілька hasConnection=falseв рядок. Я не впевнений, чи робив це @dennmatt навмисно чи ні, але в нашому випадку використання побічних ефектів не setStateбуло ( викликався лише двічі з однаковим значенням).


3

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

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

Зверніть увагу, що на Android це не гарантує підключення до Інтернету. Наприклад, програма може мати доступ до Wi-Fi, але це може бути VPN або готельний Wi-Fi без доступу.

Якщо вам дійсно потрібно перевірити підключення до Інтернету www, кращим буде вибір

пакет даних_зв'язку_перевірки


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 у попередній відповіді на це питання. Я завжди намагаюся використовувати у своїх проектах якомога менше зовнішніх пакетів - оскільки я знаю, що зовнішні пакети є єдиними [потенційними] точками збоїв у створеному мною програмному забезпеченні. Тож зв’язати ДВА зовнішні пакети лише для простої реалізації, як це, для мене було непросто .

Тим не менше, я взяв код аберні та модифікував його, щоб зробити його більш струнким та розумним. Під розумним я маю на увазі, що він споживає потужність пакету підключення у своїй функції, але потім витрачає його внутрішньо, не повертаючи найцінніших результатів цього пакету (тобто ідентифікації мережі). Отже, ось модифікована версія рішення аберні:

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

Просто намагаюся спростити код, використовуючи Connectivity Package у 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
}

Проблема з цим на 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 для перевірки доступу до Інтернету, навіть якщо з'єднання доступне через wifi або мобільний телефон, воно працює добре: ось код для перевірки з'єднання:

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)?

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.