From 525b3f1583ce2a2ce7ac9bd6c29b214eb6645558 Mon Sep 17 00:00:00 2001 From: Alex Fernandez Date: Thu, 15 Jan 2026 02:23:01 -0500 Subject: [PATCH 1/8] fix some bugs --- CHANGELOG.md | 6 +++ lib/src/components/search_field.dart | 22 ++++----- lib/src/components/tab_view.dart | 64 ++++++++++++------------- lib/src/providers/app_bar_provider.dart | 4 +- pubspec.yaml | 2 +- 5 files changed, 50 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46cec32..ee74c59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.0.2 + +- fix: freeze when you type just white space into the input field when categories are visible +- fix: infinite loading when you type just white space into emoji/stickers +- fix: cursor jumping to the end of the input if you type anywhere but the end + ## 1.0.1 - chore: Add discontinued notice diff --git a/lib/src/components/search_field.dart b/lib/src/components/search_field.dart index 1da9c00..7f7eaab 100644 --- a/lib/src/components/search_field.dart +++ b/lib/src/components/search_field.dart @@ -18,10 +18,7 @@ class TenorSelectedCategoryStyle { const TenorSelectedCategoryStyle({ this.height = 52, - this.padding = const EdgeInsets.only( - left: 14, - top: 1, - ), + this.padding = const EdgeInsets.only(left: 14, top: 1), this.icon = const Icon( Icons.arrow_back_ios_new, size: 15, @@ -100,16 +97,13 @@ class _TenorSearchFieldState extends State { _appBarProvider.addListener(_listenerQuery); // Set Texfield Controller - _textEditingController = widget.searchFieldController ?? - TextEditingController( - text: _appBarProvider.queryText, - ); + _textEditingController = + widget.searchFieldController ?? + TextEditingController(text: _appBarProvider.queryText); WidgetsBinding.instance.addPostFrameCallback((_) { // Establish the debouncer - final debouncer = TenorDebouncer( - delay: _appBarProvider.debounce, - ); + final debouncer = TenorDebouncer(delay: _appBarProvider.debounce); // Listener TextField _textEditingController.addListener(() { @@ -218,7 +212,8 @@ class _TenorSearchFieldState extends State { padding: const EdgeInsets.all(8), child: Icon( Icons.clear, - color: widget.style.hintStyle.color ?? + color: + widget.style.hintStyle.color ?? const Color(0xFF8A8A86), size: 20, ), @@ -249,6 +244,9 @@ class _TenorSearchFieldState extends State { // listener query void _listenerQuery() { + // Update only when it's different. IE you tap on a category. Otherwise the cursor will jump to the end. + if (_textEditingController.text == _appBarProvider.queryText) return; + _textEditingController.text = _appBarProvider.queryText; } } diff --git a/lib/src/components/tab_view.dart b/lib/src/components/tab_view.dart index 004324f..7357fcb 100644 --- a/lib/src/components/tab_view.dart +++ b/lib/src/components/tab_view.dart @@ -14,9 +14,7 @@ const featuredCategoryPath = '##trending-gifs'; class TenorTabViewStyle { final Color mediaBackgroundColor; - const TenorTabViewStyle({ - this.mediaBackgroundColor = Colors.white, - }); + const TenorTabViewStyle({this.mediaBackgroundColor = Colors.white}); } class TenorTabView extends StatefulWidget { @@ -31,7 +29,8 @@ class TenorTabView extends StatefulWidget { String? pos, int limit, TenorCategory? category, - )? onLoad; + )? + onLoad; final Function(TenorResult? gif)? onSelected; final bool showCategories; final TenorTabViewStyle style; @@ -48,8 +47,8 @@ class TenorTabView extends StatefulWidget { this.showCategories = false, this.style = const TenorTabViewStyle(), super.key, - }) : featuredCategory = featuredCategory ?? '📈 Featured', - gifsPerRow = gifsPerRow ?? 3; + }) : featuredCategory = featuredCategory ?? '📈 Featured', + gifsPerRow = gifsPerRow ?? 3; @override State createState() => _TenorTabViewState(); @@ -146,9 +145,7 @@ class _TenorTabViewState extends State Widget build(BuildContext context) { super.build(context); if (_list.isEmpty && _categories.isEmpty) { - return const Center( - child: CircularProgressIndicator(), - ); + return const Center(child: CircularProgressIndicator()); } if (_appBarProvider.queryText.isEmpty && @@ -190,8 +187,8 @@ class _TenorTabViewState extends State _tabProvider.attributionType == TenorAttributionType.poweredBy ? null : EdgeInsets.only( - bottom: MediaQuery.of(context).padding.bottom, - ), + bottom: MediaQuery.of(context).padding.bottom, + ), scrollDirection: _scrollDirection, ), ), @@ -206,24 +203,24 @@ class _TenorTabViewState extends State crossAxisCount: widget.gifsPerRow, crossAxisSpacing: 8, keyboardDismissBehavior: _appBarProvider.keyboardDismissBehavior, - itemBuilder: (ctx, idx) => ClipRRect( - borderRadius: BorderRadius.circular(8), - child: TenorSelectableGif( - backgroundColor: widget.style.mediaBackgroundColor, - onTap: (selectedResult) => _selectedGif( - selectedResult, + itemBuilder: + (ctx, idx) => ClipRRect( + borderRadius: BorderRadius.circular(8), + child: TenorSelectableGif( + backgroundColor: widget.style.mediaBackgroundColor, + onTap: (selectedResult) => _selectedGif(selectedResult), + result: _list[idx], + ), ), - result: _list[idx], - ), - ), itemCount: _list.length, mainAxisSpacing: 8, // Add safe area padding if `TenorAttributionType.poweredBy` is disabled - padding: _tabProvider.attributionType == TenorAttributionType.poweredBy - ? null - : EdgeInsets.only( - bottom: MediaQuery.of(context).padding.bottom, - ), + padding: + _tabProvider.attributionType == TenorAttributionType.poweredBy + ? null + : EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom, + ), scrollDirection: _scrollDirection, ), ); @@ -332,7 +329,7 @@ class _TenorTabViewState extends State if (widget.onLoad != null) { final response = await widget.onLoad?.call( - _appBarProvider.queryText, + _appBarProvider.queryText.trim(), offset, requestLimit, _appBarProvider.selectedCategory, @@ -378,18 +375,14 @@ class _TenorTabViewState extends State } // return result to the consumer - Navigator.pop( - context, - gif.copyWith( - source: _tabProvider.selectedTab.name, - ), - ); + Navigator.pop(context, gif.copyWith(source: _tabProvider.selectedTab.name)); } // if you scroll within a threshhold of the bottom of the screen, load more gifs void _scrollControllerListener() { // trending-gifs, etc - final customCategorySelected = _appBarProvider.selectedCategory != null && + final customCategorySelected = + _appBarProvider.selectedCategory != null && _appBarProvider.queryText == ''; if (customCategorySelected || @@ -404,6 +397,11 @@ class _TenorTabViewState extends State // When the text in the search input changes void _appBarProviderListener() { + // Prevent searches with only spaces + if (_appBarProvider.queryText.isNotEmpty && + _appBarProvider.queryText.trim().isEmpty) { + return; + } setState(() { _list = []; _collection = null; diff --git a/lib/src/providers/app_bar_provider.dart b/lib/src/providers/app_bar_provider.dart index 327ad19..b3bd8fa 100644 --- a/lib/src/providers/app_bar_provider.dart +++ b/lib/src/providers/app_bar_provider.dart @@ -25,8 +25,8 @@ class TenorAppBarProvider with ChangeNotifier { Duration debounce, { required this.keyboardDismissBehavior, TenorCategory? selectedCategory, - }) : _selectedCategory = selectedCategory, - super() { + }) : _selectedCategory = selectedCategory, + super() { _queryText = queryText; _debounce = debounce; } diff --git a/pubspec.yaml b/pubspec.yaml index 46a0a32..a66bb76 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: tenor_flutter -version: 1.0.1 +version: 1.0.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 9ada7d17a58899bc26bd5df2ac28f1bd014c0a89 Mon Sep 17 00:00:00 2001 From: Alex Fernandez Date: Thu, 15 Jan 2026 02:40:33 -0500 Subject: [PATCH 2/8] tweaks --- lib/src/components/tab_view.dart | 15 ++++++++++----- lib/src/providers/app_bar_provider.dart | 3 +++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/src/components/tab_view.dart b/lib/src/components/tab_view.dart index 7357fcb..8e998f2 100644 --- a/lib/src/components/tab_view.dart +++ b/lib/src/components/tab_view.dart @@ -329,7 +329,7 @@ class _TenorTabViewState extends State if (widget.onLoad != null) { final response = await widget.onLoad?.call( - _appBarProvider.queryText.trim(), + _appBarProvider.queryText, offset, requestLimit, _appBarProvider.selectedCategory, @@ -397,11 +397,16 @@ class _TenorTabViewState extends State // When the text in the search input changes void _appBarProviderListener() { + final queryText = _appBarProvider.queryText; + final trimmedQueryText = _appBarProvider.queryText.trim(); + final trimmedPreviousQueryText = _appBarProvider.previousQueryText.trim(); + + // do nothing if the text did not change + if (trimmedQueryText == trimmedPreviousQueryText) return; + // Prevent searches with only spaces - if (_appBarProvider.queryText.isNotEmpty && - _appBarProvider.queryText.trim().isEmpty) { - return; - } + if (queryText.isNotEmpty && trimmedQueryText.isEmpty) return; + setState(() { _list = []; _collection = null; diff --git a/lib/src/providers/app_bar_provider.dart b/lib/src/providers/app_bar_provider.dart index b3bd8fa..07c9671 100644 --- a/lib/src/providers/app_bar_provider.dart +++ b/lib/src/providers/app_bar_provider.dart @@ -6,12 +6,15 @@ class TenorAppBarProvider with ChangeNotifier { String _queryText = ''; String get queryText => _queryText; + String _previousQueryText = ''; + String get previousQueryText => _previousQueryText; TenorCategory? _selectedCategory; Duration _debounce = Duration.zero; Duration get debounce => _debounce; set queryText(String queryText) { + _previousQueryText = _queryText; _queryText = queryText; // reset selected category if (_queryText.isEmpty) { From 7c6780e45a0ca8f85bed8fe8cdcd383e161440b2 Mon Sep 17 00:00:00 2001 From: Alex Fernandez Date: Thu, 15 Jan 2026 02:53:34 -0500 Subject: [PATCH 3/8] formatting --- analysis_options.yaml | 3 +++ lib/src/components/search_field.dart | 23 +++++++++++++---------- lib/src/components/tab_view.dart | 15 ++++++++++++--- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index b825346..db98472 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -7,3 +7,6 @@ analyzer: linter: rules: require_trailing_commas: true + +formatter: + trailing_commas: preserve \ No newline at end of file diff --git a/lib/src/components/search_field.dart b/lib/src/components/search_field.dart index 7f7eaab..bc8047c 100644 --- a/lib/src/components/search_field.dart +++ b/lib/src/components/search_field.dart @@ -18,7 +18,10 @@ class TenorSelectedCategoryStyle { const TenorSelectedCategoryStyle({ this.height = 52, - this.padding = const EdgeInsets.only(left: 14, top: 1), + this.padding = const EdgeInsets.only( + left: 14, + top: 1, + ), this.icon = const Icon( Icons.arrow_back_ios_new, size: 15, @@ -99,11 +102,15 @@ class _TenorSearchFieldState extends State { // Set Texfield Controller _textEditingController = widget.searchFieldController ?? - TextEditingController(text: _appBarProvider.queryText); + TextEditingController( + text: _appBarProvider.queryText, + ); WidgetsBinding.instance.addPostFrameCallback((_) { // Establish the debouncer - final debouncer = TenorDebouncer(delay: _appBarProvider.debounce); + final debouncer = TenorDebouncer( + delay: _appBarProvider.debounce, + ); // Listener TextField _textEditingController.addListener(() { @@ -196,8 +203,7 @@ class _TenorSearchFieldState extends State { left: 4, child: Icon( Icons.search, - color: - widget.style.hintStyle.color ?? const Color(0xFF8A8A86), + color: widget.style.hintStyle.color ?? const Color(0xFF8A8A86), size: 22, ), ), @@ -212,9 +218,7 @@ class _TenorSearchFieldState extends State { padding: const EdgeInsets.all(8), child: Icon( Icons.clear, - color: - widget.style.hintStyle.color ?? - const Color(0xFF8A8A86), + color: widget.style.hintStyle.color ?? const Color(0xFF8A8A86), size: 20, ), ), @@ -235,8 +239,7 @@ class _TenorSearchFieldState extends State { // when they focus the input, maximize viewing space _sheetProvider.scrollController.animateTo( _sheetProvider.maxExtent, - duration: - animationStyle?.duration ?? tenorDefaultAnimationStyle.duration!, + duration: animationStyle?.duration ?? tenorDefaultAnimationStyle.duration!, curve: animationStyle?.curve ?? Curves.linear, ); } diff --git a/lib/src/components/tab_view.dart b/lib/src/components/tab_view.dart index 8e998f2..fefcb98 100644 --- a/lib/src/components/tab_view.dart +++ b/lib/src/components/tab_view.dart @@ -14,7 +14,9 @@ const featuredCategoryPath = '##trending-gifs'; class TenorTabViewStyle { final Color mediaBackgroundColor; - const TenorTabViewStyle({this.mediaBackgroundColor = Colors.white}); + const TenorTabViewStyle({ + this.mediaBackgroundColor = Colors.white, + }); } class TenorTabView extends StatefulWidget { @@ -145,7 +147,9 @@ class _TenorTabViewState extends State Widget build(BuildContext context) { super.build(context); if (_list.isEmpty && _categories.isEmpty) { - return const Center(child: CircularProgressIndicator()); + return const Center( + child: CircularProgressIndicator(), + ); } if (_appBarProvider.queryText.isEmpty && @@ -375,7 +379,12 @@ class _TenorTabViewState extends State } // return result to the consumer - Navigator.pop(context, gif.copyWith(source: _tabProvider.selectedTab.name)); + Navigator.pop( + context, + gif.copyWith( + source: _tabProvider.selectedTab.name, + ), + ); } // if you scroll within a threshhold of the bottom of the screen, load more gifs From 66252fd16c05ae0b74947dcc600ad204f90be704 Mon Sep 17 00:00:00 2001 From: Alex Fernandez Date: Thu, 15 Jan 2026 02:56:41 -0500 Subject: [PATCH 4/8] nit --- lib/src/providers/app_bar_provider.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/providers/app_bar_provider.dart b/lib/src/providers/app_bar_provider.dart index 07c9671..9d1316d 100644 --- a/lib/src/providers/app_bar_provider.dart +++ b/lib/src/providers/app_bar_provider.dart @@ -31,6 +31,7 @@ class TenorAppBarProvider with ChangeNotifier { }) : _selectedCategory = selectedCategory, super() { _queryText = queryText; + _previousQueryText = queryText; _debounce = debounce; } From fd05b0d84c329710125026068fb43d7a6995f446 Mon Sep 17 00:00:00 2001 From: Alex Fernandez Date: Thu, 15 Jan 2026 03:06:15 -0500 Subject: [PATCH 5/8] fix more edge cases --- lib/src/components/tab_view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/components/tab_view.dart b/lib/src/components/tab_view.dart index fefcb98..bf28334 100644 --- a/lib/src/components/tab_view.dart +++ b/lib/src/components/tab_view.dart @@ -152,7 +152,7 @@ class _TenorTabViewState extends State ); } - if (_appBarProvider.queryText.isEmpty && + if (_appBarProvider.queryText.trim().isEmpty && _appBarProvider.selectedCategory == null && widget.showCategories) { return Padding( @@ -333,7 +333,7 @@ class _TenorTabViewState extends State if (widget.onLoad != null) { final response = await widget.onLoad?.call( - _appBarProvider.queryText, + _appBarProvider.queryText.trim(), offset, requestLimit, _appBarProvider.selectedCategory, From b35951186e067836be8b3667738eb0e6517da843 Mon Sep 17 00:00:00 2001 From: Alex Fernandez Date: Thu, 15 Jan 2026 03:09:33 -0500 Subject: [PATCH 6/8] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee74c59..300fdac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - fix: infinite loading when you type just white space into emoji/stickers - fix: cursor jumping to the end of the input if you type anywhere but the end +[All Code Changes](https://github.com/Flyclops/tenor_flutter/compare/1.0.1...1.0.2) + ## 1.0.1 - chore: Add discontinued notice From 744a60363e52817644a5fcdfd8639b20b1f8bdd9 Mon Sep 17 00:00:00 2001 From: Alex Fernandez Date: Thu, 15 Jan 2026 03:19:59 -0500 Subject: [PATCH 7/8] remove extra padding when safe area is there --- 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 bf28334..92cdf3d 100644 --- a/lib/src/components/tab_view.dart +++ b/lib/src/components/tab_view.dart @@ -189,7 +189,7 @@ class _TenorTabViewState extends State // Add safe area padding if `TenorAttributionType.poweredBy` is disabled padding: _tabProvider.attributionType == TenorAttributionType.poweredBy - ? null + ? EdgeInsets.zero : EdgeInsets.only( bottom: MediaQuery.of(context).padding.bottom, ), From 49134975d9964cdf03790d4532b1c9c5b0663386 Mon Sep 17 00:00:00 2001 From: Alex Fernandez Date: Thu, 15 Jan 2026 12:26:53 -0500 Subject: [PATCH 8/8] remove previous query check --- lib/src/components/tab_view.dart | 12 ++++-------- lib/src/providers/app_bar_provider.dart | 4 ---- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/lib/src/components/tab_view.dart b/lib/src/components/tab_view.dart index 92cdf3d..89ed167 100644 --- a/lib/src/components/tab_view.dart +++ b/lib/src/components/tab_view.dart @@ -406,15 +406,11 @@ class _TenorTabViewState extends State // When the text in the search input changes void _appBarProviderListener() { - final queryText = _appBarProvider.queryText; - final trimmedQueryText = _appBarProvider.queryText.trim(); - final trimmedPreviousQueryText = _appBarProvider.previousQueryText.trim(); - - // do nothing if the text did not change - if (trimmedQueryText == trimmedPreviousQueryText) return; - // Prevent searches with only spaces - if (queryText.isNotEmpty && trimmedQueryText.isEmpty) return; + if (_appBarProvider.queryText.isNotEmpty && + _appBarProvider.queryText.trim().isEmpty) { + return; + } setState(() { _list = []; diff --git a/lib/src/providers/app_bar_provider.dart b/lib/src/providers/app_bar_provider.dart index 9d1316d..b3bd8fa 100644 --- a/lib/src/providers/app_bar_provider.dart +++ b/lib/src/providers/app_bar_provider.dart @@ -6,15 +6,12 @@ class TenorAppBarProvider with ChangeNotifier { String _queryText = ''; String get queryText => _queryText; - String _previousQueryText = ''; - String get previousQueryText => _previousQueryText; TenorCategory? _selectedCategory; Duration _debounce = Duration.zero; Duration get debounce => _debounce; set queryText(String queryText) { - _previousQueryText = _queryText; _queryText = queryText; // reset selected category if (_queryText.isEmpty) { @@ -31,7 +28,6 @@ class TenorAppBarProvider with ChangeNotifier { }) : _selectedCategory = selectedCategory, super() { _queryText = queryText; - _previousQueryText = queryText; _debounce = debounce; }