Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Free disk space
uses: endersonmenezes/free-disk-space@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `.dockerignore` for optimized build context
- GitHub Actions workflow: code analysis, Android (Docker + DockerHub), iOS (macOS runner)
- Automated quality checks, artifact uploads, and build summaries
- Added `NetworkService` for monitoring network availability with `connectivity_plus` integration and unit tests.

### Changed

Expand Down
2 changes: 2 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:label="flutter_starter_template"
android:name="${applicationName}"
Expand Down
22 changes: 14 additions & 8 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1194,10 +1194,12 @@ PODS:
- BoringSSL-GRPC/Implementation (0.0.37):
- BoringSSL-GRPC/Interface (= 0.0.37)
- BoringSSL-GRPC/Interface (0.0.37)
- cloud_firestore (6.0.3):
- cloud_firestore (6.1.0):
- Firebase/Firestore (= 12.4.0)
- firebase_core
- Flutter
- connectivity_plus (0.0.1):
- Flutter
- Firebase/Auth (12.4.0):
- Firebase/CoreOnly
- FirebaseAuth (~> 12.4.0)
Expand All @@ -1216,18 +1218,18 @@ PODS:
- firebase_core
- FirebaseAnalytics (= 12.4.0)
- Flutter
- firebase_auth (6.1.1):
- firebase_auth (6.1.2):
- Firebase/Auth (= 12.4.0)
- firebase_core
- Flutter
- firebase_core (4.2.1):
- Firebase/CoreOnly (= 12.4.0)
- Flutter
- firebase_crashlytics (5.0.4):
- firebase_crashlytics (5.0.5):
- Firebase/Crashlytics (= 12.4.0)
- firebase_core
- Flutter
- firebase_storage (13.0.3):
- firebase_storage (13.0.4):
- Firebase/Storage (= 12.4.0)
- firebase_core
- Flutter
Expand Down Expand Up @@ -1493,6 +1495,7 @@ PODS:

