Home > Mobile >  How to check for internet connection once for every screen in Flutter?
How to check for internet connection once for every screen in Flutter?

Time:12-24

I want to check for internet connection at every screen on my app just like Telegram does and whenever user goes offline, show an Offline banner on the top of the screen.

I have tried using connectivity_plus and internet_connection_checker plugins to check for this but the problem is I have to subscribe to a stream for this and if the device goes offline once then there is no way to subscribe to it again without clicking a button.

getConnectivity() =>
    subscription = Connectivity().onConnectivityChanged.listen(
      (ConnectivityResult result) async {
        isDeviceConnected = await InternetConnectionChecker().hasConnection;
        if (!isDeviceConnected && isAlertSet == false) {
          setState(() {
            constants.offline = true;
            print('Constants().offline ${constants.offline}');
            isAlertSet = true;
          });
        }
        print('off');
      },
    );

I'm using this code right now to check this issue but I don't want to replicate this code on each and every screen and even if I do replicate it then there will be a lot of subscriptions that I'll be subscribing to, which will mean that all the subscriptions will be disposed at the same time causing all sorts of issues.

CodePudding user response:

One solution to this problem would be to use a StreamBuilder widget to listen for changes in the internet connectivity and rebuild the widget tree when the connectivity changes. This way, you won't have to replicate the code to check for internet connectivity on each screen, and you also won't have to worry about disposing of subscriptions because the StreamBuilder widget handles that automatically.

Here's an example of how you can use a StreamBuilder to display an Offline banner whenever the device is offline:

StreamBuilder(
  stream: Connectivity().onConnectivityChanged,
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      var result = snapshot.data;
      if (result == ConnectivityResult.none) {
        return OfflineBanner();
      } else {
        return Container();
      }
    } else {
      return Container();
    }
  },
)

This code will listen for changes in the internet connectivity and rebuild the widget tree whenever the connectivity changes. If the connectivity is lost (i.e., ConnectivityResult.none), it will display an OfflineBanner widget. Otherwise, it will display an empty Container widget.

You can wrap this StreamBuilder widget in a parent widget that is present on all screens in your app (e.g., a Scaffold widget), so that it will be displayed on all screens. This way, you only have to check for internet connectivity once, and the banner will be displayed on all screens whenever the device is offline.

CodePudding user response:

If you have custom Scaffold, then you have to edit it. Otherwise, create a new one and change all Scaffolds to your custom one. This allows you to easily apply changes that should be on all pages.

Then, in the CustomScaffold create a Stack that contains page content and ValueListenableBuilder that listens to connection changes and if there is no internet displays error banner.

class CustomScaffold extends StatefulWidget {
  const CustomScaffold({Key? key}) : super(key: key);

  @override
  State<CustomScaffold> createState() => _CustomScaffoldState();
}

class _CustomScaffoldState extends State<CustomScaffold> with WidgetsBindingObserver {
  StreamSubscription? connectivitySubscription;
  ValueNotifier<bool> isNetworkDisabled = ValueNotifier(false);

  void _checkCurrentNetworkState() {
    Connectivity().checkConnectivity().then((connectivityResult) {
      isNetworkDisabled.value = connectivityResult == ConnectivityResult.none;
    });
  }

  initStateFunc() {
    _checkCurrentNetworkState();

    connectivitySubscription = Connectivity().onConnectivityChanged.listen(
          (ConnectivityResult result) {
        isNetworkDisabled.value = result == ConnectivityResult.none;
      },
    );
  }

  @override
  void initState() {
    WidgetsBinding.instance.addObserver(this);
    initStateFunc();
    super.initState();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);

    if (state == AppLifecycleState.resumed) {
      _checkCurrentNetworkState();
    }
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    connectivitySubscription?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      fit: StackFit.expand,
      children: [
        Scaffold(
        ...
        ),
        ValueListenableBuilder(
          valueListenable: isNetworkDisabled,
          builder: (_, bool networkDisabled, __) =>
              Visibility(
                visible: networkDisabled,
                child: YourErrorBanner(),
              ),
        ),
      ],
    );
  }
}

CodePudding user response:

  1. First I created an abstract class called BaseScreenWidget
  2. used bloc state management to listen each time the internet connection changed then show toast or show upper banner with Blocbuilder
abstract class BaseScreenWidget extends StatelessWidget {
  const BaseScreenWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        baseBuild(context),
        BlocConsumer<InternetConnectionBloc, InternetConnectionState>(
          listener: (context, state) {
            // if (!state.isConnected) {
            //   showToast("No Internet Connection");
            // }
          },
          builder: (context, state) {
            if (!state.isConnected) {
              return const NoInternetWidget();
            }
            return const SizedBox.shrink();
          },
        ),
      ],
    );
  }

  Widget baseBuild(BuildContext context);
}

  1. Made each screen only screen widgets contains Scaffold to extends BaseScreenWidget
class MainScreen extends BaseScreenWidget {
  const MainScreen({super.key});

