From 1548198e039bc1ba616cef03366fc5777b1e5653 Mon Sep 17 00:00:00 2001 From: Joachim Valente Date: Thu, 21 Jan 2021 22:16:27 -0800 Subject: [PATCH 1/4] fix side view offset when main offset does not start at 0 --- .gitignore | 1 + lib/side_header_list_view.dart | 137 +++++++++++++++------------------ 2 files changed, 65 insertions(+), 73 deletions(-) diff --git a/.gitignore b/.gitignore index 75e9658..139c6a9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ .pub/ packages pubspec.lock +.dart_tool/ diff --git a/lib/side_header_list_view.dart b/lib/side_header_list_view.dart index be93aff..2e3f8a1 100644 --- a/lib/side_header_list_view.dart +++ b/lib/side_header_list_view.dart @@ -3,7 +3,6 @@ library side_header_list_view; import 'package:flutter/material.dart'; import 'package:meta/meta.dart'; - /** * SideHeaderListView for Flutter * @@ -12,7 +11,6 @@ import 'package:meta/meta.dart'; * Released under BSD License. */ - typedef bool HasSameHeader(int a, int b); class SideHeaderListView extends StatefulWidget { @@ -21,97 +19,90 @@ class SideHeaderListView extends StatefulWidget { final IndexedWidgetBuilder itemBuilder; final EdgeInsets padding; final HasSameHeader hasSameHeader; - final itemExtend; + final double itemExtent; SideHeaderListView({ Key key, this.itemCount, - @required this.itemExtend, + @required this.itemExtent, @required this.headerBuilder, @required this.itemBuilder, @required this.hasSameHeader, this.padding, - }) - : super(key: key); + }) : super(key: key); @override - _SideHeaderListViewState createState() => new _SideHeaderListViewState(); + _SideHeaderListViewState createState() => _SideHeaderListViewState(); } class _SideHeaderListViewState extends State { int currentPosition = 0; + final _controller = ScrollController(); @override - Widget build(BuildContext context) { - return new Stack( - children: [ - new Positioned( - child: new Opacity( - opacity: _shouldShowHeader(currentPosition) ? 0.0 : 1.0, - child: widget.headerBuilder(context, currentPosition >= 0 ? currentPosition : 0), - ), - top: 0.0 + (widget.padding?.top ?? 0), - left: 0.0 + (widget.padding?.left ?? 0), - ), - new ListView.builder( - padding: widget.padding, - itemCount: widget.itemCount, - itemExtent: widget.itemExtend, - controller: _getScrollController(), - itemBuilder: (BuildContext context, int index) { - return new Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - new FittedBox( - child: new Opacity( - opacity: _shouldShowHeader(index) ? 1.0 : 0.0, - child: widget.headerBuilder(context, index), - ), - ), - new Expanded(child: widget.itemBuilder(context, index)) - ], - ); - }), - ], - ); + void initState() { + super.initState(); + // Reposition side view each time the main list view is scrolled + _controller.addListener(_reposition); + // In case the initial offset is not 0, we reposition the side view to the + // correct offset after first build + // TODO: this produces a flickering effect -- we should try to position it + // correctly before the first build + WidgetsBinding.instance.addPostFrameCallback((_) => _reposition()); } - bool _shouldShowHeader(int position) { - if(position < 0){ - return true; - } - if (position == 0 && currentPosition < 0) { - return true; - } - - if ( - position != 0 && - position != currentPosition && - !widget.hasSameHeader(position, position - 1)) { - return true; - } + @override + void dispose() { + _controller?.removeListener(_reposition); + super.dispose(); + } - if ( - position != widget.itemCount -1 && - !widget.hasSameHeader(position, position + 1) && - position == currentPosition) { - return true; - } - return false; + void _reposition() { + print('new position = ${(_controller.offset / widget.itemExtent).floor()}'); + setState(() => + currentPosition = (_controller.offset / widget.itemExtent).floor()); } - ScrollController _getScrollController() { - var controller = new ScrollController(); - controller.addListener(() { - var pixels = controller.offset; - var newPosition = (pixels / widget.itemExtend).floor(); + @override + Widget build(BuildContext context) => Stack( + children: [ + Positioned( + child: Opacity( + opacity: _shouldShowHeader(currentPosition) ? 0.0 : 1.0, + child: widget.headerBuilder( + context, currentPosition >= 0 ? currentPosition : 0), + ), + top: 0.0 + (widget.padding?.top ?? 0), + left: 0.0 + (widget.padding?.left ?? 0), + ), + ListView.builder( + padding: widget.padding, + itemCount: widget.itemCount, + itemExtent: widget.itemExtent, + controller: _controller, + itemBuilder: (BuildContext context, int index) => Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FittedBox( + child: Opacity( + opacity: _shouldShowHeader(index) ? 1.0 : 0.0, + child: widget.headerBuilder(context, index), + ), + ), + Expanded(child: widget.itemBuilder(context, index)) + ], + ), + ), + ], + ); - if (newPosition != currentPosition) { - setState(() { - currentPosition = newPosition; - }); - } - }); - return controller; - } + bool _shouldShowHeader(int position) => + (position < 0) || + (position == 0 && currentPosition < 0) || + (position != 0 && + position != currentPosition && + !widget.hasSameHeader(position, position - 1)) || + (position != widget.itemCount - 1 && + !widget.hasSameHeader(position, position + 1) && + position == currentPosition); } From 1dbd2826d145b2e6645f9940f8de78760c4293f5 Mon Sep 17 00:00:00 2001 From: Joachim Valente Date: Thu, 21 Jan 2021 22:18:24 -0800 Subject: [PATCH 2/4] remove print --- lib/side_header_list_view.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/side_header_list_view.dart b/lib/side_header_list_view.dart index 2e3f8a1..b353be5 100644 --- a/lib/side_header_list_view.dart +++ b/lib/side_header_list_view.dart @@ -58,7 +58,6 @@ class _SideHeaderListViewState extends State { } void _reposition() { - print('new position = ${(_controller.offset / widget.itemExtent).floor()}'); setState(() => currentPosition = (_controller.offset / widget.itemExtent).floor()); } From fdcaca746ba30bde080fec1eae1afa69c9a7233b Mon Sep 17 00:00:00 2001 From: Joachim Valente Date: Thu, 21 Jan 2021 22:23:24 -0800 Subject: [PATCH 3/4] no need to ? --- lib/side_header_list_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/side_header_list_view.dart b/lib/side_header_list_view.dart index b353be5..9cb9ee5 100644 --- a/lib/side_header_list_view.dart +++ b/lib/side_header_list_view.dart @@ -53,7 +53,7 @@ class _SideHeaderListViewState extends State { @override void dispose() { - _controller?.removeListener(_reposition); + _controller.removeListener(_reposition); super.dispose(); } From 569525b7659e9cc4e031ea689174b9567351bf7f Mon Sep 17 00:00:00 2001 From: Joachim Valente Date: Fri, 12 Mar 2021 00:51:21 -0800 Subject: [PATCH 4/4] accept controller --- lib/side_header_list_view.dart | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/side_header_list_view.dart b/lib/side_header_list_view.dart index 9cb9ee5..f6ac383 100644 --- a/lib/side_header_list_view.dart +++ b/lib/side_header_list_view.dart @@ -20,6 +20,7 @@ class SideHeaderListView extends StatefulWidget { final EdgeInsets padding; final HasSameHeader hasSameHeader; final double itemExtent; + final ScrollController controller; SideHeaderListView({ Key key, @@ -28,6 +29,7 @@ class SideHeaderListView extends StatefulWidget { @required this.headerBuilder, @required this.itemBuilder, @required this.hasSameHeader, + @required this.controller, this.padding, }) : super(key: key); @@ -37,13 +39,12 @@ class SideHeaderListView extends StatefulWidget { class _SideHeaderListViewState extends State { int currentPosition = 0; - final _controller = ScrollController(); @override void initState() { super.initState(); // Reposition side view each time the main list view is scrolled - _controller.addListener(_reposition); + widget.controller.addListener(_reposition); // In case the initial offset is not 0, we reposition the side view to the // correct offset after first build // TODO: this produces a flickering effect -- we should try to position it @@ -53,13 +54,13 @@ class _SideHeaderListViewState extends State { @override void dispose() { - _controller.removeListener(_reposition); + widget.controller.removeListener(_reposition); super.dispose(); } void _reposition() { - setState(() => - currentPosition = (_controller.offset / widget.itemExtent).floor()); + setState(() => currentPosition = + (widget.controller.offset / widget.itemExtent).floor()); } @override @@ -78,7 +79,7 @@ class _SideHeaderListViewState extends State { padding: widget.padding, itemCount: widget.itemCount, itemExtent: widget.itemExtent, - controller: _controller, + controller: widget.controller, itemBuilder: (BuildContext context, int index) => Row( crossAxisAlignment: CrossAxisAlignment.start, children: [