@@ -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 ({
0 commit comments