Skip to content

Commit a21654f

Browse files
author
Michael Adeyemo
committed
observe internet connectivity
0 parents  commit a21654f

18 files changed

+471
-0
lines changed

.gitignore

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.buildlog/
9+
.history
10+
.svn/
11+
migrate_working_dir/
12+
13+
# IntelliJ related
14+
*.iml
15+
*.ipr
16+
*.iws
17+
.idea/
18+
19+
# The .vscode folder contains launch configuration and tasks you configure in
20+
# VS Code which you may wish to be included in version control, so this line
21+
# is commented out by default.
22+
#.vscode/
23+
24+
# Flutter/Dart/Pub related
25+
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
26+
/pubspec.lock
27+
**/doc/api/
28+
.dart_tool/
29+
.packages
30+
build/

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 0.0.1
2+
3+
* TODO: Describe initial release.

LICENSE

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TODO: Add your license here.

README.md

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!--
2+
This README describes the package. If you publish this package to pub.dev,
3+
this README's contents appear on the landing page for your package.
4+
5+
For information about how to write a good package README, see the guide for
6+
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
7+
8+
For general information about developing packages, see the Dart guide for
9+
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
10+
and the Flutter guide for
11+
[developing packages and plugins](https://flutter.dev/developing-packages).
12+
-->
13+
14+
TODO: Put a short description of the package here that helps potential users
15+
know whether this package might be useful for them.
16+
17+
## Features
18+
19+
TODO: List what your package can do. Maybe include images, gifs, or videos.
20+
21+
## Getting started
22+
23+
TODO: List prerequisites and provide or point to information on how to
24+
start using the package.
25+
26+
## Usage
27+
28+
TODO: Include short and useful examples for package users. Add longer examples
29+
to `/example` folder.
30+
31+
```dart
32+
const like = 'sample';
33+
```
34+
35+
## Additional information
36+
37+
TODO: Tell users more about the package: where to find more information, how to
38+
contribute to the package, how to file issues, what response they can expect
39+
from the package authors, and more.

analysis_options.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
include: package:flutter_lints/flutter.yaml
2+
3+
# Additional information about this file can be found at
4+
# https://dart.dev/guides/language/analysis-options
5+
6+
linter:
7+
rules:
8+
unnecessary_string_interpolations: false
9+
unnecessary_statements: true
10+
library_private_types_in_public_api: false

lib/constants.dart

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import 'models/internet_address.dart';
2+
3+
const kDefaultInternetAddresses = [
4+
InternetAddress(
5+
host: _googleHost, port: _defaultPort, timeOut: _defaultTimeOut),
6+
InternetAddress(
7+
host: _cloudFlareHost, port: _defaultPort, timeOut: _defaultTimeOut),
8+
];
9+
10+
const kDefaultInterval = Duration(seconds: 5);
11+
const kDefaultInitialDuration = Duration(seconds: 0);
12+
const _googleHost = '8.8.8.8';
13+
const _cloudFlareHost = '1.1.1.1';
14+
const _defaultPort = 53;
15+
const _defaultTimeOut = Duration(seconds: 3);

lib/internet_connectivity.dart

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import 'dart:async';
2+
3+
import 'constants.dart';
4+
import 'internet_observing_settings/internet_observing_strategy.dart';
5+
6+
class InternetConnectivity {
7+
late StreamController<bool> _internetAccessCheckController;
8+
late final InternetObservingStrategy _internetObservingStrategy;
9+
bool? _lastInternetAccessCheck;
10+
Timer? _intervalTimer, disposeTimer;
11+
bool _streamIsClosed = false;
12+
13+
InternetConnectivity({InternetObservingStrategy? internetObservingStrategy}) {
14+
_internetObservingStrategy =
15+
internetObservingStrategy ?? DefaultObServingStrategy();
16+
_internetAccessCheckController = StreamController<bool>.broadcast(
17+
onCancel: _onCancelStream, onListen: _emitInitialInternetAccess);
18+
}
19+
20+
Future<bool> get hasInternetAccess =>
21+
_internetObservingStrategy.hasInternetAccess;
22+
23+
Stream<bool> get observeInternetAccess =>
24+
_internetAccessCheckController.stream;
25+
26+
Future<void> _emitInternetAccess() async {
27+
if (_streamIsClosed) return;
28+
_intervalTimer?.cancel();
29+
final isConnected = await hasInternetAccess;
30+
_addToStream(isConnected);
31+
_intervalTimer =
32+
Timer(_internetObservingStrategy.interval, _emitInternetAccess);
33+
}
34+
35+
Future<void> _emitInitialInternetAccess() async {
36+
_disposeAfterDuration();
37+
final isConnected = await Future.delayed(
38+
_internetObservingStrategy.initialDuration ?? kDefaultInitialDuration,
39+
() => hasInternetAccess);
40+
_addToStream(isConnected);
41+
_emitInternetAccess();
42+
}
43+
44+
void _addToStream(bool value) {
45+
if (!_hasListener) return;
46+
if (value == _lastInternetAccessCheck) return;
47+
_lastInternetAccessCheck = value;
48+
_internetAccessCheckController.add(value);
49+
if (value && _internetObservingStrategy.disposeOnFirstConnected) {
50+
_internetAccessCheckController.close();
51+
}
52+
if (!value && _internetObservingStrategy.disposeOnFirstDisconnected) {
53+
_internetAccessCheckController.close();
54+
}
55+
}
56+
57+
void _onCancelStream() {
58+
_intervalTimer?.cancel();
59+
disposeTimer?.cancel();
60+
_lastInternetAccessCheck = null;
61+
_streamIsClosed = true;
62+
}
63+
64+
void _disposeAfterDuration() {
65+
if (_internetObservingStrategy.duration != null) {
66+
if (!_hasListener) return;
67+
disposeTimer = Timer(_internetObservingStrategy.duration!, () {
68+
_internetAccessCheckController.close();
69+
});
70+
}
71+
}
72+
73+
bool get _hasListener => _internetAccessCheckController.hasListener;
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import '../constants.dart';
2+
import 'socket_connection_strategy.dart';
3+
4+
class DefaultObServingStrategy extends SocketConnectionStrategy {
5+
DefaultObServingStrategy(
6+
{super.timeOut,
7+
super.interval = kDefaultInterval,
8+
super.initialDuration = kDefaultInitialDuration,
9+
super.internetAddresses = kDefaultInternetAddresses});
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import '../constants.dart';
2+
import 'socket_connection_strategy.dart';
3+
4+
class DisposeAfterDurationStrategy extends SocketConnectionStrategy {
5+
@override
6+
final Duration duration;
7+
8+
DisposeAfterDurationStrategy(
9+
{required this.duration,
10+
super.timeOut,
11+
super.interval = kDefaultInterval,
12+
super.initialDuration = kDefaultInitialDuration,
13+
super.internetAddresses = kDefaultInternetAddresses});
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import '../constants.dart';
2+
import 'socket_connection_strategy.dart';
3+
4+
class DisposeOnFirstConnectedStrategy extends SocketConnectionStrategy {
5+
DisposeOnFirstConnectedStrategy(
6+
{super.timeOut,
7+
super.interval = kDefaultInterval,
8+
super.initialDuration = kDefaultInitialDuration,
9+
super.internetAddresses = kDefaultInternetAddresses});
10+
11+
@override
12+
bool get disposeOnFirstConnected => true;
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import '../constants.dart';
2+
import 'socket_connection_strategy.dart';
3+
4+
class DisposeOnFirstDisconnectedStrategy extends SocketConnectionStrategy {
5+
DisposeOnFirstDisconnectedStrategy(
6+
{super.timeOut,
7+
super.interval = kDefaultInterval,
8+
super.initialDuration = kDefaultInitialDuration,
9+
super.internetAddresses = kDefaultInternetAddresses});
10+
11+
@override
12+
bool get disposeOnFirstDisconnected => true;
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export 'default_observing_strategy.dart';
2+
export 'dispose_after_duration.dart';
3+
export 'dispose_on_first_connected.dart';
4+
export 'dispose_on_first_disconnected.dart';
5+
export 'socket_connection_strategy.dart';
6+
7+
abstract class InternetObservingStrategy {
8+
9+
Duration? get initialDuration;
10+
11+
Duration get interval;
12+
13+
Duration? get duration => null;
14+
15+
Future<bool> get hasInternetAccess;
16+
17+
bool get disposeOnFirstConnected => false;
18+
19+
bool get disposeOnFirstDisconnected => false;
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import 'dart:async';
2+
import 'dart:io';
3+
4+
import '../models/internet_address.dart';
5+
import 'internet_observing_strategy.dart';
6+
7+
abstract class SocketConnectionStrategy extends InternetObservingStrategy {
8+
@override
9+
final Duration? initialDuration;
10+
11+
@override
12+
final Duration interval;
13+
14+
final Duration? timeOut;
15+
16+
List<InternetAddress> internetAddresses;
17+
18+
SocketConnectionStrategy(
19+
{this.initialDuration,
20+
required this.interval,
21+
this.timeOut,
22+
required this.internetAddresses}) {
23+
if (timeOut != null) {
24+
internetAddresses = internetAddresses
25+
.map((e) => e.copyWith(timeOut: timeOut))
26+
.toList(growable: false);
27+
}
28+
}
29+
30+
@override
31+
Future<bool> get hasInternetAccess async {
32+
final futures = internetAddresses
33+
.map((internetAddress) => _hasInternet(internetAddress));
34+
final stream = Stream.fromFutures(futures);
35+
return stream.any((element) => element);
36+
}
37+
38+
Future<bool> _hasInternet(InternetAddress internetAddress) async {
39+
Socket? sock;
40+
try {
41+
sock = await Socket.connect(
42+
internetAddress.host,
43+
internetAddress.port,
44+
timeout: internetAddress.timeOut,
45+
)
46+
..destroy();
47+
return true;
48+
} catch (_) {
49+
sock?.destroy();
50+
return false;
51+
}
52+
}
53+
}

lib/models/internet_address.dart

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class InternetAddress {
2+
final String host;
3+
final int port;
4+
final Duration timeOut;
5+
6+
const InternetAddress(
7+
{required this.host, required this.port, required this.timeOut});
8+
9+
InternetAddress copyWith(
10+
{String? host, int? port, Duration? timeOut}) =>
11+
InternetAddress(
12+
host: host ?? this.host,
13+
port: port ?? this.port,
14+
timeOut: timeOut ?? this.timeOut);
15+
}
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
library observe_internet_connectivity;
2+
3+
export 'constants.dart' show kDefaultInternetAddresses;
4+
export 'internet_connectivity.dart';
5+
export 'internet_observing_settings/internet_observing_strategy.dart';
6+
export 'models/internet_address.dart';
7+
export 'widgets/internet_connectivity_builder.dart';
8+
export 'widgets/internet_connectivity_listener.dart';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import 'package:flutter/material.dart';
2+
3+
import '../internet_connectivity.dart';
4+
5+
6+
7+
8+
typedef InternetAccessBuilder = Widget Function(
9+
BuildContext context, bool hasInternetAccess, Widget? child);
10+
11+
// ignore: must_be_immutable
12+
class InternetConnectivityBuilder extends StatelessWidget {
13+
InternetConnectivityBuilder({
14+
Key? key,
15+
this.child,
16+
required this.connectivityBuilder,
17+
InternetConnectivity? internetConnectivity,
18+
this.initialData = false,
19+
}) : super(key: key) {
20+
_internetConnectivity = internetConnectivity ?? InternetConnectivity();
21+
}
22+
23+
final Widget? child;
24+
InternetConnectivity? _internetConnectivity;
25+
final InternetAccessBuilder connectivityBuilder;
26+
final bool initialData;
27+
28+
@override
29+
Widget build(BuildContext context) {
30+
return StreamBuilder<bool>(
31+
initialData: initialData,
32+
stream: _internetConnectivity!.observeInternetAccess,
33+
builder: (context, snapshot) {
34+
return connectivityBuilder.call(context, snapshot.data!, child);
35+
},
36+
);
37+
}
38+
}

0 commit comments

Comments
 (0)