Flutter 重复造轮子 (2) Avatar 头像显示组件的封装

详细可以访问仓库 HcUi: 重复创造Flutter 的轮子 在原有组件上拓展 展现出新的特性 (gitee.com)

介绍

CircleAvatar组件增强版.头像组件,可以适配文字,图片以及图标。可以设置背景颜色为渐变不局限于圆形 可以增加阴影以及边框
根据官方圆形头像组件 修改 不局限于圆形可以变化任意形状 实现方式为AnimatedContainer的 foregroundDecoration


Screenshot_2023-09-07-16-39-51-0900154547.png

代码演示

基础用法

            const HcAvatar(
              radius:10.0,
              size: 20.0,
              backgroundImageUrl: 'assets/images/group.png',
              child: Text("张三"),
            ),

渐变背景

HcAvatar支持传入Gradient来构建渐变背景

            const HcAvatar(
              radius:10.0,
              size: 20.0,
              gradient: LinearGradient(
                colors: [Colors.blue, Colors.yellow],
                stops: [0.1, 0.9],
                transform: GradientRotation(pi / 2),
              ),
              child: Text("张三"),
            ),

展示图标

child组件一般用来展示文字/icon 如果需要展示图片请用backgroundImageUrl/foregroundImageUrl

            const HcAvatar(
              radius:20.0,
              size: 30.0,
              child: Icon(Icons.icecream_outlined),
            ),

增加阴影

            const HcAvatar(
              radius: 20.0,
              size: 40.0,
              gradient: LinearGradient(
                colors: [Colors.blue, Colors.yellow],
                stops: [0.1, 0.9],
                transform: GradientRotation(pi / 2),
              ),
              boxShadow: [
                BoxShadow(
                  color: Colors.amber,
                  offset: Offset(0, 10),
                  blurRadius: 10,
                )
              ],
              child: Icon(Icons.icecream_outlined),
            ),

增加边框

 HcAvatar(
                    borderColor: Colors.purple,
                    borderWidth: 3,
                    foregroundImageUrl: 'assets/images/icon.jpeg',
                  ),

修改大小

  HcAvatar(
                    size: 20,
                    foregroundImageUrl: 'assets/images/icon.jpeg',
                  ),

特殊用法

同时设置前景图和背景图 前景图报错展示背景图 背景图报错展示背景颜色

      HcAvatar(
                    foregroundImageUrl: 'assets/images/i1con.jpeg',
                    backgroundImageUrl: 'assets/images/used.png',
                  ),

只展示文字不展示图片

        HcAvatar(
                    child: Text("张三"),
                  ),

背景图片+文字

   HcAvatar(
                    child: Text("张三"),
                    backgroundImageUrl: 'assets/images/icon.jpeg',
                  ),

渐变背景+Icon

      HcAvatar(
                    child: Icon(Icons.icecream_outlined),
                    gradient: HcGradientUtil.generateLinearGradient(
                        colors: [Colors.purple, Colors.black12]),

                  ),

API

props

参数 说明 类型 默认值 是否必填
size 组件大小 double 40.0 false
radius 头像圆角 double 20.0 false
backgroundColor 图片背景纯色(优先级低于渐变) color - false
borderColor 边框颜色 color - false
borderWidth 边框宽度 double - false
gradient 渐变背景(背景图显示出错时展示) Gradient - false
foregroundColor 前景色 color - false
backgroundImageUrl 背景图片地址(前景图片显示出错展示) String - false
foregroundImageUrl 前景图片地址 String - false
onForegroundImageError 前景图片显示出错的回调 Function - false
onBackgroundImageError 背景图片出错的回调 Function - false
child 背景色上显示的内容 Widget - false
boxShadow 背景阴影 List<BoxShadow> - false

显示顺序

foregroundImageUrl>backgroundImageUrl>gradient>backgroundColor<p>
foregroundColor的作用是调节child的文字颜色

Function

方法名 说明 参数 返回类型
onForegroundImageError/ onBackgroundImageError 背景图片出错的回调 Function(Object exception, StackTrace? stackTrace) void

项目源码

checkAndFixImagePath方法在Flutter 重复造轮子 (1) Image 图片显示组件的封装中有不在此赘述

///  头像组件
class HcAvatar extends StatelessWidget {
  //图片的大小
  final double size;

  //图标的圆角
  final double radius;

  //背景颜色
  final Color? backgroundColor;

  //边框颜色
  final Color? borderColor;

  //边框宽度
  final double borderWidth;

  //渐变色 渐变色颜色优先度大于Color
  // foregroundImageUrl>backgroundImageUrl>gradient>backgroundColor
  final Gradient? gradient;

  //前景色
  final Color? foregroundColor;

