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
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( snapConnectivity) {
home: StreamBuilder(
stream: Connectivity().onConnectivityChanged,
builder: (cxt, AsyncSnapshot
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();
}
}
)
);
This code does not seems to work for other pages. Only in the main page it does check the internet connectivity. Please let us know how to handle other pages as well.
Please share the source code