DEPENDENCIES:
- cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`)
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
Expand Down Expand Up @@ -1539,6 +1542,8 @@ SPEC REPOS:
EXTERNAL SOURCES:
cloud_firestore:
:path: ".symlinks/plugins/cloud_firestore/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
firebase_analytics:
:path: ".symlinks/plugins/firebase_analytics/ios"
firebase_auth:
Expand All @@ -1561,13 +1566,14 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
abseil: a05cc83bf02079535e17169a73c5be5ba47f714b
BoringSSL-GRPC: dded2a44897e45f28f08ae87a55ee4bcd19bc508
cloud_firestore: 79014bb3b303d451717ed5fe69fded8a2b2e8dc2
cloud_firestore: 7a6d8a533ec7418a7fe46b3a5dabf55661a5b298
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e
firebase_analytics: 67fbdd9f3c04e55048024f3da21cfc36f05e56cf
firebase_auth: c2b8be95d602d4e8a9148fae72333ef78e69cc20
firebase_auth: 9225db04db5d8e3b46dc8940e04bc6aec6833e27
firebase_core: f1aafb21c14f497e5498f7ffc4dc63cbb52b2594
firebase_crashlytics: 83c7467d7534975a4d779af43bd226d0a4616464
firebase_storage: 0ba617a05b24aec050395e4d5d3773c0d7518a15
firebase_crashlytics: c039028126cb45e32f4c217aa392408b0963d081
firebase_storage: d558bbfa99449fff39352db83c466c8515fa1afa
FirebaseAnalytics: 0fc2b20091f0ddd21bf73397cf8f0eb5346dc24f
FirebaseAppCheckInterop: f734c802f21fe1da0837708f0f9a27218c8a4ed0
FirebaseAuth: 4a2aed737c84114a9d9b33d11ae1b147d6b94889
Expand Down
4 changes: 0 additions & 4 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -341,14 +341,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
Expand Down
10 changes: 10 additions & 0 deletions lib/core/di/di.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:flutter/foundation.dart';
import 'package:get_it/get_it.dart';
import 'package:logger/logger.dart';

import '../services/network/network_service.dart';
import '../services/network/network_service_impl.dart';
import '../utils/analytics/app_analytics.dart';
import '../utils/analytics/debug_analytics_impl.dart';
import '../utils/analytics/firebase_analytics_impl.dart';
Expand All @@ -26,4 +29,11 @@ void setupDI() {
? FirebaseAnalyticsImpl(di<FirebaseAnalytics>())
: DebugAnalyticsImpl(di<AppLogger>()),
);

/// Connectivity
di.registerLazySingleton<Connectivity>(() => Connectivity());
di.registerLazySingleton<NetworkService>(
() => NetworkServiceImpl(di<Connectivity>()),
dispose: (param) => param.dispose(),
);
}
17 changes: 17 additions & 0 deletions lib/core/services/network/network_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/// Abstract interface for monitoring network availability.
abstract interface class NetworkService {
/// Stream that emits network availability changes.
Stream<bool> get connectionStream;

/// Latest known network availability value.
bool get isConnected;

/// Initializes the service, performing the initial connectivity check.
Future<void> init();

/// Performs a one-shot check of current network availability.
Future<bool> hasNetwork();

/// Releases resources (stream subscription, controllers).
Future<void> dispose();
}
53 changes: 53 additions & 0 deletions lib/core/services/network/network_service_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import 'dart:async';

import 'package:connectivity_plus/connectivity_plus.dart';

import 'network_service.dart';

/// Implementation of [NetworkService] using connectivity_plus package.
final class NetworkServiceImpl implements NetworkService {
final Connectivity _connectivity;

final _controller = StreamController<bool>.broadcast();
late final StreamSubscription<List<ConnectivityResult>> _subscription;
bool? _lastHasNetwork;

/// Creates an instance of [NetworkServiceImpl].
NetworkServiceImpl(this._connectivity) {
_subscription = _connectivity.onConnectivityChanged.listen(_onConnectivityChanged);
}

void _onConnectivityChanged(List<ConnectivityResult> results) {
_emit(!results.contains(ConnectivityResult.none));
}

/// Emits [hasNetwork] to [connectionStream] when it changes.
void _emit(bool hasNetwork) {
if (_lastHasNetwork == hasNetwork) return;
_lastHasNetwork = hasNetwork;
if (!_controller.isClosed) _controller.add(hasNetwork);
}

@override
Future<void> init() async {
_emit(await hasNetwork());
}

@override
Future<bool> hasNetwork() async {
final results = await _connectivity.checkConnectivity();
return !results.contains(ConnectivityResult.none);
}

@override
Stream<bool> get connectionStream => _controller.stream;

@override
bool get isConnected => _lastHasNetwork ?? false;

@override
Future<void> dispose() async {
await _subscription.cancel();
await _controller.close();
}
}
32 changes: 32 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.1"
connectivity_plus:
dependency: "direct main"
description:
name: connectivity_plus
sha256: "33bae12a398f841c6cda09d1064212957265869104c478e5ad51e2fb26c3973c"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
convert:
dependency: transitive
description:
Expand Down Expand Up @@ -249,6 +265,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.3"
dbus:
dependency: transitive
description:
name: dbus
sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
url: "https://pub.dev"
source: hosted
version: "0.7.11"
diff_match_patch:
dependency: transitive
description:
Expand Down Expand Up @@ -757,6 +781,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
nm:
dependency: transitive
description:
name: nm
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
url: "https://pub.dev"
source: hosted
version: "0.5.0"
node_preamble:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies:
bloc_test: ^10.0.0
cached_network_image: ^3.4.1
cloud_firestore: ^6.0.3
connectivity_plus: ^7.0.0
dio: ^5.9.0
equatable: ^2.0.7
firebase_analytics: ^12.0.4
Expand Down
Loading