diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7acc845..cf60077 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,12 +11,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install environment uses: subosito/flutter-action@v2.12.0 with: - flutter-version: 3.10.6 + flutter-version: 3.16.9 - name: Get dependencies run: flutter pub get diff --git a/CHANGELOG.md b/CHANGELOG.md index 333e497..061a016 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.1 +- Support new connectivity plus multiple connection type +- Fix crash on iphone. + ## 2.2.0 - Removed InternetAddress constraint on IO platforms (when creating a socket connection check) diff --git a/README.md b/README.md index 174ebef..27d2095 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A plugin that wraps [connectivity_plus](https://pub.dev/packages/connectivity_pl - [ConnectionChecker init](#connectionchecker-init) - [connectionStream](#get-connectionstream) - [isConnected](#get-isconnected) - - [connectionType](#get-connectiontype) + - [connectionTypes](#get-connectiontypes) - [untilConnects](#untilconnects) ## Motivation @@ -127,7 +127,7 @@ if (hasInternetConnection) { } ``` -### get connectionType +### get connectionTypes Returns the current connection type. @@ -136,8 +136,8 @@ Example: ```dart final connecteo = ConnectionChecker(); -final type = await connecteo.connectionType; -if (type == ConnectionType.mobile) { +final types = await connecteo.connectionTypes; +if (types.contains(ConnectionType.mobile)) { // Handle the logic when the application uses cellular data } ``` diff --git a/example/main.dart b/example/main.dart index aae6926..9f83c6b 100644 --- a/example/main.dart +++ b/example/main.dart @@ -34,7 +34,7 @@ class _ConnecteoExamplePageState extends State { late StreamSubscription _streamSubscription; Color? _backgroundColor; - ConnectionType? _connectionType; + List? _connectionTypes; @override void initState() { @@ -67,14 +67,14 @@ class _ConnecteoExamplePageState extends State { children: [ OutlinedButton( onPressed: () async { - final type = await _connecteo.connectionType; + final types = await _connecteo.connectionTypes; setState(() { - _connectionType = type; + _connectionTypes = types; }); }, child: const Text('Check connection type'), ), - Text('Connection type value: $_connectionType'), + Text('Connection type value: $_connectionTypes'), ], ), ) diff --git a/lib/src/connection_checker.dart b/lib/src/connection_checker.dart index 30cbe91..7966086 100644 --- a/lib/src/connection_checker.dart +++ b/lib/src/connection_checker.dart @@ -103,7 +103,8 @@ class ConnectionChecker { int? failureAttempts, Connectivity? connectivity, HostReachabilityChecker? hostReachabilityChecker, - Mapper? connectionTypeMapper, + Mapper, List>? + connectionTypeMapper, }) : _checkHostReachability = checkHostReachability, _hostReachabilityTimeout = hostReachabilityTimeout, _baseUrlLookupAddress = baseUrlLookupAddress, @@ -124,7 +125,8 @@ class ConnectionChecker { factory ConnectionChecker.test({ required Connectivity connectivity, required HostReachabilityChecker hostReachabilityChecker, - required Mapper connectionTypeMapper, + required Mapper, List> + connectionTypeMapper, bool checkHostReachability = true, List? checkConnectionEntriesNative, List? checkConnectionEntriesWeb, @@ -148,7 +150,8 @@ class ConnectionChecker { } final Connectivity _connectivity; - final Mapper _connectionTypeMapper; + final Mapper, List> + _connectionTypeMapper; final List? _checkConnectionEntries; final Duration? _hostReachabilityTimeout; @@ -186,7 +189,7 @@ class ConnectionChecker { Stream.periodic(_requestInterval), ], // ignore: cast_nullable_to_non_nullable - (events) => events.first as ConnectionType, + (events) => events.first as List, ) .flatMap(_isConnectionTypeReachableStream) .scan(_accumulateFailures, 0) @@ -204,12 +207,12 @@ class ConnectionChecker { /// it is more secure to listen for [connectionStream]. Future get isConnected async { final result = await _connectivity.checkConnectivity(); - final isConnectionTypeOnline = - _connectionTypeMapper.call(result).onlineType; + final isConnectionTypesContainsOnline = + _connectionTypeMapper.call(result).containsOnline; final reachability = await Future.wait([_hostReachable, _baseUrlReachable]); - final isHostReachable = [isConnectionTypeOnline, ...reachability] - .every((reachable) => reachable == true); + final isHostReachable = [isConnectionTypesContainsOnline, ...reachability] + .every((reachable) => reachable); return isHostReachable; } @@ -222,7 +225,7 @@ class ConnectionChecker { /// [ConnectionType.vpn] does not guarantee you have got internet connection. /// However, it may be helpful when you want to implement specific logic for /// cellular data connection for example. - Future get connectionType async { + Future> get connectionTypes async { final connectivityResult = await _connectivity.checkConnectivity(); return _connectionTypeMapper.call(connectivityResult); } @@ -233,13 +236,13 @@ class ConnectionChecker { } Stream _isConnectionTypeReachableStream( - ConnectionType connectionType, + List connectionTypeList, ) async* { final isHostReachable = await _hostReachable; if (!isHostReachable) { yield false; } else { - if (connectionType.onlineType) { + if (connectionTypeList.containsOnline) { yield true; } else { yield false; diff --git a/lib/src/connection_type_mapper.dart b/lib/src/connection_type_mapper.dart index f8c44f3..f920a7f 100644 --- a/lib/src/connection_type_mapper.dart +++ b/lib/src/connection_type_mapper.dart @@ -6,10 +6,14 @@ abstract class Mapper { } class ConnectionTypeMapper - implements Mapper { + implements Mapper, List> { @override - ConnectionType call(ConnectivityResult input) { - switch (input) { + List call(List input) { + return input.map(_map).toList(); + } + + ConnectionType _map(ConnectivityResult connectivityResult) { + switch (connectivityResult) { case ConnectivityResult.bluetooth: return ConnectionType.bluetooth; case ConnectivityResult.ethernet: @@ -27,3 +31,7 @@ class ConnectionTypeMapper } } } + +extension ListConnectionTypeExt on List { + bool get containsOnline => isNotEmpty && any((value) => value.onlineType); +} diff --git a/lib/src/host_reachability_checker.dart b/lib/src/host_reachability_checker.dart index 52d81e2..584019e 100644 --- a/lib/src/host_reachability_checker.dart +++ b/lib/src/host_reachability_checker.dart @@ -68,7 +68,7 @@ class DefaultHostReachabilityChecker implements HostReachabilityChecker { ), ); - return connectionResults.any((result) => result == true); + return connectionResults.any((result) => result); } Future _canIoReachHost({ @@ -117,7 +117,7 @@ class WebHostReachabilityChecker implements HostReachabilityChecker { ), ); - return connectionResults.any((result) => result == true); + return connectionResults.any((result) => result); } Future _canWebReachHost({ diff --git a/pubspec.yaml b/pubspec.yaml index 0426aee..4452b64 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: connecteo description: A plugin that wraps connectivity_plus and adds some additional data connection checks. -version: 2.2.0 +version: 2.2.1 repository: https://github.com/Iteo/connecteo issue_tracker: https://github.com/Iteo/connecteo/issues homepage: https://github.com/Iteo/connecteo @@ -11,10 +11,10 @@ environment: flutter: ">=3.10.0" dependencies: - connectivity_plus: ^5.0.2 + connectivity_plus: ^6.0.1 flutter: sdk: flutter - http: ^1.1.0 + http: ^1.1.2 rxdart: ^0.27.7 dev_dependencies: diff --git a/test/connection_checker_test.dart b/test/connection_checker_test.dart index bef8d02..d30596f 100644 --- a/test/connection_checker_test.dart +++ b/test/connection_checker_test.dart @@ -54,9 +54,9 @@ void main() { ), ).thenAnswer((_) => Future.value(true)); when(() => connectivity.checkConnectivity()) - .thenAnswer((_) => Future.value(ConnectivityResult.wifi)); + .thenAnswer((_) => Future.value([ConnectivityResult.wifi])); when(() => connectionTypeMapper.call(any())) - .thenAnswer((_) => ConnectionType.wifi); + .thenAnswer((_) => [ConnectionType.wifi]); final result = await connectionChecker.isConnected; @@ -77,9 +77,9 @@ void main() { ), ).thenAnswer((_) => Future.value(true)); when(() => connectivity.checkConnectivity()) - .thenAnswer((_) => Future.value(ConnectivityResult.wifi)); + .thenAnswer((_) => Future.value([ConnectivityResult.wifi])); when(() => connectionTypeMapper.call(any())) - .thenAnswer((_) => ConnectionType.wifi); + .thenAnswer((_) => [ConnectionType.wifi]); final result = await connectionChecker.isConnected; @@ -100,9 +100,9 @@ void main() { ), ).thenAnswer((_) => Future.value(true)); when(() => connectivity.checkConnectivity()) - .thenAnswer((_) => Future.value(ConnectivityResult.none)); + .thenAnswer((_) => Future.value([ConnectivityResult.none])); when(() => connectionTypeMapper.call(any())) - .thenAnswer((_) => ConnectionType.none); + .thenAnswer((_) => [ConnectionType.none]); final result = await connectionChecker.isConnected; @@ -125,9 +125,9 @@ void main() { when(() => hostReachabilityChecker.canReachAnyHost()) .thenAnswer((_) => Future.value(true)); when(() => connectivity.checkConnectivity()) - .thenAnswer((_) => Future.value(ConnectivityResult.wifi)); + .thenAnswer((_) => Future.value([ConnectivityResult.wifi])); when(() => connectionTypeMapper.call(any())) - .thenAnswer((_) => ConnectionType.wifi); + .thenAnswer((_) => [ConnectionType.wifi]); final result = await connectionChecker.isConnected; @@ -140,19 +140,19 @@ void main() { }); }); - group('connectionType', () { - test('returns current connectionType', () async { - const expected = ConnectionType.wifi; + group('connectionTypes', () { + test('returns current connectionTypes', () async { + const expected = [ConnectionType.wifi]; when(() => connectivity.checkConnectivity()) - .thenAnswer((_) => Future.value(ConnectivityResult.wifi)); + .thenAnswer((_) => Future.value([ConnectivityResult.wifi])); when(() => connectionTypeMapper.call(any())).thenAnswer((_) => expected); - final result = await connectionChecker.connectionType; + final result = await connectionChecker.connectionTypes; expect(result, expected); verify(() => connectivity.checkConnectivity()).called(1); - verify(() => connectionTypeMapper.call(ConnectivityResult.wifi)) + verify(() => connectionTypeMapper.call([ConnectivityResult.wifi])) .called(1); }); }); @@ -160,7 +160,7 @@ void main() { group('untilConnects', () { test('returns the Future once, after the connection is renewed', () async { final completer = Completer(); - final controller = StreamController(); + final controller = StreamController>(); when(() => hostReachabilityChecker.canReachAnyHost()) .thenAnswer((_) => Future.value(true)); when( @@ -170,19 +170,19 @@ void main() { ).thenAnswer((_) => Future.value(true)); when(() => connectivity.onConnectivityChanged) .thenAnswer((_) => controller.stream); - when(() => connectionTypeMapper.call(ConnectivityResult.wifi)) - .thenAnswer((_) => ConnectionType.wifi); - when(() => connectionTypeMapper.call(ConnectivityResult.none)) - .thenAnswer((_) => ConnectionType.none); + when(() => connectionTypeMapper.call([ConnectivityResult.wifi])) + .thenAnswer((_) => [ConnectionType.wifi]); + when(() => connectionTypeMapper.call([ConnectivityResult.none])) + .thenAnswer((_) => [ConnectionType.none]); when(() => connectivity.checkConnectivity()) - .thenAnswer((_) => Future.value(ConnectivityResult.wifi)); + .thenAnswer((_) => Future.value([ConnectivityResult.wifi])); connectionChecker.untilConnects().whenComplete(() { completer.complete(true); }); - controller.add(ConnectivityResult.none); + controller.add([ConnectivityResult.none]); await Future.delayed(requestInterval); - controller.add(ConnectivityResult.wifi); + controller.add([ConnectivityResult.wifi]); expect(completer.future, completion(true)); @@ -194,7 +194,7 @@ void main() { test( 'returns [true] from Stream while hosts are reachable, base url is reachable and connection type is online', () async { - final controller = StreamController(); + final controller = StreamController>(); when(() => hostReachabilityChecker.canReachAnyHost()) .thenAnswer((_) => Future.value(true)); when( @@ -205,9 +205,9 @@ void main() { when(() => connectivity.onConnectivityChanged) .thenAnswer((_) => controller.stream); when(() => connectionTypeMapper.call(any())) - .thenAnswer((_) => ConnectionType.wifi); + .thenAnswer((_) => [ConnectionType.wifi]); when(() => connectivity.checkConnectivity()) - .thenAnswer((_) => Future.value(ConnectivityResult.wifi)); + .thenAnswer((_) => Future.value([ConnectivityResult.wifi])); expectLater( connectionChecker.connectionStream, @@ -219,7 +219,7 @@ void main() { .called(1); }, ); - controller.add(ConnectivityResult.wifi); + controller.add([ConnectivityResult.wifi]); controller.close(); }); @@ -227,7 +227,7 @@ void main() { test( 'returns [true, false] from Stream while hosts are reachable, base url is reachable but connection dropped', () async { - final controller = StreamController(); + final controller = StreamController>(); when(() => hostReachabilityChecker.canReachAnyHost()) .thenAnswer((_) => Future.value(true)); when( @@ -237,12 +237,12 @@ void main() { ).thenAnswer((_) => Future.value(true)); when(() => connectivity.onConnectivityChanged) .thenAnswer((_) => controller.stream); - when(() => connectionTypeMapper.call(ConnectivityResult.wifi)) - .thenAnswer((_) => ConnectionType.wifi); - when(() => connectionTypeMapper.call(ConnectivityResult.none)) - .thenAnswer((_) => ConnectionType.none); + when(() => connectionTypeMapper.call([ConnectivityResult.wifi])) + .thenAnswer((_) => [ConnectionType.wifi]); + when(() => connectionTypeMapper.call([ConnectivityResult.none])) + .thenAnswer((_) => [ConnectionType.none]); when(() => connectivity.checkConnectivity()) - .thenAnswer((_) => Future.value(ConnectivityResult.wifi)); + .thenAnswer((_) => Future.value([ConnectivityResult.wifi])); expectLater( connectionChecker.connectionStream, @@ -253,9 +253,9 @@ void main() { verify(() => hostReachabilityChecker.hostLookup(baseUrl: baseUrl)) .called(1); }); - controller.add(ConnectivityResult.wifi); + controller.add([ConnectivityResult.wifi]); await Future.delayed(requestInterval); - controller.add(ConnectivityResult.none); + controller.add([ConnectivityResult.none]); controller.close(); }); @@ -263,7 +263,7 @@ void main() { test( 'returns [false] from Stream while hosts are not reachable although connection type is online', () async { - final controller = StreamController.broadcast(); + final controller = StreamController>.broadcast(); when(() => hostReachabilityChecker.canReachAnyHost()) .thenAnswer((_) => Future.value(false)); when( @@ -273,10 +273,10 @@ void main() { ).thenAnswer((_) => Future.value(false)); when(() => connectivity.onConnectivityChanged) .thenAnswer((_) => controller.stream); - when(() => connectionTypeMapper.call(ConnectivityResult.wifi)) - .thenAnswer((_) => ConnectionType.wifi); + when(() => connectionTypeMapper.call([ConnectivityResult.wifi])) + .thenAnswer((_) => [ConnectionType.wifi]); when(() => connectivity.checkConnectivity()) - .thenAnswer((_) => Future.value(ConnectivityResult.wifi)); + .thenAnswer((_) => Future.value([ConnectivityResult.wifi])); expectLater( connectionChecker.connectionStream, @@ -290,7 +290,7 @@ void main() { ); }, ); - controller.add(ConnectivityResult.wifi); + controller.add([ConnectivityResult.wifi]); controller.close(); });