diff --git a/docs/plans/PROGRESS.md b/docs/plans/PROGRESS.md index 8bbcd43..96d5cf7 100644 --- a/docs/plans/PROGRESS.md +++ b/docs/plans/PROGRESS.md @@ -1220,3 +1220,5 @@ $ flutter test - 第 4 行仅最热/最新在滚动区内,44 圆形筛选按钮固定在滚动区外;`flutter analyze` 无问题。 - 四行统一复用 `_filterRow` 构建,筛选区仅保留一层左右 12 padding。 - 新增量化测试断言四行首项 x 均为 12、相邻 chip 间距均为 10;`flutter analyze` 无问题。 +- 修复横向 viewport 对短内容居中:滚动内容最小宽度撑满视口后由 Row.start 排列。 +- `flutter test test/recommend/all_dramas_filter_alignment_test.dart`:1 项测试通过。 diff --git a/lib/pages/recommend/widgets/all_dramas_filter_grid.dart b/lib/pages/recommend/widgets/all_dramas_filter_grid.dart index 5ddcda2..245f8af 100644 --- a/lib/pages/recommend/widgets/all_dramas_filter_grid.dart +++ b/lib/pages/recommend/widgets/all_dramas_filter_grid.dart @@ -186,26 +186,41 @@ class _AllDramasFilterGridState extends State { }) { return _filterRow( row: row, - child: SingleChildScrollView( - key: Key('all-filter-scroll-$row'), + child: _horizontalChipScroll( + scrollKey: Key('all-filter-scroll-$row'), + chips: [ + for (var index = 0; index < options.length; index++) ...[ + _chip( + key: Key('all-filter-${options[index].id}'), + label: options[index].label, + selected: selected == options[index].id, + onTap: () => onSelected(options[index].id), + ), + if (index < options.length - 1) + const SizedBox(width: AppDimens.filterItemGap), + ], + ], + ), + ); + } + + Widget _horizontalChipScroll({ + required Key scrollKey, + required List chips, + }) { + return LayoutBuilder( + builder: (context, constraints) => SingleChildScrollView( + key: scrollKey, scrollDirection: Axis.horizontal, physics: const BouncingScrollPhysics(), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - for (var index = 0; index < options.length; index++) ...[ - _chip( - key: Key('all-filter-${options[index].id}'), - label: options[index].label, - selected: selected == options[index].id, - onTap: () => onSelected(options[index].id), - ), - if (index < options.length - 1) - const SizedBox(width: AppDimens.filterItemGap), - ], - ], + child: ConstrainedBox( + constraints: BoxConstraints(minWidth: constraints.maxWidth), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: chips, + ), ), ), ); @@ -286,30 +301,23 @@ class _AllDramasFilterGridState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( - child: SingleChildScrollView( - key: const Key('all-filter-scroll-3'), - scrollDirection: Axis.horizontal, - physics: const BouncingScrollPhysics(), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - _chip( - key: const Key('all-sort-hot'), - label: l10n.hot, - selected: !_newest, - onTap: () => setState(() => _newest = false), - ), - const SizedBox(width: AppDimens.filterItemGap), - _chip( - key: const Key('all-sort-new'), - label: l10n.newest, - selected: _newest, - onTap: () => setState(() => _newest = true), - ), - ], - ), + child: _horizontalChipScroll( + scrollKey: const Key('all-filter-scroll-3'), + chips: [ + _chip( + key: const Key('all-sort-hot'), + label: l10n.hot, + selected: !_newest, + onTap: () => setState(() => _newest = false), + ), + const SizedBox(width: AppDimens.filterItemGap), + _chip( + key: const Key('all-sort-new'), + label: l10n.newest, + selected: _newest, + onTap: () => setState(() => _newest = true), + ), + ], ), ), const SizedBox(width: AppDimens.filterItemGap),