Skip to content

Commit c67adae

Browse files
committed
fix(ObserverUtils): refine the logic of calcAnchorTabIndex method
1 parent 8fd0ac0 commit c67adae

File tree

2 files changed

+117
-26
lines changed

2 files changed

+117
-26
lines changed

lib/src/utils/src/observer_utils.dart

Lines changed: 77 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -39,30 +39,27 @@ class ObserverUtils {
3939
/// Calculate the anchor tab index.
4040
static int calcAnchorTabIndex({
4141
required ObserveModel observeModel,
42-
required List<int> tabIndexs,
42+
@Deprecated(
43+
'It will be removed in version 2, please use [tabIndexes] instead',
44+
)
45+
List<int>? tabIndexs,
46+
List<int>? tabIndexes,
4347
required int currentTabIndex,
4448
}) {
45-
if (currentTabIndex >= tabIndexs.length) {
49+
assert(
50+
tabIndexs != null || tabIndexes != null,
51+
'tabIndexes and tabIndexs cannot both be null.',
52+
);
53+
List<int> indexes = tabIndexes ?? tabIndexs ?? [];
54+
if (currentTabIndex >= indexes.length) {
4655
return currentTabIndex;
4756
}
4857
if (observeModel is ListViewObserveModel) {
49-
final topIndex = observeModel.firstChild?.index ?? 0;
50-
final index = tabIndexs.indexOf(topIndex);
51-
if (isValidListIndex(index)) {
52-
return index;
53-
}
54-
var targetTabIndex = currentTabIndex - 1;
55-
if (targetTabIndex < 0 || targetTabIndex >= tabIndexs.length) {
56-
return currentTabIndex;
57-
}
58-
var curIndex = tabIndexs[currentTabIndex];
59-
var lastIndex = tabIndexs[currentTabIndex - 1];
60-
if (curIndex > topIndex && lastIndex < topIndex) {
61-
final lastTabIndex = tabIndexs.indexOf(lastIndex);
62-
if (isValidListIndex(lastTabIndex)) {
63-
return lastTabIndex;
64-
}
65-
}
58+
return calcAnchorTabIndexForList(
59+
firstIndex: observeModel.firstChild?.index,
60+
tabIndexes: indexes,
61+
currentTabIndex: currentTabIndex,
62+
);
6663
} else if (observeModel is GridViewObserveModel) {
6764
final firstGroupChildList = observeModel.firstGroupChildList;
6865
if (firstGroupChildList.isEmpty) {
@@ -72,22 +69,22 @@ class ObserverUtils {
7269
GridViewObserveDisplayingChildModel mainChildModel =
7370
firstGroupChildList.first;
7471
for (var firstGroupChildModel in firstGroupChildList) {
75-
final index = tabIndexs.indexOf(firstGroupChildModel.index);
72+
final index = indexes.indexOf(firstGroupChildModel.index);
7673
if (isValidListIndex(index)) {
77-
// Found the target index from tabIndexs, return directly.
74+
// Found the target index from indexes, return directly.
7875
return index;
7976
}
8077
if (mainChildModel.trailingMarginToViewport <
8178
firstGroupChildModel.trailingMarginToViewport) {
8279
mainChildModel = firstGroupChildModel;
8380
}
8481
}
85-
// Target index not found from tabIndexs.
82+
// Target index not found from indexes.
8683
var targetTabIndex = currentTabIndex - 1;
87-
if (targetTabIndex < 0 || targetTabIndex >= tabIndexs.length) {
84+
if (targetTabIndex < 0 || targetTabIndex >= indexes.length) {
8885
return currentTabIndex;
8986
}
90-
var curIndex = tabIndexs[currentTabIndex];
87+
var curIndex = indexes[currentTabIndex];
9188
final firstGroupIndexList =
9289
firstGroupChildList.map((e) => e.index).toList();
9390
final minOffset = mainChildModel.layoutOffset;
@@ -111,6 +108,62 @@ class ObserverUtils {
111108
return currentTabIndex;
112109
}
113110

111+
/// Calculate the anchor tab index for list type.
112+
///
113+
/// - [firstIndex] is the index of the first child widget.
114+
/// - [tabIndexes] is the list of indexes of all tabs.
115+
/// - [currentTabIndex] is the current tab index.
116+
static int calcAnchorTabIndexForList({
117+
int? firstIndex,
118+
required List<int> tabIndexes,
119+
required int currentTabIndex,
120+
}) {
121+
// Example:
122+
// ====== exact match ======
123+
// tabIndexes: [0, 6, 9, 11, 12, 16]
124+
// firstIndex: 12
125+
// result: 4 (the index of 12 in tabIndexes)
126+
//
127+
// ====== no exact match ======
128+
// tabIndexes: [0, 6, 9, 11, 12, 16]
129+
// firstIndex: 10
130+
// result: 2 (the index of 9 in tabIndexes)
131+
132+
if (tabIndexes.isEmpty) return currentTabIndex;
133+
if (firstIndex == null) return currentTabIndex;
134+
final target = firstIndex;
135+
// If the target value is less than the minimum value, currentTabIndex is
136+
// returned.
137+
if (target < tabIndexes.first) return currentTabIndex;
138+
// If the target value is greater than or equal to the maximum value, the
139+
// maximum value is returned.
140+
if (target >= tabIndexes.last) return tabIndexes.length - 1;
141+
142+
// Two-point search
143+
int left = 0;
144+
int right = tabIndexes.length - 1;
145+
// The currentTabIndex is returned by default.
146+
int resultIndex = currentTabIndex;
147+
148+
while (left <= right) {
149+
int mid = (left + right) ~/ 2;
150+
int midValue = tabIndexes[mid];
151+
152+
if (midValue == target) {
153+
// Find an equal value and return its index.
154+
return mid;
155+
} else if (midValue < target) {
156+
// Update the index of elements with the largest less than the target
157+
// value.
158+
resultIndex = mid;
159+
left = mid + 1;
160+
} else {
161+
right = mid - 1;
162+
}
163+
}
164+
return resultIndex;
165+
}
166+
114167
/// Determines whether the offset at the bottom of the target child widget
115168
/// is below the specified offset.
116169
static bool isBelowOffsetWidgetInSliver({

test/observer_utils_test.dart

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ void main() {
4444
List<int> tabIndexes = [0, 5, 10];
4545
int tabIndex = ObserverUtils.calcAnchorTabIndex(
4646
observeModel: observeModel!,
47-
tabIndexs: tabIndexes,
47+
tabIndexes: tabIndexes,
4848
currentTabIndex: 0,
4949
);
5050
expect(tabIndex, 1);
@@ -53,11 +53,49 @@ void main() {
5353
await tester.pumpAndSettle();
5454
tabIndex = ObserverUtils.calcAnchorTabIndex(
5555
observeModel: observeModel!,
56-
tabIndexs: tabIndexes,
56+
tabIndexes: tabIndexes,
5757
currentTabIndex: 1,
5858
);
5959
expect(tabIndex, 1);
6060

6161
scrollController.dispose();
6262
});
63+
64+
testWidgets('check calcAnchorTabIndexForList', (tester) async {
65+
List<int> tabIndexes = [0, 6, 9, 11, 12, 16];
66+
67+
// ====== exact match ======
68+
// tabIndexes: [0, 6, 9, 11, 12, 16]
69+
// firstIndex: 12
70+
// result: 4 (the index of 12 in tabIndexes)
71+
expect(
72+
ObserverUtils.calcAnchorTabIndexForList(
73+
firstIndex: 12,
74+
tabIndexes: tabIndexes,
75+
currentTabIndex: 0,
76+
),
77+
4,
78+
);
79+
80+
// ====== no exact match ======
81+
// tabIndexes: [0, 6, 9, 11, 12, 16]
82+
// firstIndex: 10
83+
// result: 2 (the index of 9 in tabIndexes)
84+
expect(
85+
ObserverUtils.calcAnchorTabIndexForList(
86+
firstIndex: 10,
87+
tabIndexes: tabIndexes,
88+
currentTabIndex: 0,
89+
),
90+
2,
91+
);
92+
expect(
93+
ObserverUtils.calcAnchorTabIndexForList(
94+
firstIndex: 17,
95+
tabIndexes: tabIndexes,
96+
currentTabIndex: 0,
97+
),
98+
5,
99+
);
100+
});
63101
}

0 commit comments

Comments
 (0)