Flutter — 仅用三个步骤就能帮你把文本变得炫酷!

前言:

前天,一位不愿意透露姓名的朋友找到我,问我怎么样才能把文本变得炫酷一些,他想用图片嵌入到自己的名字里去,用来当作朋友圈的背景。我直接回了一句,你PS下不就好了。他回我一句:想要这样效果的人比较多,全部都PS的话怕不是电脑要干冒烟...能不能用代码自动生成下(请你喝奶茶??)。作为一个乐于助人的人,看到朋友有困难,而且实现起来也不复杂,那我必须要帮忙啊~

注:本文是一篇整活文,让大家看的开心最重要~文章只对核心代码做分析,完整代码在这里

话不多说,直接上图:

填入文本中的可以是手动上传的图片,也可以是彩色小块。

2.png

功能实现步骤分析:

1.数据的获取 — 获取输入的文本数据、获取输入的图片数据。

2.将输入的文本生成为图片

3.解析文本图片,替换像素为图片

简单三步骤,实现朴素到炫酷的转换~

1.数据的获取 — 获取输入的文本数据、获取输入的图片数据。

  • 定义需要存放的数据

    //用于获取输入的文本
    TextEditingController textEditingController = TextEditingController();
    
    //存放输入的图片
    List<File> imagesPath = [];
    
  • 输入框

    3.png

```
Container(
  margin: const EdgeInsets.all(25.0),
  child: TextField(
    controller: textEditingController,
    decoration: const InputDecoration(
        hintText: "请输入文字",
        border: OutlineInputBorder(
            borderRadius: BorderRadius.all(Radius.circular(16.0)))),
  ),
),
```
  • 九宫格图片封装

    4.png
    @override
    Widget build(BuildContext context) {
      var maxWidth = MediaQuery.of(context).size.width;
    
      //计算不同数量时,图片的大小
      var _ninePictureW = (maxWidth - _space * 2 - 2 * _itemSpace - lRSpace);
      ...
    
      return Offstage(
        offstage: imgData!.length == -1,
        child: SizedBox(
          width: _bgWidth,
          height: _bgHeight,
          child: GridView.builder(
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                // 可以直接指定每行(列)显示多少个Item
                crossAxisCount: _crossAxisCount, // 一行的Widget数量
                crossAxisSpacing: _itemSpace, // 水平间距
                mainAxisSpacing: _itemSpace, // 垂直间距
                childAspectRatio: _childAspectRatio, // 子Widget宽高比例
              ),
              // 禁用滚动事件
              physics: const NeverScrollableScrollPhysics(),
              // GridView内边距
              padding: const EdgeInsets.only(left: _space, right: _space),
              itemCount:
                  imgData!.length < 9 ? imgData!.length + 1 : imgData!.length,
              itemBuilder: (context, index) {
                if (imgData!.isEmpty) {
                  return _addPhoto(context);
                } else if (index < imgData!.length) {
                  return _itemCell(context, index);
                } else if (index == imgData!.length) {
                  return _addPhoto(context);
                }
                return SizedBox();
              }),
        ),
      );
    }
    
  • 添加图片

    使用A佬的wechat_assets_picker,要的就是效率~

    Future<void> selectAssets() async {
      //获取图片
      final List<AssetEntity>? result = await AssetPicker.pickAssets(
        context,
      );
      List<File> images = [];
      //循环取出File
      if (result != null) {
        for (int i = 0; i < result.length; i++) {
          AssetEntity asset = result[i];
          File? file = await asset.file;
          if (file != null) {
            images.add(file);
          }
        }
      }
      //更新状态,修改存放File的数组
      setState(() {
        imagesPath = images;
      });
    }
    

2.将输入的文本生成为图片

  • 构建输入的文本布局

    RepaintBoundary(
        key: repaintKey,
        child: Container(
          color: Colors.white,
          width: MediaQuery.of(context).size.width,
          height: 300,
            //image是解析图片的数据
          child: image != null
              ? PhotoLayout(
                  n: 1080,
                  m: 900,
                  image: image!,
                  fileImages: widget.images)
              : 
            //将输入的文本布局
            Center(
                  child: Text(
                    widget.photoText,
                    style: const TextStyle(
                        fontSize: 100, fontWeight: FontWeight.bold),
                  ),
                ),
        )),
    
  • 通过RepaintBoundary将生成的布局生成Uint8List数据

    /// 获取截取图片的数据,并解码
      Future<img.Image?> getImageData() async {
        //生成图片数据
        BuildContext buildContext = repaintKey.currentContext!;
        Uint8List imageBytes;
        RenderRepaintBoundary boundary =
            buildContext.findRenderObject() as RenderRepaintBoundary;
    
        double dpr = ui.window.devicePixelRatio;
        ui.Image image = await boundary.toImage(pixelRatio: dpr);
        // image.width
        ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
        imageBytes = byteData!.buffer.asUint8List();
    
        var tempDir = await getTemporaryDirectory();
        //生成file文件格式
        var file =
            await File('${tempDir.path}/image_${DateTime.now().millisecond}.png')
                .create();
        //转成file文件
        file.writeAsBytesSync(imageBytes);
        //存放生成的图片到本地
        // final result = await ImageGallerySaver.saveFile(file.path);
        return img.decodeImage(imageBytes);
      }
    

3.解析文本图片,替换像素为图片

  • 判断文本像素,在对应像素位置生成图片

    Widget buildPixel(int x, int y) {
      int index = x * n + y;
      //根据给定的x和y坐标,获取像素的颜色编码
      Color color = Color(image.getPixel(y, x));
      //判断是不是白色的像素点,如果是,则用SizedBox替代
      if (color == Colors.white) {
        return const SizedBox.shrink();
      }
      else {
        //如果不是,则代表是文本所在的像素,替换为输入的图片
        return Image.file(
            fileImages![index % fileImages!.length],
            fit: BoxFit.cover,
          );
      }
    }
    
  • 构建最终生成的图片

    @override
    Widget build(BuildContext context) {
      List<Widget> children = [];
        //按点去渲染图片的像素位置,每次加10是因为,图像的像素点很多,如果每一个点都替换为图片,第一是效果不好,第二是渲染的时间很久。
      for (int i = 0; i < n; i = i+10) {
        List<Widget> columnChildren = [];
        for (int x = 0; x < m; x = x+10) {
          columnChildren.add(
            Expanded(
              child: buildPixel(x, i),
            ),
          );
        }
        children.add(Expanded(
            child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: columnChildren,
        )));
      }
      //CrossAxisAlignment.stretch:子控件完全填充交叉轴方向的空间
      return Row(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: children,
      );
    }
    

这样就实现了文本替换为图片的功能啦~

关于我

Hello,我是Taxze,如果您觉得文章对您有价值,希望您能给我的文章点个??,有问题需要联系我的话:我在这里 ,也可以通过掘金的新的私信功能联系到我。如果您觉得文章还差了那么点东西,也请通过关注督促我写出更好的文章~万一哪天我进步了呢???

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

推荐阅读更多精彩内容