added connectivity service

This commit is contained in:
Mehul Ahal 2022-12-20 09:47:07 +01:00
parent 8e54cfffc5
commit 193ae3b0ea
12 changed files with 384 additions and 8 deletions

View file

@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View file

@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

41
ios/Podfile Normal file
View file

@ -0,0 +1,41 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

View file

@ -0,0 +1,9 @@
abstract class ConstDurations {
static const Duration tripleDefaultAnimationDuration = Duration(milliseconds: 1200);
static const Duration doubleDefaultAnimationDuration = Duration(milliseconds: 800);
static const Duration oneAndHalfDefaultAnimationDuration = Duration(milliseconds: 600);
static const Duration defaultAnimationDuration = Duration(milliseconds: 400);
static const Duration halfDefaultAnimationDuration = Duration(milliseconds: 200);
static const Duration quarterDefaultAnimationDuration = Duration(milliseconds: 100);
static const Duration zero = Duration.zero;
}

View file

@ -0,0 +1,133 @@
import 'dart:async';
import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:internet_connection_checker/internet_connection_checker.dart';
import '/locator.dart';
/// Used to observe the current connection type.
class ConnectionService {
ConnectionService({
required Connectivity connectivity,
required InternetConnectionChecker internetConnectionChecker,
bool shouldInitialize = true,
}) : _connectivity = connectivity,
_internetConnectionChecker = internetConnectionChecker {
if (shouldInitialize) initialize();
}
ConnectivityResult? _connectivityResult;
ConnectivityResult? get connectivityResult => _connectivityResult;
final Connectivity _connectivity;
final Map<String, StreamSubscription> _connectivitySubscriptions = {};
final InternetConnectionChecker _internetConnectionChecker;
final Completer _isInitialized = Completer();
Completer<bool>? _hasInternetConnection;
final ValueNotifier<bool> _hasInternetConnectionListenable = ValueNotifier(false);
ValueListenable<bool> get hasInternetConnectionListenable => _hasInternetConnectionListenable;
Future<bool> get hasInternetConnection async {
try {
if (_hasInternetConnection == null) {
_hasInternetConnection = Completer<bool>();
try {
final hasInternetConnection = await _internetConnectionChecker.hasConnection;
_hasInternetConnection!.complete(hasInternetConnection);
return hasInternetConnection;
} catch (error) {
_hasInternetConnection!.complete(false);
return false;
} finally {
_hasInternetConnection = null;
}
} else {
final awaitedHasInternet = await _hasInternetConnection!.future;
return awaitedHasInternet;
}
} on SocketException catch (_, __) {
return false;
}
}
Future<void> initialize() async {
try {
final tag = runtimeType.toString();
if (_connectivitySubscriptions[tag] != null) {
await dispose();
}
_connectivitySubscriptions[tag] = _connectivity.onConnectivityChanged.listen(
_onConnectivityChanged,
cancelOnError: false,
onError: (error, stack) {},
);
await _onConnectivityChanged(await _connectivity.checkConnectivity());
_internetConnectionChecker.onStatusChange.listen((final event) {
switch (event) {
case InternetConnectionStatus.connected:
_hasInternetConnectionListenable.value = true;
break;
case InternetConnectionStatus.disconnected:
_hasInternetConnectionListenable.value = false;
break;
}
});
_isInitialized.complete();
} catch (error, stackTrace) {}
}
Future<void> dispose() async {
for (final subscription in _connectivitySubscriptions.values) {
await subscription.cancel();
}
_connectivitySubscriptions.clear();
_connectivityResult = null;
}
Future<void> addListener({
required String tag,
required Future<void> Function({
required ConnectivityResult connectivityResult,
required bool hasInternet,
})
listener,
bool tryCallListenerOnAdd = true,
}) async {
try {
if (_connectivityResult != null && tryCallListenerOnAdd)
await listener(
connectivityResult: _connectivityResult!, hasInternet: await hasInternetConnection);
_connectivitySubscriptions[tag] =
_connectivity.onConnectivityChanged.listen((connectivityResult) async {
await listener(
connectivityResult: connectivityResult, hasInternet: await hasInternetConnection);
});
} catch (error, stackTrace) {}
}
Future<void> removeListener({required String tag}) async {
try {
final subscription = _connectivitySubscriptions[tag];
if (subscription != null) {
await subscription.cancel();
} else {}
} catch (error, stackTrace) {}
}
Future<void> _onConnectivityChanged(ConnectivityResult connectivityResult) async {
try {
_connectivityResult = connectivityResult;
} catch (error, stackTrace) {}
}
static ConnectionService get locate => Locator.locate();
}
class NoInternetException implements Exception {}

View file