  @override
  Widget baseBuild(BuildContext context) {
     return const Scaffold(
      body: MainScreenBody(),
    );
  }
}
  • it's very helpful to wrap the Column with SafeArea in the build method in BaseScreen.

CodePudding user response:

USE THIS SIMPLE TECHNIQUE only need this package: Internet Connection Checker. If you turn off your network it will tell you

connection_checker.dart

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

import 'package:internet_connection_checker/internet_connection_checker.dart';

final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

class CheckMyConnection {
  static bool isConnect = false;
  static bool isInit = false;

  static hasConnection(
      {required void Function() hasConnection,
      required void Function() noConnection}) async {
    Timer.periodic(const Duration(seconds: 1), (_) async {
      isConnect = await InternetConnectionChecker().hasConnection;
      if (isInit == false && isConnect == true) {
        isInit = true;
        hasConnection.call();
      } else if (isInit == true && isConnect == false) {
        isInit = false;
        noConnection.call();
      }
    });
  }
}

base.dart

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

class Base extends StatefulWidget {
  final String title;
  const Base({Key? key, required this.title}) : super(key: key);

  @override
  State<Base> createState() => _BaseState();
}

class _BaseState extends State<Base> {
  final snackBar1 = SnackBar(
    content: const Text(
      'Internet Connected',
      style: TextStyle(color: Colors.white),
    ),
    backgroundColor: Colors.green,
  );

  final snackBar2 = SnackBar(
    content: const Text(
      'No Internet Connection',
      style: TextStyle(color: Colors.white),
    ),
    backgroundColor: Colors.red,
  );

  @override
  void initState() {
    super.initState();
    CheckMyConnection.hasConnection(hasConnection: () {
      ScaffoldMessenger.of(navigatorKey.currentContext!)
          .showSnackBar(snackBar1);
    }, noConnection: () {
      ScaffoldMessenger.of(navigatorKey.currentContext!)
          .showSnackBar(snackBar2);
    });
  }

  @override
  Widget build(BuildContext context) {
  return DefaultTabController(
        length: 3,
        child: Scaffold(
          key: navigatorKey,
          appBar: AppBar(
            bottom: const TabBar(
              tabs: [
                Tab(icon: Icon(Icons.directions_car)),
                Tab(icon: Icon(Icons.directions_transit)),
                Tab(icon: Icon(Icons.directions_bike)),
              ],
            ),
            title: const Text('Tabs Demo'),
          ),
          body: const TabBarView(
            children: [
              Icon(Icons.directions_car),
              Icon(Icons.directions_transit),
              Icon(Icons.directions_bike),
            ],
          ),
        ),
      );
  }
}

CodePudding user response:

I myself use connectivity_plus and I have never found the problem you mentioned (if the device goes offline once then there is no way to subscribe to it again without clicking a button), you can use my example. If the user's internet is disconnected, a modal will appear. If the user is connected again, the modal will be deleted automatically. Anyway, I put the option to check the internet again in the modal

class CheckConnectionStream extends GetxController {
 bool isModalEnable = false;
 final loadingCheckConnectivity = false.obs;

 ConnectivityResult _connectionStatus = ConnectivityResult.none;
 final Connectivity _connectivity = Connectivity();
 late StreamSubscription<ConnectivityResult> _connectivitySubscription;

 Future<void> initConnectivity() async {
  late ConnectivityResult result;
 try {
    result = await _connectivity.checkConnectivity();

    loadingCheckConnectivity.value = false;
  } on PlatformException {
    return;
  }

  return _updateConnectionStatus(result);
 }

Future<void> _updateConnectionStatus(ConnectivityResult result) async {
   _connectionStatus = result;

if (result == ConnectivityResult.none) {
  if (isModalEnable != true) {
    isModalEnable = true;
    showDialogIfNotConnect();
  }
  } else {
  if (isModalEnable) {
    Get.back();
  }
  isModalEnable = false;
}
}

 showDialogIfNotConnect() {
    Get.defaultDialog(
      barrierDismissible: false,
      title: "check your network".tr,
       onWillPop: () async {
         return false;
       },
       middleText: "Your device is not currently connected to the Internet".tr,
      titleStyle: TextStyle(
      color: Get.isDarkMode ? Colors.white : Colors.black,
      ),
      middleTextStyle: TextStyle(
      color: Get.isDarkMode ? Colors.white : Colors.black,
      ),
    radius: 30,
    actions: [
      Obx(() => loadingCheckConnectivity.value
          ? const CustomLoading(
              height: 30.0,
              radius: 30.0,
            )
          : ElevatedButton(
              onPressed: () async {
                loadingCheckConnectivity.value = true;
                EasyDebounce.debounce(
                    'check connectivity',
                   
                    const Duration(milliseconds: 1000), () async {
                  await initConnectivity();
                });
              },
              child: Text(
                'try again'.tr,
                style: const TextStyle(color: Colors.white),
              ),
            ))
    ]);
}

@override
void onInit() {
 super.onInit();
  initConnectivity();

  _connectivitySubscription =
    _connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
 }

@override
 void onClose() {
 _connectivitySubscription.cancel();
 super.onClose();
 }
}
  • Related