56 lines
1.8 KiB
Dart
56 lines
1.8 KiB
Dart
|
|
import 'package:flutter/material.dart';
|
|||
|
|
|
|||
|
|
/// 响应式断点工具:用 [Size.shortestSide] 而非 width 判定窗口尺寸,
|
|||
|
|
/// 使手机横屏(1600×900 → shortSide=900)正确落入 medium 档。
|
|||
|
|
///
|
|||
|
|
/// 详见 docs/手机端UI优化方案-v2.md §7.4。
|
|||
|
|
enum AppWindowSize { compact, medium, expanded }
|
|||
|
|
|
|||
|
|
class AppResponsive {
|
|||
|
|
AppResponsive(this.size, this.orientation);
|
|||
|
|
|
|||
|
|
final Size size;
|
|||
|
|
final Orientation orientation;
|
|||
|
|
|
|||
|
|
factory AppResponsive.of(BuildContext context) => AppResponsive(
|
|||
|
|
MediaQuery.sizeOf(context),
|
|||
|
|
MediaQuery.orientationOf(context),
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
bool get isLandscape => orientation == Orientation.landscape;
|
|||
|
|
|
|||
|
|
AppWindowSize get windowSize => switch (size.shortestSide) {
|
|||
|
|
< 600 => AppWindowSize.compact,
|
|||
|
|
< 840 => AppWindowSize.medium, // ★ 手机横屏 900 落这里
|
|||
|
|
_ => AppWindowSize.expanded,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 横屏且宽度够时启用主从(双栏)布局。
|
|||
|
|
bool get useMasterDetail => size.width >= 840 && isLandscape;
|
|||
|
|
|
|||
|
|
/// 自适应网格列数(列表 / 卡片网格)。
|
|||
|
|
int gridCount({double minTileWidth = 320}) =>
|
|||
|
|
(size.width / minTileWidth).floor().clamp(2, 6);
|
|||
|
|
|
|||
|
|
/// 内容最大宽度(限宽居中,避免横屏拉爆)。
|
|||
|
|
double get contentMaxWidth => switch (windowSize) {
|
|||
|
|
AppWindowSize.compact => double.infinity,
|
|||
|
|
AppWindowSize.medium => 560,
|
|||
|
|
AppWindowSize.expanded => 720,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 栅格间距。
|
|||
|
|
double get gutter => switch (windowSize) {
|
|||
|
|
AppWindowSize.compact => 12,
|
|||
|
|
AppWindowSize.medium => 24,
|
|||
|
|
_ => 32,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// 卡片内边距(横屏更宽松)。
|
|||
|
|
double get cardPadding => windowSize == AppWindowSize.compact ? 16 : 20;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
extension AppResponsiveX on BuildContext {
|
|||
|
|
AppResponsive get responsive => AppResponsive.of(this);
|
|||
|
|
}
|