@ -0,0 +1,58 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:mc_gallery/features/core/services/logging_service.dart';
import 'package:mc_gallery/locator.dart';
class OverlayService {
const OverlayService({
required LoggingService loggingService,
}) : _loggingService = loggingService;
final LoggingService _loggingService;
final Map<int, AnimationController> _animationControllerMap = const {};
final Map<int, OverlayEntry> _overlayEntryMap = const {};
Future<void> playOverlayEntry({
required BuildContext context,
required AnimationController animationController,
required OverlayEntry overlayEntry,
}) async {
try {
_animationControllerMap[animationController.hashCode] = animationController;
_overlayEntryMap[overlayEntry.hashCode] = overlayEntry;
Overlay.of(
context,
rootOverlay: true,
)!
.insert(overlayEntry);
await animationController.forward();
if (overlayEntry.mounted) overlayEntry.remove();
_overlayEntryMap.remove(overlayEntry.hashCode);
animationController.dispose();
_animationControllerMap.remove(animationController.hashCode);
} catch (error, stackTrace) {
_loggingService.handle(error, stackTrace);
}
}
void dispose() {
for (final overlayEntry in _overlayEntryMap.values) {
if (overlayEntry.mounted) {
overlayEntry.remove();
}
}
_overlayEntryMap.clear();
for (final animationController in _animationControllerMap.values) {
if (animationController.isAnimating) {
animationController.dispose();
}
}
_animationControllerMap.clear();
}
static OverlayService get locate => Locator.locate();
}

View file

@ -0,0 +1,71 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import '../data/constants/const_durations.dart';
import '../services/connection_service.dart';
class McgScaffold extends StatelessWidget {
const McgScaffold({
this.appBar,
this.bodyBuilderCompleter,
this.body,
this.waitingWidget,
this.forceInternetCheck = false,
super.key,
});
final AppBar? appBar;
/// Awaits an external signal (complete) before building the body.
final Completer? bodyBuilderCompleter;
final Widget? body;
/// Custom widget to be used while awaiting [bodyBuilderCompleter].
///
/// Defaults to using [PlatformCircularProgressIndicator].
final Widget? waitingWidget;
/// Enabling listing to [ConnectionState], showing a small text at the top, when connectivity is lost.
final bool forceInternetCheck;
@override
Widget build(BuildContext context) {
final Widget loginOptionsBody = bodyBuilderCompleter != null
? FutureBuilder(
future: bodyBuilderCompleter!.future,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
case ConnectionState.active:
return Center(child: waitingWidget ?? const CircularProgressIndicator());
case ConnectionState.done:
return body ?? const SizedBox.shrink();
}
},
)
: body ?? const SizedBox.shrink();
return Scaffold(
appBar: appBar,
body: forceInternetCheck
? SingleChildScrollView(
child: ValueListenableBuilder<bool>(
valueListenable: ConnectionService.locate.hasInternetConnectionListenable,
builder: (context, hasInternetConnection, _) => Column(
children: [
AnimatedSwitcher(
duration: ConstDurations.defaultAnimationDuration,
child: !hasInternetConnection ? Text('No internet') : const SizedBox.shrink(),
),
loginOptionsBody,
],
),
),
)
: loginOptionsBody,
);
}
}

View file

@ -1,8 +1,13 @@
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:get_it/get_it.dart';
import 'package:internet_connection_checker/internet_connection_checker.dart';
import 'package:mc_gallery/features/core/abstracts/router/app_router.dart';
import 'package:mc_gallery/features/core/services/logging_service.dart';
import 'package:mc_gallery/features/core/services/navigation_service.dart';
import 'features/core/services/connection_service.dart';
import 'features/core/services/overlay_service.dart';
GetIt get locate => Locator.instance();
class Locator {
@ -14,8 +19,6 @@ class Locator {
_registerAPIs();
_registerViewModels();
_registerLazySingletons();
_registerFactories();
await _registerServices(locator);
@ -27,10 +30,6 @@ class Locator {
static void _registerViewModels() {}
static void _registerLazySingletons() {}
static void _registerFactories() {}
static _registerServices(GetIt it) {
it.registerLazySingleton(
() => NavigationService(
@ -40,6 +39,18 @@ class Locator {
it.registerFactory(
() => LoggingService(),
);
it.registerLazySingleton(
() => ConnectionService(
connectivity: Connectivity(),
internetConnectionChecker: InternetConnectionChecker(),
),
);
it.registerLazySingleton(
() => OverlayService(
loggingService: LoggingService.locate,
),
dispose: (param) => param.dispose(),
);
}
static _registerRepos(GetIt locator) {}

View file

@ -71,6 +71,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.16.0"
connectivity_plus:
dependency: "direct main"
description:
name: connectivity_plus
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.3"
convert:
dependency: transitive
description:
@ -99,6 +113,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.4"
dbus:
dependency: transitive
description:
name: dbus
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.8"
dio:
dependency: "direct main"
description:
@ -113,6 +134,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
file:
dependency: transitive
description:
@ -169,7 +197,7 @@ packages:
name: go_router
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.4"
version: "6.0.0"
http:
dependency: transitive
description:
@ -184,6 +212,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.2"
internet_connection_checker:
dependency: "direct main"
description:
name: internet_connection_checker
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0+1"
intl:
dependency: transitive
description:
@ -240,6 +275,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
nm:
dependency: transitive
description:
name: nm
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
package_config:
dependency: transitive
description:
@ -275,6 +317,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.0"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
pointycastle:
dependency: transitive
description:

View file

@ -14,7 +14,7 @@ dependencies:
cupertino_icons: ^1.0.5
# Routing
go_router: ^5.2.4
go_router: ^6.0.0
# Service locator
get_it: ^7.2.0
@ -24,6 +24,8 @@ dependencies:
# Util backend
intl_utils: ^2.8.1
connectivity_plus: ^3.0.2
internet_connection_checker: ^1.0.0+1
# Logging
talker: ^2.1.0+1