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