  //图片背景Url
  final String? backgroundImageUrl;

  //图片前景Url
  final String? foregroundImageUrl;

  //出错的回调
  final ImageErrorListener? onForegroundImageError;

  //出错的回调
  final ImageErrorListener? onBackgroundImageError;

  //子组件
  final Widget? child;

  //阴影
  final List<BoxShadow>? boxShadow;

  const HcAvatar(
      {Key? key,
      this.size = HcSize.defaultAvatarSize,
      this.radius = HcSize.defaultRadius,
      this.backgroundColor,
      this.foregroundColor,
      this.boxShadow,
      this.borderColor,
      this.borderWidth = HcSize.defaultAvatarBorderWidth,
      this.gradient,
      this.child,
      this.backgroundImageUrl,
      this.foregroundImageUrl,
      this.onForegroundImageError,
      this.onBackgroundImageError})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    final ThemeData theme = Theme.of(context);
    final Color? effectiveForegroundColor = foregroundColor ??
        (theme.useMaterial3 ? theme.colorScheme.onPrimaryContainer : null);
    final TextStyle effectiveTextStyle = theme.useMaterial3
        ? theme.textTheme.titleMedium!
        : theme.primaryTextTheme.titleMedium!;
    TextStyle textStyle =
        effectiveTextStyle.copyWith(color: effectiveForegroundColor);
    Color? effectiveBackgroundColor = backgroundColor ??
        (theme.useMaterial3 ? theme.colorScheme.primaryContainer : null);
    if (effectiveBackgroundColor == null) {
      switch (ThemeData.estimateBrightnessForColor(textStyle.color!)) {
        case Brightness.dark:
          effectiveBackgroundColor = theme.primaryColorLight;
          break;
        case Brightness.light:
          effectiveBackgroundColor = theme.primaryColorDark;
          break;
      }
    } else if (effectiveForegroundColor == null) {
      switch (ThemeData.estimateBrightnessForColor(backgroundColor!)) {
        case Brightness.dark:
          textStyle = textStyle.copyWith(color: theme.primaryColorLight);
          break;
        case Brightness.light:
          textStyle = textStyle.copyWith(color: theme.primaryColorDark);
          break;
      }
    }

    return AnimatedContainer(
      constraints: BoxConstraints(
        minHeight: size,
        minWidth: size,
        maxWidth: size + borderWidth * 2,
        maxHeight: size + borderWidth * 2,
      ),
      duration: kThemeChangeDuration,
      decoration: BoxDecoration(
        boxShadow: boxShadow,
        border: borderColor != null
            ? Border.all(color: borderColor!, width: borderWidth)
            : null,
        gradient: gradient,
        borderRadius:
            BorderRadius.circular((size + borderWidth * 2) / (size / radius)),
        color: effectiveBackgroundColor,
        image: backgroundImageUrl != null
            ? DecorationImage(
                image: _buildImageProvider(backgroundImageUrl),
                onError: onBackgroundImageError,
                fit: BoxFit.cover,
              )
            : null,
      ),
      foregroundDecoration: foregroundImageUrl != null
          ? BoxDecoration(
              image: DecorationImage(
                image: _buildImageProvider(foregroundImageUrl!),
                onError: onForegroundImageError,
                fit: BoxFit.cover,
              ),
              // boxShadow: boxShadow,
              borderRadius: BorderRadius.circular(
                  (size + borderWidth * 2) / (size / radius)),
            )
          : null,
      child: child == null
          ? null
          : Center(
              child: MediaQuery(
                // Need to ignore the ambient textScaleFactor here so that the
                // text doesn't escape the avatar when the textScaleFactor is large.
                data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
                child: IconTheme(
                  data: theme.iconTheme.copyWith(color: textStyle.color),
                  child: DefaultTextStyle(
                    style: textStyle,
                    child: child!,
                  ),
                ),
              ),
            ),
    );
  }

  ImageProvider _buildImageProvider(imagePath) {
    Map<String, dynamic> imageInfo = HCFileUtil.checkAndFixImagePath(imagePath);
    HcImageType type = imageInfo['type'];
    imagePath = imageInfo['url'];
    ImageProvider imageProvider = AssetImage(imagePath);
    switch (type) {
      case HcImageType.assets:
        imageProvider = AssetImage(imagePath);
        break;
      case HcImageType.file:
        imageProvider = FileImage(File(imagePath));
        break;
      case HcImageType.base64:
        Uint8List bytes = const Base64Decoder().convert(imagePath);
        imageProvider = MemoryImage(bytes);
        break;
      case HcImageType.network:
        imageProvider = CachedNetworkImageProvider(imagePath);
        break;
    }

    return imageProvider;
  }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容