2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-04-28 05:26:47 +00:00

Scroll fix (#633)

* Fix scrolling behaviuor

* Debounce paginated search
This commit is contained in:
Oliver 2025-04-15 23:42:48 +10:00 committed by GitHub
parent 72a78291b2
commit 1d5377ea20
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 54 additions and 32 deletions

View File

@ -6,6 +6,7 @@
- Support "extra line items" for sales orders - Support "extra line items" for sales orders
- Display start date for purchase orders - Display start date for purchase orders
- Display start date for sales orders - Display start date for sales orders
- Fix scrolling behaviour for some widgets
- Updated search functionality - Updated search functionality
- Updated translations - Updated translations

View File

@ -60,7 +60,9 @@ class _AttachmentWidgetState extends RefreshableState<AttachmentWidget> {
prefix: widget.imagePrefix, prefix: widget.imagePrefix,
); );
FilePickerDialog.pickImageFromCamera().then((File? file) { 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), icon: Icon(TablerIcons.file_upload),
onPressed: () async { onPressed: () async {
FilePickerDialog.pickFileFromDevice().then((File? file) { FilePickerDialog.pickFileFromDevice().then((File? file) {
upload(context, file); upload(context, file).then((_) {
refresh(context);
});
}); });
} }
) )

View File

@ -1,3 +1,5 @@
import "dart:async";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
@ -254,6 +256,9 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta
// Text controller // Text controller
final TextEditingController searchController = TextEditingController(); final TextEditingController searchController = TextEditingController();
// Debounce timer
Timer? _debounceTimer;
// Pagination controller // Pagination controller
final PagingController<int, InvenTreeModel> _pagingController = PagingController(firstPageKey: 0); final PagingController<int, InvenTreeModel> _pagingController = PagingController(firstPageKey: 0);
@ -307,6 +312,9 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta
} }
params["search"] = "${_search}"; params["search"] = "${_search}";
} else {
// Remove search term if it is empty
params.remove("search");
} }
// Use custom query ordering if available // Use custom query ordering if available
@ -368,11 +376,34 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta
// Callback function when the search term is updated // Callback function when the search term is updated
void updateSearchTerm() { void updateSearchTerm() {
searchTerm = searchController.text;
_pagingController.refresh();
if (mounted) { if (searchTerm == searchController.text) {
setState(() {}); // 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(() {});
}
});
} }
} }
@ -465,12 +496,12 @@ abstract class PaginatedSearchState<T extends PaginatedSearchWidget> extends Sta
icon: Icon(showSearchWidget ? Icons.zoom_out : Icons.search, size: icon_size) icon: Icon(showSearchWidget ? Icons.zoom_out : Icons.search, size: icon_size)
)); ));
_icons.add(IconButton( // _icons.add(IconButton(
onPressed: () async { // onPressed: () async {
updateSearchTerm(); // updateSearchTerm();
}, // },
icon: Icon(Icons.refresh, size: icon_size), // icon: Icon(Icons.refresh, size: icon_size),
)); // ));
return ListTile( return ListTile(
title: Text( title: Text(

View File

@ -39,21 +39,14 @@ mixin BaseWidgetProperties {
// Function to construct a body // Function to construct a body
Widget getBody(BuildContext context) { Widget getBody(BuildContext context) {
// Default body calls getTiles() // Default implementation is to return a ListView
return SingleChildScrollView( // Override getTiles to replace the internal context
return ListView(
physics: AlwaysScrollableScrollPhysics(), physics: AlwaysScrollableScrollPhysics(),
child: Column( children: getTiles(context),
children: [
ListView(
children: getTiles(context),
shrinkWrap: true,
)
],
)
); );
} }
/* /*
* Construct the top AppBar for this view * 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)); 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( Scaffold view = Scaffold(
key: scaffoldKey, key: scaffoldKey,
appBar: buildAppBar(context, scaffoldKey), appBar: buildAppBar(context, scaffoldKey),
@ -291,8 +276,9 @@ abstract class RefreshableState<T extends StatefulWidget> extends State<T> with
body: RefreshIndicator( body: RefreshIndicator(
key: refreshKey, key: refreshKey,
notificationPredicate: (ScrollNotification notification) { notificationPredicate: (ScrollNotification notification) {
return notification.depth == predicateDepth; return true;
}, },
onRefresh: () async { onRefresh: () async {
refresh(context); refresh(context);
}, },