【Flutter 开发必备】AzListView 组件全解析,打造丝滑索引列表!

简介: 在 Flutter 开发中,AzListView 是实现字母索引分类列表的理想选择。它支持 A-Z 快速跳转、悬浮分组标题、自定义 UI 和高效性能,适用于通讯录、城市选择等场景。本文将详细解析 AzListView 的核心参数和实战示例,助你轻松实现流畅的索引列表。

【Flutter 开发必备】AzListView 组件全解析,打造丝滑索引列表!

在 Flutter 开发中,遇到需要按字母索引分类展示列表的需求怎么办?AzListView 组件让你的列表不仅美观,还能流畅滚动!今天,我们就来全面解析 AzListView 的用法,让你的 Flutter 开发更上一层楼!

🔥 为什么选择 AzListView?

在开发通讯录、城市选择等需要索引的列表时,普通 ListView 显然不够用,而 AzListView 提供了以下超强能力:

字母索引导航:支持 A-Z 索引快速跳转。
Sticky Headers:分组标题悬浮效果,让分类一目了然。
可定制 UI:支持灵活修改索引样式、列表样式。
高效性能:基于 ListView.builder,性能优秀,支持大数据量列表。

🛠 AzListView 组件核心参数解析

在使用 AzListView 之前,我们先来看一下它的核心参数:

参数名 类型 说明
data List\ 数据源,需继承 ISuspensionBean 接口
itemBuilder IndexedWidgetBuilder 列表项构建方法
suspensionWidget Widget 悬浮索引组件(分组标题)
indexBarOptions IndexBarOptions 索引栏配置,如颜色、字体等
header Widget 头部组件,可选

📌 实战示例:快速实现一个索引通讯录

接下来,我们用一个示例来展示 AzListView 的强大之处。

1️⃣ 准备数据

class Contact extends ISuspensionBean {
   
  String name;
  String tagIndex;
  Contact({
   required this.name, required this.tagIndex});

  
  String getSuspensionTag() => tagIndex;
}

List<Contact> contacts = [
  Contact(name: "Alice", tagIndex: "A"),
  Contact(name: "Bob", tagIndex: "B"),
  Contact(name: "Charlie", tagIndex: "C"),
];

2️⃣ 使用 AzListView 渲染 UI

AzListView(
  data: contacts,
  itemBuilder: (context, index) {
   
    final contact = contacts[index];
    return ListTile(
      title: Text(contact.name),
    );
  },
  suspensionWidget: Container(
    alignment: Alignment.centerLeft,
    color: Colors.blue,
    padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
    child: Text("索引", style: TextStyle(color: Colors.white, fontSize: 16)),
  ),
  indexBarOptions: IndexBarOptions(
    indexHintAlignment: Alignment.centerRight,
    indexHintTextStyle: TextStyle(color: Colors.white, fontSize: 24),
    indexHintDecoration: BoxDecoration(
      color: Colors.blueAccent,
      shape: BoxShape.circle,
    ),
  ),
)

3️⃣ 运行效果

运行后,你将会看到一个丝滑的索引列表,点击右侧索引还能快速跳转到相应分组!

🎨 高级玩法:自定义索引栏

如果想让索引栏更符合你的 UI 需求,可以使用 IndexBarOptions 进行自定义。

indexBarOptions: IndexBarOptions(
  needRebuild: true,
  indexBarMargin: EdgeInsets.all(10),
  indexHintOffset: Offset(-20, 0),
  selectTextStyle: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
  selectItemDecoration: BoxDecoration(
    shape: BoxShape.circle,
    color: Colors.redAccent,
  ),
)

💡 总结

AzListView 让 Flutter 开发中的索引列表变得超级简单,无论是通讯录、城市列表还是分类商品,都可以轻松实现。掌握这些用法,你的应用将更加专业、流畅!

🔥 你在开发中遇到哪些列表索引的挑战?欢迎在评论区交流,一起学习进步!

完整示例如下:

import 'package:azlistview/azlistview.dart';
import "package:beehive/constants/constant.dart";
import "package:beehive/controllers/friend/friend_list_logic.dart";
import "package:beehive/controllers/user_manager.dart";
import "package:beehive/pages/constants/user_profile_panel/user_profile_panel_view.dart";
import "package:beehive/protos/ws/ws.pb.dart";
import "package:beehive/routes/routes.dart";
import "package:cached_network_image/cached_network_image.dart";
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pinyin/pinyin.dart';  // 导入拼音库

class FriendListPage extends StatelessWidget {
   
