检查Flutter应用程序上是否有可用的Internet连接

Ris*_*esh 35 dart flutter flutter-plugin

我有一个网络电话要执行.但在此之前,我需要检查设备是否具有互联网连接.

这是我到目前为止所做的:

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

以上方法不起作用.

Gün*_*uer 71

__CODE__插件在其文档中声明,它仅在存在网络连接时提供信息,但如果网络连接到Internet则不提供

请注意,在Android上,这并不保证与Internet的连接.例如,该应用程序可能具有WiFi访问权限,但它可能是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');
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,在中国国内无法访问google.com,因此如果在中国使用该示例,该示例将挂起。为了扩大您的受众群体,请避免使用google.com,而改用example.com。最终结果=等待InternetAddress.lookup('example.com'); (14认同)
  • 哦,是的,我完全忘记了这一点!实际上我认为我可以继续使用`await`,我可以在`lookup()`后面附加`.timeout`。 (7认同)
  • 这并不能解决 Flutter 2 中的问题。正如 Günter Zöchbauer 所说,第一次离线时无法解析主机名。但第二次我猜它会缓存它并在你离线时返回地址 (4认同)
  • 我收到错误“isNotEmpty 未在 InternetAddress 内声明” (3认同)
  • @BarnettTemwaMsiska“不起作用”是您在寻求支持时通常应该避免的事情。可能是您的计算机没有能力,导致 CI​​A 侵入了您的计算机并安装了间谍软件。如果您需要指导,请提供有关您尝试过的内容以及结果与您的预期有何不同的正确信息。也许更好的是用重现所需的信息创建一个新问题。 (3认同)
  • 这可以在后台实现吗?就像我有一个等待执行和等待互联网的任务队列,但应用程序已关闭? (2认同)
  • 这对我不起作用,如果有wifi但没有互联网连接时,如果(result.isNotEmpty && result [0] .rawAddress.isNotEmpty)返回true。 (2认同)
  • 嘿伙计们,当我们将其连接到 wifi 时,它就可以工作了。但是 wifi 没有互联网,它仍然显示已连接...这个问题有什么解决方案吗? (2认同)
  • 我没有连接,它只是尝试解析主机名。如果它获得 IP,则假定它已连接到互联网。 (2认同)

abe*_*nee 32

我发现仅使用连接包不足以判断互联网是否可用。在 Android 中,它只检查是否有 WIFI 或移动数据是否打开,它不会检查实际的互联网连接。在我的测试过程中,即使没有移动信号 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;
    }
  }
Run Code Online (Sandbox Code Playgroud)

if (await DataConnectionChecker().hasConnection)部分对于移动和 wifi 连接是相同的,可能应该移至单独的功能。我没有在这里这样做以使其更具可读性。

这是我的第一个 Stack Overflow 答案,希望对某人有所帮助。

  • 唯一的原因是,在 IOS 上,连接包几乎可以立即告知没有连接。如果我只使用 data_connection_checker 包,iOS 上的应用程序必须等到它发出的 http 请求超时(大约 10 秒),然后才能返回 false。但在某些情况下这可能是可以接受的。连接包还可以判断您是否正在使用 WIFI 还是移动数据,我在这里不需要知道,但了解一下可能会很有用。 (3认同)
  • 欢迎来到 stackoverflow。只是想知道,与首先使用“await DataConnectionChecker().hasConnection”相比,有什么优势? (2认同)
  • DataConnectionChecker 已弃用。使用 internet_connection_checker https://pub.dev/packages/internet_connection_checker (2认同)

den*_*mat 26

对于在此居住的其他人,我想补充一下GünterZöchbauer的答案,这是我实施实用程序的解决方案,以了解是否有互联网,无论是否有其他东西。

免责声明:

我对Dart和Flutter都是新手,所以这可能不是最好的方法,但希望获得反馈。


将flutter_connectivity与GünterZöchbauer的连接测试相结合

我的要求

