Wednesday, May 25, 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 are only going to 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. 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 status and emit new connection status.

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

1- Add dependencies

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

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

2- Verify if we have internet connection.

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

enum ConnectionStatus {  
  online,  
  offline,  
}

Then we create a class that will help us to check the internet connection. Everytime 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

Now we are going to create the widget that will use a ValueListenableBuilder to listen for the statuses emmited 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 just have to add the WarningWidgetValueNotifier widget in 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