diff --git a/lib/common/widgets/stat/danmu.dart b/lib/common/widgets/stat/danmu.dart index 2d4b4689..e5803d6b 100644 --- a/lib/common/widgets/stat/danmu.dart +++ b/lib/common/widgets/stat/danmu.dart @@ -31,6 +31,7 @@ class StatDanMu extends StatelessWidget { fontSize: size == 'medium' ? 12 : 11, color: color, ), + overflow: TextOverflow.clip, semanticsLabel: '${Utils.numFormat(danmu!)}条弹幕', ) ], diff --git a/lib/common/widgets/stat/view.dart b/lib/common/widgets/stat/view.dart index b68a6d97..9c6f92a5 100644 --- a/lib/common/widgets/stat/view.dart +++ b/lib/common/widgets/stat/view.dart @@ -34,6 +34,7 @@ class StatView extends StatelessWidget { fontSize: size == 'medium' ? 12 : 11, color: color, ), + overflow: TextOverflow.clip, semanticsLabel: '${Utils.numFormat(view!)}次${goto == "picture" ? "浏览" : "播放"}', ), diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index 6dd0d1d1..962d947b 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -38,96 +38,111 @@ class VideoCardH extends StatelessWidget { final int aid = videoItem.aid; final String bvid = videoItem.bvid; final String heroTag = Utils.makeHeroTag(aid); - return Semantics( - label: Utils.videoItemSemantics(videoItem), - excludeSemantics: true, - child: GestureDetector( - onLongPress: () { - if (longPress != null) { - longPress!(); - } - }, - // onLongPressEnd: (details) { - // if (longPressEnd != null) { - // longPressEnd!(); - // } - // }, - child: InkWell( - onTap: () async { - try { - final int cid = videoItem.cid ?? - await SearchHttp.ab2c(aid: aid, bvid: bvid); - Get.toNamed('/video?bvid=$bvid&cid=$cid', - arguments: {'videoItem': videoItem, 'heroTag': heroTag}); - } catch (err) { - SmartDialog.showToast(err.toString()); + return Stack(children: [ + Semantics( + label: Utils.videoItemSemantics(videoItem), + excludeSemantics: true, + child: GestureDetector( + onLongPress: () { + if (longPress != null) { + longPress!(); } }, - child: Padding( - padding: const EdgeInsets.fromLTRB( - StyleString.safeSpace, 5, StyleString.safeSpace, 5), - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints boxConstraints) { - final double width = (boxConstraints.maxWidth - - StyleString.cardSpace * - 6 / - MediaQuery.textScalerOf(context).scale(1.0)) / - 2; - return Container( - constraints: const BoxConstraints(minHeight: 88), - height: width / StyleString.aspectRatio, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder( - builder: (BuildContext context, - BoxConstraints boxConstraints) { - final double maxWidth = boxConstraints.maxWidth; - final double maxHeight = boxConstraints.maxHeight; - return Stack( - children: [ - Hero( - tag: heroTag, - child: NetworkImgLayer( - src: videoItem.pic as String, - width: maxWidth, - height: maxHeight, + // onLongPressEnd: (details) { + // if (longPressEnd != null) { + // longPressEnd!(); + // } + // }, + child: InkWell( + onTap: () async { + try { + final int cid = videoItem.cid ?? + await SearchHttp.ab2c(aid: aid, bvid: bvid); + Get.toNamed('/video?bvid=$bvid&cid=$cid', + arguments: {'videoItem': videoItem, 'heroTag': heroTag}); + } catch (err) { + SmartDialog.showToast(err.toString()); + } + }, + child: Padding( + padding: const EdgeInsets.fromLTRB( + StyleString.safeSpace, 5, StyleString.safeSpace, 5), + child: LayoutBuilder( + builder: + (BuildContext context, BoxConstraints boxConstraints) { + final double width = (boxConstraints.maxWidth - + StyleString.cardSpace * + 6 / + MediaQuery.textScalerOf(context).scale(1.0)) / + 2; + return Container( + constraints: const BoxConstraints(minHeight: 88), + height: width / StyleString.aspectRatio, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder( + builder: (BuildContext context, + BoxConstraints boxConstraints) { + final double maxWidth = boxConstraints.maxWidth; + final double maxHeight = + boxConstraints.maxHeight; + return Stack( + children: [ + Hero( + tag: heroTag, + child: NetworkImgLayer( + src: videoItem.pic as String, + width: maxWidth, + height: maxHeight, + ), ), - ), - PBadge( - text: Utils.timeFormat(videoItem.duration!), - right: 6.0, - bottom: 6.0, - type: 'gray', - ), - // if (videoItem.rcmdReason != null && - // videoItem.rcmdReason.content != '') - // pBadge(videoItem.rcmdReason.content, context, - // 6.0, 6.0, null, null), - ], - ); - }, + PBadge( + text: + Utils.timeFormat(videoItem.duration!), + right: 6.0, + bottom: 6.0, + type: 'gray', + ), + // if (videoItem.rcmdReason != null && + // videoItem.rcmdReason.content != '') + // pBadge(videoItem.rcmdReason.content, context, + // 6.0, 6.0, null, null), + ], + ); + }, + ), ), - ), - VideoContent( - videoItem: videoItem, - source: source, - showOwner: showOwner, - showView: showView, - showDanmaku: showDanmaku, - showPubdate: showPubdate, - ) - ], - ), - ); - }, + VideoContent( + videoItem: videoItem, + source: source, + showOwner: showOwner, + showView: showView, + showDanmaku: showDanmaku, + showPubdate: showPubdate, + ) + ], + ), + ); + }, + ), ), ), + )), + if (source == 'normal') + Positioned( + bottom: 1, + right: 10, + child: VideoPopupMenu( + size: 30, + iconSize: 16, + videoItem: videoItem, ), - )); + ), + ]); } } @@ -227,6 +242,7 @@ class VideoContent extends StatelessWidget { ), ), ), + const SizedBox(height: 4), Row( children: [ if (showView) ...[ @@ -242,18 +258,10 @@ class VideoContent extends StatelessWidget { danmu: videoItem.stat.danmu as int, ), const Spacer(), - if (source == 'normal') - SizedBox( - width: 24, - height: 24, - child: VideoPopupMenu( - size: 32, - iconSize: 18, - videoItem: videoItem, - ), - ), + if (source == 'normal') const SizedBox(width: 24), ], ), + const SizedBox(height: 2), ], ), ), diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 06fe9e9f..61a9e7ba 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -128,63 +128,74 @@ class VideoCardV extends StatelessWidget { @override Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(videoItem.id); - return Semantics( - label: Utils.videoItemSemantics(videoItem), - excludeSemantics: true, - child: Card( - elevation: 0, - clipBehavior: Clip.hardEdge, - margin: EdgeInsets.zero, - child: GestureDetector( - onLongPress: () { - if (longPress != null) { - longPress!(); - } - }, - // onLongPressEnd: (details) { - // if (longPressEnd != null) { - // longPressEnd!(); - // } - // }, - child: InkWell( - onTap: () async => onPushDetail(heroTag), - child: Column( - children: [ - AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder(builder: (context, boxConstraints) { - double maxWidth = boxConstraints.maxWidth; - double maxHeight = boxConstraints.maxHeight; - return Stack( - children: [ - Hero( - tag: heroTag, - child: NetworkImgLayer( - src: videoItem.pic, - width: maxWidth, - height: maxHeight, + return Stack(children: [ + Semantics( + label: Utils.videoItemSemantics(videoItem), + excludeSemantics: true, + child: Card( + elevation: 0, + clipBehavior: Clip.hardEdge, + margin: EdgeInsets.zero, + child: GestureDetector( + onLongPress: () { + if (longPress != null) { + longPress!(); + } + }, + // onLongPressEnd: (details) { + // if (longPressEnd != null) { + // longPressEnd!(); + // } + // }, + child: InkWell( + onTap: () async => onPushDetail(heroTag), + child: Column( + children: [ + AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder(builder: (context, boxConstraints) { + double maxWidth = boxConstraints.maxWidth; + double maxHeight = boxConstraints.maxHeight; + return Stack( + children: [ + Hero( + tag: heroTag, + child: NetworkImgLayer( + src: videoItem.pic, + width: maxWidth, + height: maxHeight, + ), ), - ), - if (videoItem.duration > 0) - PBadge( - bottom: 6, - right: 7, - size: 'small', - type: 'gray', - text: Utils.timeFormat(videoItem.duration), - // semanticsLabel: - // '时长${Utils.durationReadFormat(Utils.timeFormat(videoItem.duration))}', - ) - ], - ); - }), - ), - VideoContent(videoItem: videoItem) - ], + if (videoItem.duration > 0) + PBadge( + bottom: 6, + right: 7, + size: 'small', + type: 'gray', + text: Utils.timeFormat(videoItem.duration), + // semanticsLabel: + // '时长${Utils.durationReadFormat(Utils.timeFormat(videoItem.duration))}', + ) + ], + ); + }), + ), + VideoContent(videoItem: videoItem) + ], + ), ), - ), - )), - ); + )), + ), + if (videoItem.goto == 'av') + Positioned( + right: 0, + bottom: 1, + child: VideoPopupMenu( + size: 30, + iconSize: 16, + videoItem: videoItem, + )), + ]); } } @@ -203,7 +214,7 @@ class VideoContent extends StatelessWidget { Row( children: [ Expanded( - child: Text(videoItem.title, + child: Text(videoItem.title + "\n", // semanticsLabel: "${videoItem.title}", maxLines: 2, overflow: TextOverflow.ellipsis, @@ -260,23 +271,17 @@ class VideoContent extends StatelessWidget { videoItem.owner.name, // semanticsLabel: "Up主:${videoItem.owner.name}", maxLines: 1, - overflow: TextOverflow.ellipsis, + overflow: TextOverflow.clip, style: TextStyle( + height: 1.5, fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, color: Theme.of(context).colorScheme.outline, ), ), ), - if (videoItem.goto == 'av') ...[ - VideoPopupMenu( - size: 24, - iconSize: 14, - videoItem: videoItem, - ), - ] else ...[ - const SizedBox(height: 24) - ] + if (videoItem.goto == 'av') + const SizedBox(width: 24) ], ), ], diff --git a/lib/models/common/dynamics_type.dart b/lib/models/common/dynamics_type.dart index 337f6aec..f4e20a4b 100644 --- a/lib/models/common/dynamics_type.dart +++ b/lib/models/common/dynamics_type.dart @@ -7,5 +7,5 @@ enum DynamicsType { extension BusinessTypeExtension on DynamicsType { String get values => ['all', 'video', 'pgc', 'article'][index]; - String get labels => ['全部', '视频', '追番', '专栏'][index]; + String get labels => ['全部', '投稿', '番剧', '专栏'][index]; } diff --git a/lib/models/home/rcmd/result.dart b/lib/models/home/rcmd/result.dart index 84cec841..9c4d2186 100644 --- a/lib/models/home/rcmd/result.dart +++ b/lib/models/home/rcmd/result.dart @@ -68,12 +68,10 @@ class RecVideoItemAppModel { ? RcmdReason.fromJson(json['rcmd_reason_style']) : null; // 由于app端api并不会直接返回与owner的关注状态 - // 所以借用推荐原因是否为“已关注”、“新关注”等判别关注状态,从而与web端接口等效 - isFollowed = rcmdReason != null && - rcmdReason!.content != null && - rcmdReason!.content!.contains('关注') - ? 1 - : 0; + // 所以借用推荐原因是否为“已关注”、“新关注”判别关注状态,从而与web端接口等效 + String rcmdReasonContent = rcmdReason?.content ?? ''; + isFollowed = + (rcmdReasonContent == '已关注') || (rcmdReasonContent == '新关注') ? 1 : 0; // 如果是,就无需再显示推荐原因,交由view统一处理即可 if (isFollowed == 1) { rcmdReason = null; diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 0ae43373..2e62a57c 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -1,6 +1,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:PiliPalaX/common/constants.dart'; @@ -324,7 +326,8 @@ class _MemberPageState extends State Image.asset( 'assets/images/lv/lv${_memberController.memberInfo.value.level}.png', height: 11, - semanticLabel: '等级${_memberController.memberInfo.value.level}', + semanticLabel: + '等级${_memberController.memberInfo.value.level}', ), const SizedBox(width: 6), if (_memberController @@ -337,7 +340,8 @@ class _MemberPageState extends State _memberController.memberInfo.value.vip! .label!['img_label_uri_hans'], height: 20, - semanticLabel: _memberController.memberInfo.value.vip!.label!['text'], + semanticLabel: _memberController + .memberInfo.value.vip!.label!['text'], ), ] else if (_memberController .memberInfo.value.vip!.status == @@ -349,9 +353,26 @@ class _MemberPageState extends State _memberController.memberInfo.value.vip! .label!['img_label_uri_hans_static'], height: 20, - semanticLabel: _memberController.memberInfo.value.vip!.label!['text'], + semanticLabel: _memberController + .memberInfo.value.vip!.label!['text'], ), - ] + ], + TextButton( + child: Text("UID ${_memberController.mid}", + style: TextStyle( + color: Theme.of(context) + .colorScheme.secondary.withOpacity(0.5), + fontSize: 12, + // fontWeight: FontWeight.w200, + )), + onPressed: () { + Clipboard.setData( + ClipboardData( + text: _memberController.mid.toString()), + ); + SmartDialog.showToast( + '已复制${_memberController.mid}至剪贴板'); + }), ], ), if (_memberController diff --git a/lib/pages/member/widgets/profile.dart b/lib/pages/member/widgets/profile.dart index ab363875..e25852aa 100644 --- a/lib/pages/member/widgets/profile.dart +++ b/lib/pages/member/widgets/profile.dart @@ -224,7 +224,7 @@ class ProfilePanel extends StatelessWidget { onPressed: () { Get.toNamed('/webview', parameters: { 'url': 'https://account.bilibili.com/account/home', - 'pageTitle': '编辑资料(建议浏览器打开)', + 'pageTitle': '个人中心(建议浏览器打开)', 'type': 'url' }); }, @@ -235,7 +235,7 @@ class ProfilePanel extends StatelessWidget { backgroundColor: Theme.of(context).colorScheme.primary, ), - child: const Text('编辑资料'), + child: const Text('个人中心(web)'), ) ], if (ctr.ownerMid == -1) ...[ diff --git a/lib/pages/member_archive/view.dart b/lib/pages/member_archive/view.dart index 42f5eeb0..52bc9970 100644 --- a/lib/pages/member_archive/view.dart +++ b/lib/pages/member_archive/view.dart @@ -49,7 +49,7 @@ class _MemberArchivePageState extends State { appBar: AppBar( titleSpacing: 0, centerTitle: false, - title: Text('他的投稿', style: Theme.of(context).textTheme.titleMedium), + title: Text('Ta的投稿', style: Theme.of(context).textTheme.titleMedium), actions: [ Obx( () => TextButton.icon( diff --git a/lib/pages/member_dynamics/view.dart b/lib/pages/member_dynamics/view.dart index 19462f22..4937bd66 100644 --- a/lib/pages/member_dynamics/view.dart +++ b/lib/pages/member_dynamics/view.dart @@ -58,7 +58,7 @@ class _MemberDynamicsPageState extends State { appBar: AppBar( titleSpacing: 0, centerTitle: false, - title: Text('他的动态', style: Theme.of(context).textTheme.titleMedium), + title: Text('Ta的动态', style: Theme.of(context).textTheme.titleMedium), ), body: CustomScrollView( physics: const AlwaysScrollableScrollPhysics(), diff --git a/lib/pages/member_seasons/view.dart b/lib/pages/member_seasons/view.dart index c78a4bfb..b26eeaff 100644 --- a/lib/pages/member_seasons/view.dart +++ b/lib/pages/member_seasons/view.dart @@ -43,7 +43,7 @@ class _MemberSeasonsPageState extends State { appBar: AppBar( titleSpacing: 0, centerTitle: false, - title: Text('他的专栏', style: Theme.of(context).textTheme.titleMedium), + title: Text('Ta的专栏', style: Theme.of(context).textTheme.titleMedium), ), body: Padding( padding: const EdgeInsets.only( diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index e347e9de..8022133a 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -156,7 +156,7 @@ class _MinePageState extends State { ), ), ), - const SizedBox(height: 10), + const SizedBox(height: 13), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -173,7 +173,7 @@ class _MinePageState extends State { ), ], ), - const SizedBox(height: 5), + const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -190,7 +190,7 @@ class _MinePageState extends State { ])) ], ), - const SizedBox(height: 25), + const SizedBox(height: 22), if (_mineController.userInfo.value.levelInfo != null) ...[ LayoutBuilder( builder: (context, BoxConstraints box) { @@ -246,7 +246,7 @@ class _MinePageState extends State { }, ), ], - const SizedBox(height: 30), + const SizedBox(height: 26), Padding( padding: const EdgeInsets.only(left: 12, right: 12), child: LayoutBuilder( diff --git a/lib/pages/preview/view.dart b/lib/pages/preview/view.dart index 73bd09ba..23bc8792 100644 --- a/lib/pages/preview/view.dart +++ b/lib/pages/preview/view.dart @@ -279,6 +279,7 @@ class _ImagePreviewState extends State IconButton( onPressed: () => Get.back(), icon: const Icon(Icons.close, color: Colors.white), + tooltip: '关闭', ), ], )), diff --git a/lib/pages/rcmd/controller.dart b/lib/pages/rcmd/controller.dart index 1f1115ff..2a93e636 100644 --- a/lib/pages/rcmd/controller.dart +++ b/lib/pages/rcmd/controller.dart @@ -75,7 +75,7 @@ class RcmdController extends GetxController { _currentPage += 1; // 若videoList数量太小,可能会影响翻页,此时再次请求 // 为避免请求到的数据太少时还在反复请求,要求本次返回数据大于1条才触发 - if (res['data'].length > 1 && videoList.length < 10) { + if (res['data'].length > 1 && videoList.length < 20) { queryRcmdFeed('onLoad'); } } diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index 3babecca..cc07e7e7 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -149,13 +149,15 @@ class _RcmdPageState extends State return SliverGrid( gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( // 行间距 - mainAxisSpacing: StyleString.safeSpace, + mainAxisSpacing: StyleString.cardSpace, // 列间距 crossAxisSpacing: StyleString.safeSpace, // 最大宽度 maxCrossAxisExtent: Grid.maxRowWidth, - mainAxisExtent: Grid.calculateActualWidth(context, Grid.maxRowWidth, StyleString.safeSpace) / StyleString.aspectRatio+ - MediaQuery.textScalerOf(context).scale(96), + mainAxisExtent: Grid.calculateActualWidth( + context, Grid.maxRowWidth, StyleString.safeSpace) / + StyleString.aspectRatio + + MediaQuery.textScalerOf(context).scale(92), ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index c4ef2a2a..41eefa8f 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -214,7 +214,7 @@ class ReplyItem extends StatelessWidget { margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4), child: Semantics( label: replyItem?.content?.message ?? "", - excludeSemantics: true, + // excludeSemantics: true, child: Text.rich( style: const TextStyle(height: 1.75), maxLines: @@ -231,6 +231,7 @@ class ReplyItem extends StatelessWidget { stack: 'normal', type: 'line', fs: 9, + semanticsLabel: '置顶', ), ), buildContent(context, replyItem!, replyReply, null), @@ -389,7 +390,9 @@ class ReplyItemRow extends StatelessWidget { i == 0 && (extraRow == 1 || replies!.length > 1) ? 5 : 6, ), child: Semantics( - label: replies![i].member.uname + ' ' + replies![i].content.message, + label: replies![i].member.uname + + ' ' + + replies![i].content.message, excludeSemantics: true, child: Text.rich( overflow: TextOverflow.ellipsis, @@ -538,11 +541,19 @@ InlineSpan buildContent( final RegExp pattern = RegExp(patternStr); List matchedStrs = []; void addPlainTextSpan(str) { - spanChilds.add(TextSpan( - text: str, - recognizer: TapGestureRecognizer() - ..onTap = () => - replyReply?.call(replyItem.root == 0 ? replyItem : fReplyItem))); + spanChilds.add(WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: ExcludeSemantics( + child: Text( + str, + style: const TextStyle(height: 1.75), + )))); + // TextSpan( + // + // text: str, + // recognizer: TapGestureRecognizer() + // ..onTap = () => replyReply + // ?.call(replyItem.root == 0 ? replyItem : fReplyItem))))); } // 分割文本并处理每个部分 @@ -554,13 +565,14 @@ InlineSpan buildContent( // 处理表情 final int size = content.emote[matchStr]['meta']['size']; spanChilds.add(WidgetSpan( - child: NetworkImgLayer( + child: ExcludeSemantics( + child: NetworkImgLayer( src: content.emote[matchStr]['url'], type: 'emote', width: size * 20, height: size * 20, semanticsLabel: matchStr, - ), + )), )); } else if (matchStr.startsWith("@") && content.atNameToMid.containsKey(matchStr.substring(1))) { @@ -836,6 +848,7 @@ InlineSpan buildContent( src: pictureItem['img_src'], width: box.maxWidth / 2, height: height, + semanticsLabel: '图片1,共1张', ), ), height > Get.size.height * 0.9 @@ -871,11 +884,13 @@ InlineSpan buildContent( ); }, 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']), + 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张', + ), ); }, ), @@ -883,31 +898,34 @@ InlineSpan buildContent( } spanChilds.add( WidgetSpan( - 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, - ), - ); - }, - ), + 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, + ), + ); + }, + )), ), ); }