From 70cc7622998709081d870387372d5fe429e0499b Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Fri, 6 Sep 2024 20:01:54 +0800 Subject: [PATCH] mod: reply pic showing --- lib/common/widgets/imageview.dart | 61 ++ lib/common/widgets/nine_grid_view.dart | 560 ++++++++++++++++++ .../detail/reply/widgets/reply_item.dart | 257 ++++---- 3 files changed, 756 insertions(+), 122 deletions(-) create mode 100644 lib/common/widgets/imageview.dart create mode 100644 lib/common/widgets/nine_grid_view.dart diff --git a/lib/common/widgets/imageview.dart b/lib/common/widgets/imageview.dart new file mode 100644 index 00000000..027ba4f0 --- /dev/null +++ b/lib/common/widgets/imageview.dart @@ -0,0 +1,61 @@ +import 'dart:math'; + +import 'package:PiliPalaX/common/widgets/network_img_layer.dart'; +import 'package:PiliPalaX/common/widgets/nine_grid_view.dart'; +import 'package:PiliPalaX/pages/preview/view.dart'; +import 'package:flutter/material.dart'; + +Widget image( + double maxWidth, + List picArr, +) { + double imageWidth = (maxWidth - 2 * 5) / 3; + double imageHeight = imageWidth; + if (picArr.length == 1) { + dynamic width = picArr[0]['img_width']; + dynamic height = picArr[0]['img_height']; + double ratioWH = width / height; + double ratioHW = height / width; + double maxRatio = 22 / 9; + imageWidth = ratioWH > 1.5 + ? maxWidth + : (ratioWH >= 1 || (height > width && ratioHW < 1.5)) + ? 2 * imageWidth + : imageWidth; + imageHeight = imageWidth * min(ratioHW, maxRatio); + } + return NineGridView( + type: NineGridType.weiBo, + margin: const EdgeInsets.only(top: 6), + bigImageWidth: imageWidth, + bigImageHeight: imageHeight, + space: 5, + height: picArr.length == 1 ? imageHeight : null, + width: picArr.length == 1 ? imageWidth : maxWidth, + itemCount: picArr.length, + itemBuilder: (context, index) => GestureDetector( + onTap: () { + showDialog( + useSafeArea: false, + context: context, + builder: (context) { + return ImagePreview( + initialPage: index, + imgList: picArr.map((item) => item['img_src'] as String).toList(), + ); + }, + ); + }, + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: NetworkImgLayer( + src: picArr[index]['img_src'], + width: imageWidth, + height: imageWidth, + origAspectRatio: + picArr[index]['img_width'] / picArr[index]['img_height'], + ), + ), + ), + ); +} diff --git a/lib/common/widgets/nine_grid_view.dart b/lib/common/widgets/nine_grid_view.dart new file mode 100644 index 00000000..51fd019c --- /dev/null +++ b/lib/common/widgets/nine_grid_view.dart @@ -0,0 +1,560 @@ +import 'package:flutter/material.dart'; + +import 'dart:async'; +import 'dart:collection'; +import 'dart:math' as math; + +/** + * @Author: Sky24n + * @GitHub: https://github.com/Sky24n + * @Description: NineGridView. + * @Date: 2020/06/16 + */ + +/// NineGridView Type. +enum NineGridType { + /// normal NineGridView. + normal, + + /// like WeChat NineGridView. + weChat, + + /// like WeiBo International NineGridView. + weiBo, + + /// like WeChat group. + weChatGp, + + /// like DingTalk group. + dingTalkGp, + + /// like QQ group. + qqGp, +} + +/// big images size cache map. +Map ngvBigImageSizeMap = HashMap(); + +/// NineGridView. +/// like WeChat, WeiBo International, WeChat group, DingTalk group, QQ group. +/// +/// Another [NineGridView](https://github.com/flutterchina/flukit) in [flukit](https://github.com/flutterchina/flukit) UI Kit,using GridView implementation。 +class NineGridView extends StatefulWidget { + /// create NineGridView. + /// If you want to show a single big picture. + /// It is recommended to use a medium-quality picture, because the original picture is too large and takes time to load. + /// 单张大图建议使用中等质量图片,因为原图太大加载耗时。 + /// you need input (bigImageWidth + bigImageHeight) or (bigImage + bigImageUrl). + const NineGridView({ + super.key, + this.width, + this.height, + this.space = 3, + this.arcAngle = 0, + this.initIndex = 1, + this.padding = EdgeInsets.zero, + this.margin = EdgeInsets.zero, + this.alignment, + this.color, + this.decoration, + this.type = NineGridType.weChat, + required this.itemCount, + required this.itemBuilder, + this.bigImageWidth, + this.bigImageHeight, + this.bigImage, + this.bigImageUrl, + }); + + /// View width. + final double? width; + + /// View height. + final double? height; + + /// The number of logical pixels between each child. + final double space; + + /// QQ group arc angle (0 ~ 180). + final double arcAngle; + + /// QQ group init index (0 or 1). def 1. + final int initIndex; + + /// View padding. + final EdgeInsets padding; + + /// View margin. + final EdgeInsets margin; + + /// Align the [child] within the container. + final AlignmentGeometry? alignment; + + /// The color to paint behind the [child]. + final Color? color; + + /// The decoration to paint behind the [child]. + final Decoration? decoration; + + /// NineGridView type. + final NineGridType type; + + /// The total number of children this delegate can provide. + final int itemCount; + + /// Called to build children for the view. + final IndexedWidgetBuilder itemBuilder; + + /// Single big picture width. + final double? bigImageWidth; + + /// Single big picture height. + final double? bigImageHeight; + + /// It is recommended to use a medium-quality picture, because the original picture is too large and takes time to load. + /// 单张大图建议使用中等质量图片,因为原图太大加载耗时。 + /// Single big picture Image. + final Image? bigImage; + + /// Single big picture url. + final String? bigImageUrl; + + @override + State createState() { + return _NineGridViewState(); + } +} + +/// _NineGridViewState. +class _NineGridViewState extends State { + /// init view size. + Rect _initSize(BuildContext context) { + int itemCount = math.min(9, widget.itemCount); + EdgeInsets padding = widget.padding; + if (itemCount == 0) { + return Rect.fromLTRB(0, 0, padding.horizontal, padding.vertical); + } + double width = widget.width ?? + (MediaQuery.of(context).size.width - widget.margin.horizontal); + width = width - padding.horizontal; + double space = widget.space; + double itemW; + if (widget.type == NineGridType.weiBo && + (itemCount == 1 || itemCount == 2 || itemCount == 4)) { + itemW = (width - space) / 2; + } else { + itemW = (width - space * 2) / 3; + } + bool fourGrid = (itemCount == 4 && widget.type != NineGridType.normal); + int column = fourGrid ? 2 : math.min(3, itemCount); + int row = fourGrid ? 2 : (itemCount / 3).ceil(); + double realWidth = + itemW * column + space * (column - 1) + padding.horizontal; + double realHeight = itemW * row + space * (row - 1) + padding.vertical; + return Rect.fromLTRB(itemW, 0, realWidth, realHeight); + } + + /// build nine grid view. + Widget _buildChild(BuildContext context, double itemW) { + int itemCount = math.min(9, widget.itemCount); + double space = widget.space; + int column = (itemCount == 4 && widget.type != NineGridType.normal) ? 2 : 3; + List list = []; + for (int i = 0; i < itemCount; i++) { + list.add(Positioned( + top: (space + itemW) * (i ~/ column), + left: (space + itemW) * (i % column), + child: SizedBox( + width: itemW, + height: itemW, + child: widget.itemBuilder(context, i), + ))); + } + return Stack( + children: list, + ); + } + + /// build one child. + Widget? _buildOneChild(BuildContext context) { + double? bigImgWidth = widget.bigImageWidth?.toDouble(); + double? bigImgHeight = widget.bigImageHeight?.toDouble(); + if (!_isZero(bigImgWidth) && !_isZero(bigImgHeight)) { + return _getOneChild(context, bigImgWidth!, bigImgHeight!); + } else if (widget.bigImage != null) { + String bigImageUrl = widget.bigImageUrl!; + Rect? bigImgRect = ngvBigImageSizeMap[bigImageUrl]; + bigImgWidth = bigImgRect?.width; + bigImgHeight = bigImgRect?.height; + if (!_isZero(bigImgWidth) && !_isZero(bigImgHeight)) { + return _getOneChild(context, bigImgWidth!, bigImgHeight!); + } else { + _ImageUtil().getImageSize(widget.bigImage)?.then((rect) { + ngvBigImageSizeMap[bigImageUrl] = rect; + if (!mounted) return; + setState(() {}); + }).catchError((e) {}); + } + } + return null; + } + + /// get one child. + Widget _getOneChild(BuildContext context, double width, double height) { + Rect rect = _getBigImgSize(width, height); + return SizedBox( + width: rect.width, + height: rect.height, + child: widget.itemBuilder(context, 0), + ); + } + + /// build weChat group. + Widget _buildWeChatGroup(BuildContext context) { + int itemCount = math.min(9, widget.itemCount); + double width = widget.width! - widget.padding.horizontal; + double space = widget.space; + double itemW; + + int column = itemCount < 5 ? 2 : 3; + int row = 0; + if (itemCount == 1) { + row = 1; + itemW = width; + } else if (itemCount < 5) { + row = itemCount == 2 ? 1 : 2; + itemW = (width - space) / 2; + } else if (itemCount < 7) { + row = 2; + itemW = (width - space * 2) / 3; + } else { + row = 3; + itemW = (width - space * 2) / 3; + } + + int first = itemCount % column; + List list = []; + for (int i = 0; i < itemCount; i++) { + double left; + if (first > 0 && i < first) { + left = (width - itemW * first - space * (first - 1)) / 2 + + (itemW + space) * i; + } else { + left = (space + itemW) * ((i - first) % column); + } + + int itemIndex = (first > 0 && i < first) + ? 0 + : (first > 0 ? (i + column - first) : i) ~/ column; + + double top = (width - itemW * row - space * (row - 1)) / 2 + + (space + itemW) * itemIndex; + + list.add(Positioned( + top: top, + left: left, + child: SizedBox( + width: itemW, + height: itemW, + child: widget.itemBuilder(context, i), + ))); + } + return Stack( + children: list, + ); + } + + /// build dingTalk group. + Widget _buildDingTalkGroup(BuildContext context) { + double width = widget.width! - widget.padding.horizontal; + int itemCount = math.min(4, widget.itemCount); + double itemW = (width - widget.space) / 2; + List children = []; + for (int i = 0; i < itemCount; i++) { + children.add(Positioned( + top: (widget.space + itemW) * (i ~/ 2), + left: (widget.space + itemW) * + (((itemCount == 3 && i == 2) ? i + 1 : i) % 2), + child: SizedBox( + width: itemCount == 1 ? width : itemW, + height: + (itemCount == 1 || itemCount == 2 || (itemCount == 3 && i == 0)) + ? width + : itemW, + child: widget.itemBuilder(context, i), + ))); + } + return ClipOval( + child: Stack( + children: children, + ), + ); + } + + /// build QQ group. + Widget _buildQQGroup(BuildContext context) { + double width = widget.width! - widget.padding.horizontal; + int itemCount = math.min(5, widget.itemCount); + if (itemCount == 1) { + return ClipOval( + child: SizedBox( + width: width, + height: width, + child: widget.itemBuilder(context, 0), + )); + } + + List children = []; + double startDegree = 0; + double r = 0; + double r1 = 0; + double centerX = width / 2; + double centerY = width / 2; + switch (itemCount) { + case 2: + startDegree = 135; + r = width / (2 + 2 * math.sin(math.pi / 4)); + r1 = r; + break; + case 3: + startDegree = 210; + r = width / (2 + 4 * math.sin(math.pi * (3 - 2) / (2 * 3))); + r1 = r / math.cos(math.pi * (3 - 2) / (2 * 3)); + double R = r * + (1 + math.sin(math.pi / itemCount)) / + math.sin(math.pi / itemCount); + double dy = 0.5 * (width - R - r * (1 + 1 / math.tan(math.pi / 3))); + centerY = dy + r + r1; + break; + case 4: + startDegree = 180; + r = width / 4; + r1 = r / math.cos(math.pi / 4); + break; + case 5: + startDegree = 126; + r = width / (2 + 4 * math.sin(math.pi * (5 - 2) / (2 * 5))); + r1 = r / math.cos(math.pi * (5 - 2) / (2 * 5)); + double R = r * + (1 + math.sin(math.pi / itemCount)) / + math.sin(math.pi / itemCount); + double dy = 0.5 * (width - R - r * (1 + 1 / math.tan(math.pi / 5))); + centerY = dy + r + r1; + break; + } + + for (int i = 0; i < itemCount; i++) { + double degree1 = (itemCount == 2 || itemCount == 4) ? (-math.pi / 4) : 0; + double x = centerX + r1 * math.sin(degree1 + i * 2 * math.pi / itemCount); + double y = centerY - r1 * math.cos(degree1 + i * 2 * math.pi / itemCount); + + double degree = startDegree + i * 2 * 180 / itemCount; + if (degree >= 360) degree = degree % 360; + double previousX = r + 2 * r * math.sin(degree / 180 * math.pi); + double previousY = r - 2 * r * math.cos(degree / 180 * math.pi); + + Widget child = Positioned.fromRect( + rect: Rect.fromCircle(center: Offset(x, y), radius: r), + child: ClipPath( + clipper: QQClipper( + total: itemCount, + index: i, + initIndex: widget.initIndex, + previousX: previousX, + previousY: previousY, + degree: degree, + arcAngle: widget.arcAngle, + space: widget.space, + ), + child: widget.itemBuilder(context, i), + ), + ); + children.add(child); + } + + return Stack(children: children); + } + + /// double is zero. + bool _isZero(double? value) { + return value == null || value == 0; + } + + /// get big image size. + Rect _getBigImgSize(double originalWidth, double originalHeight) { + double width = widget.width ?? + (MediaQuery.of(context).size.width - widget.margin.horizontal); + width = width - widget.padding.horizontal; + double itemW = (width - widget.space * 2) / 3; + + //double devicePixelRatio = MediaQuery.of(context)?.devicePixelRatio ?? 3; + double devicePixelRatio = 1.0; + double tempWidth = originalWidth / devicePixelRatio; + double tempHeight = originalHeight / devicePixelRatio; + double maxW = itemW * 2 + widget.space; + double minW = width / 2; + + double relWidth = tempWidth >= maxW ? maxW : math.max(minW, tempWidth); + + double relHeight; + double ratio = tempWidth / tempHeight; + if (tempWidth == tempHeight) { + relHeight = relWidth; + } else if (tempWidth > tempHeight) { + relHeight = relWidth / (math.min(ratio, 4 / 3)); + } else { + relHeight = relWidth / (math.max(ratio, 3 / 4)); + } + return Rect.fromLTRB(0, 0, relWidth, relHeight); + } + + @override + Widget build(BuildContext context) { + Widget? child = Container(); + double? realWidth = widget.width; + double? realHeight = widget.height; + switch (widget.type) { + case NineGridType.normal: + case NineGridType.weiBo: + case NineGridType.weChat: + Rect size = _initSize(context); + if (widget.itemCount == 1) { + child = _buildOneChild(context); + if (child == null) { + realWidth = size.right; + realHeight = size.bottom; + child = _buildChild(context, size.left); + } + } else { + realWidth = size.right; + realHeight = size.bottom; + child = _buildChild(context, size.left); + } + break; + case NineGridType.weChatGp: + child = _buildWeChatGroup(context); + break; + case NineGridType.dingTalkGp: + child = _buildDingTalkGroup(context); + break; + case NineGridType.qqGp: + child = _buildQQGroup(context); + break; + } + return Container( + alignment: widget.alignment, + color: widget.color, + decoration: widget.decoration, + margin: widget.margin, + padding: widget.padding, + width: realWidth, + height: realHeight, + child: child, + ); + } +} + +/// image util. +class _ImageUtil { + late ImageStreamListener listener; + late ImageStream imageStream; + + /// get image size. + Future? getImageSize(Image? image) { + if (image == null) { + return null; + } + Completer completer = Completer(); + listener = ImageStreamListener( + (ImageInfo info, bool synchronousCall) { + imageStream.removeListener(listener); + if (!completer.isCompleted) { + completer.complete(Rect.fromLTWH( + 0, 0, info.image.width.toDouble(), info.image.height.toDouble())); + } + }, + onError: (dynamic exception, StackTrace? stackTrace) { + imageStream.removeListener(listener); + if (!completer.isCompleted) { + completer.completeError(exception, stackTrace); + } + }, + ); + imageStream = image.image.resolve(const ImageConfiguration()); + imageStream.addListener(listener); + return completer.future; + } +} + +/// QQ Clipper. +class QQClipper extends CustomClipper { + QQClipper({ + this.total = 0, + this.index = 0, + this.initIndex = 1, + this.previousX = 0, + this.previousY = 0, + this.degree = 0, + this.arcAngle = 0, + this.space = 0, + }) : assert(arcAngle >= 0 && arcAngle <= 180); + + final int total; + final int index; + final int initIndex; + final double previousX; + final double previousY; + final double degree; + final double arcAngle; + final double space; + + @override + Path getClip(Size size) { + double r = size.width / 2; + Path path = Path(); + List points = []; + + if (total == 2 && index == initIndex) { + path.addOval(Rect.fromLTRB(0, 0, size.width, size.height)); + } else { + /// arcAngle and space, prefer to use arcAngle. + double spaceA = arcAngle > 0 + ? (arcAngle / 2) + : (math.acos((r - math.min(r, space)) / r) / math.pi * 180); + double startA = degree + spaceA; + double endA = degree - spaceA; + for (double i = startA; i <= 360 + endA; i = i + 1) { + double x1 = r + r * math.sin(d2r(i)); + double y1 = r - r * math.cos(d2r(i)); + points.add(Offset(x1, y1)); + } + + double spaceB = math.atan( + r * math.sin(d2r(spaceA)) / (2 * r - r * math.cos(d2r(spaceA)))) / + math.pi * + 180; + double r1 = (2 * r - r * math.cos(d2r(spaceA))) / math.cos(d2r(spaceB)); + double startB = degree - 180 - spaceB; + double endB = degree - 180 + spaceB; + List pointsB = []; + for (double i = startB; i < endB; i = i + 1) { + double x1 = previousX + r1 * math.sin(d2r(i)); + double y1 = previousY - r1 * math.cos(d2r(i)); + pointsB.add(Offset(x1, y1)); + } + points.addAll(pointsB.reversed); + path.addPolygon(points, true); + } + return path; + } + + /// degree to radian. + double d2r(double degree) { + return degree / 180 * math.pi; + } + + @override + bool shouldReclip(CustomClipper oldClipper) { + return this != oldClipper; + } +} diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index f14d80d0..a9a11631 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -1,3 +1,4 @@ +import 'package:PiliPalaX/common/widgets/imageview.dart'; import 'package:PiliPalaX/http/video.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -938,130 +939,142 @@ InlineSpan buildContent( } // 图片渲染 if (content.pictures.isNotEmpty) { - final List picList = []; - final int len = content.pictures.length; + // final List picList = []; + // final int len = content.pictures.length; spanChildren.add(const TextSpan(text: '\n')); - if (len == 1) { - Map pictureItem = content.pictures.first; - picList.add(pictureItem['img_src']); - spanChildren.add( - WidgetSpan( - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints box) { - double maxHeight = box.maxWidth * 0.6; // 设置最大高度 - // double width = (box.maxWidth / 2).truncateToDouble(); - double height = 100; - try { - height = ((box.maxWidth / - 2 * - pictureItem['img_height'] / - pictureItem['img_width'])) - .truncateToDouble(); - } catch (_) {} + spanChildren.add( + WidgetSpan( + child: LayoutBuilder( + builder: (_, constraints) => image( + constraints.maxWidth, + content.pictures, + ), + ), + ), + ); + // if (len == 1) { + // Map pictureItem = content.pictures.first; + // picList.add(pictureItem['img_src']); + // spanChildren.add( + // WidgetSpan( + // child: LayoutBuilder( + // builder: (BuildContext context, BoxConstraints box) { + // double maxHeight = box.maxWidth * 0.6; // 设置最大高度 + // // double width = (box.maxWidth / 2).truncateToDouble(); + // double height = 100; + // try { + // height = ((box.maxWidth / + // 2 * + // pictureItem['img_height'] / + // pictureItem['img_width'])) + // .truncateToDouble(); + // } catch (_) {} - return GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - context: context, - builder: (BuildContext context) { - return ImagePreview(initialPage: 0, imgList: picList); - }, - ); - }, - child: Container( - padding: const EdgeInsets.only(top: 4), - constraints: BoxConstraints(maxHeight: maxHeight), - width: box.maxWidth / 2, - height: height, - child: Stack( - children: [ - Positioned.fill( - child: NetworkImgLayer( - src: pictureItem['img_src'], - width: box.maxWidth / 2, - height: height, - semanticsLabel: '图片1,共1张', - ), - ), - height > Get.size.height * 0.9 - ? const PBadge( - text: '长图', - right: 8, - bottom: 8, - ) - : const SizedBox(), - ], - ), - ), - ); - }, - ), - ), - ); - } else if (len > 1) { - List list = []; - for (var i = 0; i < len; i++) { - picList.add(content.pictures[i]['img_src']); - list.add( - LayoutBuilder( - builder: (context, BoxConstraints box) { - return GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - context: context, - builder: (context) { - return ImagePreview(initialPage: i, imgList: picList); - }, - ); - }, - child: NetworkImgLayer( - src: content.pictures[i]['img_src'], - width: box.maxWidth, - height: box.maxWidth, - origAspectRatio: content.pictures[i]['img_width'] / - content.pictures[i]['img_height'], - semanticsLabel: '图片${i + 1},共$len张', - ), - ); - }, - ), - ); - } - spanChildren.add( - WidgetSpan( - child: Semantics( - explicitChildNodes: true, - container: true, - child: LayoutBuilder( - builder: (context, BoxConstraints box) { - double maxWidth = box.maxWidth; - double crossCount = len < 3 ? 2 : 3; - double height = maxWidth / - crossCount * - (len % crossCount == 0 - ? len ~/ crossCount - : len ~/ crossCount + 1) + - 6; - return Container( - padding: const EdgeInsets.only(top: 6), - height: height, - child: GridView.count( - padding: EdgeInsets.zero, - physics: const NeverScrollableScrollPhysics(), - crossAxisCount: crossCount.toInt(), - mainAxisSpacing: 4.0, - crossAxisSpacing: 4.0, - childAspectRatio: 1, - children: list, - ), - ); - }, - )), - ), - ); - } + // return GestureDetector( + // onTap: () { + // showDialog( + // useSafeArea: false, + // context: context, + // builder: (BuildContext context) { + // return ImagePreview(initialPage: 0, imgList: picList); + // }, + // ); + // }, + // child: Container( + // padding: const EdgeInsets.only(top: 4), + // constraints: BoxConstraints(maxHeight: maxHeight), + // width: box.maxWidth / 2, + // height: height, + // child: Stack( + // children: [ + // Positioned.fill( + // child: NetworkImgLayer( + // src: pictureItem['img_src'], + // width: box.maxWidth / 2, + // height: height, + // semanticsLabel: '图片1,共1张', + // ), + // ), + // height > Get.size.height * 0.9 + // ? const PBadge( + // text: '长图', + // right: 8, + // bottom: 8, + // ) + // : const SizedBox(), + // ], + // ), + // ), + // ); + // }, + // ), + // ), + // ); + // } else if (len > 1) { + // List list = []; + // for (var i = 0; i < len; i++) { + // picList.add(content.pictures[i]['img_src']); + // list.add( + // LayoutBuilder( + // builder: (context, BoxConstraints box) { + // return GestureDetector( + // onTap: () { + // showDialog( + // useSafeArea: false, + // context: context, + // builder: (context) { + // return ImagePreview(initialPage: i, imgList: picList); + // }, + // ); + // }, + // child: NetworkImgLayer( + // src: content.pictures[i]['img_src'], + // width: box.maxWidth, + // height: box.maxWidth, + // origAspectRatio: content.pictures[i]['img_width'] / + // content.pictures[i]['img_height'], + // semanticsLabel: '图片${i + 1},共$len张', + // ), + // ); + // }, + // ), + // ); + // } + // spanChildren.add( + // WidgetSpan( + // // child: NineGridView(), + // child: Semantics( + // explicitChildNodes: true, + // container: true, + // child: LayoutBuilder( + // builder: (context, BoxConstraints box) { + // double maxWidth = box.maxWidth; + // double crossCount = len < 3 ? 2 : 3; + // double height = maxWidth / + // crossCount * + // (len % crossCount == 0 + // ? len ~/ crossCount + // : len ~/ crossCount + 1) + + // 6; + // return Container( + // padding: const EdgeInsets.only(top: 6), + // height: height, + // child: GridView.count( + // padding: EdgeInsets.zero, + // physics: const NeverScrollableScrollPhysics(), + // crossAxisCount: crossCount.toInt(), + // mainAxisSpacing: 4.0, + // crossAxisSpacing: 4.0, + // childAspectRatio: 1, + // children: list, + // ), + // ); + // }, + // ), + // ), + // ), + // ); + // } } // 笔记链接