From a0e4edb508c391c518c9e6124843bf83a9bf6f54 Mon Sep 17 00:00:00 2001 From: daniel-michel <65034538+daniel-michel@users.noreply.github.com> Date: Tue, 9 Jan 2024 12:47:42 +0100 Subject: [PATCH] fix: accurate loading indicator for search, add button to clear search --- lib/main.dart | 54 +++++++++++++++++++------- lib/model/delayed_function_caller.dart | 2 + lib/model/live_search.dart | 8 +--- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 614cb48..31b4648 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -41,6 +41,7 @@ class _HomePageState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late LiveSearch liveSearch; + late TextEditingController _searchController; @override void initState() { @@ -49,12 +50,14 @@ class _HomePageState extends State vsync: this, // the SingleTickerProviderStateMixin duration: const Duration(milliseconds: 300), ); + _searchController = TextEditingController(); liveSearch = LiveSearch(widget.manager); } @override void dispose() { _controller.dispose(); + _searchController.dispose(); super.dispose(); } @@ -62,21 +65,42 @@ class _HomePageState extends State Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: TextField( - decoration: const InputDecoration( - hintText: "Search", - border: InputBorder.none, - ), - onChanged: (value) { - setState(() { - if (value.isEmpty) { - _controller.reverse(); - } else { - _controller.forward(); - } - liveSearch.updateSearch(value); - }); - }, + title: Row( + children: [ + Expanded( + child: TextField( + controller: _searchController, + decoration: const InputDecoration( + hintText: "Search", + border: InputBorder.none, + ), + onChanged: (value) { + setState(() { + if (value.isEmpty) { + _controller.reverse(); + } else { + _controller.forward(); + } + liveSearch.updateSearch(value); + }); + }, + ), + ), + AnimatedBuilder( + animation: _controller, + builder: (context, child) { + if (liveSearch.searchTerm.isEmpty) return Container(); + return IconButton( + icon: const Icon(Icons.clear), + onPressed: () { + _searchController.clear(); + liveSearch.updateSearch(""); + _controller.reverse(); + }, + ); + }, + ), + ], ), actions: [HamburgerMenu(widget.manager)], ), diff --git a/lib/model/delayed_function_caller.dart b/lib/model/delayed_function_caller.dart index 46efcba..d478b96 100644 --- a/lib/model/delayed_function_caller.dart +++ b/lib/model/delayed_function_caller.dart @@ -7,6 +7,8 @@ class DelayedFunctionCaller { DelayedFunctionCaller(this.function, this.duration); + get scheduled => _timer != null && _timer!.isActive; + void call() { // If a timer is already active, return. if (_timer != null && _timer!.isActive) { diff --git a/lib/model/live_search.dart b/lib/model/live_search.dart index 4386636..f306524 100644 --- a/lib/model/live_search.dart +++ b/lib/model/live_search.dart @@ -9,32 +9,29 @@ class LiveSearch extends ChangeNotifier { Duration minTimeBetweenRequests = const Duration(milliseconds: 200); late final DelayedFunctionCaller _searchCaller; final MovieManager manager; - bool loading = false; bool searchingOnline = false; LiveSearch(this.manager) { _searchCaller = DelayedFunctionCaller(searchOnline, minTimeBetweenRequests); } + get loading => searchingOnline || _searchCaller.scheduled; + void updateSearch(String search) { searchTerm = search; if (searchTerm.isEmpty) { return; } searchResults = manager.localSearch(search); - loading = true; _searchCaller.call(); notifyListeners(); } void searchOnline() async { if (searchTerm.isEmpty) { - loading = false; - notifyListeners(); return; } if (searchingOnline) { - loading = true; _searchCaller.call(); notifyListeners(); return; @@ -55,7 +52,6 @@ class LiveSearch extends ChangeNotifier { notifyListeners(); } finally { searchingOnline = false; - loading = false; notifyListeners(); } }