@@ -58,7 +58,9 @@ class _AdLoaderWidgetState extends State<AdLoaderWidget> {
58
58
final AdCacheService _adCacheService = AdCacheService ();
59
59
60
60
// Completer to manage the lifecycle of the ad loading future.
61
- // This helps in cancelling pending operations if the widget is disposed.
61
+ // This helps in cancelling pending operations if the widget is disposed
62
+ // or updated, preventing `setState` calls on an unmounted widget
63
+ // and avoiding `StateError` from completing a completer multiple times.
62
64
Completer <void >? _loadAdCompleter;
63
65
64
66
@override
@@ -78,14 +80,24 @@ class _AdLoaderWidgetState extends State<AdLoaderWidget> {
78
80
'AdLoaderWidget updated for new placeholder ID: '
79
81
'${widget .adPlaceholder .id }. Re-loading ad.' ,
80
82
);
81
- // Cancel the previous loading operation if it's still active and not yet completed.
83
+ // Cancel the previous loading operation if it's still active and not yet
84
+ // completed. This prevents a race condition if a new load is triggered
85
+ // while an old one is still in progress.
82
86
if (_loadAdCompleter != null && ! _loadAdCompleter! .isCompleted) {
83
87
_loadAdCompleter? .completeError (
84
88
StateError ('Ad loading cancelled: Widget updated with new ID.' ),
85
89
);
86
90
}
87
- _loadAdCompleter = null ; // Clear the old completer
88
- _loadedAd = null ; // Clear the old ad
91
+ _loadAdCompleter = null ; // Clear the old completer for the new load
92
+
93
+ // Immediately set the widget to a loading state to prevent UI flicker.
94
+ // This ensures a smooth transition from the old ad (or no ad) to the
95
+ // loading indicator for the new ad.
96
+ setState (() {
97
+ _loadedAd = null ;
98
+ _isLoading = true ;
99
+ _hasError = false ;
100
+ });
89
101
_loadAd (); // Start loading the new ad
90
102
}
91
103
}
@@ -118,10 +130,6 @@ class _AdLoaderWidgetState extends State<AdLoaderWidget> {
118
130
// if the widget is removed from the tree while the async operation
119
131
// is still in progress.
120
132
if (! mounted) return ;
121
- setState (() {
122
- _isLoading = true ;
123
- _hasError = false ;
124
- });
125
133
// Attempt to retrieve the ad from the cache first.
126
134
final cachedAd = _adCacheService.getAd (widget.adPlaceholder.id);
127
135
@@ -135,7 +143,11 @@ class _AdLoaderWidgetState extends State<AdLoaderWidget> {
135
143
_loadedAd = cachedAd;
136
144
_isLoading = false ;
137
145
});
138
- _loadAdCompleter? .complete (); // Complete the completer on success
146
+ // Complete the completer only if it hasn't been completed already
147
+ // (e.g., by dispose() or didUpdateWidget() cancelling an old load).
148
+ if (_loadAdCompleter? .isCompleted == false ) {
149
+ _loadAdCompleter! .complete (); // Complete the completer on success
150
+ }
139
151
return ;
140
152
}
141
153
@@ -164,7 +176,10 @@ class _AdLoaderWidgetState extends State<AdLoaderWidget> {
164
176
_loadedAd = adFeedItem.nativeAd;
165
177
_isLoading = false ;
166
178
});
167
- _loadAdCompleter? .complete (); // Complete the completer on success
179
+ // Complete the completer only if it hasn't been completed already.
180
+ if (_loadAdCompleter? .isCompleted == false ) {
181
+ _loadAdCompleter! .complete (); // Complete the completer on success
182
+ }
168
183
} else {
169
184
_logger.warning (
170
185
'Failed to load ad for placeholder ID: ${widget .adPlaceholder .id }. No ad returned.' ,
@@ -175,9 +190,12 @@ class _AdLoaderWidgetState extends State<AdLoaderWidget> {
175
190
_hasError = true ;
176
191
_isLoading = false ;
177
192
});
178
- _loadAdCompleter? .completeError (
179
- StateError ('Failed to load ad: No ad returned.' ),
180
- ); // Complete with error
193
+ // Complete the completer with an error only if it hasn't been completed already.
194
+ if (_loadAdCompleter? .isCompleted == false ) {
195
+ _loadAdCompleter? .completeError (
196
+ StateError ('Failed to load ad: No ad returned.' ),
197
+ ); // Complete with error
198
+ }
181
199
}
182
200
} catch (e, s) {
183
201
_logger.severe (
@@ -191,7 +209,10 @@ class _AdLoaderWidgetState extends State<AdLoaderWidget> {
191
209
_hasError = true ;
192
210
_isLoading = false ;
193
211
});
194
- _loadAdCompleter? .completeError (e); // Complete with error
212
+ // Complete the completer with an error only if it hasn't been completed already.
213
+ if (_loadAdCompleter? .isCompleted == false ) {
214
+ _loadAdCompleter? .completeError (e); // Complete with error
215
+ }
195
216
}
196
217
}
197
218
0 commit comments