  final FriendListLogic friendListLogic = Get.find<FriendListLogic>();

  
  Widget build(BuildContext context) {
   
    // 获取好友列表并分组
    List<AZFriendItem> azItems = _buildAzItems();
    // 生成字母索引列表
    List<String> indexTags = _getIndexTags(azItems);
    // 初始化 AZListView
    return AzListView(
      data: azItems,
      itemCount: azItems.length,
      itemBuilder: (context, index) {
   
        final item = azItems[index];
        return Container(
          padding: const EdgeInsets.symmetric(horizontal: 4,vertical: 2),
          margin: EdgeInsets.symmetric(vertical: 1), // 为每个分组项添加间距
          decoration: BoxDecoration(
            color: Colors.white,  // 设置背景颜色
            borderRadius: BorderRadius.circular(8),  // 设置圆角
          ),
          child: ListTile(
            leading: _buildAvatar(
              item.info.friendUser.faceURL,
              friendListLogic.getFriendOnlineStatus(item.info.friendUser.userID),
            ),
            title: Text(
              UserManager.to.userNameMap[item.info.friendUser.userID] ?? item.info.friendUser.nickname,
              overflow: TextOverflow.ellipsis,
              style: TextStyle(
                color: Color(0xFFDBA100),
                fontSize: 18,
                fontFamily: 'Alibaba PuHuiTi 3.0',
                fontWeight: FontWeight.w700,
                height: 0.07,
                letterSpacing: -0.32,
              ),
            ),
            onTap: () {
   
              Get.toNamed(
                Routes.userProfilePanel,
                arguments: {
   
                  'userInfo': item.info.friendUser,
                  'actionType': UserDetailActionType.deleteFriend,
                },
              );
            },
          ),
        );
      },
      // 分组悬停头部
      susItemBuilder: (context, index) {
   
        final tag = azItems[index].getSuspensionTag();
        return Container(
            height: 29,
            padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
            clipBehavior: Clip.antiAlias,
            decoration: BoxDecoration(),
            alignment: Alignment.centerLeft,
            child: Row(
              mainAxisSize: MainAxisSize.min,
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Text(
                  tag,
                  style: TextStyle(
                    color: Color(0xFF787878),
                    fontSize: 14,
                    fontFamily: 'Alibaba PuHuiTi 3.0',
                    fontWeight: FontWeight.w500,
                    height: 0.11,
                    letterSpacing: -0.32,
                  ),
                ),
              ],
            ),
          );
      },
      //字母导航栏与其他组件的间距
      indexBarMargin: EdgeInsets.all(0.0),
      indexBarItemHeight: 18, //字母索引项的高度
      indexHintBuilder: (context, hint) => CircleAvatar(
        child: Text(hint, style: TextStyle(color: Colors.white)),
        backgroundColor: Colors.blue,
      ),
      indexBarData: indexTags,
      //定制字母导航栏
      indexBarOptions: IndexBarOptions(
        needRebuild: true,
        selectItemDecoration: BoxDecoration(
          color: Color(0xFFDBA100),  // 选中字母的背景颜色
          shape: BoxShape.circle,
        ),
      ),
    );
  }

  List<AZFriendItem> _buildAzItems() {
   
    List<AZFriendItem> azItems = friendListLogic.friendList.map((friendInfo) {
   
      String tag = friendInfo.friendUser.nickname;

      // 如果昵称为空,则归为 #
      if (tag.isEmpty) {
   
        tag = "#";
      } else {
   
        // 判断第一个字符是否为字母或汉字
        String firstChar = tag[0];

        // 处理中文昵称,获取拼音首字母
        if (isChinese(firstChar)) {
   
          String pinyin = PinyinHelper.getPinyin(tag)[0].toUpperCase();
          // 如果拼音首字母是非字母字符(如符号),则归为 #
          tag = RegExp(r'[A-Z]').hasMatch(pinyin) ? pinyin : '#';
        } else if (RegExp(r'[A-Za-z]').hasMatch(firstChar)) {
   
          // 对于英文,直接使用首字母
          tag = firstChar.toUpperCase();
        } else {
   
          // 对于其他非字母字符(如符号、数字等),归为 #
          tag = '#';
        }
      }

      return AZFriendItem(
        tag: tag,
        info: friendInfo,
      );
    }).toList();

    // 排序并初始化分组标签
    SuspensionUtil.sortListBySuspensionTag(azItems);
    SuspensionUtil.setShowSuspensionStatus(azItems);

    return azItems;
  }

  List<String> _getIndexTags(List<AZFriendItem> azItems) {
   
    // 提取所有标签,并去重
    Set<String> tagSet = {
   };
    for (var item in azItems) {
   
      tagSet.add(item.getSuspensionTag());
    }
    return tagSet.toList()..sort(); // 对标签进行排序
  }

  // 判断字符是否是中文
  bool isChinese(String char) {
   
    final regExp = RegExp(r'[\u4e00-\u9fa5]');
    return regExp.hasMatch(char);
  }


  // 构建头像组件
  Widget _buildAvatar(String? faceURL, bool onlineStatus) {
   
    Widget avatar;
    const double avatarSize = 44.0;
    if (faceURL != null && faceURL.isNotEmpty) {
   
      if (faceURL.startsWith('http')) {
   
        avatar = CachedNetworkImage(
          imageUrl: faceURL,
          placeholder: (context, url) => CircleAvatar(
            backgroundColor: Colors.grey.shade300,
            radius: avatarSize/2,
          ),
          imageBuilder: (context, imageProvider) => CircleAvatar(
            backgroundColor: Colors.grey.shade300,
            backgroundImage: imageProvider,
            radius: avatarSize/2,
          ),
        );
      } else {
   
        avatar = CircleAvatar(
          backgroundColor: Colors.grey.shade300,
          backgroundImage: AssetImage(faceURL),
          radius: avatarSize/2,
        );
      }
    } else {
   
      avatar = CircleAvatar(
        backgroundImage: const AssetImage(defaultAvatar),
        backgroundColor: Colors.grey.shade300,
        radius: avatarSize/2,
      );
    }

    return Stack(
      children: [
        avatar,
        Positioned(
          right: 0,
          bottom: 0,
          child: Container(
            width: 12,
            height: 12,
            decoration: BoxDecoration(
              shape: BoxShape.circle,
              color: onlineStatus ? Colors.green : Colors.grey,
              border: Border.all(
                color: Colors.white,
                width: 2,
              ),
            ),
          ),
        ),
      ],
    );
  }
}

