Flutter极致的业务封装📦——各类聊天气泡(一)

7,477 阅读6分钟

前言

  真的有段时间没写博客了,因为过去的一段时间工作实在是太忙了😅,但忙也有忙的好处,在整个过程中自己的学习也非常快,在忙碌中充实自己,学习一些新的知识。因为刚刚实现了纯Flutter端的IM功能(太TM难了!🤣)在整个实现过程中,把自己对UI方面的一些理解写成blog。今天!给大家隆重介绍,我的气泡家庭🔮,就是你们看见的 微信里balbla~的聊天气泡了。

Show Time

朴实无华 —— 文字气泡 📒

  首先就是比较normal,🐳 但又是最重要的,文字气泡,这个气泡,其实不需要太多功能,实用、简洁、准确,就能一把抓住人心,简单的说就是让用户觉得很舒服。先来看看我们的成品吧:



  别问我为啥有圆角,因为我们的美工组是这么设计的,有人觉得好看有人觉得丑,但我感觉还可以吧。给大家介绍一下关键点吧,最关键的点就是我们如何控制气泡的宽度和高度,做到文字达到一定长度后自动换行呢 🤔?主角登场——ConstrainedBox,ConstrainedBox是怎样一个Widget呢?顾名思义ConstrainedBox可以给你的Widget加上一些限制,那可太棒了,我们要的效果换个思路其实就是限制气泡的最大宽度,但是我们不限制高度,对吧。所以我们的文字气泡就诞生了🚀

Widget textBubble(String content,Color colors,Color txtColor,double bottomleft,double bottomRight){
  return ConstrainedBox(
    constraints: BoxConstraints(
        maxWidth: 500.w
    ),
    child: Container(
      margin: EdgeInsets.only(top: 10.h),
      padding: EdgeInsets.symmetric(
        horizontal: 34.w,
        vertical: 18.h,
      ),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.only(bottomLeft: Radius.circular(bottomleft), bottomRight: Radius.circular(bottomRight),topRight: Radius.circular(0.0),topLeft: Radius.circular(5.0)),
        color: colors,
      ),
      child: Text(content,style: TextStyle(color:txtColor,fontSize: 28.sp),),
    ),
  );
}

  忽略一些业务数值 📈,来看这个结构,其实我们就是用了一个ConstrainedBox规定了最大宽度,同时定义了四个角的弧度,这两个地方是实现的关键 👀。

细节体现 —— 语音气泡 🔊

  语音是聊天中必不可少的一环,部分用户非常喜欢发语音,如果你只是简单的想做一个语音气泡,很简单,把上面文字气泡里的文字替换为一张语音的图片即可,但是你会发现用户体验非常差,你自己都不想用😑。我个人无法接受这样的产品,所以大家可以跟我一起来改进我们的气泡:

播放动画

  既然谈到用户体验,动画的播放必不可少,用户点了语音气泡单纯的播放声音,用户不知道点的是哪个气泡,用户感受上非常差 🤨。这个动画往往难倒了许多同学。其实没有这么难,我们有美工啊!大家参考微信或者下面的成品图,思考一下,我们是否有必要自己去画一个动画出来?其实没有必要,我们的喇叭,其实就是三张图片的不停替换罢了 💡。


  这样一个语音图片让它动起来 ⛓,我们其实只需要~一个白点的图片、一个白点加圆弧的图片


  看吧,三张图片轮流换就是一个动画了呀!实现方案就是我们的定时器 ⏰,用一个定时器没半秒切换一下图片,亲测效果非常nice 👍

    _timer = new Timer.periodic(new Duration(milliseconds: 500), (timer) {
      if(i == 3)
        {
          i = 1;
        }
      else
        {
          i ++;
        }
     setState(() {
       if(i == 1)
         {
           _voiceImage = "assets/images/voice_point_right.png";
         }
       if(i == 2)
         {
           _voiceImage = "assets/images/voice_step_left.png";
         }
       if(i == 3)
         {
           _voiceImage = "assets/images/voice_left.png";
         }
     });
    });

气泡长度

  其实在用户的交互上还有非常关键的一个小细节,就是这个语音气泡的长度 📐,我们可以自己写一个小算法,让不同的语音时长来控制气泡的长度,这个小的改变会极大的提高页面的美观,这边给大家意思一下,你要控制的气泡宽度,一定要控制最长长度、最短长度,1s 的语音和 100s 的语音很容易让你的长度过长 🕹

 double width = 0.0;
  width = timeLength * 2 * 20.w;
  if(width < 150.w)
  {
    width = 150.w;
  }
  if(width > 400.w)
  {
    width = 400.w;
  }

  最终的我们的语音气泡的效果就是这样了,加上点击动画,真是爱了爱了:

源码

