added connectivity service
This commit is contained in:
parent
8e54cfffc5
commit
193ae3b0ea
12 changed files with 384 additions and 8 deletions
|
@ -1 +1,2 @@
|
|||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
|
41
ios/Podfile
Normal file
41
ios/Podfile
Normal 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
|
9
lib/features/core/data/constants/const_durations.dart
Normal file
9
lib/features/core/data/constants/const_durations.dart
Normal 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;
|
||||
}
|
|
@ -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 {}
|
58
lib/features/core/services/overlay_service.dart
Normal file
58
lib/features/core/services/overlay_service.dart
Normal 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();
|
||||
}
|
71
lib/features/core/widgets/mcg_scaffold.dart
Normal file
71
lib/features/core/widgets/mcg_scaffold.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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) {}
|
||||
|
|
51
pubspec.lock
51
pubspec.lock
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue