In the last article, we completed the list of contacts. This article describes the functionality that completes the index bar on the right side of the address book.
Display index bar
We’ve done the layout of my page before, and my page has a list and a photo button, which is very similar to the index bar layout that we’re going to implement today. The layout of my page is as follows:The layout of the contacts interface, as well as the layout of my pages, is implemented using a Stack containing lists and other subviews. The index bar is attached to the right side of the screen, and the subviews inside are top to bottom. So it was natural to think of using a c-segment containing a Column to do this. The combination of c-stack as we talked about before, these two can be combined, just like the constrained layout in our iOS, you can set the upper left width, the height and so on. Not to mention Column, which we’ve used many times. So the code looks like this:
Extracting IndexBar
At this point, we can see that the index bar has a lot of functionality that we need to implement, which is a little bit complicated. If the code is written in friends_page. Dart, it would be a bit redundant, so we can implement the index bar as a stand-alone Widget. Create a new index_bar.dart file with the following code:
Achieve IndexBar click toggle state
When IndexBar is not touched, the default background color is not displayed and the text is black. When we start clicking on the IndexBar, the background color is displayed and the text becomes white. To achieve this function, the main thing is toGestureDetector
Of the two methods.onVerticalDragDown
The method will be called when you touch the IndexBar with your finger,onVerticalDragEnd
Will be called when the finger is released from the screen. With these two approaches, requirements can be implemented. The code is as follows:Since we are changing the color of the Text, we use the variable _textColor to initialize the Text;
Gets the currently selected index
The same is toGestureDetector
The use of a gesture method,onVerticalDragUpdate
The timing of this method, this method will be called over and over again as the finger moves. There is one methodDragUpdateDetails
Parameter, which contains information about the coordinates of the finger. These are just coordinates relative to the entire screen, which can be converted to coordinates relative to the IndexBar, and then computed to find out which index we are currently selecting. The code is as follows:The ~/ in the method is the flutter specific operator, which means to divide and round. Clamp () is a handle on the boundary case, meaning that the result of the call to the function is between its two arguments.
Callback the selected index
Callbacks are the same thing as blocks in OC and closures in Swift. The underlined variables inside the flutter are private and not externally accessible. Therefore, the exposed parameters cannot be written in_IndexBarState
Class, it needs to be in the IndexBar class. Declare a closure (or block) property that is passed as a mandatory parameter during initialization.So, infriends_page.dart
File initializationIndexBar
You need to pass in a closure. thenIndexBar
In the internalonVerticalDragUpdate
To call the closure, you can call back the currently selected subscript to the outside.At this point, a small problem is that when you click the IndexBar, the callback does not execute, but only when you click and move your finger. So you need to be able toonVerticalDragDown
A closure is also called inside the method. Now, if I just putonVerticalDragUpdate
Method is copied toonVerticalDragDown
There is no problem in the method, but it is obvious that there is too much duplicated code.So you can extract a method and put the duplicate code together.And then it’s a lot easier to call.
Optimize the frequency of callback execution
The callback has been successfully implemented, but judging from the printed results, the same index will be called back many times. This creates unnecessary performance costs when scrolling through our friends list. When you only need to scroll once, you end up scrolling countless times to the same position. So here we need to optimize, and a natural idea is to record one_currentIndexLetter
, each time the callback is executed, determine whether the first letter of the callback is the same as_currentIndexLetter
If they are the same, there is no need for a callback, only if they are different, the callback is performed.The code is as follows:The frequency of the callback is normal.
Scroll through the buddy list ListView
Every scrollable widget has a Controller property that controls the behavior of the scrollbar. The controller property is aScrollController
Object. You can use it to specify a location to scroll to, go back to the top, and so on. Scrolling through the friends list requires a new objectScrollController
Instance, and set it to the ListViewcontroller
Properties can then be used by usingScrollController
Example to manipulate ListView scrolling.Let’s set the scroll offset to a fixed value of 250 and see what happens. You can see that when we click on the IndexBar, the ListView scrolls to an offset of 250. The next step is to deal with the actual offset of the scroll.
The actual offset of the roll is calculated from our data source. Because the height of our cell is determined, the height of the cell with the group head is not displayed as 54, but the height of the cell with the group head is 54 + 30 = 84. Using the first letter as the key, the offset is calculated and recorded using a Map(similar to a dictionary in iOS). Since the first is not a letter, it is a search symbol, and its corresponding offset is also fixed at 0. So you can specify that when you initialize the Map. The other heights are computed in the initState method. The code is as follows:
With this Map in hand, in the callback method for IndexBar, we can get the offset based on the first letter given to us by the IndexBar callback. The code is as follows:
At this point, our IndexBar basically implements scrolling listViews. But after scrolling a few times, there is a small problem… When you scroll to the bottom of the group headers, the ListView will first scroll to the specified position and then roll back to the bottom. It’s easy to see why. The header content behind it doesn’t fit the entire screen. So we need to do something here. We’re basically listening for the scroll of the ListView, so if we were in iOS we would want to get the contentSize of the scroll view and then subtract the height of the UITableView, which is the maximum scroll range of the UITableView. In the Flutter, we don’t have to calculate any of this.
If you need to get some information about the ListView scrolling, you can wrap it inNotificationListener
Inside, it has oneonNotification
Property is a closure that can be called back to give us some information about scrolling. Included in the closure parameterScrollNotification note
The inside. Exactly scrolling related information is contained inScrollNotification
The properties of themetrics
The inside. It contains information such as the current scrolling offset, the maximum range that can be scrolled (this is the height of the contentSize minus the height of the UITableView in our iOS), and so on. The complete code is as follows: 将_maxScrollExtent
Just define it as a property. It is important to note that you cannot set the initial value to 0, otherwise you will not be able to scroll the ListView using IndexBar before you scroll the ListView.This completes the IndexBar’s ability to scroll through the ListView.
Display indicator
Finally comes the final step, the indicator that displays our IndexBar. The first consideration is the layout. The original IndexBar had only one column, the right subscript. Now we need a container on the left to display our indicators, so the root view of IndexBar should consider changing to Row. The irregular pattern in the background of the indicator can be displayed using a picture that has been prepared. For the middle Text, use Text. Let’s take a look at the layout code:
You can change the width if you don’t feel comfortable with the position. Then is to the indicator display and hide to do the control, the indicator display and hide the control, should say with the background color display and hide is similar. Both are controlled in those two methods of gestures. Use a bool variable to control the display and hide of the indicator. Operate on the bool variable in the touch and leave methods of the gesture, and then setState() implements the display and hide of the indicator. Then there’s the display text for the indicator. This text is ours_currentIndexLetter
, just use it directly. Finally, how to control the up and down displacement of the entire IndexBar. Through the use of Alignment, it is found that the upper and lower displacements of IndexBar can be controlled. By constantly modifying the Alignment y value, you will find a suitable y value pointing to the first magnifying glass, so -y will point to the last letter Z. I tried it a couple of times and found that when y=-1.13, the indicator was pointing just right at the first magnifying glass. So now the problem is to divide 1.13 * 2 = 2.26_index_words.length - 1
And then obtain the y-value of the corresponding Alignment based on the selected subscript. When we select the first one, the index is 0, and the y value should be -1.13. When we select the last one, the index is_index_words.length - 1
, the y value should be 1.13. This information will give you a formula for calculating y. The final code looks like this:
Add two variables_showIndicator
and_indicatorAlignmentY
.Using these two variables there’s also_currentIndexLetter
At this point, we have finally implemented the encapsulation of the IndexBar for contacts. The next section covers some network requests…