我不想在检查连接的任何地方都使用一堆重复的代码,而是希望它在发生更改时自动更新组件或其他任何关心连接的内容。

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

初始化

首先,我们必须确保调用单例的初始化。但是只有一次。这取决于您,但是我在我的应用程序中做到了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();   
}
Run Code Online (Sandbox Code Playgroud)

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) {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

希望其他人觉得这有用!


示例github回购示例:https : //github.com/dennmat/flutter-connectiontest-example

在模拟器中切换飞行模式以查看结果

  • 啊,好的,我看到了。所以再次供您将来参考,您发布的错误只是编辑器试图打开它认为发生错误的文件。真正的错误应该在您的编辑器调试控制台/堆栈跟踪面板中可用。所以我猜 runApp 会返回我认为它会在整个程序生命周期中运行。看到这是主要的,在这里处理并不是真正必要的,所以只需删除`connectionStatus.dispose()`,假设您像上面那样在`main()`中设置它。将更新帖子和链接到 github 示例。 (3认同)
  • 我们不应该在小部件的 dispose() 函数中取消订阅吗?我看到这是在其他 StreamController 示例中完成的,例如:/sf/ask/3135177951/ (3认同)
  • 测试了代码,它对我有用,我需要更多信息来提供帮助。 (2认同)

Tus*_*dey 10

使用

dependencies:
  connectivity: ^0.4.2
Run Code Online (Sandbox Code Playgroud)

我们从资源中得到的是

      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;
      }
Run Code Online (Sandbox Code Playgroud)

对我来说,未来没什么大问题,我们必须像这样:

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

因此,要解决此问题,我创建了一个类,该类接受带有boolean isNetworkPresent参数的函数,如下所示

methodName(bool isNetworkPresent){}
Run Code Online (Sandbox Code Playgroud)

实用程序类是

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);
  }
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

并使用连通性检查实用程序

  fetchPrefrence(bool isNetworkPresent) {
    if(isNetworkPresent){

    }else{

    }
  }
Run Code Online (Sandbox Code Playgroud)

我将使用此语法

NetworkCheck networkCheck = new NetworkCheck();
networkCheck.checkInternet(fetchPrefrence)
Run Code Online (Sandbox Code Playgroud)


kri*_*tev 7

我创建了一个(我认为)可以可靠地处理这个问题的包。

pub.dev 上的包

GitHub 上的包

非常欢迎讨论。您可以使用 GitHub 上的问题跟踪器。


我不再认为下面是一个可靠的方法:


想在@Oren 的回答中添加一些内容:您真的应该再添加一个 catch,它会捕获所有其他异常(为了安全起见),或者只是完全删除异常类型并使用处理所有异常的 catch:

情况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;
}
Run Code Online (Sandbox Code Playgroud)

甚至更简单...

案例2:


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


cod*_*shi 7

嗯,我读了几乎所有的帖子,@dennmat 的帖子对我来说最有用。虽然它对我不起作用而且也已经过时了。我已经使用 flutter 更新connectivity包进行了更新(即connectivity_plus)和data_connection_checker(检查移动和 wifi 是否有实际的互联网连接)。
读完这篇文章后,您将能够持续监听互联网连接。

1. 添加依赖
a) connectivity_plus: ^1.0.6
b) data_connection_checker: ^0.3.4

2. 处理所有连接的自定义类。

import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:data_connection_checker/data_connection_checker.dart'; 

