Categories
Flutter

Listen To Internet Connection State In Flutter App

In this post, we will learn how we can update the app state according to the device’s network connection state in Flutter.

Introduction

In any app that requires internet connection, we must always check whether the device is connected with the internet or not. If there is no internet connection, user should be notified that the app needs to be connected with the Internet.

Also, app should be notified for the change in network connection state as well.

For getting this information about network state, Flutter team has created connectivity package.

Setup

Start by importing the connectivity package in your project by adding the dependency in your pubspec.yaml file.

connectivity: ^0.4.2

The package provides a Connectivity class that can listen to connectivity change states via “onConnectivityChanged” method.

The “onConnectivityChanged” returns a Stream of “ConnectivityResult” and is defined as:

/// Fires whenever the connectivity state changes.
  Stream<ConnectivityResult> get onConnectivityChanged {
    if (_onConnectivityChanged == null) {
      _onConnectivityChanged = eventChannel
          .receiveBroadcastStream()
          .map((dynamic event) => _parseConnectivityResult(event));
    }
    return _onConnectivityChanged;
  }

Our app should listen to the stream returned by “onConnectivityChanged” which should update the app state and then notify users accordingly.

In order to implement this, first import the package:

import 'package:connectivity/connectivity.dart';

Next, listen to the Stream returned by the Connectivity class and update the app state accordingly:

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Routing & Navigation"),
        ),
        body: StreamBuilder(
            stream: Connectivity().onConnectivityChanged,
            builder: (BuildContext ctxt,
                AsyncSnapshot<ConnectivityResult> snapShot) {
              if (!snapShot.hasData) return CircularProgressIndicator();
              var result = snapShot.data;
              switch (result) {
                case ConnectivityResult.none:
                  print("no net");
                  return Center(child: Text("No Internet Connection!"));
                case ConnectivityResult.mobile:
                case ConnectivityResult.wifi:
                  print("yes net");
                  return Center(
                    child: Text('Welcome to home Page'),
                  );
                default:
                  return Center(child: Text("No Internet Connection!"));
              }
            })
      );
  }
}

Here, we are using the StreamBuilder widget to build the body. It basically listens to stream returned by Connectivity().onConnectionChanged method and rebuilds the body accordingly.

Listening To Internet Connection State In Between HTTP Requests

The example above is a simple illustration of how we can listen to internet connection state in a simple Flutter app. However, in a more complex app, you need to check for connection throughout the application.

For example your application might be getting some data or posting via HTTP requests in multiple screens. This is a common scenario for most applications.

So how do you make sure there is internet connection before making any HTTP requests throughout the application?

Solution:

Here is one approach for the solution, which you could use.

  • Listen to connection state in the application’s root.
  • Create a handler that shows a “No Internet Connection” view.
  • Pass the handler along to the class that makes HTTP requests
  • Call handler if the HTTP request response throws SocketException.

For the above approach, start by creating a class ConnectionStatusSingleton which exposes connectionChange as a Stream.

Create Connection Status Notifier

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

Now we have something that we can rely on to check for internet connection state. Next, we need to subscribe to this state stream.

Subscribe To Notifier Stream

In your main.dart class where you have the application root widget, you should listen to the connection state change event.

...
class _MainApp extends State<MainApp> {

  bool _hasNetworkConnection;
  bool _fallbackViewOn;

  @override
  void initState() {
    super.initState();
    _hasNetworkConnection = false;
    _fallbackViewOn = false;

    ConnectionStatusSingleton connectionStatus =
        ConnectionStatusSingleton.getInstance();
    connectionStatus.connectionChange.listen(_updateConnectivity);
  }
....

Handle Internet Connection On/Off State

Now, the _updateConnectivity event gets fired whenever there is change in internet connection state.

void _updateConnectivity(dynamic hasConnection) {

    if (!_hasNetworkConnection) {
      if (!_fallbackViewOn) {
        navigatorKey.currentState.pushNamed(FallbackConnection.route);
        setState(() {
          _fallbackViewOn = true;
          _hasNetworkConnection = hasConnection;
        });
      }
    } else {
      if (_fallbackViewOn) {
        navigatorKey.currentState.pop(context);
        setState(() {
          _fallbackViewOn = false;
          _hasNetworkConnection = hasConnection;
        });
      }
    }
  }

Here, we make sure that if there is no internet connection and also if Fallback view is not currently pushed, only then we show the Fallback view. The Fallback view is simply a widget that shows “No Internet Connection” message for the user.

Similarly, if the connection is back on, we remove the Fallback view.

In order to push and pop the Fallback view, we are using an instance of navigator via GlobalKey.

Learn More: Navigation When There Is No Context

Now we have created a connection handler. Finally, we need to make sure this gets called during every HTTP requests.

Call Handler During HTTP Request SocketException

Pass the connection handler function to the class that makes HTTP requests. Ideally, you should have designed your application in such a way that all your HTTP requests and made from a single class.

....
class BaseActionService {
  final VoidCallback onConnectionLost;
BaseActionService(
      this.onConnectionLost,
)
....

Wrap your connection requests in a try/catch block and if it throws a SocketException, call the handler.

try {
....
...
}
catch (e, stackTrace) {
      if(e is SocketException) {
        print('No Internet Connection');
        onConnectionLost();
...
}

Conclusion

In this post, we looked at how we can handle internet connection state change in a Flutter application. First we looked at a basic example which is best suited for a simple application. Then we detailed a process to manage connection state change which is idea for a large application that makes lots of HTTP requests.

Learn More: Building An Expense Manager App In Flutter

3 replies on “Listen To Internet Connection State In Flutter App”

connectionStatus.connectionChange.listen(_updateConnectivity);
this function is not getting call.inside init state method.
Please can you help me out this issue.
I’m trying to handle internet connection on each class.it’ll notify user whenever there is no internetconnection.
Please reply and help me to resolve this issue

Hi Sumit,

You do not need to check for internet connection n each class or widget. Just wrap your main app entry widget in this way and you should be good to go.

return MaterialApp(
home: StreamBuilder(
stream: Connectivity().onConnectivityChanged,
builder: (cxt, AsyncSnapshot snapConnectivity) {
if(snapConnectivity.data == null) {
return Center(child: CircularProgressIndicator(),);
}

var result = snapConnectivity.data;

switch (result) {
case ConnectivityResult.mobile:
case ConnectivityResult.wifi:
return YOUR_APP_HOME();
case ConnectivityResult.none:
default:
return NoInternetNotifierScreen();
}
}
)
);

Leave a Reply

Your email address will not be published. Required fields are marked *