Verifique se há uma conexão com a Internet disponível no aplicativo Flutter

99

Tenho uma chamada de rede a ser executada. Mas antes de fazer isso preciso verificar se o dispositivo tem conectividade com a Internet.

Isso é o que eu fiz até agora:

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

O método acima não está funcionando.

Rissmon Suresh
fonte

Respostas:

193

O plug-in de conectividade declara em seus documentos que só fornece informações se houver uma conexão de rede, mas não se a rede estiver conectada à Internet

Observe que, no Android, isso não garante a conexão à Internet. Por exemplo, o aplicativo pode ter acesso wi-fi, mas pode ser uma VPN ou um wi-fi de hotel sem acesso.

Você pode usar

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');
}
Günter Zöchbauer
fonte
2
Estou recebendo o erro "isNotEmpty não está declarado no InternetAddress"
Rissmon Suresh
2
Isso pode ser alcançado em segundo plano? Como se eu tivesse uma fila de tarefas pendentes para execução e esperando pela Internet, mas o aplicativo está fechado?
Vidor Vistrom
61
Observe que google.com não está acessível na China e, como tal, o exemplo ficará travado se for usado na China. Para expandir seu público, evite usar google.com e, em vez disso, use example.com. resultado final = espera InternetAddress.lookup ('exemplo.com');
otboss
4
Isso não funciona para mim if (result.isNotEmpty && result[0].rawAddress.isNotEmpty)retorna verdadeiro quando há wi-fi, mas não há conexão com a internet.
Denn
5
Oh, sim, esqueci completamente disso! Na verdade, acho que posso continuar usando await, posso apenas acrescentar .timeoutdepois lookup().
Michel Feinstein
73

Para quem quer que chegue aqui, gostaria de acrescentar à resposta de Günter Zöchbauer esta foi a minha solução para implementar um utilitário para saber se há internet ou não, independentemente de mais nada.

Aviso Legal:

Eu sou novo no Dart e no Flutter, então esta pode não ser a melhor abordagem, mas adoraria receber feedback.


Combinando flutter_connectivity e teste de conexão de Günter Zöchbauer

Meus requisitos

Eu não queria ter um monte de código repetido em qualquer lugar que precisasse verificar a conexão e queria atualizar automaticamente os componentes ou qualquer outra coisa que se preocupasse com a conexão sempre que houvesse uma mudança.

ConnectionStatusSingleton

Primeiro, configuramos um Singleton. Se você não estiver familiarizado com esse padrão, há muitas informações boas online sobre ele. Mas a essência é que você deseja criar uma única instância de uma classe durante o ciclo de vida do aplicativo e ser capaz de usá-la em qualquer lugar.

Este singleton se conecta flutter_connectivitye escuta as mudanças de conectividade, a seguir testa a conexão de rede e usa um StreamControllerpara atualizar tudo o que for importante.

Se parece com isso:

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

Uso

Inicialização

Primeiro, temos que nos certificar de que chamamos o initialize de nosso singleton. Mas apenas uma vez. Isso depende de você, mas eu fiz isso no meu aplicativo 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();   
}

Em Widgetou em outro lugar

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) {
        ...
    }
}

Espero que outra pessoa ache isso útil!


Exemplo de repo github: https://github.com/dennmat/flutter-connectiontest-example

Alterne o modo avião no emulador para ver o resultado

dennmat
fonte
2
Testei o código e está funcionando para mim, preciso de mais informações para ajudar.
dennmat
3
Ahh, ok eu vejo isso. Então, novamente para sua referência futura, o erro que você está postando é apenas o editor tentando abrir o arquivo onde ele acha que o erro ocorreu. O erro real deve estar disponível no console de depuração de editores / painel de rastreamento de pilha. Portanto, acho que o runApp retorna, presumi que seria executado durante toda a vida do programa. Vendo que isso é principalmente, o descarte não é realmente necessário aqui, então apenas remova connectionStatus.dispose()assumindo que você está configurando da main()forma acima. Irá atualizar a postagem e o link para o exemplo do github.
dennmat
1
Para detectar apenas se wi-fi ou celular está sendo comutado, você só precisa de conectividade de vibração. Este wrapper verifica a conexão depois que a troca ocorre. Mas não alertará todas as mudanças na rede. Se você estiver usando o emulador, alternar o modo avião é a maneira mais fácil de perder a conexão com a Internet. Se você estiver em um dispositivo real, certifique-se de que ainda não está conectado a uma rede móvel com dados.
dennmat
1
Existem algumas opções para isso, você pode modificar o acima para usar o Timer para testar com frequência. Ou apenas teste freqüentemente usando o utilitário Timer. Consulte: api.dartlang.org/stable/2.1.0/dart-async/Timer-class.html Outra opção é testar a conexão antes de cada solicitação enviada. Embora pareça que você pode estar procurando por algo como websockets. De qualquer forma, boa sorte
dennmat
3
Não deveríamos cancelar a assinatura na função dispose () do widget? Vejo que isso é feito em outros exemplos do StreamController como aqui: stackoverflow.com/questions/44788256/updating-data-in-flutter
Oren
41

