connectivity_watcher 3.1.0 copy "connectivity_watcher: ^3.1.0" to clipboard
connectivity_watcher: ^3.1.0 copied to clipboard

A Flutter package for fast internet checks with subsecond response times and customizable offline UI.

example/lib/main.dart

import 'package:connectivity_watcher/connectivity_watcher.dart';
import 'package:connectivity_watcher/core/manager/socket_internet_checker.dart';
import 'package:dio/dio.dart';

import 'package:example/no_internet.dart';
import 'package:flutter/material.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  
  ZoConnectivityWatcher().setUp(
    internetChecker: StealthInternetChecker(
      heartbeatUrl: "https://www.google.com/",
    ),
  );

  // Initialize the ping system (defaults to pinging 8.8.8.8)
  ZoConnectivityWatcher().initPingService();

  runApp(MyApp());
}

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

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

  @override
  Widget build(BuildContext context) {
    return ZoConnectivityWrapper(
      /// connectivityStyle: NoConnectivityStyle.CUSTOM,
      navigationKey: navigatorKey,
      connectivityStyle: NoConnectivityStyle.CUSTOM,
      customAlert: AlertDialog(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(16.0),
        ),
        title: Row(
          children: [
            Icon(
              Icons.wifi_off,
              color: Colors.redAccent,
            ),
            SizedBox(width: 8.0),
            Text(
              'No Internet Connection',
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
          ],
        ),
        content: Text(
          'It looks like you are not connected to the internet. Please check your connection and try again.',
          style: TextStyle(fontSize: 16.0),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(),
            child: Text('CANCEL', style: TextStyle(color: Colors.grey)),
          ),
          ElevatedButton(
            onPressed: () {},
            child: Text('RETRY'),
          ),
        ],
      ),

      offlineWidget: CustomNoInternetWrapper(
        builder: (context) {
          return CustomNoInternet();
        },
      ),
      // Place your custom no internet Widget
      builder: (context, connectionKey) {
        return MaterialApp(
            navigatorKey: navigatorKey,
            debugShowCheckedModeBanner: false,
            title: 'Connectivity_Watcher',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: ApiTestDashboard());
      },
    );
  }
}

class ApiTestDashboard extends StatefulWidget {
  @override
  _ApiTestDashboardState createState() => _ApiTestDashboardState();
}

class _ApiTestDashboardState extends State<ApiTestDashboard> {
  late final Dio _dio;
  String _lastResult = '';

  @override
  void initState() {
    super.initState();
    _dio = Dio();
    ZoConnectivityWatcher().setupDioLogger(_dio);
  }

