diff --git a/CHANGELOG.md b/CHANGELOG.md index 67cee3e..1cda9e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.0.1 +FIX +- Added `indicatorHeight` property to `InfintieScrollTabView`. It will override indicator height if specified non-null value. +- The tabs now prevent double tap. +- Fixed a bug that tapping tab causes changing page to unexpected index sometime. ## 1.0.0 FEAT - Changed internal structure about indicator. It considers specified `separator`'s size now. diff --git a/example/pubspec.lock b/example/pubspec.lock index 5bcf5a5..1a3a79f 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -66,7 +66,7 @@ packages: path: ".." relative: true source: path - version: "1.0.0" + version: "1.0.1" matcher: dependency: transitive description: diff --git a/lib/src/infinite_scroll_tab_view.dart b/lib/src/infinite_scroll_tab_view.dart index c87192a..d99def0 100644 --- a/lib/src/infinite_scroll_tab_view.dart +++ b/lib/src/infinite_scroll_tab_view.dart @@ -29,6 +29,7 @@ class InfiniteScrollTabView extends StatelessWidget { this.backgroundColor, this.onPageChanged, this.indicatorColor = Colors.pinkAccent, + this.indicatorHeight, this.tabHeight = 44.0, this.tabPadding = 12.0, }) : super(key: key); @@ -83,6 +84,14 @@ class InfiniteScrollTabView extends StatelessWidget { /// Defaults to [Colors.pinkAccent], and must not be null. final Color indicatorColor; + /// The height of indicator. + /// + /// If this is null, the indicator height is aligned to [separator] height, or + /// it also null, then fallbacks to 2.0. + /// + /// This must 1.0 or higher. + final double? indicatorHeight; + /// The height of tab contents. /// /// Defaults to 44.0. @@ -97,6 +106,10 @@ class InfiniteScrollTabView extends StatelessWidget { @override Widget build(BuildContext context) { + if (indicatorHeight != null) { + assert(indicatorHeight! >= 1.0); + } + return LayoutBuilder( builder: (context, constraint) => InnerInfiniteScrollTabView( size: constraint.biggest, @@ -111,6 +124,7 @@ class InfiniteScrollTabView extends StatelessWidget { backgroundColor: backgroundColor, onPageChanged: onPageChanged, indicatorColor: indicatorColor, + indicatorHeight: indicatorHeight, defaultLocale: Localizations.localeOf(context), tabHeight: tabHeight, tabPadding: tabPadding, diff --git a/lib/src/inner_infinite_scroll_tab_view.dart b/lib/src/inner_infinite_scroll_tab_view.dart index 2983c28..ef6907d 100644 --- a/lib/src/inner_infinite_scroll_tab_view.dart +++ b/lib/src/inner_infinite_scroll_tab_view.dart @@ -24,6 +24,7 @@ class InnerInfiniteScrollTabView extends StatefulWidget { this.backgroundColor, this.onPageChanged, required this.indicatorColor, + this.indicatorHeight, required this.defaultLocale, required this.tabHeight, required this.tabPadding, @@ -41,6 +42,7 @@ class InnerInfiniteScrollTabView extends StatefulWidget { final Color? backgroundColor; final ValueChanged? onPageChanged; final Color indicatorColor; + final double? indicatorHeight; final Locale defaultLocale; final double tabHeight; final double tabPadding; @@ -58,7 +60,7 @@ class InnerInfiniteScrollTabViewState extends State ); late final _pageController = CycledScrollController(); - bool _isContentChangingByTab = false; + final ValueNotifier _isContentChangingByTab = ValueNotifier(false); bool _isTabForceScrolling = false; late double _previousTextScaleFactor = widget.textScaleFactor; @@ -85,7 +87,8 @@ class InnerInfiniteScrollTabViewState extends State final List> _tabSizeTweens = []; List> get tabSizeTweens => _tabSizeTweens; - double get indicatorWidth => widget.separator?.width ?? 2.0; + double get indicatorWidth => + widget.indicatorHeight ?? widget.separator?.width ?? 2.0; late final _indicatorAnimationController = AnimationController(vsync: this, duration: _tabAnimationDuration) @@ -182,7 +185,7 @@ class InnerInfiniteScrollTabViewState extends State }); _pageController.addListener(() { - if (_isContentChangingByTab) return; + if (_isContentChangingByTab.value) return; final currentIndexDouble = _pageController.offset / widget.size.width; final currentIndex = currentIndexDouble.floor(); @@ -210,11 +213,12 @@ class InnerInfiniteScrollTabViewState extends State } void _onTapTab(int modIndex, int rawIndex) async { + _isContentChangingByTab.value = true; + widget.onTabTap?.call(modIndex); widget.onPageChanged?.call(modIndex); HapticFeedback.selectionClick(); - _selectedIndex.value = modIndex; _isTabPositionAligned.value = true; final sizeOnIndex = _calculateTabSizeFromIndex(modIndex); @@ -236,25 +240,24 @@ class InnerInfiniteScrollTabViewState extends State .animate(_indicatorAnimationController); _indicatorAnimationController.forward(from: 0); - _isContentChangingByTab = true; // 現在のスクロール位置とページインデックスを取得 final currentOffset = _pageController.offset; - final currentModIndex = - (currentOffset ~/ widget.size.width) % widget.contentLength; // 選択したページまでの距離を計算する // modの境界をまたぐ場合を考慮して、近い方向を指すように正負を調整する final move = calculateMoveIndexDistance( - currentModIndex, modIndex, widget.contentLength); + _selectedIndex.value, modIndex, widget.contentLength); final targetPageOffset = currentOffset + move * widget.size.width; + _selectedIndex.value = modIndex; + await _pageController.animateTo( targetPageOffset, duration: _tabAnimationDuration, curve: Curves.ease, ); - _isContentChangingByTab = false; + _isContentChangingByTab.value = false; } @override @@ -265,35 +268,12 @@ class InnerInfiniteScrollTabViewState extends State children: [ SizedBox( height: widget.tabHeight + (widget.separator?.width ?? 0), - child: CycledListView.builder( - scrollDirection: Axis.horizontal, - controller: _tabController, - contentCount: widget.contentLength, - itemBuilder: (context, modIndex, rawIndex) { - return Material( - color: widget.backgroundColor, - child: InkWell( - onTap: () => _onTapTab(modIndex, rawIndex), - child: ValueListenableBuilder( - valueListenable: _selectedIndex, - builder: (context, index, _) => - ValueListenableBuilder( - valueListenable: _isTabPositionAligned, - builder: (context, tab, _) => _TabContent( - isTabPositionAligned: tab, - selectedIndex: index, - indicatorColor: widget.indicatorColor, - tabPadding: widget.tabPadding, - modIndex: modIndex, - tabBuilder: widget.tabBuilder, - separator: widget.separator, - indicatorWidth: indicatorWidth, - ), - ), - ), - ), - ); - }, + child: ValueListenableBuilder( + valueListenable: _isContentChangingByTab, + builder: (context, value, _) => AbsorbPointer( + absorbing: value, + child: _buildTabSection(), + ), ), ), Positioned( @@ -334,6 +314,38 @@ class InnerInfiniteScrollTabViewState extends State ); } + Widget _buildTabSection() { + return CycledListView.builder( + scrollDirection: Axis.horizontal, + controller: _tabController, + contentCount: widget.contentLength, + itemBuilder: (context, modIndex, rawIndex) { + return Material( + color: widget.backgroundColor, + child: InkWell( + onTap: () => _onTapTab(modIndex, rawIndex), + child: ValueListenableBuilder( + valueListenable: _selectedIndex, + builder: (context, index, _) => ValueListenableBuilder( + valueListenable: _isTabPositionAligned, + builder: (context, tab, _) => _TabContent( + isTabPositionAligned: tab, + selectedIndex: index, + indicatorColor: widget.indicatorColor, + tabPadding: widget.tabPadding, + modIndex: modIndex, + tabBuilder: widget.tabBuilder, + separator: widget.separator, + indicatorWidth: indicatorWidth, + ), + ), + ), + ), + ); + }, + ); + } + @override void dispose() { _tabController.dispose(); diff --git a/pubspec.yaml b/pubspec.yaml index 3475942..68fb73c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: infinite_scroll_tab_view description: A Flutter package for tab view component that can scroll infinitely. -version: 1.0.0 +version: 1.0.1 repository: https://github.com/cb-cloud/flutter_infinite_scroll_tab_view environment: