This is the sixth day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

For the address book page, we can divide it into the list part and the index bar part on the right. Because the index bar is on the top of the list, we use Stack part for the body part of this page. Let’s look at each part of the implementation in detail.

List part implementation

As a whole, the list can be divided into four fixed cells at the top and contact cells at the bottom, so we define two data sources _headerData and _listDatas for this piece, but we use the same cell. The model corresponding to cell is also the same, but the parameters are differentiated, and the parameters of cell are the same. But we can also see that the head of the initials of the contact part of the same will have an index head, we can use the group head of view in the iOS to achieve, but without the concept of set in the Flutter, so we in the cell to add the index view, only through the logic with parameters to determine whether show the index head view part. Let’s take a look at the implementation of the key code section.

List some

Container( color: CahtThemColor, child: ListView.builder(itemBuilder: _itemForRow, itemCount: _headerData.length + _listDatas.length,) ), Widget _itemForRow(BuildContext context, int index) { if (index < _headerData.length) { return _FriendCell(imageAssets: _headerData[index].imageAssets, name: _headerData[index].name); } index = index - _headerData.length; Return _FriendCell(imageUrl: _listDatas[index]. ImageUrl, name: _listDatas[index]. Name, groupTitle: (index > 0 && _listDatas[index].indexLetter == _listDatas[index -1].indexLetter) ? '' : _listDatas[index].indexLetter); }Copy the code

For the list part, we wrapped a layer with Container to facilitate later changes. In the _itemForRow method, we initialize the cell by passing different parameters to determine whether index is greater than _headerdata. length. For each group of cells with the same initial letter, we judge whether to display the index header view by judging whether the current model is equal to the indexLetter of the next model.

Implementation of the cell part

class _FriendCell extends StatelessWidget { _FriendCell({this.imageUrl = '', this.name = '', this.groupTitle = '', this.imageAssets = ''}); final String imageUrl; final String name; final String groupTitle; final String imageAssets; @override Widget build(BuildContext context) { return Column( children: [ groupTitle.length > 0 ? Container( height: 30, color: CahtThemColor, alignment: Alignment.centerLeft, padding: EdgeInsets.only(left: 15), child: groupTitle.length > 0 ? Text(groupTitle, style: TextStyle(color: Colors.grey),) : null, ) : Container(color: Colors. White, child: Row(children: [Container(margin: EdgeInsets. All (10), width: Circular (6.0), image: color: RGB (50, 50, 50) imageUrl.length > 0 ? DecorationImage( image: NetworkImage(imageUrl) ) : DecorationImage( image: Container(// color: color. red, width: screenWidth(context) -54, child: Column( children: [ Container( alignment: Alignment.centerLeft, height: 54, child: Text( name, style: TextStyle(fontSize: 18),),), Container(height: 0.5, color: CahtThemColor,), / / the underline],),), / / nickname],),), / / the contents of a cell,); }}Copy the code

If a class we just want to use in the current file can be defined by underlining. The whole cell is divided into two parts: the group header and the content part. So the whole thing is done using the Column widget.

Implementation of the model part

class Friends {
  final String imageUrl;
  final String name;
  final String indexLetter;
  final String imageAssets;
  Friends({this.imageUrl = '', this.name = '', this.indexLetter = '', this.imageAssets = ''});
}
Copy the code

The implementation of an indexed view

The indexed view part turns black and transparent when we click, so the whole thing is stateful, inherited from the StatefulWidget. Because the index bar is suspended in the middle of the right side of the view, so the whole of the Positioned piece, the total height we set here is one half of the whole screen, the width is 30, is 0 away from the right and one eighth of the screen from the top.

Index view content section

for (int i = 0; i < INDEX_WORDS.length; i++) {
      _words.add(
          Expanded(child: Text(INDEX_WORDS[i],
            style: TextStyle(fontSize: 10, color: _textColor),))
      );
    }
Copy the code
child: Container(
          color: _bkColor,
          child: Column(
            children: _words,
          ),
        ),
Copy the code

The INDEX_WORDS array holds index characters. We iterate over the number group in the build method, adding the corresponding part of each character to _words. The parts in the index bar are arranged up and down and divided equally. The parts corresponding to each index character are Expanded and the Column part is used as a whole.

Index bar click to change status color

Child: GestureDetector(// Index bar onVerticalDragDown: (DragDownDetails details){setState(() {_bkColor = color.fromrgbo (1, 1, 1, 0.4); _textColor = Colors.white; }); }, // Cancel onVerticalDragEnd: (DragEndDetails details){setState(() {_bkColor = color.fromrgbo (1, 1, 1, 0.0); _textColor = Colors.black; }); },Copy the code

When the index bar is clicked, the background color and text color need to be changed. We define two variables _bkColor and _textColor for the index bar. When the index bar is clicked or unclicked, setState method is called and the values of these two variables are changed.

Index bar drag and drop

// Get the selected item text String _getIndex(BuildContext Context, Offset globalPosition) {// Get the click widget box, RenderBox box = context.findrenderObject () as RenderBox; Double y = box. GlobalToLocal (globalPosition).dy; double y = box. GlobalToLocal (globalPosition).dy; Var itemHeight = screenHight(context) / 2 / index_words.length; Int index = (y ~/ itemHeight).clamp(0, INDEX_WORDS.length-1); return INDEX_WORDS[index]; }Copy the code
onVerticalDragUpdate: (DragUpdateDetails details){
          print(_getIndex(context, details.globalPosition));
        },
Copy the code

When we drag the index bar up and down, we need to get the index character corresponding to the click position. We can get the current part, which is the index bar, by using context.findRenderObject() as RenderBox. We then use Box. GlobalToLocal to get the distance (x, y) between the current touch position and the top left corner (0, 0) of the index bar. This gives us the y value. INDEX_WORDS[index] can be used to get the clicked character. Finally, the value range of index should be judged by clamp.

Extraction of global variables

Here we define a const file that extracts the global variable process.