Monday, July 18, 2022

Connectivity plus. Show your users if they have internet connection

Do you have a Flutter App and you would like to show the users when they are offline, like in the next image:

Before we start, you can download the source code from Github. The source code has examples using: cubit (flutter bloc), getx, ChangeNotifier (provider), and ValueNotifier.

In this tutorial, we will only use ValueNotifier, so you will not have to add any state management package.

Requiriments

Connectivity Plus: This plugin allows Flutter apps to discover network connectivity. For example, when you are connected or disconnected from wifi. But this package will not tell us if we have or not internet connection.
RxDart: We will use it to keep the connection state and emit a new connection status.

To know if we have an internet connection, we will check this code from stackoverflow.

1- Add dependencies

Go to the file pubspec.yaml and add the following packages.

dependencies:  
  flutter:  
    sdk: flutter  
 
  rxdart: ^0.27.3  
  connectivity_plus: ^2.3.0

2- Verify if we have an internet connection.

First, we create an enum to know the current internet status.

enum ConnectionStatus {  
  online,  
  offline,  
}

Then we create a class to help us check the internet connection. Every time the network status change, this class will emit a new connection status.

class CheckInternetConnection {
  final Connectivity _connectivity = Connectivity();

  // Default will be online. This controller will help to emit new states when the connection changes.
  final _controller = BehaviorSubject.seeded(ConnectionStatus.online);
  StreamSubscription? _connectionSubscription;

  CheckInternetConnection() {
    _checkInternetConnection();
  }

  // The [ConnectionStatusValueNotifier] will subscribe to this 
  // stream and everytime the connection status change it 
  // will update it's value
  Stream<ConnectionStatus> internetStatus() {
    _connectionSubscription ??= _connectivity.onConnectivityChanged
        .listen((_) => _checkInternetConnection());
    return _controller.stream;
  }

  // Code from stackoverflow
  Future<void> _checkInternetConnection() async {
    try {
	  // Sometimes, after we connect to a network, this function will
	  // be called but the device still do not have internet connection. 
	  // This 3 seconds delay will give some time to the device to 
	  // connect to the internet in order to avoid false-positives
      await Future.delayed(const Duration(seconds: 3));
      final result = await InternetAddress.lookup('google.com');
      if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
        _controller.sink.add(ConnectionStatus.online);
      } else {
        _controller.sink.add(ConnectionStatus.offline);
      }
    } on SocketException catch (_) {
      _controller.sink.add(ConnectionStatus.offline);
    }
  }

  Future<void> close() async {
  	// Cancel subscription and close controller
    await _connectionSubscription?.cancel();
    await _controller.close();
  }
}

This class has to be initialized only one time during the lifecycle of the app. In this example, I will initialize as a global variable inside the main.dart file:

// Initialize only one time   
final internetChecker = CheckInternetConnection();  
  
void main() async {  
  runApp(  
    MyApp(),  
  );  
}

3- State management

To keep the state we are going to use ValueNotifier so we are going to create a new class:

class ConnectionStatusValueNotifier extends ValueNotifier<ConnectionStatus> {
  // Will keep a subscription to
  // the class [CheckInternetConnection]
  late StreamSubscription _connectionSubscription;  
  
  ConnectionStatusValueNotifier() : super(ConnectionStatus.online) {
    // Everytime there a new connection status is emitted
    // we will update the [value]. This will make the widget
    // to rebuild
    _connectionSubscription = internetChecker  
        .internetStatus()  
        .listen((newStatus) => value = newStatus);  
  }  
  
    
  void dispose() {  
	 _connectionSubscription.cancel();  
	 super.dispose();  
  }  
}

4- No internet connection Widget

We will create the widget that will use a ValueListenableBuilder to listen for the statuses emitted by the class ConnectionStatusValueNotifier.

class WarningWidgetValueNotifier extends StatelessWidget {
  const WarningWidgetValueNotifier({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return ValueListenableBuilder(
      valueListenable: ConnectionStatusValueNotifier(),
      builder: (context, ConnectionStatus status, child) {
        return Visibility(
          visible: status != ConnectionStatus.online,
          child: Container(
            padding: const EdgeInsets.all(16),
            height: 60,
            color: Colors.brown,
            child: Row(
              children: [
                const Icon(Icons.wifi_off),
                const SizedBox(width: 8),
                const Text('No internet connection.'),
              ],
            ),
          ),
        );
      },
    );
  }
}

5- Usage of the widget: WarningWidgetValueNotifier

To show the user there if there is no internet connection, we have to add the WarningWidgetValueNotifier widget on any screen that we would like to show the warning. For example:

class MyApp extends StatelessWidget {
  const MyApp();

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Value notifier Example'),
      ),
      body: Column(
        children: <Widget>[
          const WarningWidgetValueNotifier(),
          const Text('Abigail'),
          const Text('Alexandra'),
          const Text('Amelia'),
        ],
      ),
    );
  }
}

Conclusion

Showing to the users of your app if they are offline is very easy to implement. We did not even have to add complex packages or state management dependencies.

I made a video (in spanish) to show the end result:

0 comments:

Post a Comment

Entradas populares