mirror of
				https://github.com/inventree/inventree-app.git
				synced 2025-10-31 13:25:40 +00:00 
			
		
		
		
	Scroll fix (#633)
* Fix scrolling behaviuor * Debounce paginated search
This commit is contained in:
		| @@ -6,6 +6,7 @@ | ||||
| - Support "extra line items" for sales orders | ||||
| - Display start date for purchase orders | ||||
| - Display start date for sales orders | ||||
| - Fix scrolling behaviour for some widgets | ||||
| - Updated search functionality | ||||
| - Updated translations | ||||
|  | ||||
|   | ||||
| @@ -60,7 +60,9 @@ class _AttachmentWidgetState extends RefreshableState<AttachmentWidget> { | ||||
|             prefix: widget.imagePrefix, | ||||
|           ); | ||||
|           FilePickerDialog.pickImageFromCamera().then((File? file) { | ||||
|             upload(context, file); | ||||
|             upload(context, file).then((_) { | ||||
|               refresh(context); | ||||
|             }); | ||||
|           }); | ||||
|         } | ||||
|       ), | ||||
| @@ -68,7 +70,9 @@ class _AttachmentWidgetState extends RefreshableState<AttachmentWidget> { | ||||
|         icon: Icon(TablerIcons.file_upload), | ||||
|         onPressed: () async { | ||||
|           FilePickerDialog.pickFileFromDevice().then((File? file) { | ||||
|             upload(context, file); | ||||
|             upload(context, file).then((_) { | ||||
|               refresh(context); | ||||
|             }); | ||||
|           }); | ||||
|         } | ||||
|       ) | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import "dart:async"; | ||||
|  | ||||
| import "package:flutter/material.dart"; | ||||
|  | ||||
| import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; | ||||
| @@ -254,6 +256,9 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta | ||||
|   // Text controller | ||||
|   final TextEditingController searchController = TextEditingController(); | ||||
|  | ||||
|   // Debounce timer | ||||
|   Timer? _debounceTimer; | ||||
|  | ||||
|   // Pagination controller | ||||
|   final PagingController<int, InvenTreeModel> _pagingController = PagingController(firstPageKey: 0); | ||||
|  | ||||
| @@ -307,6 +312,9 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta | ||||
|         } | ||||
|  | ||||
|         params["search"] = "${_search}"; | ||||
|       } else { | ||||
|         // Remove search term if it is empty | ||||
|         params.remove("search"); | ||||
|       } | ||||
|  | ||||
|       // Use custom query ordering if available | ||||
| @@ -368,12 +376,35 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta | ||||
|  | ||||
|   // Callback function when the search term is updated | ||||
|   void updateSearchTerm() { | ||||
|  | ||||
|     if (searchTerm == searchController.text) { | ||||
|       // No change | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     // Debounce the search term | ||||
|     if (_debounceTimer?.isActive ?? false) { | ||||
|       _debounceTimer?.cancel(); | ||||
|     } | ||||
|  | ||||
|     if (searchController.text.isEmpty) { | ||||
|       // An empty search term evaluates immediately | ||||
|       searchTerm = ""; | ||||
|       _pagingController.refresh(); | ||||
|  | ||||
|       if (mounted) { | ||||
|         setState(() {}); | ||||
|       } | ||||
|     } else { | ||||
|       _debounceTimer = Timer(const Duration(milliseconds: 500), () { | ||||
|         searchTerm = searchController.text; | ||||
|         _pagingController.refresh(); | ||||
|  | ||||
|         if (mounted) { | ||||
|           setState(() {}); | ||||
|         } | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Function to construct a single paginated item | ||||
| @@ -465,12 +496,12 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta | ||||
|         icon: Icon(showSearchWidget ? Icons.zoom_out : Icons.search, size: icon_size) | ||||
|     )); | ||||
|  | ||||
|     _icons.add(IconButton( | ||||
|       onPressed: () async { | ||||
|         updateSearchTerm(); | ||||
|       }, | ||||
|       icon: Icon(Icons.refresh, size: icon_size), | ||||
|     )); | ||||
|     // _icons.add(IconButton( | ||||
|     //   onPressed: () async { | ||||
|     //     updateSearchTerm(); | ||||
|     //   }, | ||||
|     //   icon: Icon(Icons.refresh, size: icon_size), | ||||
|     // )); | ||||
|  | ||||
|     return ListTile( | ||||
|       title: Text( | ||||
|   | ||||
| @@ -39,21 +39,14 @@ mixin BaseWidgetProperties { | ||||
|   // Function to construct a body | ||||
|   Widget getBody(BuildContext context) { | ||||
|  | ||||
|     // Default body calls getTiles() | ||||
|     return SingleChildScrollView( | ||||
|     // Default implementation is to return a ListView | ||||
|     // Override getTiles to replace the internal context | ||||
|     return ListView( | ||||
|       physics: AlwaysScrollableScrollPhysics(), | ||||
|       child: Column( | ||||
|         children: [ | ||||
|           ListView( | ||||
|       children: getTiles(context), | ||||
|             shrinkWrap: true, | ||||
|           ) | ||||
|         ], | ||||
|       ) | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /* | ||||
|    * Construct the top AppBar for this view | ||||
|    */ | ||||
| @@ -274,14 +267,6 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> with | ||||
|  | ||||
|     Widget body = tabs.isEmpty ? getBody(context) : TabBarView(children: getTabs(context)); | ||||
|  | ||||
|     // predicateDepth needs to be different based on the child type | ||||
|     // hack, determined experimentally | ||||
|     int predicateDepth = 0; | ||||
|  | ||||
|     if (tabs.isNotEmpty) { | ||||
|       predicateDepth = 1; | ||||
|     } | ||||
|  | ||||
|     Scaffold view = Scaffold( | ||||
|       key: scaffoldKey, | ||||
|       appBar: buildAppBar(context, scaffoldKey), | ||||
| @@ -291,8 +276,9 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> with | ||||
|       body: RefreshIndicator( | ||||
|         key: refreshKey, | ||||
|         notificationPredicate: (ScrollNotification notification) { | ||||
|           return notification.depth == predicateDepth; | ||||
|           return true; | ||||
|         }, | ||||
|  | ||||
|         onRefresh: () async { | ||||
|           refresh(context); | ||||
|         }, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user