Flutter 122: 图解自定义半遮挡头像 SeriesCircleProfile & CircleAvatar

简介: 0 基础学习 Flutter,第一百二十二步:简单实现系列头像半遮挡效果!

    小菜在学习过程中,想实现一行半遮挡的用户头像的功能,横向展示过程中,具体包括 右侧头像逐个半遮挡左侧头像左侧头像逐个半遮挡右侧头像 两种展示;

  • 可展示本地图或网络图;
  • 可自定义末尾图标;
  • 可自定义边框样式;

    整个自定义过程主要是通过基础的 Stack 层级和 Positioned 设置偏移量来完成,小菜仅记录一下在测试过程中遇到的小问题;
g

SeriesCircleProfile

1. 左右半遮挡

    小菜预想的核心功能,是实现不同方向的叠加遮挡效果;其中合适准备采用 Stack 小组件作为头像的层级展示,在通过 Positioned 设置偏移量来设置半遮挡效果;其中 Stack 中的子 Widget 是以栈的方式存储的,即数组后面的元素会覆盖前面的元素;因此左右半遮挡效果主要通过 Positioned 设置偏移量来完成;小菜通过定义 isCoverUp 来判断半遮挡顺序;

1.1 右侧半遮挡左侧

    右侧半遮挡左侧相对较容易,仅需将数组中元素向右侧偏移固定偏移量即可;

List<Widget> _listWid = [Container(height: size)];
for (int i = 0; i < urlList.length; i++) {
  _listWid.add(_itemWid(isAsset ?? false, urlList[i], i, null));
}
return Stack(children: _listWid);

1.2 左侧半遮挡右侧

    左侧半遮挡右侧,小菜通过数组倒序方式,然后再将数组向右侧设置固定偏移量;

List<Widget> _listWid = [Container(height: size)];
for (int i = urlList.length - 1; i >= 0; i--) {
  _listWid.add(_itemWid(isAsset ?? false, urlList[i], i, null));
}
return Stack(children: _listWid);

2. 本地图 & 网络图

    小菜在自定义传递头像 URL 时考虑到,可能是网络图也可能是本地图,甚至是两者混合展示的;主要分为两类:

2.1 纯本地图 & 纯网络图

    小菜设置 isAsseturlList 中公共的图片属性,本地图或网络图;

if (isCoverUp ?? true) {
  if (urlList != null) {
    for (int i = urlList.length - 1; i >= 0; i--) {
      _listWid.add(_itemWid(isAsset ?? false, urlList[i], i, null));
    }
  }
} else {
  if (urlList != null) {
    for (int i = 0; i < urlList.length; i++) {
      _listWid.add(_itemWid(isAsset ?? false, urlList[i], i, null));
    }
  }
}

2.2 本地图片 + 网络图混合

    小菜设置一个 List<Map<bool, String>> 类型的 mixUrlListMap 中存储是否为本地图和图片地址,遍历加载时通过 bool 类型判断即可;

if (isCoverUp ?? true) {
  if (mixUrlList != null) {
    for (int i = mixUrlList.length - 1; i >= 0; i--) {
      _listWid.add(_itemWid(mixUrlList[i].keys.first, mixUrlList[i].values.first, i, null));
    }
  }
} else {
  if (mixUrlList != null) {
    for (int i = 0; i < mixUrlList.length; i++) {
      _listWid.add(_itemWid(mixUrlList[i].keys.first ?? false, mixUrlList[i].values.first, i, null));
    }
  }
}

3. 末尾图标

    在用户头像较多点情况下,很多场景需要一个末尾省略图标,小菜提供了一个 endIconWidget 以供自定义图标的样式,可以是图片或文字或其他等展示效果;值得注意的是,之前小菜设置了 右侧半遮挡左侧左侧半遮挡右侧 两种效果;因此该图标不仅需要对应的偏移量,还需要针对这两种情况先后不同顺序添加在 Stack 栈内;

List<Widget> _listWid = [Container(height: size)];
if (isCoverUp ?? true) {
  if (urlList != null) {
    if (endIcon != null) {
      _listWid.add(_itemWid(isAsset ?? false, null, urlList.length, endIcon));
    }
    for (int i = urlList.length - 1; i >= 0; i--) {
      _listWid.add(_itemWid(isAsset ?? false, urlList[i], i, null));
    }
  } else if (mixUrlList != null) {
    if (endIcon != null) {
      _listWid.add(_itemWid(isAsset ?? false, null, mixUrlList.length, endIcon));
    }
    for (int i = mixUrlList.length - 1; i >= 0; i--) {
      _listWid.add(_itemWid(mixUrlList[i].keys.first, mixUrlList[i].values.first, i, null));
    }
  }
} else {
  if (urlList != null) {
    for (int i = 0; i < urlList.length; i++) {
      _listWid.add(_itemWid(isAsset ?? false, urlList[i], i, null));
    }
    if (endIcon != null) {
      _listWid.add(_itemWid(isAsset ?? false, null, urlList.length, endIcon));
    }
  } else if (mixUrlList != null) {
    for (int i = 0; i < mixUrlList.length; i++) {
      _listWid.add(_itemWid(mixUrlList[i].keys.first ?? false, mixUrlList[i].values.first, i, null));
    }
    if (endIcon != null) {
      _listWid.add(_itemWid(isAsset ?? false, null, mixUrlList.length, endIcon));
    }
  }
}

