From ca61576e8ed9dc8e51e8fc150068d47dd9148289 Mon Sep 17 00:00:00 2001 From: Josh Chung Date: Mon, 1 Dec 2025 17:52:20 -0500 Subject: [PATCH 1/6] fix a race condition that causes another infinite loop. --- lib/src/components/tab_view.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/src/components/tab_view.dart b/lib/src/components/tab_view.dart index a6a453e..7f6d8de 100644 --- a/lib/src/components/tab_view.dart +++ b/lib/src/components/tab_view.dart @@ -276,6 +276,12 @@ class _TenorTabViewState extends State // 2 - there are no more gifs to load if (_tabProvider.selectedTab != tab || !_hasMoreGifs) return; + // If we are already loading, then we're already in the process of loading. So wait for 100ms and then try again + if (_isLoading) { + await Future.delayed(const Duration(milliseconds: 100)); + continue; + } + await _loadMore(); } }); From 64a576c08d802a2ad95864fdfefcf919b491bdc2 Mon Sep 17 00:00:00 2001 From: Josh Chung Date: Mon, 1 Dec 2025 17:54:57 -0500 Subject: [PATCH 2/6] update version and changelog --- CHANGELOG.md | 6 ++++++ pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa5cc93..0001790 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.1.2 + +- fix: the endless loop can still happen if you switch between tabs quickly and time it perfectly while still loading gifs. + +[All Code Changes](https://github.com/Flyclops/tenor_flutter/compare/0.1.1...0.1.2) + ## 0.1.1 - fix: if you switch tabs very quickly before the initial gif fetch then you will get stuck in an endless loop and crash the app. diff --git a/pubspec.yaml b/pubspec.yaml index 03e4c08..ab110d6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: tenor_flutter -version: 0.1.1 +version: 0.1.2 description: An opinionated yet customizable Flutter package for searching and selecting from a list of GIFs/Stickers from the Tenor GIF search API. homepage: https://github.com/flyclops repository: https://github.com/flyclops/tenor_flutter From 8892ef30e8780fd280a9fab05348d61f48d9154f Mon Sep 17 00:00:00 2001 From: Josh Chung Date: Mon, 1 Dec 2025 18:04:02 -0500 Subject: [PATCH 3/6] updated comment --- lib/src/components/tab_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/components/tab_view.dart b/lib/src/components/tab_view.dart index 7f6d8de..31f2270 100644 --- a/lib/src/components/tab_view.dart +++ b/lib/src/components/tab_view.dart @@ -276,7 +276,7 @@ class _TenorTabViewState extends State // 2 - there are no more gifs to load if (_tabProvider.selectedTab != tab || !_hasMoreGifs) return; - // If we are already loading, then we're already in the process of loading. So wait for 100ms and then try again + // If we're already loading, then we don't want to load more. So wait for 100ms and then try again if (_isLoading) { await Future.delayed(const Duration(milliseconds: 100)); continue; From 39f9e530cbc46a32762a6e34478527aea0d9953c Mon Sep 17 00:00:00 2001 From: Alex Fernandez Date: Tue, 2 Dec 2025 09:16:13 -0500 Subject: [PATCH 4/6] remove while loop in favor of a recursive call post all initial checks, etc --- lib/src/components/tab_view.dart | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/lib/src/components/tab_view.dart b/lib/src/components/tab_view.dart index 31f2270..588d5ba 100644 --- a/lib/src/components/tab_view.dart +++ b/lib/src/components/tab_view.dart @@ -270,19 +270,8 @@ class _TenorTabViewState extends State // Wait for a frame so that we can ensure that `scrollController` is attached WidgetsBinding.instance.addPostFrameCallback((_) async { - while (_scrollController.position.extentAfter == 0) { - // Stop trying to load more and exit the loop if: - // 1 - the selected tab has changed - // 2 - there are no more gifs to load - if (_tabProvider.selectedTab != tab || !_hasMoreGifs) return; - - // If we're already loading, then we don't want to load more. So wait for 100ms and then try again - if (_isLoading) { - await Future.delayed(const Duration(milliseconds: 100)); - continue; - } - - await _loadMore(); + if (_scrollController.position.extentAfter == 0) { + _loadMore(fillScrollableArea: true); } }); } @@ -312,18 +301,16 @@ class _TenorTabViewState extends State } } - Future _loadMore() async { + Future _loadMore({bool fillScrollableArea = false}) async { // 1 - prevent non active tabs from loading more // 2 - if it's loading don't load more // 3 - if there are no more gifs to load, don't load more if (_tabProvider.selectedTab != tab || _isLoading || !_hasMoreGifs) return; - try { // fail safe if categories are empty when we load more (network issues) if (widget.showCategories && _categories.isEmpty) { _loadCatagories(); } - // api says there are no more gifs, so lets stop requesting if (_collection?.next == '') { setState(() { @@ -373,6 +360,10 @@ class _TenorTabViewState extends State _isLoading = false; rethrow; } + + if (fillScrollableArea && _scrollController.position.extentAfter == 0) { + _loadMore(fillScrollableArea: true); + } } // Return selected gif From 649cd7b1f228b9eec7083e591e931d09feeeac53 Mon Sep 17 00:00:00 2001 From: Alex Fernandez Date: Tue, 2 Dec 2025 09:16:44 -0500 Subject: [PATCH 5/6] formatting --- lib/src/components/tab_view.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/components/tab_view.dart b/lib/src/components/tab_view.dart index 588d5ba..aa582cc 100644 --- a/lib/src/components/tab_view.dart +++ b/lib/src/components/tab_view.dart @@ -306,11 +306,13 @@ class _TenorTabViewState extends State // 2 - if it's loading don't load more // 3 - if there are no more gifs to load, don't load more if (_tabProvider.selectedTab != tab || _isLoading || !_hasMoreGifs) return; + try { // fail safe if categories are empty when we load more (network issues) if (widget.showCategories && _categories.isEmpty) { _loadCatagories(); } + // api says there are no more gifs, so lets stop requesting if (_collection?.next == '') { setState(() { From eb7dd7d2b1a3893af43e6035dfb7dad6492b9e83 Mon Sep 17 00:00:00 2001 From: Alex Fernandez Date: Tue, 2 Dec 2025 09:26:13 -0500 Subject: [PATCH 6/6] guard against race condition --- lib/src/components/tab_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/components/tab_view.dart b/lib/src/components/tab_view.dart index aa582cc..004324f 100644 --- a/lib/src/components/tab_view.dart +++ b/lib/src/components/tab_view.dart @@ -364,7 +364,7 @@ class _TenorTabViewState extends State } if (fillScrollableArea && _scrollController.position.extentAfter == 0) { - _loadMore(fillScrollableArea: true); + Future.microtask(() => _loadMore(fillScrollableArea: true)); } }