Skip to content

Commit 4b4055c

Browse files
committed
ConnectivityHandlerMixin
1 parent 2571ad3 commit 4b4055c

5 files changed

Lines changed: 605 additions & 98 deletions

File tree

packages/flutter/example/lib/live_list/main.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,10 @@ class _MyAppState extends State<MyApp> {
8484
Expanded(
8585
child: ParseLiveListWidget<ParseObject>(
8686
query: _queryBuilder,
87+
fromJson: (Map<String, dynamic> json) =>
88+
ParseObject('Test')..fromJson(json),
8789
duration: const Duration(seconds: 1),
88-
childBuilder: (BuildContext context,
89-
ParseLiveListElementSnapshot<ParseObject> snapshot) {
90+
childBuilder: (BuildContext context, ParseLiveListElementSnapshot<ParseObject> snapshot [int? index]) {
9091
if (snapshot.failed) {
9192
return const Text('something went wrong!');
9293
} else if (snapshot.hasData) {

packages/flutter/lib/parse_server_sdk_flutter.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:io';
66
import 'dart:math';
77
import 'dart:ui';
88
import 'package:parse_server_sdk/parse_server_sdk.dart';
9+
import 'package:parse_server_sdk_flutter/src/mixins/connectivity_handler_mixin.dart';
910
import 'package:parse_server_sdk_flutter/src/utils/parse_cached_live_list.dart';
1011
import 'package:path/path.dart' as path;
1112
import 'package:connectivity_plus/connectivity_plus.dart';
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import 'dart:async';
2+
import 'package:connectivity_plus/connectivity_plus.dart';
3+
import 'package:flutter/foundation.dart';
4+
import 'package:flutter/material.dart'; // Needed for State
5+
6+
/// Mixin to handle connectivity checks and state updates for Parse Live Widgets.
7+
///
8+
/// Requires the consuming State class to implement abstract methods for
9+
/// loading data, disposing live resources, and providing configuration.
10+
mixin ConnectivityHandlerMixin<T extends StatefulWidget> on State<T> {
11+
// State variables managed by the mixin
12+
bool _isOffline = false;
13+
late StreamSubscription<List<ConnectivityResult>> _connectivitySubscription;
14+
final Connectivity _connectivity = Connectivity();
15+
ConnectivityResult? _connectionStatus;
16+
17+
// Abstract methods to be implemented by the consuming State class
18+
/// Loads data from the server (e.g., initializes LiveQuery).
19+
Future<void> loadDataFromServer();
20+
21+
/// Loads data from the local cache.
22+
Future<void> loadDataFromCache();
23+
24+
/// Disposes any active LiveQuery resources.
25+
void disposeLiveList();
26+
27+
/// A prefix string for debug logs (e.g., "List", "Grid").
28+
String get connectivityLogPrefix;
29+
30+
/// Indicates if offline mode is enabled in the widget's configuration.
31+
bool get isOfflineModeEnabled;
32+
33+
/// Getter to access the internal offline state.
34+
bool get isOffline => _isOffline;
35+
36+
/// Initializes the connectivity handler. Call this in initState.
37+
void initConnectivityHandler() {
38+
_internalInitConnectivity(); // Perform initial check
39+
40+
// Listen for subsequent changes
41+
_connectivitySubscription =
42+
_connectivity.onConnectivityChanged.listen((List<ConnectivityResult> results) {
43+
final newResult = results.contains(ConnectivityResult.mobile)
44+
? ConnectivityResult.mobile
45+
: results.contains(ConnectivityResult.wifi)
46+
? ConnectivityResult.wifi
47+
: results.contains(ConnectivityResult.none)
48+
? ConnectivityResult.none
49+
: ConnectivityResult.other;
50+
51+
_internalUpdateConnectionStatus(newResult);
52+
});
53+
}
54+
55+
/// Disposes the connectivity handler resources. Call this in dispose.
56+
void disposeConnectivityHandler() {
57+
_connectivitySubscription.cancel();
58+
}
59+
60+
/// Performs the initial connectivity check.
61+
Future<void> _internalInitConnectivity() async {
62+
try {
63+
var connectivityResults = await _connectivity.checkConnectivity();
64+
final initialResult = connectivityResults.contains(ConnectivityResult.mobile)
65+
? ConnectivityResult.mobile
66+
: connectivityResults.contains(ConnectivityResult.wifi)
67+
? ConnectivityResult.wifi
68+
: connectivityResults.contains(ConnectivityResult.none)
69+
? ConnectivityResult.none
70+
: ConnectivityResult.other;
71+
72+
await _internalUpdateConnectionStatus(initialResult, isInitialCheck: true);
73+
} catch (e) {
74+
debugPrint('$connectivityLogPrefix Error during initial connectivity check: $e');
75+
// Default to offline on error
76+
await _internalUpdateConnectionStatus(ConnectivityResult.none, isInitialCheck: true);
77+
}
78+
}
79+
80+
/// Updates the connection status and triggers appropriate data loading.
81+
Future<void> _internalUpdateConnectionStatus(ConnectivityResult result, {bool isInitialCheck = false}) async {
82+
// Only react if the status is actually different
83+
if (result == _connectionStatus) {
84+
debugPrint('$connectivityLogPrefix Connectivity status unchanged: $result');
85+
return;
86+
}
87+
88+
debugPrint('$connectivityLogPrefix Connectivity status changed: From $_connectionStatus to $result');
89+
final previousStatus = _connectionStatus;
90+
_connectionStatus = result; // Update current status
91+
92+
// Determine current and previous online state
93+
bool wasOnline = previousStatus != null && previousStatus != ConnectivityResult.none;
94+
bool isOnline = result == ConnectivityResult.mobile || result == ConnectivityResult.wifi;
95+
96+
// --- Handle State Transitions ---
97+
if (isOnline && !wasOnline) {
98+
// --- Transitioning TO Online ---
99+
_isOffline = false;
100+
debugPrint('$connectivityLogPrefix Transitioning Online: $result. Loading data from server...');
101+
await loadDataFromServer(); // Call the implementation from the consuming class
102+
} else if (!isOnline && wasOnline) {
103+
// --- Transitioning TO Offline ---
104+
_isOffline = true;
105+
debugPrint('$connectivityLogPrefix Transitioning Offline: $result. Disposing liveList and loading from cache...');
106+
disposeLiveList(); // Call the implementation
107+
await loadDataFromCache(); // Call the implementation
108+
} else if (isInitialCheck) {
109+
// --- Handle Initial State (only runs once) ---
110+
if (isOnline) {
111+
_isOffline = false;
112+
debugPrint('$connectivityLogPrefix Initial State Online: $result. Loading data from server...');
113+
await loadDataFromServer();
114+
} else {
115+
_isOffline = true;
116+
debugPrint('$connectivityLogPrefix Initial State Offline: $result. Loading from cache...');
117+
// Only load from cache if offline mode is actually enabled
118+
if (isOfflineModeEnabled) {
119+
await loadDataFromCache();
120+
} else {
121+
debugPrint('$connectivityLogPrefix Offline mode disabled, skipping initial cache load.');
122+
// Optionally clear items or show empty state here if needed
123+
}
124+
}
125+
} else {
126+
// --- No Online/Offline Transition ---
127+
debugPrint('$connectivityLogPrefix Connectivity changed within same state (Online/Offline): $result');
128+
// Optional: Reload data even if staying online (e.g., wifi -> mobile)
129+
// if (isOnline) {
130+
// await loadDataFromServer();
131+
// }
132+
}
133+
}
134+
}

0 commit comments

Comments
 (0)