return Stack(children: _listWid);

4. 自定义 Border

    对于个性化需求,是否需要边框,以及边框颜色和粗细,这些属于相对简单边缘的功能点;小菜予以补充,添加了 isBorder、borderColor 和 borderWidth 属性;

return Positioned(
    left: (spaceWidth ?? _kSpaceWidth) * index,
    child: Container(
        width: size, height: size,
        decoration: BoxDecoration(
            border: (isBorder ?? false) == false ? null : Border.all(color: borderColor ?? _kBorderColor, width: borderWidth ?? _kBorderWidth),
            color: Colors.grey, shape: BoxShape.circle),
        child: PhysicalModel(
            color: Colors.transparent, shape: BoxShape.circle,
            clipBehavior: Clip.antiAlias, borderRadius: BorderRadius.all(Radius.circular(20.0)),
            child: Container(width: size, height: size,
                child: endIcon ?? (asset ? Image.asset(url) : Image.network(url))))));

CircleAvatar

    小菜在设置圆形头像时了解到 CircleAvatar 组件,Flutter 提供了很多绘制圆形的方法,小菜趁此机会简单学习一下 CircleAvatarCircleAvatar 比较特殊,通常用于用户图片和内容一同展示,且为了保持效果一致,给定用户的姓名缩写应始终与相同的背景色配对;

源码分析

const CircleAvatar({
    Key key,
    this.child,             // 子 Widget
    this.backgroundColor,   // 背景色
    this.backgroundImage,   // 背景图
    this.foregroundColor,   // 子 Widget 颜色
    this.radius,            // 半径
    this.minRadius,         // 最小半径
    this.maxRadius,         // 最大半径
})

    简单分析源码可得,主要是通过 BoxConstraints 来限制最大最小半径,而 backgroundImage 来设置背景图;

案例尝试

1. child

    childCircleAvatar 中居中展示的子 Widget,一般是 TextView,用于展示姓名等;若设置图片则不会进行圆形裁切;

return CircleAvatar(radius: 40.0, child: Text(index == 0 ? 'ACE' : 'Hi'));

2. backgroundImage

    backgroundImageCircleAvatar 图片背景,默认居中裁切,注意 backgroundImage 对应的是 ImageProvider 而非 Widget,因此加载图片时只能采用 AssetImageNetworkImage 方式;

return CircleAvatar(
    radius: 40.0,
    backgroundImage: index != 0
        ? NetworkImage('https://locadeserta.com/game/assets/images/background/landing/1.jpg')
        : AssetImage('images/icon_hzw01.jpg'));

3. backgroundColor & foregroundColor

    backgroundColorforegroundColor 分别对应背景颜色和子 child 颜色,除非两个颜色均设置,否则两个背景色会之间会自动匹配;默认 backgroundColor 对应 Theme 的主题颜色;

return CircleAvatar(
    radius: 40.0,
    child: Text(index == 0 ? 'ACE' : 'Hi'),
    backgroundColor: (index == 0) ? null : Colors.deepOrange,
    foregroundColor: (index == 0) ? null : Colors.blue);

4. radius & minRadius & maxRadius

    了解源码可得,CircleAvatar 是通过 BoxConstraints 来限制半径范围的;若设置 radius 则其余两个不生效;默认 minRadius20 像素密度;

return CircleAvatar(
    maxRadius: 50.0,
    child: Text('ACE'),
    backgroundColor: Colors.deepOrange,
    foregroundColor: Colors.white,
    backgroundImage: AssetImage('images/icon_hzw01.jpg'));


    SeriesCircleProfile & CircleAvatar 案例源码


    小菜对于系列折叠头像的自定义较为简单,没有使用复杂的 Canvas 绘制,而是通过 StackPositioned 进行展示,逻辑更为简单;如有错误,请多多指导!