insira a descrição da imagem aqui

Exemplo completo demonstrando um ouvinte da conectividade com a Internet e sua fonte.

Crédito para: conectividade e 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();
}
CopsOnRoad
fonte
via firebase, SDK é possível?
LOG_TAG
@LOG_TAG Você não precisa usar o Firebase para isso.
CopsOnRoad
1
@CopsOnRoad Muito obrigado. você salvou meu tempo.
Nimisha Ranipa
Map _source = {ConnectivityResult.none: false}; Por que você usou "falso" aqui
Faruk AYDIN
@CopsOnRoad Obrigado! Eu usei esse método, mas esse método me dá a primeira vez NoInternetConnection! Por que primeiro me dê nenhum? Esta é minha impressão de depuração: conectividadeResult.não conectividadeResult.wifi conectividadeResult.wifi.
Faruk AYDIN
23

Eu descobri que apenas usando a conectividade pacote de não era suficiente para saber se a internet estava disponível ou não. No Android, ele só verifica se há WIFI ou se os dados móveis estão ativados, não verifica se há uma conexão real com a Internet. Durante meu teste, mesmo sem sinal de celular, ConnectivityResult.mobile retornaria verdadeiro.

Com o IOS, meu teste descobriu que o plug-in de conectividade detecta corretamente se há uma conexão com a Internet quando o telefone não tem sinal, o problema era apenas com o Android.

A solução que encontrei foi usar o pacote data_connection_checker junto com o pacote de conectividade. Isso apenas garante que haja uma conexão com a Internet, fazendo solicitações a alguns endereços confiáveis. O tempo limite padrão para a verificação é de cerca de 10 segundos.

Minha função isInternet concluída era um pouco assim:

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

A if (await DataConnectionChecker().hasConnection)parte é a mesma para conexões móveis e wi-fi e provavelmente deve ser movida para uma função separada. Não fiz isso aqui para deixar mais legível.

Esta é minha primeira resposta Stack Overflow, espero que ajude alguém.

Abernee
fonte
1
Bem-vindo ao stackoverflow. Apenas me perguntando: qual é a vantagem de apenas usar await DataConnectionChecker().hasConnectionem primeiro lugar?
herbert
2
A única razão é que no IOS o pacote de conectividade pode dizer quase instantaneamente que não há conexão. Se eu apenas usasse o pacote data_connection_checker, o aplicativo no IOS teria que esperar até que a solicitação http que ele fez expirasse, cerca de 10 segundos, antes de retornar false. No entanto, isso pode ser aceitável em alguns casos. O pacote de conectividade também pode dizer se você está usando WIFI ou dados móveis, o que não preciso saber aqui, mas pode ser útil saber.
abernee
Isso funciona perfeitamente com poucas modificações de sintaxe no código acima. 1. você precisa alterar Future <Bool> para future <bool>), porque os tipos estão em minúsculas. 2. Adicione ponto e vírgula (;) para a 4ª última instrução de retorno.
TDM
Obrigado TDM, editei a resposta com suas modificações.
abernee 02 de
20

Usando

dependencies:
  connectivity: ^0.4.2

o que temos de resouces é

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

O futuro é um pouco problemático para mim, temos que implementá-lo sempre, como:

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

Então, para resolver este problema, criei uma classe que aceita uma função com o parâmetro booleano isNetworkPresent como este

methodName(bool isNetworkPresent){}

E a Classe de Utilidade é

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

E para usar o utilitário de verificação de conectividade

  fetchPrefrence(bool isNetworkPresent) {
    if(isNetworkPresent){

    }else{

    }
  }

vou usar esta sintaxe

NetworkCheck networkCheck = new NetworkCheck();
networkCheck.checkInternet(fetchPrefrence)
Tushar Pandey
fonte
6

Eu criei um pacote que (eu acho) lida de forma confiável com esse problema.

O pacote em pub.dev

O pacote no GitHub

A discussão é muito bem-vinda. Você pode usar o rastreador de problemas no GitHub.


Não acho mais que o método abaixo seja confiável:


Quer acrescentar algo à resposta de @Oren : você realmente deve adicionar mais uma captura, que irá capturar todas as outras exceções (apenas para garantir), OU apenas remover o tipo de exceção completamente e usar uma captura, que lida com todas as exceções:

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

ou ainda mais simples ...

Caso 2:


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

Eu fiz uma classe base para o estado do widget

State<LoginPage>Use em vez de usar, BaseState<LoginPage> então apenas use a variável booleana isOnline

Text(isOnline ? 'is Online' : 'is Offline')

