注册

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

前言:

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

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

话不多说,直接上图:

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

1.gif

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,
    );
    }

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


作者:编程的平行世界
链接:https://juejin.cn/post/7173112836569169927
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册