Widget voiceBubble(int timeLength,Color colors,double bottomleft,double bottomRight, String voiceImage){
  double width = 0.0;
  width = timeLength * 2 * 10.w;
  if(width < 150.w)
  {
    width = 150.w;
  }
  if(width > 400.w)
  {
    width = 400.w;
  }
  return   Container(
    margin: EdgeInsets.only(top: 10.h),
    width: width,
    padding: EdgeInsets.symmetric(
      horizontal: 34.w,
      vertical: 18.h,
    ),
    decoration: BoxDecoration(
      borderRadius: BorderRadius.only(bottomLeft: Radius.circular(bottomleft), bottomRight: Radius.circular(bottomRight),topRight: Radius.circular(0.0),topLeft: Radius.circular(5.0)),
      color: colors,
    ),
    child: Row(
      mainAxisAlignment: MainAxisAlignment.end,
      children: [
        Text((timeLength).toString() + "''",style: TextStyle(color: Colors.white),),
        SizedBox(width: 20.w,),
        Image.asset(voiceImage,color: Colors.white,width: 24.w),
      ],
    ),
  );
}

老实敦厚 —— 图片气泡 🖼

  图片气泡也是我们日常聊天中不可或缺的一环,如何让图片给用户进行展示,接下来我将给大家展示如何做一个fantastic的图片气泡!图片气泡,大家也可以先仔细思考需要注意哪几个点,你作为用户,收到了一条图片消息,你最在意的是什么?是什么?

图片的Size

  打开你的相册,翻翻几张照片,你不难发现,除了你用手机相机拍摄的照片,你还会有,从第三方平台下载的,自己剪裁的。那你的气泡也不能固定长宽,需要有强大的动态性。这点的制作我和美工组一起参考了微信的一些方案 🍭。我们给图片规定了最大宽度、最大高度,在这个范围内我们会按照图片本身的大小进行展示,但是超过这个长宽,我们会进行类似压缩,裁切 ✂️ 的操作,让图片看起来尽可能工整。竖向为主的图片依旧保持竖直方向的构图,横向的图片也类似。

图片的展示

  图片肯定需要给用户进行更进一步的展示,让用户进行大图的查看 🚠,对吧,如何进行大图的查看,参考市面上大多数app的做法——新启页面!同时,为了把用户体验做到极致,我们还要给界面跳转,新增跳转动画,就是Flutter提供的,非常适合我们业务场景的Hero动画,用于图片的大图展示 🎱。

图片的缩放

  既然已经到了图片的展示,我们就还要追求更极致的用户体验,我们还让他能缩放!功能直接通过插件即可实现,熟悉一下API即可,但是要注意缩放比例的限制,我直接参看🍎 Apple的缩放限制,没别的意思,Apple的用户体验无人能及。

最终效果:

源码:

Widget imageBubble(BuildContext context,String url){
  final String imgUrl = "";
  return GestureDetector(
    onTap: (){
      Navigator.push(
        context,
        CupertinoPageRoute(
          builder: (context) => HeroPhotoViewRouteWrapper(
              url: url,
              minScale: 0.1,
              maxScale: 1.5,
              imageProvider: NetworkImage(
                  url
              )
          ),
        ),
      );
    },
    child: Hero(
      tag: "${url}",
      child: Container(
        margin: EdgeInsets.only(top: 10.h),
        padding: EdgeInsets.symmetric(
          vertical: 10.h,
        ),
        child: ConstrainedBox(
          constraints: BoxConstraints(maxHeight: 300.h,maxWidth: 400.w),
          child: Container(
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(10.0),
              color: Theme.of(context).brightness == Brightness.light ? Color.fromRGBO(237, 237, 237, 1) : Color.fromRGBO(17, 17, 17, 1),
            ),
            child: ClipRRect(![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e4fa8e3341974cf688fb2238394bda77~tplv-k3u1fbpfcp-watermark.image)
              borderRadius: BorderRadius.circular(10.0),
              child: CachedNetworkImage(
                imageUrl: url,
                placeholder: (context, url) => SizedBox(
                  width: 100.w,
                  height: 100.h,
                  child: Center(
                    child: LoadingIndicator(indicatorType: Indicator.ballClipRotateMultiple, color: Theme.of(context).brightness == Brightness.light ? Color.fromRGBO(61, 93, 237, 1) : Color.fromRGBO(54, 83, 193, 1),),
                  ),
                ),
                errorWidget: (context, url, error) => new Icon(Icons.error),
              ),
            ),
          ),
        ),
      ),
    ),
  );
}

结尾

  以上就是我给各位介绍的我的一部分气泡 🧽,如果各位有兴趣,我后续还会给大家分享更多的美丽复杂气泡,在整个开发过程中一定要注意用户体验,在用户的角度思考 🔫,有些很简单的功能也会在很大程度上改善用户体验哦。

  🐳 欢迎访问我的🔗 github