Primeiro, adicione o plug-in de conectividade:

dependencies:
  connectivity: ^0.4.3+2

Em seguida, adicione a classe 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;
  }
}

E você precisa lançar o widget em seu estado assim

@override
  void castStatefulWidget() {
    // ignore: unnecessary_statements
    widget is StudentBoardingPage;
  }
amorenew
fonte
2
como posso usar esta classe?
DolDurma de
@DolDurma Basta adicioná-lo e importá-lo, em vez de State <LoginPage> use BaseState <LoginPage> e então apenas use a variável booleana isOnline
amorenew
com este código, não consigo obter valiables widget. por exemplo: RegisterBloc get _registerBloc => widget.registerBloc;recebo este erro, error: The getter 'registerBloc' isn't defined for the class 'StatefulWidget'. (undefined_getter at lib\screens\fragmemt_register\view\register_mobile_number.dart:29)consulte esta class _FragmentRegisterMobileNumberState extends BaseState<FragmentRegisterMobileNumber> with SingleTickerProviderStateMixin { RegisterBloc get _registerBloc => widget.registerBloc;
implementação
@DolDurma Não tenho certeza de qual é o problema sem uma amostra do GitHub porque essas informações não são suficientes
amorenew
1
por favor, verifique este repo e me mostre como posso usar is_onlinepara fazer login no console github.com/MahdiPishguy/flutter-connectivity-sample
DolDurma
3

Seguindo a resposta de @dennmatt , percebi que InternetAddress.lookuppodem retornar resultados bem-sucedidos mesmo que a conexão com a Internet esteja desligada - testei conectando do meu simulador ao WiFi doméstico e, em seguida, desconectando o cabo do roteador. Acho que o motivo é que o roteador armazena em cache os resultados da pesquisa de domínio para que não precise consultar os servidores DNS em cada solicitação de pesquisa.

De qualquer forma, se você usa Firestore como eu, pode substituir o bloco try-SocketException-catch por uma transação vazia e capturar 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;
}

Além disso, observe que previousConnectioné definido antes da verificação de intenet assíncrona, então, teoricamente, se checkConnection()for chamado várias vezes em um curto espaço de tempo, pode haverhasConnection=true em uma linha ou vários hasConnection=falseem uma linha. Não tenho certeza se @dennmatt fez de propósito ou não, mas em nosso caso de uso não houve efeitos colaterais ( setStatesó foi chamado duas vezes com o mesmo valor).

Oren
fonte
3

A conectividade: o pacote não garante a conexão real com a internet (pode ser apenas conexão wi-fi sem acesso à internet).

Citação da documentação:

Observe que, no Android, isso não garante a conexão à Internet. Por exemplo, o aplicativo pode ter acesso wi-fi, mas pode ser uma VPN ou um wi-fi de hotel sem acesso.

Se você realmente precisa verificar a conexão com a Internet www, a melhor escolha seria

pacote data_connection_checker

Andrew
fonte
1

Aqui está minha solução, ela verifica a conectividade com a Internet, bem como a conexão de dados. Espero que gostem.

Em primeiro lugar, adicione dependências em seu pubsec.yaml
dependencies:        
    data_connection_checker:
E aqui está a parte principal da minha solução
import 'dart:async';

import 'package:data_connection_checker/data_connection_checker.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Data Connection Checker",
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  StreamSubscription<DataConnectionStatus> listener;

  var Internetstatus = "Unknown";

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
//    _updateConnectionStatus();
      CheckInternet();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    listener.cancel();
    super.dispose();
  }

  CheckInternet() async {
    // Simple check to see if we have internet
    print("The statement 'this machine is connected to the Internet' is: ");
    print(await DataConnectionChecker().hasConnection);
    // returns a bool

    // We can also get an enum instead of a bool
    print("Current status: ${await DataConnectionChecker().connectionStatus}");
    // prints either DataConnectionStatus.connected
    // or DataConnectionStatus.disconnected

    // This returns the last results from the last call
    // to either hasConnection or connectionStatus
    print("Last results: ${DataConnectionChecker().lastTryResults}");

    // actively listen for status updates
    listener = DataConnectionChecker().onStatusChange.listen((status) {
      switch (status) {
        case DataConnectionStatus.connected:
          Internetstatus="Connectd TO THe Internet";
          print('Data connection is available.');
          setState(() {

          });
          break;
        case DataConnectionStatus.disconnected:
          Internetstatus="No Data Connection";
          print('You are disconnected from the internet.');
          setState(() {

          });
          break;
      }
    });

    // close listener after 30 seconds, so the program doesn't run forever
//    await Future.delayed(Duration(seconds: 30));
//    await listener.cancel();
    return await await DataConnectionChecker().connectionStatus;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Data Connection Checker"),
      ),
      body: Container(
        child: Center(
          child: Text("$Internetstatus"),
        ),
      ),
    );
  }
}
Tirth Raj
fonte
1

