Проверьте наличие интернет-соединения в приложении 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