network_watcher 0.0.1
network_watcher: ^0.0.1 copied to clipboard
A robust Flutter plugin for monitoring network connectivity changes with support for WiFi, Mobile, Ethernet, VPN, and Bluetooth connections on Android and iOS.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:network_watcher/network_watcher.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Network Watcher Demo',
theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
home: const NetworkWatcherDemo(),
);
}
}
class NetworkWatcherDemo extends StatefulWidget {
const NetworkWatcherDemo({super.key});
@override
State<NetworkWatcherDemo> createState() => _NetworkWatcherDemoState();
}
class _NetworkWatcherDemoState extends State<NetworkWatcherDemo> {
ConnectivityResult? _currentConnectivity;
final List<ConnectivityEvent> _connectivityHistory = [];
StreamSubscription<ConnectivityResult>? _subscription;
bool _isMonitoring = false;
@override
void initState() {
super.initState();
_checkCurrentConnectivity();
}
@override
void dispose() {
_subscription?.cancel();
super.dispose();
}
Future<void> _checkCurrentConnectivity() async {
try {
final result = await NetworkWatcher.instance.checkConnectivity();
setState(() {
_currentConnectivity = result;
});
} catch (e) {
_showError('Error checking connectivity: $e');
}
}
void _startMonitoring() {
if (_isMonitoring) return;
_subscription = NetworkWatcher.instance.onConnectivityChanged.listen(
(ConnectivityResult result) {
setState(() {
_currentConnectivity = result;
_connectivityHistory.insert(
0,
ConnectivityEvent(timestamp: DateTime.now(), result: result),
);
// Keep only last 20 events
if (_connectivityHistory.length > 20) {
_connectivityHistory.removeLast();
}
});
},
onError: (error) {
_showError('Stream error: $error');
},
);
setState(() {
_isMonitoring = true;
});
}
void _stopMonitoring() {
_subscription?.cancel();
_subscription = null;
setState(() {
_isMonitoring = false;
});
}
void _clearHistory() {
setState(() {
_connectivityHistory.clear();
});
}
void _showError(String message) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message), backgroundColor: Colors.red),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Network Watcher Demo'), elevation: 2),
body: Column(
children: [
// Current Status Card
Card(
margin: const EdgeInsets.all(16),
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
_getConnectionIcon(),
size: 48,
color: _getConnectionColor(),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Current Status',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
_getStatusText(),
style: TextStyle(
fontSize: 16,
color: _getConnectionColor(),
fontWeight: FontWeight.w500,
),
),
],
),
),
],
),
if (_currentConnectivity != null) ...[
const Divider(height: 24),
_buildConnectionDetails(),
],
],
),
),
),
// Control Buttons
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: _checkCurrentConnectivity,
icon: const Icon(Icons.refresh),
label: const Text('Check Now'),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton.icon(
onPressed: _isMonitoring
? _stopMonitoring
: _startMonitoring,
icon: Icon(_isMonitoring ? Icons.stop : Icons.play_arrow),
label: Text(
_isMonitoring ? 'Stop Monitor' : 'Start Monitor',
),
style: ElevatedButton.styleFrom(
backgroundColor: _isMonitoring
? Colors.red
: Colors.green,
foregroundColor: Colors.white,
),
),
),
],
),
),
// History Section
Padding(
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Connection History',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
if (_connectivityHistory.isNotEmpty)
TextButton.icon(
onPressed: _clearHistory,
icon: const Icon(Icons.clear_all, size: 18),
label: const Text('Clear'),
),
],
),
),
// History List
Expanded(
child: _connectivityHistory.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.history, size: 64, color: Colors.grey[400]),
const SizedBox(height: 16),
Text(
'No history yet.\nStart monitoring to see changes.',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
color: Colors.grey[600],
),
),
],
),
)
: ListView.builder(
itemCount: _connectivityHistory.length,
itemBuilder: (context, index) {
final event = _connectivityHistory[index];
return _buildHistoryItem(event);
},
),
),
],
),
);
}
Widget _buildConnectionDetails() {
final connectivity = _currentConnectivity!;
return Wrap(
spacing: 8,
runSpacing: 8,
children: [
_buildDetailChip('Connected', connectivity.isConnected),
if (connectivity.isWifi) _buildDetailChip('WiFi', true),
if (connectivity.isMobile) _buildDetailChip('Mobile', true),
if (connectivity.isEthernet) _buildDetailChip('Ethernet', true),
if (connectivity.isVpn) _buildDetailChip('VPN', true),
if (connectivity.isBluetooth) _buildDetailChip('Bluetooth', true),
],
);
}
Widget _buildDetailChip(String label, bool value) {
return Chip(
label: Text(label),
backgroundColor: value ? Colors.green[100] : Colors.red[100],
side: BorderSide(color: value ? Colors.green : Colors.red, width: 1),
);
}
Widget _buildHistoryItem(ConnectivityEvent event) {
return Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: ListTile(
leading: Icon(
_getConnectionIconForResult(event.result),
color: _getConnectionColorForResult(event.result),
),
title: Text(
event.result.types.map((t) => t.name).join(', '),
style: const TextStyle(fontWeight: FontWeight.w500),
),
subtitle: Text(
_formatTimestamp(event.timestamp),
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
trailing: event.result.isConnected
? const Icon(Icons.check_circle, color: Colors.green, size: 20)
: const Icon(Icons.cancel, color: Colors.red, size: 20),
),
);
}
String _getStatusText() {
if (_currentConnectivity == null) {
return 'Checking...';
}
if (!_currentConnectivity!.isConnected) {
return 'No Connection';
}
return _currentConnectivity!.types
.map((t) => t.name.toUpperCase())
.join(' + ');
}
IconData _getConnectionIcon() {
if (_currentConnectivity == null) {
return Icons.signal_wifi_statusbar_null;
}
return _getConnectionIconForResult(_currentConnectivity!);
}
IconData _getConnectionIconForResult(ConnectivityResult result) {
if (!result.isConnected) {
return Icons.signal_wifi_off;
}
if (result.isWifi) {
return Icons.wifi;
}
if (result.isMobile) {
return Icons.signal_cellular_alt;
}
if (result.isEthernet) {
return Icons.settings_ethernet;
}
if (result.isVpn) {
return Icons.vpn_key;
}
if (result.isBluetooth) {
return Icons.bluetooth;
}
return Icons.device_unknown;
}
Color _getConnectionColor() {
if (_currentConnectivity == null) {
return Colors.grey;
}
return _getConnectionColorForResult(_currentConnectivity!);
}
Color _getConnectionColorForResult(ConnectivityResult result) {
return result.isConnected ? Colors.green : Colors.red;
}
String _formatTimestamp(DateTime timestamp) {
final now = DateTime.now();
final difference = now.difference(timestamp);
if (difference.inSeconds < 60) {
return '${difference.inSeconds}s ago';
} else if (difference.inMinutes < 60) {
return '${difference.inMinutes}m ago';
} else {
return '${timestamp.hour.toString().padLeft(2, '0')}:${timestamp.minute.toString().padLeft(2, '0')}:${timestamp.second.toString().padLeft(2, '0')}';
}
}
}
class ConnectivityEvent {
final DateTime timestamp;
final ConnectivityResult result;
ConnectivityEvent({required this.timestamp, required this.result});
}