前言:
前天,一位不愿意透露姓名的朋友找到我,问我怎么样才能把文本变得炫酷一些,他想用图片嵌入到自己的名字里去,用来当作朋友圈的背景。我直接回了一句,你PS下不就好了。他回我一句:想要这样效果的人比较多,全部都PS的话怕不是电脑要干冒烟...能不能用代码自动生成下(请你喝奶茶🍹)。作为一个乐于助人的人,看到朋友有困难,而且实现起来也不复杂,那我必须要帮忙啊~
注:本文是一篇整活文,让大家看的开心最重要~文章只对核心代码做分析,完整代码在这里
话不多说,直接上图:
填入文本中的可以是手动上传的图片,也可以是彩色小块。
功能实现步骤分析:
1.数据的获取 — 获取输入的文本数据、获取输入的图片数据。
2.将输入的文本生成为图片
3.解析文本图片,替换像素为图片
简单三步骤,实现朴素到炫酷的转换~
1.数据的获取 — 获取输入的文本数据、获取输入的图片数据。
定义需要存放的数据
//用于获取输入的文本 TextEditingController textEditingController = TextEditingController(); //存放输入的图片 List<File> imagesPath = [];
输入框
Container( margin: const EdgeInsets.all(25.0), child: TextField( controller: textEditingController, decoration: const InputDecoration( hintText: "请输入文字", border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(16.0)))), ), ),
九宫格图片封装
@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,如果您觉得文章对您有价值,希望您能给我的文章点个❤️,有问题需要联系我的话:我在这里 ,也可以通过掘金的新的私信功能联系到我。如果您觉得文章还差了那么点东西,也请通过关注督促我写出更好的文章~万一哪天我进步了呢?😝