  void _showResult(String method, dynamic response) {
    if (mounted) {
      setState(() {
        _lastResult = '$method → ${response.statusCode}';
      });
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('$method completed — ${response.statusCode}'),
          duration: const Duration(seconds: 1),
        ),
      );
    }
  }

  void _showError(String method, dynamic e) {
    if (mounted) {
      setState(() {
        _lastResult = '$method → ERROR';
      });
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('$method failed'),
          backgroundColor: Colors.red,
          duration: const Duration(seconds: 1),
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF5F5F5),
      appBar: AppBar(
        title: const Text('API Test Dashboard'),
        backgroundColor: const Color(0xFF1E1E2C),
        foregroundColor: Colors.white,
        elevation: 0,
        actions: [
          const Center(
            child: Padding(
              padding: EdgeInsets.symmetric(horizontal: 16.0),
              child: ZoPingWidget(),
            ),
          ),
          IconButton(
            icon: const Icon(Icons.monitor_heart_outlined),
            tooltip: 'Network Logs',
            onPressed: () {
              ZoConnectivityWatcher().showNetworkLogsScreen(context);
            },
          ),
        ],
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // Connection status
            ZoNetworkAwareWidget(
              builder: (context, status) {
                final isConnected =
                    status == ConnectivityWatcherStatus.connected;
                return Container(
                  padding:
                      const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
                  decoration: BoxDecoration(
                    color: isConnected
                        ? Colors.green.shade50
                        : Colors.red.shade50,
                    borderRadius: BorderRadius.circular(12),
                    border: Border.all(
                      color: isConnected
                          ? Colors.green.shade300
                          : Colors.red.shade300,
                    ),
                  ),
                  child: Row(
                    children: [
                      Icon(
                        isConnected ? Icons.wifi : Icons.wifi_off,
                        color: isConnected ? Colors.green : Colors.red,
                      ),
                      const SizedBox(width: 12),
                      Text(
                        isConnected ? 'Connected' : 'Disconnected',
                        style: TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.w600,
                          color: isConnected
                              ? Colors.green.shade800
                              : Colors.red.shade800,
                        ),
                      ),
                    ],
                  ),
                );
              },
            ),
            const SizedBox(height: 16),

            // Last result
            if (_lastResult.isNotEmpty)
              Container(
                padding: const EdgeInsets.all(12),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(12),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black.withOpacity(0.05),
                      blurRadius: 8,
                      offset: const Offset(0, 2),
                    ),
                  ],
                ),
                child: Text(
                  'Last: $_lastResult',
                  style: TextStyle(
                    fontFamily: 'monospace',
                    fontSize: 14,
                    color: Colors.grey.shade700,
                  ),
                ),
              ),
            const SizedBox(height: 24),

            // Section: GET Requests
            _buildSectionHeader('GET Requests', Icons.download),
            const SizedBox(height: 8),
            _buildRequestButton(
              label: 'GET Single Post',
              subtitle: '/posts/1',
              color: Colors.blue,
              icon: Icons.article,
              onTap: () async {
                try {
                  final res = await _dio.get(
                    "https://jsonplaceholder.typicode.com/posts/1",
                  );
                  _showResult('GET', res);
                } catch (e) {
                  _showError('GET', e);
                }
              },
            ),
            const SizedBox(height: 8),
            _buildRequestButton(
              label: 'GET with Query Params',
              subtitle: '/comments?postId=1',
              color: Colors.blue.shade700,
              icon: Icons.filter_list,
              onTap: () async {
                try {
                  final res = await _dio.get(
                    "https://jsonplaceholder.typicode.com/comments",
                    queryParameters: {"postId": 1},
                  );
                  _showResult('GET (query)', res);
                } catch (e) {
                  _showError('GET (query)', e);
                }
              },
            ),
            const SizedBox(height: 8),
            _buildRequestButton(
              label: 'GET All Users',
              subtitle: '/users',
              color: Colors.blue.shade400,
              icon: Icons.people,
              onTap: () async {
                try {
                  final res = await _dio.get(
                    "https://jsonplaceholder.typicode.com/users",
                  );
                  _showResult('GET Users', res);
                } catch (e) {
                  _showError('GET Users', e);
                }
              },
            ),
            const SizedBox(height: 20),

            // Section: POST Requests
            _buildSectionHeader('POST Requests', Icons.upload),
            const SizedBox(height: 8),
            _buildRequestButton(
              label: 'POST JSON Body',
              subtitle: '/posts (create new)',
              color: Colors.green,
              icon: Icons.add_circle,
              onTap: () async {
                try {
                  final res = await _dio.post(
                    "https://jsonplaceholder.typicode.com/posts",
                    data: {
                      "title": "Network Inspector Test",
                      "body": "Testing POST request",
                      "userId": 1,
                    },
                  );
                  _showResult('POST', res);
                } catch (e) {
                  _showError('POST', e);
                }
              },
            ),
            const SizedBox(height: 8),
            _buildRequestButton(
              label: 'POST FormData (Multipart)',
              subtitle: 'httpbin.org/post',
              color: Colors.green.shade700,
              icon: Icons.attach_file,
              onTap: () async {
                try {
                  final formData = FormData.fromMap({
                    "name": "connectivity_watcher",
                    "version": "3.0.6",
                    "description": "Testing FormData upload",
                  });
                  final res = await _dio.post(
                    "https://httpbin.org/post",
                    data: formData,
                  );
                  _showResult('POST FormData', res);
                } catch (e) {
                  _showError('POST FormData', e);
                }
              },
            ),
            const SizedBox(height: 20),

            // Section: PUT / PATCH
            _buildSectionHeader('PUT / PATCH Requests', Icons.edit),
            const SizedBox(height: 8),
            _buildRequestButton(
              label: 'PUT Full Update',
              subtitle: '/posts/1',
              color: Colors.orange,
              icon: Icons.sync,
              onTap: () async {
                try {
                  final res = await _dio.put(
                    "https://jsonplaceholder.typicode.com/posts/1",
                    data: {
                      "id": 1,
                      "title": "Updated Title",
                      "body": "Updated Body",
                      "userId": 1,
                    },
                  );
                  _showResult('PUT', res);
                } catch (e) {
                  _showError('PUT', e);
                }
              },
            ),
            const SizedBox(height: 8),
            _buildRequestButton(
              label: 'PATCH Partial Update',
              subtitle: '/posts/1 (title only)',
              color: Colors.orange.shade700,
              icon: Icons.edit_note,
              onTap: () async {
                try {
                  final res = await _dio.patch(
                    "https://jsonplaceholder.typicode.com/posts/1",
                    data: {"title": "Patched Title"},
                  );
                  _showResult('PATCH', res);
                } catch (e) {
                  _showError('PATCH', e);
                }
              },
            ),
            const SizedBox(height: 20),

            // Section: DELETE
            _buildSectionHeader('DELETE Request', Icons.delete_outline),
            const SizedBox(height: 8),
            _buildRequestButton(
              label: 'DELETE Post',
              subtitle: '/posts/1',
              color: Colors.red,
              icon: Icons.delete,
              onTap: () async {
                try {
                  final res = await _dio.delete(
                    "https://jsonplaceholder.typicode.com/posts/1",
                  );
                  _showResult('DELETE', res);
                } catch (e) {
                  _showError('DELETE', e);
                }
              },
            ),
            const SizedBox(height: 20),

            // Section: Error
            _buildSectionHeader('Error Scenarios', Icons.error_outline),
            const SizedBox(height: 8),
            _buildRequestButton(
              label: 'GET 404 Not Found',
              subtitle: '/posts/99999',
              color: Colors.grey.shade700,
              icon: Icons.broken_image,
              onTap: () async {
                try {
                  final res = await _dio.get(
                    "https://jsonplaceholder.typicode.com/posts/99999",
                  );
                  _showResult('GET 404', res);
                } catch (e) {
                  _showError('GET 404', e);
                }
              },
            ),
            const SizedBox(height: 8),
            _buildRequestButton(
              label: 'GET Invalid URL',
              subtitle: 'https://invalid.url.test/',
              color: Colors.grey.shade600,
              icon: Icons.link_off,
              onTap: () async {
                try {
                  final res = await _dio.get("https://invalid.url.test/");
                  _showResult('GET Invalid', res);
                } catch (e) {
                  _showError('GET Invalid', e);
                }
              },
            ),
            const SizedBox(height: 24),

            // View Network Logs
            SizedBox(
              height: 56,
              child: ElevatedButton.icon(
                style: ElevatedButton.styleFrom(
                  backgroundColor: const Color(0xFF1E1E2C),
                  foregroundColor: Colors.white,
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(14),
                  ),
                  elevation: 2,
                ),
                icon: const Icon(Icons.monitor_heart),
                label: const Text(
                  'View Network Logs',
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
                ),
                onPressed: () {
                  ZoConnectivityWatcher().showNetworkLogsScreen(context);
                },
              ),
            ),
            const SizedBox(height: 32),
          ],
        ),
      ),
    );
  }

  Widget _buildSectionHeader(String title, IconData icon) {
    return Row(
      children: [
        Icon(icon, size: 20, color: Colors.grey.shade600),
        const SizedBox(width: 8),
        Text(
          title,
          style: TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.bold,
            color: Colors.grey.shade800,
          ),
        ),
      ],
    );
  }

  Widget _buildRequestButton({
    required String label,
    required String subtitle,
    required Color color,
    required IconData icon,
    required VoidCallback onTap,
  }) {
    return Material(
      color: Colors.white,
      borderRadius: BorderRadius.circular(12),
      elevation: 1,
      child: InkWell(
        onTap: onTap,
        borderRadius: BorderRadius.circular(12),
        child: Container(
          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
          child: Row(
            children: [
              Container(
                width: 40,
                height: 40,
                decoration: BoxDecoration(
                  color: color.withOpacity(0.1),
                  borderRadius: BorderRadius.circular(10),
                ),
                child: Icon(icon, color: color, size: 22),
              ),
              const SizedBox(width: 14),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      label,
                      style: const TextStyle(
                        fontSize: 15,
                        fontWeight: FontWeight.w600,
                      ),
                    ),
                    const SizedBox(height: 2),
                    Text(
                      subtitle,
                      style: TextStyle(
                        fontSize: 12,
                        color: Colors.grey.shade500,
                        fontFamily: 'monospace',
                      ),
                    ),
                  ],
                ),
              ),
              Icon(Icons.chevron_right, color: Colors.grey.shade400),
            ],
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
  }
}
49
likes
150
points
641
downloads
screenshot

Documentation

API reference

Publisher

verified publisherthezerone.com

Weekly Downloads

A Flutter package for fast internet checks with subsecond response times and customizable offline UI.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

dio, flutter

More

Packages that depend on connectivity_watcher