Tive um problema com as soluções propostas, lookupnem sempre usar retorna o valor esperado.

Isso ocorre devido ao cache DNS, o valor da chamada é armazenado em cache e, em vez de fazer uma chamada adequada na próxima tentativa, ele retorna o valor armazenado em cache. Claro que isso é um problema aqui, pois significa que se você perder a conectividade e ligar para lookupele, ainda poderá retornar o valor em cache como se você tivesse internet e, inversamente, se você reconectar sua internet depoislookup retornar nulo, ele ainda retornará nulo durante o cache, que pode demorar alguns minutos, mesmo se você tiver internet agora.

TL; DR: lookupdevolver algo não significa necessariamente que você tenha internet, e não devolver nada não significa necessariamente que você não tem internet. Não é confiável.

Implementei a seguinte solução inspirando-me no data_connection_checkerplugin:

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

A chamada para _checkInternetAccessleva no máximo uma duração de timeoutpara ser concluída (3 segundos aqui), e se conseguirmos alcançar qualquer um dos DNS ela será concluída assim que o primeiro for alcançado, sem esperar pelos outros (pois chegar a um é suficiente para sabe que tem internet). Todas as chamadas para _pingDnssão feitas em paralelo.

Parece funcionar bem em uma rede IPV4, e quando não consigo testá-lo em uma rede IPV6 (não tenho acesso a uma), acho que ainda deve funcionar. Ele também funciona em compilações no modo de lançamento, mas ainda tenho que enviar meu aplicativo para a Apple para ver se eles encontram algum problema com esta solução.

Ele também deve funcionar na maioria dos países (incluindo a China). Se não funcionar em um deles, você pode adicionar um DNS à lista que pode ser acessada em seu país de destino.

Quentin
fonte
1

No final das contas ( embora com relutância ) me decidi pela solução dada por @abernee em uma resposta anterior a essa pergunta. Sempre tento usar o mínimo possível de pacotes externos em meus projetos - pois sei que os pacotes externos são os únicos pontos [potenciais] de falha no software que crio. Portanto, vincular-se a DOIS pacotes externos apenas para uma implementação simples como essa não foi fácil para mim .

Mesmo assim, peguei o código de abernee e o modifiquei para torná-lo mais enxuto e lógico. Por sensato, quero dizer que ele consome a energia do pacote de conectividade em sua função, mas depois a desperdiça internamente, não retornando as saídas mais valiosas desse pacote (ou seja, a identificação da rede). Então aqui está a versão modificada da solução da abernee:

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

}

Em seguida, você usaria essa função estática por meio de uma chamada simples de qualquer lugar em seu código da seguinte maneira:

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");

É uma pena que você tenha que vincular a DOIS PACOTES EXTERNOS para obter essa funcionalidade básica em seu projeto Flutter - mas acho que por agora é o melhor que temos. Na verdade, eu prefiro o pacote Data Connection Checker ao invés do pacote Connectivity - mas (no momento de postar isso) o primeiro estava faltando aquele recurso de identificação de rede muito importante que eu exijo do pacote Connectivity. Este é o motivo pelo qual não adotei essa abordagem [temporariamente].

SilSur
fonte
0

Apenas tentando simplificar o código usando o Pacote de conectividade no Flutter.

import 'package:connectivity/connectivity.dart';

var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {
  // I am connected to a mobile network.
} else if (connectivityResult == ConnectivityResult.wifi) {
  // I am connected to a wifi network.
} else {
  // I am not connected to the internet
}
devDeejay
fonte
O problema com isso no Android, porém, é que só porque você está conectado via wi-fi ou celular, não significa que você está conectado à internet.
Megadec
1
@Megadec, infelizmente, esse é o único problema :(
devDeejay
0

resposta tardia, mas use este pacote para verificar. Nome do pacote: data_connection_checker

em seu arquivo pubspec.yuml:

dependencies:
    data_connection_checker: ^0.3.4

crie um arquivo chamado connection.dart ou qualquer nome que desejar. importe o pacote:

import 'package:data_connection_checker/data_connection_checker.dart';

verifique se há conexão com a internet ou não:

print(await DataConnectionChecker().hasConnection);
hekmat
fonte
0

Usei o pacote data_connection_checker para verificar o acesso à internet mesmo que a conexão esteja disponível por wi-fi ou mobile, funciona bem: aqui está o código para verificar a conexão:

bool result = await DataConnectionChecker().hasConnection;
if(result == true) {
   print('YAY! Free cute dog pics!');
} else {
   print('No internet :( Reason:');
   print(DataConnectionChecker().lastTryResults);
}

veja o pacote se quiser mais informações. Pacote de verificador de conexão de dados

Muhamad Haydar Jawad
fonte