// AZListView 扩展数据模型
class AZFriendItem extends ISuspensionBean {
   
  final String tag;
  final FriendInfo info;

  AZFriendItem({
   required this.tag, required this.info});

  
  String getSuspensionTag() => tag;
}
目录
相关文章
|
11月前
|
前端开发 JavaScript
React 步骤条组件 Stepper 深入解析与常见问题
步骤条组件是构建多步骤表单或流程时的有力工具,帮助用户了解进度并导航。本文介绍了在React中实现简单步骤条的方法,包括基本结构、状态管理、样式处理及常见问题解决策略,如状态管理库的使用、自定义Hook的提取和CSS Modules的应用,以确保组件的健壮性和可维护性。
257 17
|
11月前
|
SQL 关系型数据库 MySQL
深入解析MySQL的EXPLAIN:指标详解与索引优化
MySQL 中的 `EXPLAIN` 语句用于分析和优化 SQL 查询,帮助你了解查询优化器的执行计划。本文详细介绍了 `EXPLAIN` 输出的各项指标,如 `id`、`select_type`、`table`、`type`、`key` 等,并提供了如何利用这些指标优化索引结构和 SQL 语句的具体方法。通过实战案例,展示了如何通过创建合适索引和调整查询语句来提升查询性能。
2230 10
|
9月前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
251 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
11月前
|
容器
Flutter Widget 解析
Flutter Widget 解析
136 2
|
11月前
|
前端开发 UED
React 文本区域组件 Textarea:深入解析与优化
本文介绍了 React 中 Textarea 组件的基础用法、常见问题及优化方法,包括状态绑定、初始值设置、样式自定义、性能优化和跨浏览器兼容性处理,并提供了代码案例。
364 9
|
12月前
|
开发框架 Dart Android开发
安卓与iOS的跨平台开发:Flutter框架深度解析
在移动应用开发的海洋中,Flutter作为一艘灵活的帆船,正引领着开发者们驶向跨平台开发的新纪元。本文将揭开Flutter神秘的面纱,从其架构到核心特性,再到实际应用案例,我们将一同探索这个由谷歌打造的开源UI工具包如何让安卓与iOS应用开发变得更加高效而统一。你将看到,借助Flutter,打造精美、高性能的应用不再是难题,而是变成了一场创造性的旅程。
|
8月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
745 29
|
8月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
235 4
|
8月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
8月前
|
移动开发 前端开发 JavaScript
从入门到精通:H5游戏源码开发技术全解析与未来趋势洞察
H5游戏凭借其跨平台、易传播和开发成本低的优势,近年来发展迅猛。接下来,让我们深入了解 H5 游戏源码开发的技术教程以及未来的发展趋势。

推荐镜像

更多
  • DNS