FLML Internet Checker

A lightweight, zero-dependency, and highly customizable Flutter package to monitor internet connectivity. You can easily wrap your entire app (global mode) or individual screens (local mode) to automatically handle offline states with blocking error pages or sleek non-blocking banner alerts.


Key Features

  • Zero Dependencies: Pure Dart implementation using Socket connections and DNS lookups. No reliance on platform-specific packages.
  • Global App Wrapper: Run a single checker at the root of your application (using MaterialApp.builder).
  • Route Filtering: Supports exclusions (blacklist) and inclusions (whitelist) for route-specific checking. Fully compatible with GoRouter, AutoRoute, and anonymous/custom routes.
  • Non-Blocking Banner Alerts: Option to show floating alerts at the top or bottom of the screen instead of blocking the entire page.
  • Lifecycle-Aware Checking: Pauses checking when the app is in the background to save battery, and runs immediate checks when the app is resumed.
  • Interactive Retry Action: Built-in, modern retry button with a loading state to re-verify connectivity instantly.
  • Access Anywhere: Listen to the stream or query the connection status globally from your code without UI widgets.

Getting Started

1. Setup Permissions

Android

Add the following permission to your AndroidManifest.xml file:

<uses-permission android:name="android.permission.INTERNET" />

macOS

Add the following permission to your macOS .entitlements files:

<key>com.apple.security.network.client</key>
<true/>

2. Register Router Observer (Required for Global Mode)

For route-based exclusion/inclusion to work, register InternetCheckerObserver in your MaterialApp or router configuration:

MaterialApp(
  navigatorObservers: [InternetCheckerObserver()],
  // ...
)

Usage Examples

Example 1: Global App Checker (MaterialApp.builder)

Wrap your entire app using the builder property. You can blacklist specific routes (like onboarding, splash, or local pages) that should remain accessible offline.

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

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorObservers: [InternetCheckerObserver()], // 1. Register observer
      builder: (context, child) {
        return InternetChecker(
          // 2. Exclude offline-friendly screens
          excludeRoutes: const ['/splash', '/local_settings'],
          internetConnectionText: "Whoops! No Internet",
          showRetryButton: true,
          retryButtonText: "Reload",
          child: child!,
        );
      },
      initialRoute: '/',
      routes: {
        '/': (context) => const HomeScreen(),
        '/splash': (context) => const SplashScreen(),
        '/local_settings': (context) => const SettingsScreen(),
      },
    );
  }
}

Example 2: Screen-Specific Non-Blocking Banner

Display a sleek floating banner at the bottom or top of a specific screen while still allowing the user to view and interact with the page:

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

class CachedFeedScreen extends StatelessWidget {
  const CachedFeedScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return InternetChecker(
      useBanner: true, // Enable floating alert
      bannerLocation: InternetCheckerBannerLocation.bottom, // Place at bottom
      bannerText: "Offline Mode. Interacting with local cached data.",
      bannerBackgroundColor: Colors.deepOrange,
      child: Scaffold(
        appBar: AppBar(title: const Text("News Feed")),
        body: ListView.builder(
          itemCount: 20,
          itemBuilder: (context, index) => ListTile(
            title: Text("Offline cached news item #$index"),
          ),
        ),
      ),
    );
  }
}

Example 3: Routing with Anonymous / Custom Routes

If you navigate using anonymous routes, pass the custom filter callback shouldCheckRoute to evaluate the actual Route object:

InternetChecker(
  shouldCheckRoute: (route) {
    // Exclude certain route class types
    if (route is LocalPageRoute) {
      return false;
    }
    // Or inspect settings names dynamically
    if (route.settings.name == '/offline_page') {
      return false;
    }
    return true; 
  },
  child: child!,
)

Example 4: Listen to State Globally (No UI)

Use the singleton engine to inspect or listen to connection status programmatically:

// 1. Get current status directly
bool isDeviceConnected = FLMLInternetChecker.instance.isConnected;

// 2. Listen to real-time changes
FLMLInternetChecker.instance.onStatusChange.listen((bool isConnected) {
  if (isConnected) {
    print("Internet reconnected! Syncing database...");
  } else {
    print("Device went offline.");
  }
});

// 3. Trigger manual re-check
await FLMLInternetChecker.instance.checkNow();

API Properties Reference

InternetChecker Widget

Property Type Default Description
child Widget Required The widget displayed when online or when checks are bypassed.
placeHolder Widget? null Custom widget to display when offline in blocking mode (ignores all standard styles if provided).
internetConnectionText String "No Internet Connection" Text shown on the default blocking offline screen.
textStyle TextStyle? null Custom style for the default blocking screen text.
backgroundColor Color Colors.white Background color for the default blocking screen.
useBanner bool false If true, shows a floating banner instead of blocking the page.
bannerText String "No Internet Connection" Text displayed inside the default banner.
bannerTextStyle TextStyle? null TextStyle for the banner message.
bannerBackgroundColor Color? Color(0xFFD32F2F) Background color for the default banner.
bannerLocation InternetCheckerBannerLocation bottom The position of the banner (top or bottom).
bannerBuilder Widget Function(BuildContext, bool)? null Builder to draw a completely customized banner.
excludeRoutes List<String>? null Blacklist of routes that should never trigger the offline UI.
includeRoutes List<String>? null Whitelist of routes; if set, only these routes trigger checking.
shouldCheckRoute bool Function(Route)? null Advanced custom callback to filter routes dynamically.
showRetryButton bool true Show/hide the manual retry button on the offline page.
retryButtonText String "Retry" Text shown on the retry button.
retryButtonStyle ButtonStyle? null Style for the retry button.
onOfflineModeChanged ValueChanged<bool>? null Fired when the device goes offline (true) or online (false).

FLMLInternetChecker Engine (Singleton)

Property/Method Return Type Description
FLMLInternetChecker.instance FLMLInternetChecker Accesses the shared singleton instance.
isConnected bool Queries the current connectivity state.
onStatusChange Stream<bool> Stream broadcasting real-time connection status updates.
configure(...) void Modifies hosts, ping addresses, timeouts, and poll intervals.
checkNow() Future<bool> Triggers an immediate connection check and updates state.
startPolling() void Starts the periodic connection checking daemon.
stopPolling() void Stops the periodic checking daemon.

Configuration Parameters

You can customize the engine's checking rules using configure():

FLMLInternetChecker.instance.configure(
  lookupHosts: ['dns.google', 'github.com'], // Domains to resolve DNS
  ipAddresses: ['8.8.8.8', '1.1.1.1'],      // IPs to connect via Socket TCP
  port: 53,                                 // Target port
  timeout: const Duration(seconds: 3),      // Timeout per address
  checkIntervalOnline: const Duration(seconds: 30), // Poll frequency when online
  checkIntervalOffline: const Duration(seconds: 5), // Poll frequency when offline
);

License

This project is licensed under the MIT License - see the LICENSE file for details.