@@ -62,8 +62,7 @@ class _FeedAdLoaderWidgetState extends State<FeedAdLoaderWidget> {
6262 bool _hasError = false ;
6363 final Logger _logger = Logger ('FeedAdLoaderWidget' );
6464 late final InlineAdCacheService _adCacheService;
65- late final AdService
66- _adService; // AdService will be accessed via _adCacheService
65+ late final AdService _adService;
6766
6867 /// Completer to manage the lifecycle of the ad loading future.
6968 /// This helps in cancelling pending operations if the widget is disposed
@@ -106,11 +105,13 @@ class _FeedAdLoaderWidgetState extends State<FeedAdLoaderWidget> {
106105 // Immediately set the widget to a loading state to prevent UI flicker.
107106 // This ensures a smooth transition from the old ad (or no ad) to the
108107 // loading indicator for the new ad.
109- setState (() {
110- _loadedAd = null ;
111- _isLoading = true ;
112- _hasError = false ;
113- });
108+ if (mounted) {
109+ setState (() {
110+ _loadedAd = null ;
111+ _isLoading = true ;
112+ _hasError = false ;
113+ });
114+ }
114115 _loadAd ();
115116 }
116117 }
@@ -136,15 +137,24 @@ class _FeedAdLoaderWidgetState extends State<FeedAdLoaderWidget> {
136137 /// If found, it uses the cached ad. Otherwise, it requests a new inline ad
137138 /// from the [AdService] using `getFeedAd` and stores it in the cache
138139 /// upon success.
140+ ///
141+ /// It also includes defensive checks (`mounted` ) to prevent `setState` calls
142+ /// on disposed widgets and ensures the `_loadAdCompleter` is always completed
143+ /// to prevent `StateError` s.
139144 Future <void > _loadAd () async {
140145 // Initialize a new completer for this loading operation.
141146 _loadAdCompleter = Completer <void >();
142147
143- // Ensure the widget is still mounted before calling setState .
148+ // Ensure the widget is still mounted before proceeding .
144149 // This prevents the "setState() called after dispose()" error
145150 // if the widget is removed from the tree while the async operation
146151 // is still in progress.
147- if (! mounted) return ;
152+ if (! mounted) {
153+ if (_loadAdCompleter? .isCompleted == false ) {
154+ _loadAdCompleter! .complete ();
155+ }
156+ return ;
157+ }
148158
149159 // Attempt to retrieve the ad from the cache first.
150160 final cachedAd = _adCacheService.getAd (widget.adPlaceholder.id);
@@ -154,11 +164,12 @@ class _FeedAdLoaderWidgetState extends State<FeedAdLoaderWidget> {
154164 'Using cached ad for feed placeholder ID: ${widget .adPlaceholder .id }' ,
155165 );
156166 // Ensure the widget is still mounted before calling setState.
157- if (! mounted) return ;
158- setState (() {
159- _loadedAd = cachedAd;
160- _isLoading = false ;
161- });
167+ if (mounted) {
168+ setState (() {
169+ _loadedAd = cachedAd;
170+ _isLoading = false ;
171+ });
172+ }
162173 // Complete the completer only if it hasn't been completed already.
163174 if (_loadAdCompleter? .isCompleted == false ) {
164175 _loadAdCompleter! .complete ();
@@ -178,11 +189,12 @@ class _FeedAdLoaderWidgetState extends State<FeedAdLoaderWidget> {
178189 'Ad placeholder ID ${widget .adPlaceholder .id } has no adIdentifier. '
179190 'Cannot load ad.' ,
180191 );
181- if (! mounted) return ;
182- setState (() {
183- _hasError = true ;
184- _isLoading = false ;
185- });
192+ if (mounted) {
193+ setState (() {
194+ _hasError = true ;
195+ _isLoading = false ;
196+ });
197+ }
186198 // Complete the completer normally, indicating that loading finished
187199 // but no ad was available. This prevents crashes.
188200 if (_loadAdCompleter? .isCompleted == false ) {
@@ -214,11 +226,12 @@ class _FeedAdLoaderWidgetState extends State<FeedAdLoaderWidget> {
214226 // Store the newly loaded ad in the cache.
215227 _adCacheService.setAd (widget.adPlaceholder.id, loadedAd);
216228 // Ensure the widget is still mounted before calling setState.
217- if (! mounted) return ;
218- setState (() {
219- _loadedAd = loadedAd;
220- _isLoading = false ;
221- });
229+ if (mounted) {
230+ setState (() {
231+ _loadedAd = loadedAd;
232+ _isLoading = false ;
233+ });
234+ }
222235 // Complete the completer only if it hasn't been completed already.
223236 if (_loadAdCompleter? .isCompleted == false ) {
224237 _loadAdCompleter! .complete ();
@@ -229,11 +242,12 @@ class _FeedAdLoaderWidgetState extends State<FeedAdLoaderWidget> {
229242 'No ad returned.' ,
230243 );
231244 // Ensure the widget is still mounted before calling setState.
232- if (! mounted) return ;
233- setState (() {
234- _hasError = true ;
235- _isLoading = false ;
236- });
245+ if (mounted) {
246+ setState (() {
247+ _hasError = true ;
248+ _isLoading = false ;
249+ });
250+ }
237251 // Complete the completer normally, indicating that loading finished
238252 // but no ad was available. This prevents crashes.
239253 if (_loadAdCompleter? .isCompleted == false ) {
@@ -247,11 +261,12 @@ class _FeedAdLoaderWidgetState extends State<FeedAdLoaderWidget> {
247261 s,
248262 );
249263 // Ensure the widget is still mounted before calling setState.
250- if (! mounted) return ;
251- setState (() {
252- _hasError = true ;
253- _isLoading = false ;
254- });
264+ if (mounted) {
265+ setState (() {
266+ _hasError = true ;
267+ _isLoading = false ;
268+ });
269+ }
255270 // Complete the completer normally, indicating that loading finished
256271 // but an error occurred. This prevents crashes.
257272 if (_loadAdCompleter? .isCompleted == false ) {
0 commit comments