Skip to content

Commit

Permalink
Merge pull request #4 from cb-cloud/fix_tab_behavior
Browse files Browse the repository at this point in the history
Fix tab behavior
  • Loading branch information
Kurogoma4D authored Jul 26, 2021
2 parents da6b827 + 5073144 commit 05477fa
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 40 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ packages:
path: ".."
relative: true
source: path
version: "1.0.0"
version: "1.0.1"
matcher:
dependency: transitive
description:
Expand Down
14 changes: 14 additions & 0 deletions lib/src/infinite_scroll_tab_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand All @@ -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,
Expand All @@ -111,6 +124,7 @@ class InfiniteScrollTabView extends StatelessWidget {
backgroundColor: backgroundColor,
onPageChanged: onPageChanged,
indicatorColor: indicatorColor,
indicatorHeight: indicatorHeight,
defaultLocale: Localizations.localeOf(context),
tabHeight: tabHeight,
tabPadding: tabPadding,
Expand Down
88 changes: 50 additions & 38 deletions lib/src/inner_infinite_scroll_tab_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -41,6 +42,7 @@ class InnerInfiniteScrollTabView extends StatefulWidget {
final Color? backgroundColor;
final ValueChanged<int>? onPageChanged;
final Color indicatorColor;
final double? indicatorHeight;
final Locale defaultLocale;
final double tabHeight;
final double tabPadding;
Expand All @@ -58,7 +60,7 @@ class InnerInfiniteScrollTabViewState extends State<InnerInfiniteScrollTabView>
);
late final _pageController = CycledScrollController();

bool _isContentChangingByTab = false;
final ValueNotifier<bool> _isContentChangingByTab = ValueNotifier(false);
bool _isTabForceScrolling = false;

late double _previousTextScaleFactor = widget.textScaleFactor;
Expand All @@ -85,7 +87,8 @@ class InnerInfiniteScrollTabViewState extends State<InnerInfiniteScrollTabView>
final List<Tween<double>> _tabSizeTweens = [];
List<Tween<double>> 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)
Expand Down Expand Up @@ -182,7 +185,7 @@ class InnerInfiniteScrollTabViewState extends State<InnerInfiniteScrollTabView>
});

_pageController.addListener(() {
if (_isContentChangingByTab) return;
if (_isContentChangingByTab.value) return;

final currentIndexDouble = _pageController.offset / widget.size.width;
final currentIndex = currentIndexDouble.floor();
Expand Down Expand Up @@ -210,11 +213,12 @@ class InnerInfiniteScrollTabViewState extends State<InnerInfiniteScrollTabView>
}

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);
Expand All @@ -236,25 +240,24 @@ class InnerInfiniteScrollTabViewState extends State<InnerInfiniteScrollTabView>
.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
Expand All @@ -265,35 +268,12 @@ class InnerInfiniteScrollTabViewState extends State<InnerInfiniteScrollTabView>
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<int>(
valueListenable: _selectedIndex,
builder: (context, index, _) =>
ValueListenableBuilder<bool>(
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<bool>(
valueListenable: _isContentChangingByTab,
builder: (context, value, _) => AbsorbPointer(
absorbing: value,
child: _buildTabSection(),
),
),
),
Positioned(
Expand Down Expand Up @@ -334,6 +314,38 @@ class InnerInfiniteScrollTabViewState extends State<InnerInfiniteScrollTabView>
);
}

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<int>(
valueListenable: _selectedIndex,
builder: (context, index, _) => ValueListenableBuilder<bool>(
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();
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down

0 comments on commit 05477fa

Please sign in to comment.