diff --git a/assets/release_notes.md b/assets/release_notes.md index 1bdcb63f..7604eec8 100644 --- a/assets/release_notes.md +++ b/assets/release_notes.md @@ -4,6 +4,7 @@ - Add ability to scan in received items using supplier barcodes - Store API token, rather than username:password - Ensure that user will lose access if token is revoked by server +- Improve scroll-to-refresh behaviour across multiple widgets ### 0.12.8 - September 2023 diff --git a/lib/widget/paginator.dart b/lib/widget/paginator.dart index a08b749e..d3a69c6b 100644 --- a/lib/widget/paginator.dart +++ b/lib/widget/paginator.dart @@ -394,31 +394,36 @@ abstract class PaginatedSearchState extends Sta children.add( Expanded( - child: CustomScrollView( - shrinkWrap: true, - physics: ClampingScrollPhysics(), - scrollDirection: Axis.vertical, - slivers: [ - PagedSliverList.separated( - pagingController: _pagingController, - builderDelegate: PagedChildBuilderDelegate( - itemBuilder: (ctx, item, index) { - return buildItem(ctx, item); - }, - noItemsFoundIndicatorBuilder: (context) { - return NoResultsWidget(noResultsText); - } - ), - separatorBuilder: (context, item) => const Divider(height: .1), - ) - ] + child: CustomScrollView( + shrinkWrap: true, + physics: AlwaysScrollableScrollPhysics(), + scrollDirection: Axis.vertical, + slivers: [ + PagedSliverList.separated( + pagingController: _pagingController, + builderDelegate: PagedChildBuilderDelegate( + itemBuilder: (ctx, item, index) { + return buildItem(ctx, item); + }, + noItemsFoundIndicatorBuilder: (context) { + return NoResultsWidget(noResultsText); + } + ), + separatorBuilder: (context, item) => const Divider(height: 1), + ) + ] + ) ) - ) ); - return Column( + return RefreshIndicator( + child: Column( mainAxisAlignment: MainAxisAlignment.start, children: children, + ), + onRefresh: () async { + _pagingController.refresh(); + }, ); } diff --git a/lib/widget/refreshable_state.dart b/lib/widget/refreshable_state.dart index 2bfc5734..c2ce12b9 100644 --- a/lib/widget/refreshable_state.dart +++ b/lib/widget/refreshable_state.dart @@ -21,7 +21,7 @@ mixin BaseWidgetProperties { */ List appBarActions(BuildContext context) => []; - // Return a title for the appBar + // Return a title for the appBar (placeholder) String getAppBarTitle() { return "--- app bar ---"; } // Function to construct a drawer (override if needed) @@ -40,6 +40,7 @@ mixin BaseWidgetProperties { // Default body calls getTiles() return SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), child: Column( children: getTiles(context) ) @@ -202,7 +203,8 @@ mixin BaseWidgetProperties { */ abstract class RefreshableState extends State with BaseWidgetProperties { - final refreshableKey = GlobalKey(); + final scaffoldKey = GlobalKey(); + final refreshKey = GlobalKey(); // Storage for context once "Build" is called late BuildContext? _context; @@ -265,19 +267,31 @@ abstract class RefreshableState extends State 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: refreshableKey, - appBar: buildAppBar(context, refreshableKey), + key: scaffoldKey, + appBar: buildAppBar(context, scaffoldKey), drawer: getDrawer(context), floatingActionButton: buildSpeedDial(context), floatingActionButtonLocation: FloatingActionButtonLocation.miniEndDocked, body: RefreshIndicator( + key: refreshKey, + notificationPredicate: (ScrollNotification notification) { + return notification.depth == predicateDepth; + }, onRefresh: () async { refresh(context); }, child: body ), - bottomNavigationBar: buildBottomAppBar(context, refreshableKey), + bottomNavigationBar: buildBottomAppBar(context, scaffoldKey), ); // Default implementation is *not* tabbed