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

这是我目前为止所做的:

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

上述方法行不通。


当前回答

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

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

或者更简单…

案例2:


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

其他回答

在@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只被调用两次,具有相同的值)。

连接:包不保证实际的互联网连接 (可能只是没有网络连接的wifi)。

引用自文档:

请注意,在Android上,这并不保证连接到互联网。例如,应用程序可能有wifi接入,但它可能是一个VPN或酒店wifi,没有接入。

如果你真的需要检查www互联网的连接,更好的选择是

data_connection_checker包

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

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

对我来说,我只是在Firebase中创建一个数据,并使用未来构建器来等待数据。在这里,就像这样,你可以检查连接是否太慢,所以数据将被加载:

FutureBuilder(
    future: _getImage(context),
    builder: (context, snapshot) {
      switch (snapshot.connectionState) {
        case ConnectionState.none:
          return Text('Press button to start.');
        case ConnectionState.active:
        case ConnectionState.waiting:
          return Container(
              height:
                  MediaQuery.of(context).size.height / 1.25,
              width:
                  MediaQuery.of(context).size.width / 1.25,
              child: Loading());
        case ConnectionState.done:
          if (snapshot.hasData) {
            return snapshot.data;
          } else {
            return FlatButton(
              onPressed: () {
                Navigator.push(
                    context,
                    MaterialPageRoute(
                        builder: (context) =>
                            ProfilePage()));
              },
              child: Icon(
                Icons.add_a_photo,
                size: 50,
              ),
            );
          }
        // You can reach your snapshot.data['url'] in here
      }
      return null;
    },
  ),

我最终(虽然不情愿)选择了@abernee在之前回答这个问题时给出的解决方案。我总是尝试在我的项目中尽可能少地使用外部包——因为我知道外部包是我所创建的软件的唯一[潜在]故障点。所以链接到两个外部包只是为了一个简单的实现,像这样对我来说并不容易。

尽管如此,我还是采用了abernee的代码,并对其进行了修改,使其更精简、更合理。我说的明智是指他在自己的功能中消耗了Connectivity包的功能,但在内部由于没有从这个包中返回最有价值的输出(即网络标识)而浪费了它。这是阿伯尼解决方案的修改版本:

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项目中获得这个非常基本的功能-但我想目前这是我们拥有的最好的。实际上,我更喜欢数据连接检查器包而不是连接包——但是(在发布这篇文章的时候)前者缺少我从连接包中需要的非常重要的网络识别功能。这就是我(暂时)默认采用这种方法的原因。