来源: 阿策小和尚
目录
相关文章
|
1月前
|
UED 开发者 容器
Flutter&鸿蒙next 的 Sliver 实现自定义滚动效果
Flutter 提供了强大的滚动组件,如 ListView 和 GridView,但当需要更复杂的滚动效果时,Sliver 组件是一个强大的工具。本文介绍了如何使用 Sliver 实现自定义滚动效果,包括 SliverAppBar、SliverList 等常用组件的使用方法,以及通过 CustomScrollView 组合多个 Sliver 组件实现复杂布局的示例。通过具体代码示例,展示了如何实现带有可伸缩 AppBar 和可滚动列表的页面。
110 1
|
1月前
Flutter 自定义组件继承与调用的高级使用方式
本文深入探讨了 Flutter 中自定义组件的高级使用方式,包括创建基本自定义组件、继承现有组件、使用 Mixins 和组合模式等。通过这些方法,您可以构建灵活、可重用且易于维护的 UI 组件,从而提升开发效率和代码质量。
131 1
|
1月前
|
前端开发 开发者
深入探索 Flutter 鸿蒙版的画笔使用与高级自定义动画
本文深入探讨了 Flutter 中的绘图功能,重点介绍了 CustomPainter 和 Canvas 的使用方法。通过示例代码,详细讲解了如何绘制自定义图形、设置 Paint 对象的属性以及实现高级自定义动画。内容涵盖基本绘图、动画基础、渐变动画和路径动画,帮助读者掌握 Flutter 绘图与动画的核心技巧。
80 1
|
1月前
|
Dart UED 开发者
Flutter&鸿蒙next中的按钮封装:自定义样式与交互
在Flutter应用开发中,按钮是用户界面的重要组成部分。Flutter提供了多种内置按钮组件,但有时这些样式无法满足特定设计需求。因此,封装一个自定义按钮组件变得尤为重要。自定义按钮组件可以确保应用中所有按钮的一致性、可维护性和可扩展性,同时提供更高的灵活性,支持自定义颜色、形状和点击事件。本文介绍了如何创建一个名为CustomButton的自定义按钮组件,并详细说明了其样式、形状、颜色和点击事件的处理方法。
86 1
|
1月前
|
Dart 搜索推荐 API
Flutter & 鸿蒙next版本:自定义对话框与表单验证的动态反馈与错误处理
在现代移动应用开发中,用户体验至关重要。本文探讨了如何在 Flutter 与鸿蒙操作系统(HarmonyOS)中创建自定义对话框,并结合表单验证实现动态反馈与错误处理,提升用户体验。通过自定义对话框和表单验证,开发者可以提供更加丰富和友好的交互体验,同时利用鸿蒙next版本拓展应用的受众范围。
84 1
|
3月前
|
前端开发 搜索推荐
Flutter中自定义气泡框效果的实现
Flutter中自定义气泡框效果的实现
114 3
|
4月前
|
前端开发
Flutter快速实现自定义折线图,支持数据改变过渡动画
Flutter快速实现自定义折线图,支持数据改变过渡动画
109 4
Flutter快速实现自定义折线图,支持数据改变过渡动画
|
4月前
|
开发者 监控 开发工具
如何将JSF应用送上云端?揭秘在Google Cloud Platform上部署JSF应用的神秘步骤
【8月更文挑战第31天】本文详细介绍如何在Google Cloud Platform (GCP) 上部署JavaServer Faces (JSF) 应用。首先,确保已准备好JSF应用并通过Maven构建WAR包。接着,使用Google Cloud SDK登录并配置GCP环境。然后,创建`app.yaml`文件以配置Google App Engine,并使用`gcloud app deploy`命令完成部署。最后,通过`gcloud app browse`访问应用,并利用GCP的监控和日志服务进行管理和故障排查。整个过程简单高效,帮助开发者轻松部署和管理JSF应用。
64 0
|
4月前
|
开发者 容器 Java
Azure云之旅:JSF应用的神秘部署指南,揭开云原生的新篇章!
【8月更文挑战第31天】本文探讨了如何在Azure上部署JavaServer Faces (JSF) 应用,充分发挥其界面构建能力和云平台优势,实现高效安全的Web应用。Azure提供的多种服务如App Service、Kubernetes Service (AKS) 和DevOps简化了部署流程,并支持应用全生命周期管理。文章详细介绍了使用Azure Spring Cloud和App Service部署JSF应用的具体步骤,帮助开发者更好地利用Azure的强大功能。无论是在微服务架构下还是传统环境中,Azure都能为JSF应用提供全面支持,助力开发者拓展技术视野与实践机会。
21 0
|
4月前
|
开发框架 API 开发者
Flutter表单控件深度解析:从基本构建到高级自定义,全方位打造既美观又实用的移动端数据输入体验,让应用交互更上一层楼
【8月更文挑战第31天】在构建美观且功能强大的移动应用时,表单是不可或缺的部分。Flutter 作为热门的跨平台开发框架,提供了丰富的表单控件和 API,使开发者能轻松创建高质量表单。本文通过问题解答形式,深入解读 Flutter 表单控件,并通过具体示例代码展示如何构建优秀的移动应用表单。涵盖创建基本表单、处理表单提交、自定义控件样式、焦点管理和异步验证等内容,适合各水平开发者学习和参考。
111 0