class ConnectionUtil {
  //This creates the single instance by calling the `_internal` constructor specified below
  static final ConnectionUtil _singleton = new ConnectionUtil._internal();
  ConnectionUtil._internal();
  //This is what's used to retrieve the instance through the app
  static ConnectionUtil getInstance() => _singleton;
  //This tracks the current connection status
  bool hasConnection = false;
  //This is how we'll allow subscribing to connection changes
  StreamController connectionChangeController = StreamController();
  //flutter_connectivity
  final Connectivity _connectivity = Connectivity();
  void initialize() {
    _connectivity.onConnectivityChanged.listen(_connectionChange);
  }
  //flutter_connectivity's listener
  void _connectionChange(ConnectivityResult result) {
    hasInternetInternetConnection();
  }
  Stream get connectionChange => connectionChangeController.stream;
  Future<bool> hasInternetInternetConnection() async {
    bool previousConnection = hasConnection;
    var connectivityResult = await (Connectivity().checkConnectivity());
    //Check if device is just connect with mobile network or wifi
    if (connectivityResult == ConnectivityResult.mobile ||
        connectivityResult == ConnectivityResult.wifi) {
      //Check there is actual internet connection with a mobile network or wifi
      if (await DataConnectionChecker().hasConnection) {
        // Network data detected & internet connection confirmed.
        hasConnection = true;
      } else {
        // Network data detected but no internet connection found.
        hasConnection = false;
      }
    }
    // device has no mobile network and wifi connection at all
    else {
      hasConnection = false;
    }
    // The connection status changed send out an update to all listeners
    if (previousConnection != hasConnection) {
      connectionChangeController.add(hasConnection);
    }
    return hasConnection;
  }
}
Run Code Online (Sandbox Code Playgroud)
  1. 检查任何地方的连接并监听变化。
@override
  initState() {
    print('called');
    //Create instance
    ConnectionUtil connectionStatus = ConnectionUtil.getInstance();
    //Initialize
    connectionStatus.initialize();
    //Listen for connection change
    _connectionChangeStream = connectionStatus.connectionChange.listen((event) {
      print(event);
    });

    super.initState();
  }
Run Code Online (Sandbox Code Playgroud)

现在在切换飞行模式时检查日志。你应该得到带有 true 和 false 值的日志。

注意:这在 flutter web 中不起作用,如果你想让它工作而不是使用diohttpplugin 而不是data_connection_checker.

示例项目可以在这里找到。谢谢


amo*_*new 5

我为小部件状态创建了一个基类

使用而不是State<LoginPage>使用BaseState<LoginPage> 然后只使用布尔变量 isOnline

Text(isOnline ? 'is Online' : 'is Offline')
Run Code Online (Sandbox Code Playgroud)

首先,添加连接插件:

dependencies:
  connectivity: ^0.4.3+2
Run Code Online (Sandbox Code Playgroud)

然后添加 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

你需要像这样在你的状态下投射小部件

@override
  void castStatefulWidget() {
    // ignore: unnecessary_statements
    widget is StudentBoardingPage;
  }
Run Code Online (Sandbox Code Playgroud)

  • 我该如何使用这个类? (2认同)

Cop*_*oad 5

在此处输入图片说明

完整的示例演示了Internet连接及其源的侦听器。

归功于:连接性和GünterZö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();
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您总是发现“无连接错误”,请添加“”_source.clear(); 在“setState(() =&gt; _source = source);”之前 (2认同)
  • 我将此代码与流提供程序一起使用,但即使我断开 wi-fi,isOnline 也始终返回 true。 (2认同)

Que*_*tin 5

我对建议的解决方案有疑问,使用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;
  }
Run Code Online (Sandbox Code Playgroud)

对 的调用_checkInternetAccess最多需要持续时间才能timeout完成(此处为 3 秒),如果我们可以到达任何 DNS,它将在到达第一个 DNS 后立即完成,而无需等待其他 DNS(因为到达一个就足以知道你有互联网)。所有的调用_pingDns都是并行完成的。

它似乎在 IPV4 网络上运行良好,当我无法在 IPV6 网络上测试它时(我无权访问),我认为它应该仍然可以工作。它也适用于发布模式构建,但我还必须将我的应用程序提交给 Apple,看看他们是否发现此解决方案有任何问题。

它也应该适用于大多数国家(包括中国),如果它在某个国家/地区不起作用,您可以将 DNS 添加到可从您的目标国家/地区访问的列表中。