我有一个网络调用要执行。但在此之前,我需要检查设备是否有互联网连接。

这是我目前为止所做的:

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

上述方法行不通。


当前回答

Null安全代码:

One time check: Create this method: Future<bool> hasNetwork() async { try { final result = await InternetAddress.lookup('example.com'); return result.isNotEmpty && result[0].rawAddress.isNotEmpty; } on SocketException catch (_) { return false; } } Usage: bool isOnline = await hasNetwork(); Setting up a listener: Add the following dependency to your pubspec.yaml file. connectivity_plus: ^2.0.2 Full code: void main() => runApp(MaterialApp(home: HomePage())); class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { Map _source = {ConnectivityResult.none: false}; final 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.mobile: string = 'Mobile: Online'; break; case ConnectivityResult.wifi: string = 'WiFi: Online'; break; case ConnectivityResult.none: default: string = 'Offline'; } return Scaffold( body: Center(child: Text(string)), ); } @override void dispose() { _connectivity.disposeStream(); super.dispose(); } } class MyConnectivity { MyConnectivity._(); static final _instance = MyConnectivity._(); static MyConnectivity get instance => _instance; final _connectivity = Connectivity(); final _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'); isOnline = result.isNotEmpty && result[0].rawAddress.isNotEmpty; } on SocketException catch (_) { isOnline = false; } _controller.sink.add({result: isOnline}); } void disposeStream() => _controller.close(); }


截图:

来源:connectivity_plus和Günter Zöchbauer

其他回答

在@dennmatt的回答之后,我注意到InternetAddress。查找可能会返回成功的结果,即使互联网连接断开-我测试它从我的模拟器连接到我的家庭WiFi,然后断开我的路由器的电缆。我认为原因是路由器缓存了域查找结果,所以它不必在每个查找请求时查询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 internet -check之前设置的,因此理论上,如果checkConnection()在短时间内被调用多次,那么在一行中可能有多个hasConnection=true或多个hasConnection=false。 我不确定@dennmatt是否故意这样做,但在我们的用例中没有副作用(setState只被调用两次,具有相同的值)。

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

使用BaseState<LoginPage>代替State<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;
  }

使用

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)

对于登陆这里的其他人,我想添加到Günter Zöchbauer的答案,这是我实现一个实用程序的解决方案,以知道是否有互联网,而不管其他任何事情。

免责声明:

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


结合flutter_connectivity和Günter Zöchbauer的连接测试

我的需求

我不希望在需要检查连接的任何地方出现一堆重复的代码,我希望它在发生变化时自动更新组件或其他与连接相关的东西。

ConnectionStatusSingleton

首先我们设置一个Singleton。如果你不熟悉这种模式,网上有很多关于它们的好信息。但要点在于,您希望在应用程序生命周期中创建类的单个实例,并且能够在任何地方使用它。

这个单例连接到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;
    }
}

使用

初始化

首先,我们必须确保调用了我们单例的initialize。但只有一次。 这部分取决于你,但我在我的应用程序的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 repo: https://github.com/dennmat/flutter-connectiontest-example

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

我有一个问题与提出的解决方案,使用查找并不总是返回预期的值。

这是由于DNS缓存,调用的值被缓存,而不是在下一次尝试时进行正确的调用,它会返回缓存的值。当然,这是一个问题,因为这意味着如果你失去了连接和调用查找,它仍然可以返回缓存的值,就像你有互联网一样,相反,如果你重新连接你的互联网后,查找返回null,它仍然会在缓存的持续时间内返回null,这可能是几分钟,即使你现在有互联网。

查找返回一些东西并不一定意味着你有网络,它不返回任何东西并不一定意味着你没有网络。它不可靠。

我从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的调用最多需要一个超时时间才能完成(这里是3秒),如果我们可以到达任何一个DNS,它将在到达第一个DNS时立即完成,而不需要等待其他DNS(因为到达一个DNS就足以知道您有internet)。所有对_pingDns的调用都是并行完成的。

它似乎在IPV4网络上工作得很好,当我不能在IPV6网络上测试它时(我没有访问IPV6网络),我认为它仍然可以工作。它也适用于发布模式构建,但我还必须将我的应用提交给苹果,看看他们是否发现了这个解决方案的任何问题。

它也应该在大多数国家(包括中国)工作,如果它不能在一个工作,你可以添加一个DNS到列表中,可以从你的目标国家访问。