Files
2026-06-18 15:02:28 +08:00

87 lines
2.8 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:math';
import 'package:flutter/widgets.dart';
import '../theme/spacing.dart';
/// iPad 响应式布局:
/// 宽 ≥ [breakpoint] 进入平板布局——屏边距 36、内容列统一限宽 920 居中
/// `padding: max(36, (宽-920)/2)`)、卡片列表两列(列距 18、行内等高)、
/// 底部 tab 行限宽 600 居中。手机布局完全不变。
abstract class Adaptive {
/// 平板断点(iPhone 最宽 ~440iPad 11″ 竖屏 834 起)
static const double breakpoint = 700;
/// 平板屏边距(demo `--pad-screen: 36px`
static const double padTablet = 36;
/// 内容列限宽(demo 所有页统一 920 居中)
static const double contentMaxWidth = 920;
/// 两列列表的列间距(demo `column-gap: 18px`
static const double columnGap = 18;
/// 底部 tab 行限宽(demo `.nav .tabs{max-width:600px}`
static const double tabsMaxWidth = 600;
static bool isTablet(BuildContext context) =>
MediaQuery.sizeOf(context).width >= breakpoint;
/// 屏水平边距:手机 [Spacing.page];平板 `max(36, (宽-920)/2)`
/// 即内容超 920 时左右对称留白实现居中限宽。
static double hPad(BuildContext context) {
final w = MediaQuery.sizeOf(context).width;
if (w < breakpoint) return Spacing.page;
return max(padTablet, (w - contentMaxWidth) / 2);
}
static EdgeInsets screenPadding(
BuildContext context, {
double top = 0,
double bottom = 0,
}) {
final h = hPad(context);
return EdgeInsets.fromLTRB(h, top, h, bottom);
}
}
/// 卡片列表自适应 sliver:手机单列(卡间距 [Spacing.cardGap]);
/// 平板两列——卡片两两成行、行内等高(IntrinsicHeight + stretch
/// 对应 demo CSS grid `1fr 1fr` 的等高行行为)。
SliverList adaptiveCardSliver(
BuildContext context, {
required int itemCount,
required Widget Function(BuildContext, int) itemBuilder,
double rowGap = Spacing.cardGap,
}) {
if (!Adaptive.isTablet(context)) {
return SliverList.separated(
itemCount: itemCount,
separatorBuilder: (_, _) => SizedBox(height: rowGap),
itemBuilder: itemBuilder,
);
}
final rows = (itemCount + 1) ~/ 2;
return SliverList.separated(
itemCount: rows,
separatorBuilder: (_, _) => SizedBox(height: rowGap),
itemBuilder: (c, r) {
final rightIndex = r * 2 + 1;
return IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(child: itemBuilder(c, r * 2)),
const SizedBox(width: Adaptive.columnGap),
Expanded(
child: rightIndex < itemCount
? itemBuilder(c, rightIndex)
: const SizedBox(),
),
],
),
);
},
);
}