Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      10 Ways Node.js Development Boosts AI & Real-Time Data (2025-2026 Edition)

      August 18, 2025

      Looking to Outsource React.js Development? Here’s What Top Agencies Are Doing Right

      August 18, 2025

      Beyond The Hype: What AI Can Really Do For Product Design

      August 18, 2025

      BrowserStack launches Chrome extension that bundles 10+ manual web testing tools

      August 18, 2025

      How much RAM does your Linux PC really need in 2025?

      August 19, 2025

      Have solar at home? Supercharge that investment with this other crucial component

      August 19, 2025

      I replaced my MacBook charger with this compact wall unit – and wish I’d done it sooner

      August 19, 2025

      5 reasons to switch to an immutable Linux distro today – and which to try first

      August 19, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Sentry Adds Logs Support for Laravel Apps

      August 19, 2025
      Recent

      Sentry Adds Logs Support for Laravel Apps

      August 19, 2025

      Efficient Context Management with Laravel’s Remember Functions

      August 19, 2025

      Laravel Devtoolbox: Your Swiss Army Knife Artisan CLI

      August 19, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      From plateau predictions to buggy rollouts — Bill Gates’ GPT-5 skepticism looks strangely accurate

      August 18, 2025
      Recent

      From plateau predictions to buggy rollouts — Bill Gates’ GPT-5 skepticism looks strangely accurate

      August 18, 2025

      We gave OpenAI’s open-source AI a kid’s test — here’s what happened

      August 18, 2025

      With GTA 6, next-gen exclusives, and a console comeback on the horizon, Xbox risks sitting on the sidelines — here’s why

      August 18, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»How to Build an Always Listening Network Connectivity Checker in Flutter using BLoC

    How to Build an Always Listening Network Connectivity Checker in Flutter using BLoC

    August 18, 2025

    Many mobile applications require a stable internet connection to provide a smooth user experience. And as a Flutter developer, you need a robust way to handle network state changes, ensuring that your app responds gracefully whether it’s connected, disconnected, or transitioning between states.

    This article will give you a detailed walkthrough of building a comprehensive network connectivity checker using a powerful combination of modern Flutter packages and architectural patterns.

    We will leverage:

    1. connectivity_plus: A package to check for basic network connectivity (for example, WiFi, mobile data, Ethernet).

    2. internet_connection_checker: A more reliable tool that goes beyond a simple network check by actively pinging a known URL to confirm actual internet access.

    3. A direct HTTP call to a trusted URL (like Google): As a failsafe, a direct network call can serve as a final confirmation of connectivity.

    4. rxdart with debounce: To prevent excessive and rapid network checks, which can be inefficient and drain the device’s battery.

    5. Dependency Injection with get_it and injectable: For a clean, modular, and testable codebase.

    6. State Management with BLoC and freezed: The BLoC pattern separates business logic from the UI, and freezed simplifies the creation of immutable states and events.

    7. Streams: To enable a reactive, “always-listening” approach to network status changes.

    8. fluttertoast: To provide clear, non-intrusive user feedback.

    Let’s dive in.

    Table of Contents:

    1. Prerequisites

    2. Step 1: Set Up Dependency Injection with get_it and injectable

    3. Step 2: Implement the Network Connectivity Checker

    4. Step 3: Create the BLoC for Network Connectivity

    5. Step 4: Integrate the BLoC with the UI

    6. Step 5: Display Toast Notifications

    7. Conclusion

    8. References

    Prerequisites

    Before you begin, make sure you have a basic understanding of:

    1. Flutter and Dart: The fundamentals of building apps with Flutter.

    2. Asynchronous Programming: Concepts like async, await, and Future.

    3. BLoC pattern: The core principles of BLoC (Business Logic Component) for state management.

    4. Code generation: How to use packages like build_runner to generate boilerplate code.

    Step 1: Set Up Dependency Injection with get_it and injectable

    Dependency Injection (DI) is a software design pattern that allows a class to receive its dependencies from an external source rather than creating them itself. This makes your code more flexible, reusable, and easier to test.

    Let’s look at the two tools we’ll use to implement this:

    1. get_it is a “service locator” that acts as a central registry. You register your services (dependencies) with get_it, and it provides a way to retrieve their single instance from anywhere in your app. It’s a simple and effective alternative to more complex DI frameworks.

    2. injectable is a code-generation package that works with get_it. By annotating your classes with @injectable, @lazySingleton, or @module, injectable automatically writes the boilerplate code to register your dependencies with get_it for you, saving you from manual setup.

    First, create a new Flutter project like this:

    flutter create my_injectable_project
    cd my_injectable_project
    

    Next, add the necessary packages to your pubspec.yaml file:

    dependencies:
      freezed_annotation: ^2.4.1
      rxdart: ^0.28.0
      get_it: ^7.6.7
      injectable: ^2.3.2
      internet_connection_checker: ^1.0.0+1
      connectivity_plus: ^5.0.2
      fluttertoast: ^8.2.4
      flutter_bloc: ^8.1.3
      http: ^0.13.3
    
    dev_dependencies:
      build_runner:
      freezed: ^2.4.7
      injectable_generator: ^2.4.1
    

    So what’s going on here?

    • freezed_annotation & freezed: Used for creating immutable data classes for BLoC states and events.

    • rxdart: Provides powerful stream-related operators, including debounceTime, which is essential for our connectivity checker.

    • get_it & injectable: For dependency injection.

    • internet_connection_checker & connectivity_plus: The core packages for checking network status.

    • fluttertoast: To display user notifications.

    • flutter_bloc: The main BLoC package.

    • http: A package to make HTTP requests, used for the Google URL check.

    • build_runner: The command-line tool that runs the code generators.

    • injectable_generator: The generator that works with injectable.

    Now it’s time to create the injection configuration file. Go ahead and create a file, for example, lib/core/dependency_injection/injection.dart, to set up get_it and injectable.

    import 'package:get_it/get_it.dart';
    import 'package:injectable/injectable.dart';
    import 'package:my_injectable_project/core/dependency_injection/injection.config.dart';
    
    // The global instance of GetIt
    final GetIt getIt = GetIt.instance;
    
    // The annotation @injectableInit tells injectable to generate the init method
    @injectableInit
    void configureDependencies(String env) => getIt.init(environment: env);
    
    • final GetIt getIt = GetIt.instance;: We create a static instance of GetIt that can be accessed globally.

    • @injectableInit: This annotation signals to injectable_generator that this is the file where it should generate the dependency registration code.

    • void configureDependencies(String env) => getIt.init(environment: env);: This function initializes get_it and allows us to configure it for different environments (e.g., ‘dev’, ‘prod’).

    Lastly, we need to create a module for dependencies. Create a module file, for example, lib/core/dependency_injection/register_module.dart, to register third-party classes that don’t belong to your own project structure.

    import 'package:connectivity_plus/connectivity_plus.dart';
    import 'package:get_it/get_it.dart';
    import 'package:injectable/injectable.dart';
    import 'package:internet_connection_checker/internet_connection_checker.dart';
    import 'package:my_injectable_project/core/network_info/network_info.dart';
    import 'package:my_injectable_project/features/network_info/bloc/network_info_bloc.dart';
    
    // The @module annotation marks this class as a module for injectable
    @module
    abstract class RegisterModule {
    
      // A @lazySingleton means the instance will be created only when it's first requested
      @lazySingleton
      Connectivity get connectivity => Connectivity();
    
      @lazySingleton
      InternetConnectionChecker get internetConnectionChecker => InternetConnectionChecker();
    
      @lazySingleton
      NetworkInfoImpl get networkInfo => NetworkInfoImpl(
            connectivity: connectivity,
            internetConnectionChecker: internetConnectionChecker,
          );
    
      @lazySingleton
      NetworkInfoBloc get networkInfoBloc => NetworkInfoBloc(
            networkInfo: getIt<NetworkInfo>(),
            connectivity: getIt<Connectivity>(),
          );
    }
    
    • @module: A special annotation that marks a class as a module for injectable. Modules are useful for registering third-party classes or creating instances of classes that require complex setup.

    • @lazySingleton: This annotation tells injectable to create a single instance of the class and reuse it every time it is requested. The “lazy” part means the instance is not created until it’s needed for the first time.

    Step 2: Implement the Network Connectivity Checker

    Interface and Implementation

    It’s good practice to program against an interface rather than a concrete implementation. This allows for easy swapping of implementations and makes testing simpler. Below, lib/core/network_info/network_info.dart is the abstract class while lib/core/network_info/network_info_impl.dart is the implementation. This is where the functionality of the flow lies, which the bloc uses.

    lib/core/network_info/network_info.dart:

    // The abstract class defines the contract for our network checker
    abstract class NetworkInfo {
      Future<bool> get isConnected;
    }
    

    lib/core/network_info/network_info_impl.dart:

    import 'package:connectivity_plus/connectivity_plus.dart';
    import 'package:flutter/foundation.dart';
    import 'package:http/http.dart' as http;
    import 'package:injectable/injectable.dart';
    import 'package:internet_connection_checker/internet_connection_checker.dart';
    import 'package:my_injectable_project/core/network_info/network_info.dart';
    
    // @LazySingleton(as: NetworkInfo) tells injectable to register this class
    // as a lazy singleton, and to provide it when a NetworkInfo is requested.
    @LazySingleton(as: NetworkInfo)
    class NetworkInfoImpl implements NetworkInfo {
      final Connectivity connectivity;
      final InternetConnectionChecker internetConnectionChecker;
    
      const NetworkInfoImpl({
        required this.connectivity,
        required this.internetConnectionChecker,
      });
    
      @override
      Future<bool> get isConnected async {
        try {
          bool isDeviceConnected = false;
          // First, check the connectivity type (WiFi, mobile, etc.)
          final connectivityResult = await connectivity.checkConnectivity();
          debugPrint('Connectivity Result: $connectivityResult');
    
          if (connectivityResult != ConnectivityResult.none) {
            // If there's a network type, verify actual internet access
            isDeviceConnected = await internetConnectionChecker.hasConnection ||
                await hasInternetConnection();
          }
          debugPrint('Device Connected: $isDeviceConnected');
          return isDeviceConnected;
        } catch (e) {
          debugPrint('Error checking network connection: $e');
          return false;
        }
      }
    
      // A redundant but useful check with a direct HTTP call
      Future<bool> hasInternetConnection() async {
        try {
          final response = await http.get(Uri.parse('https://www.google.com')).timeout(
            const Duration(seconds: 5),
          );
          if (response.statusCode == 200) {
            return true;
          }
        } catch (e) {
          debugPrint('Error checking internet connection: $e');
        }
        return false;
      }
    }
    
    • @LazySingleton(as: NetworkInfo): This is the key annotation. It registers NetworkInfoImpl as a singleton that implements the NetworkInfo interface. When getIt<NetworkInfo>() is called, an instance of NetworkInfoImpl will be provided.

    • connectivity.checkConnectivity(): Provides a quick check of the device’s connection type.

    • internetConnectionChecker.hasConnection: This package is more reliable than just checking the network type, as a device can be “connected” to a WiFi network without having internet access. internet_connection_checker actively pings a series of addresses to verify.

    • hasInternetConnection(): A fallback function that makes a direct HTTP request to a reliable URL like Google. This provides an extra layer of verification.

    Step 3: Create the BLoC for Network Connectivity

    The BLoC handles the business logic of checking the network status and emitting the appropriate state changes to the UI.

    lib/features/network_info/bloc/network_info_bloc.dart:

    import 'dart:async';
    import 'package:bloc/bloc.dart';
    import 'package:connectivity_plus/connectivity_plus.dart';
    import 'package:flutter/foundation.dart';
    import 'package:injectable/injectable.dart';
    import 'package:my_injectable_project/core/network_info/network_info.dart';
    import 'package:rxdart/rxdart.dart';
    import 'package:freezed_annotation/freezed_annotation.dart';
    
    part 'network_info_bloc.freezed.dart';
    part 'network_info_event.dart';
    part 'network_info_state.dart';
    
    // @injectable marks this class to be registered by injectable
    @injectable
    class NetworkInfoBloc extends Bloc<NetworkInfoEvent, NetworkInfoState> {
      final NetworkInfo networkInfo;
      final Connectivity connectivity;
      late StreamSubscription<List<ConnectivityResult>> connectivitySubscription;
    
      NetworkInfoBloc({
        required this.networkInfo,
        required this.connectivity,
      }) : super(NetworkInfoState.initial()) {
        // Custom event transformer for debouncing
        EventTransformer<T> debounce<T>(Duration duration) {
          return (events, mapper) => events.debounceTime(duration).flatMap(mapper);
        }
    
        // The 'on' method maps events to states
        on<CheckNetwork>(
          _onCheckNetwork,
          // Apply the debounce transformer to limit the rate of function calls
          transformer: debounce(
            const Duration(seconds: 1),
          ),
        );
    
        // Listen to changes from the connectivity_plus package
        connectivitySubscription = connectivity.onConnectivityChanged.listen((connectivityResult) async {
          await Future.delayed(const Duration(seconds: 1)); // Small delay to avoid race conditions
          debugPrint('Connectivity Result after delay: $connectivityResult');
          add(const CheckNetwork());
        });
      }
    
      // The event handler for CheckNetwork
      Future<void> _onCheckNetwork(
        CheckNetwork event,
        Emitter<NetworkInfoState> emit,
      ) async {
        final isConnected = await networkInfo.isConnected;
        // Only emit a new state if the network status has actually changed
        if (state.networkStatus != isConnected) {
          emit(state.copyWith(networkStatus: isConnected));
        }
        debugPrint(
            'Network Status ==> ${isConnected ? "Data connection is available." : "You are disconnected from the internet."}');
      }
    
      @override
      Future<void> close() {
        // It's crucial to cancel the stream subscription to prevent memory leaks
        connectivitySubscription.cancel();
        return super.close();
      }
    }
    
    • EventTransformer<T> debounce<T>(Duration duration): This is a custom transformer. It uses rxdart‘s debounceTime operator to wait for a specified duration of inactivity before allowing the event to be processed. This is perfect for preventing a cascade of network checks.

    • connectivity.onConnectivityChanged.listen(...): This creates a subscription to a stream of ConnectivityResult. Whenever the device’s connectivity status changes (for example, switches from WiFi to mobile data), this stream emits a new value, which in turn triggers our CheckNetwork event.

    • _onCheckNetwork(...): This function is the heart of the BLoC’s logic. It calls networkInfo.isConnected to get the current status and then emits a new state if the status has changed.

    • close(): Overriding this method is vital for proper resource management. It’s where we clean up our StreamSubscription to avoid memory leaks.

    Events and States

    freezed is a code-generation tool that makes it easy to create immutable data classes, which are essential for the BLoC pattern.

    lib/features/network_info/bloc/network_info_event.dart:

    part of 'network_info_bloc.dart';
    
    @freezed
    class NetworkInfoEvent with _$NetworkInfoEvent {
      const factory NetworkInfoEvent.checkNetwork() = CheckNetwork;
    }
    
    • @freezed: This annotation triggers freezed to generate the boilerplate code for this class.

    • const factory NetworkInfoEvent.checkNetwork() = CheckNetwork;: This defines a single event for our BLoC, which is CheckNetwork.

    lib/features/network_info/bloc/network_info_state.dart:

    part of 'network_info_bloc.dart';
    
    @freezed
    class NetworkInfoState with _$NetworkInfoState {
      const factory NetworkInfoState({required bool networkStatus}) = _NetworkInfoState;
    
      factory NetworkInfoState.initial() => const NetworkInfoState(
        networkStatus: true,
      );
    }
    
    • const factory NetworkInfoState(...): This defines our state, which simply holds a networkStatus boolean.

    • factory NetworkInfoState.initial(): A helper factory to create the initial state of the BLoC.

    Run the Code Generator

    To generate the *.freezed.dart and *.g.dart files, run the following command in your terminal:

    flutter pub run build_runner build --delete-conflicting-outputs
    

    This command will watch your project for changes and automatically regenerate the necessary files.

    Step 4: Integrate the BLoC with the UI

    Finally, we’ll connect our BLoC to the Flutter UI to react to state changes.

    In your main widget, for example, main.dart, you can access the BLoC through getIt.

    import 'package:flutter/material.dart';
    import 'package:flutter_bloc/flutter_bloc.dart';
    import 'package:fluttertoast/fluttertoast.dart';
    import 'package:my_injectable_project/core/dependency_injection/injection.dart';
    import 'package:my_injectable_project/features/network_info/bloc/network_info_bloc.dart';
    
    void main() {
      // Initialize dependency injection
      configureDependencies('dev');
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        // Provide the BLoC to the widget tree
        return BlocProvider(
          create: (context) => getIt<NetworkInfoBloc>(),
          child: MaterialApp(
            title: 'Network Checker Demo',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: const NetworkCheckerPage(),
          ),
        );
      }
    }
    
    class NetworkCheckerPage extends StatefulWidget {
      const NetworkCheckerPage({super.key});
    
      @override
      State<NetworkCheckerPage> createState() => _NetworkCheckerPageState();
    }
    
    class _NetworkCheckerPageState extends State<NetworkCheckerPage> {
      final NetworkInfoBloc networkInfoBloc = getIt<NetworkInfoBloc>();
    
      @override
      void initState() {
        super.initState();
        // Listen to the BLoC's state stream
        networkInfoBloc.stream.listen((state) {
          if (state.networkStatus) {
            toastInfo(
              msg: "Data connection is available.",
              status: Status.success,
            );
          } else {
            toastInfo(
              msg: "You are disconnected from the internet.",
              status: Status.error,
            );
          }
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Network Connectivity'),
          ),
          body: Center(
            child: BlocBuilder<NetworkInfoBloc, NetworkInfoState>(
              builder: (context, state) {
                return Text(
                  state.networkStatus
                      ? 'Connected to the Internet!'
                      : 'Disconnected from the Internet.',
                  style: TextStyle(
                    fontSize: 20,
                    color: state.networkStatus ? Colors.green : Colors.red,
                  ),
                );
              },
            ),
          ),
        );
      }
    }
    
    • BlocProvider: This widget provides the NetworkInfoBloc instance to the widget tree, making it accessible to any child widget via BlocProvider.of<NetworkInfoBloc>(context).

    • networkInfoBloc.stream.listen(...): In initState, we subscribe to the BLoC’s state stream. Each time a new state is emitted (which happens when the network status changes), our listener is triggered, and we can display a toast notification.

    • BlocBuilder: This widget is used to rebuild the UI in response to state changes. It listens for new states from the NetworkInfoBloc and rebuilds its builder function, updating the Text widget to reflect the current network status.

    Step 5: Display Toast Notifications

    The fluttertoast package provides a simple, platform-agnostic way to show non-intrusive messages.

    import 'package:flutter/material.dart';
    import 'package:fluttertoast/fluttertoast.dart';
    
    enum Status { success, error }
    
    void toastInfo({
      required String msg,
      required Status status,
    }) {
      Fluttertoast.showToast(
        msg: msg,
        toastLength: Toast.LENGTH_SHORT,
        gravity: ToastGravity.BOTTOM,
        backgroundColor: status == Status.success ? Colors.green : Colors.red,
        textColor: Colors.white,
        fontSize: 16.0,
      );
    }
    

    This helper function simplifies the process of showing toasts by allowing you to specify a message and a status, which determines the background color.

    Conclusion

    By combining the power of the BLoC pattern, dependency injection with get_it and injectable, and robust network-checking libraries, you can build a highly reliable and maintainable network connectivity checker in your Flutter application.

    This architecture ensures your app is responsive to network changes and provides a clean separation of concerns, making your codebase scalable and easy to test.

    References

    Here are some references that support the concepts and packages used in the article:

    Flutter and Dart Fundamentals:

    1. Flutter Official Documentation: Provides comprehensive guides on Flutter development, including widgets, state management, and asynchronous programming.

    2. Dart Official Documentation: Details Dart language features, including asynchronous programming with Future and Stream.

    Connectivity and Network Checking:

    1. connectivity_plus package on Pub.dev: Official documentation for the connectivity_plus plugin, explaining its usage for checking network connectivity types.

    2. internet_connection_checker package on Flutter Gems: Details the internet_connection_checker package, which verifies actual internet access by pinging external servers.

    3. http package on Pub.dev: The official documentation for making HTTP requests in Dart and Flutter.

    Dependency Injection:

    1. get_it package on Pub.dev: The official documentation for get_it, a simple service locator for Dart and Flutter.

    2. injectable package on Pub.dev: The official documentation for injectable, a code generator for get_it that simplifies dependency registration.

    3. State Management (BLoC): flutter_bloc package on Pub.dev – the official documentation for the flutter_bloc package, providing widgets and utilities for implementing the BLoC pattern.

    Immutability and Code Generation:

    1. freezed package on Pub.dev: The official documentation for freezed, a powerful code generator for creating immutable data classes.

    2. build_runner package on Pub.dev: The tool used to run code generators like injectable_generator and freezed.

    Reactive Programming (RxDart) and Streams:

    1. rxdart package on Pub.dev: Official documentation for RxDart, which extends Dart’s Stream API with powerful operators like debounceTime.

    2. “Stream class – dart:async library” on Flutter API Docs: The official Dart documentation for the Stream class.

    User Feedback:

    1. fluttertoast package on Pub.dev: Official documentation for the fluttertoast package for displaying toast messages.

    Source: freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleHow to Deploy a Next.js API with PostgreSQL and Sevalla
    Next Article Create a travel planning agentic workflow with Amazon Nova

    Related Posts

    Development

    Sentry Adds Logs Support for Laravel Apps

    August 19, 2025
    Development

    Efficient Context Management with Laravel’s Remember Functions

    August 19, 2025
    Leave A Reply Cancel Reply

    For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

    Continue Reading

    Researchers teach LLMs to solve complex planning challenges

    Artificial Intelligence

    Citrix Bleed 2: ReliaQuest Warns of Active Exploitation in NetScaler Gateway Vulnerability

    Security

    Valve responds to alleged Steam “data breach” leak of 89 million account details — here’s the full story

    News & Updates

    How to Unblock and Access Pornhub in Arkansas [2025]

    Operating Systems

    Highlights

    iOS 19 may give your iPhone a big battery life upgrade – without you needing to do a thing

    May 17, 2025

    Sick of your phone running out of juice? A new AI feature is reportedly on…

    CVE-2025-23167 – Node.js HTTP Smuggling Vulnerability

    May 19, 2025

    CVE-2025-54439 – Samsung Electronics MagicINFO 9 Server File Upload Vulnerability

    July 23, 2025

    CVE-2025-47163 – Microsoft Office SharePoint Remote Code Execution Vulnerability

    June 11, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.