Проверьте наличие интернет-соединения в приложении Flutter.

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

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

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

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

Ответ 1

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

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

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

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

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

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

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


Сочетание теста flutter_connectivity и Günter Zöchbauer

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

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

ConnectionStatusSingleton

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

Этот синглтон flutter_connectivity к 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 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 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 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

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

Ответ 3

Использование

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)

Ответ 4

enter image description here

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

Кредит: связь и 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();
}

Ответ 5

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

Пакет на pub.dev

Пакет на GitHub

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


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


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

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

Ответ 6

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

Ответ 7

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

Использование вместо 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;
  }

Ответ 8

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

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

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

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

пакет data_connection_checker