Nailathalang source
I-download ang source ZIP
CH-J SM Mobile
I-browse ang mga direktoryo at file ng isang partikular na release ng application.
CH-J / SM Mobile
175 file
sm-mobile-20261016T185235Z-8e93ecc1b59a
Idinagdag: · Binuo:
1
import 'dart:math' as math;2
import 'dart:ui' show DisplayFeature, DisplayFeatureState, DisplayFeatureType;3
import 'package:flutter/material.dart';5
/// Width classes in logical pixels. Layout never depends on a device model,6
/// physical resolution or devicePixelRatio, only on the space the OS gives us.7
enum WindowClass {8
compact,9
medium,10
expanded,11
large,12
extraLarge;14
static const mediumMin = 600.0,15
expandedMin = 840.0,16
largeMin = 1200.0,17
extraLargeMin = 1600.0;19
static WindowClass ofWidth(double width) => width >= extraLargeMin20
? extraLarge21
: width >= largeMin22
? large23
: width >= expandedMin24
? expanded25
: width >= mediumMin26
? medium27
: compact;29
bool operator >=(WindowClass other) => index >= other.index;30
}32
/// Below this window size the app keeps this logical canvas and scrolls it,33
/// instead of squeezing controls until they overlap or overflow.34
const minimumCanvas = Size(240, 320);36
/// A fold or hinge that splits the window into separately usable regions:37
/// a physical hinge with area, or a fold that is currently half opened.38
/// A flat fold (for example a fully open Galaxy/Pixel Fold) does not split.39
bool isSeparating(DisplayFeature f) =>40
f.type != DisplayFeatureType.cutout &&41
(f.bounds.shortestSide > 0 ||42
f.state == DisplayFeatureState.postureHalfOpened);44
class AdaptiveLayout {45
final Size size;46
final EdgeInsets padding;47
final List<DisplayFeature> features;48
const AdaptiveLayout(49
this.size, {50
this.padding = EdgeInsets.zero,51
this.features = const [],52
});54
factory AdaptiveLayout.of(BuildContext context) => AdaptiveLayout(55
MediaQuery.sizeOf(context),56
padding: MediaQuery.viewPaddingOf(context),57
features: MediaQuery.displayFeaturesOf(context),58
);60
WindowClass get windowClass => WindowClass.ofWidth(size.width);61
bool get landscape => size.width > size.height;63
/// System bars placed on a side edge (landscape phones, or displays whose64
/// status area sits beside the content) favour side navigation.65
bool get sideSystemBars {66
final side = math.max(padding.left, padding.right);67
return side > 0 && side > padding.top;68
}70
/// The first separating feature that crosses the whole window, if any.71
DisplayFeature? get separatingFeature {72
for (final f in features) {73
if (!isSeparating(f)) continue;74
final b = f.bounds;75
final vertical = b.height >= size.height - 1 && b.width < size.width;76
final horizontal = b.width >= size.width - 1 && b.height < size.height;77
if (vertical || horizontal) return f;78
}79
return null;80
}82
/// Rail instead of a bottom bar: enough width, or a short landscape window83
/// where a bottom bar would eat a large part of the height.84
/// A tabletop posture: the fold runs across the window.85
bool get horizontalFold {86
final f = separatingFeature;87
return f != null && f.bounds.width >= size.width - 1;88
}90
/// Rail instead of a bottom bar: enough width, or a short landscape window91
/// where a bottom bar would eat a large part of the height. In a tabletop92
/// posture the bar stays in the lower half, away from the fold.93
bool get useRail =>94
!horizontalFold &&95
(size.width >= WindowClass.mediumMin ||96
(size.width >= 480 && (landscape || sideSystemBars)));98
/// List and detail side by side.99
bool get twoPane =>100
windowClass >= WindowClass.expanded || separatingFeature != null;101
}103
/// Lays [primary] and an optional [secondary] pane out. A separating fold or104
/// hinge always gets a pane on each side and nothing under it; otherwise the105
/// secondary pane takes [secondaryWidth] at the start.106
class FoldAwareSplit extends StatefulWidget {107
final Widget primary;108
final Widget? secondary;109
final double secondaryWidth;110
const FoldAwareSplit({111
required this.primary,112
this.secondary,113
this.secondaryWidth = 360,114
super.key,115
});116
@override117
State<FoldAwareSplit> createState() => _FoldAwareSplitState();118
}120
class _FoldAwareSplitState extends State<FoldAwareSplit> {121
// Display features are in window coordinates; this is where we are in it.122
Rect? global;124
void _measure(_) {125
if (!mounted) return;126
final box = context.findRenderObject() as RenderBox?;127
if (box == null || !box.hasSize) return;128
final next = box.localToGlobal(Offset.zero) & box.size;129
if (next != global) setState(() => global = next);130
}132
@override133
Widget build(BuildContext context) {134
final features = MediaQuery.displayFeaturesOf(context);135
final fold = features.where(isSeparating).firstOrNull;136
if (fold != null) WidgetsBinding.instance.addPostFrameCallback(_measure);137
return LayoutBuilder(138
builder: (context, constraints) {139
final size = constraints.biggest;140
final secondary = widget.secondary;141
Rect? local;142
if (fold != null && global != null && global!.size == size) {143
local = fold.bounds.shift(-global!.topLeft);144
}145
if (local != null) {146
final vertical = local.height >= size.height - 1;147
final spans = vertical148
? local.top <= 1 && local.bottom >= size.height - 1149
: local.left <= 1 && local.right >= size.width - 1;150
final a = vertical ? local.left : local.top;151
final b = vertical152
? size.width - local.right153
: size.height - local.bottom;154
// A fold outside this box, or at its very edge, needs no split.155
if (spans && a >= 1 && b >= 1) {156
final gap = vertical ? local.width : local.height;157
// Vertical (book) fold: list at the start, detail at the end.158
// Horizontal (tabletop) fold: detail above, list below.159
final first = vertical ? secondary : widget.primary;160
final second = vertical ? widget.primary : secondary;161
// Insets on the fold side belong to the other pane.162
Widget pane(Widget? child, {required bool leading}) =>163
MediaQuery.removePadding(164
context: context,165
removeLeft: vertical && !leading,166
removeRight: vertical && leading,167
removeTop: !vertical && !leading,168
removeBottom: !vertical && leading,169
child: child ?? const SizedBox.shrink(),170
);171
return Flex(172
direction: vertical ? Axis.horizontal : Axis.vertical,173
crossAxisAlignment: CrossAxisAlignment.stretch,174
children: [175
SizedBox(176
width: vertical ? a : null,177
height: vertical ? null : a,178
child: pane(first, leading: true),179
),180
SizedBox(181
width: vertical ? gap : null,182
height: vertical ? null : gap,183
),184
Expanded(child: pane(second, leading: false)),185
],186
);187
}188
}189
if (secondary == null) return widget.primary;190
return Row(191
crossAxisAlignment: CrossAxisAlignment.stretch,192
children: [193
SizedBox(194
width: math.min(widget.secondaryWidth, size.width / 2),195
child: secondary,196
),197
const VerticalDivider(width: 1),198
Expanded(child: widget.primary),199
],200
);201
},202
);203
}204
}206
/// Centers scrolling content at a readable width while the scroll area itself207
/// (and its scrollbar) still spans the full width.208
EdgeInsets readablePadding(209
BoxConstraints constraints, {210
double maxWidth = 760,211
double gutter = 20,212
}) {213
// Narrow panes (split screen, one side of a fold) keep more room for content.214
if (constraints.maxWidth < 360) gutter = math.min(gutter, 8);215
final side = math.max(gutter, (constraints.maxWidth - maxWidth) / 2);216
return EdgeInsets.symmetric(horizontal: side, vertical: gutter);217
}219
/// Gives [child] at least [minHeight]; when the space is shorter (software220
/// keyboard in a landscape phone, a tiny split-screen window) the content221
/// scrolls instead of overflowing.222
class MinHeight extends StatelessWidget {223
final double minHeight;224
final Widget child;225
const MinHeight({required this.minHeight, required this.child, super.key});226
@override227
Widget build(BuildContext context) => LayoutBuilder(228
builder: (context, constraints) => constraints.maxHeight >= minHeight229
? child230
: SingleChildScrollView(231
child: SizedBox(height: minHeight, child: child),232
),233
);234
}236
/// The software keyboard leaves so little height that app chrome should step237
/// aside for the content being typed into.238
bool keyboardSqueezed(BuildContext context) {239
final keyboard = MediaQuery.viewInsetsOf(context).bottom;240
return keyboard > 0 && MediaQuery.sizeOf(context).height - keyboard < 360;241
}243
/// Places equal-width tiles into as many columns as fit [minTileWidth].244
class AdaptiveGrid extends StatelessWidget {245
final List<Widget> children;246
final double minTileWidth, spacing;247
final int maxColumns;248
const AdaptiveGrid({249
required this.children,250
this.minTileWidth = 260,251
this.spacing = 10,252
this.maxColumns = 4,253
super.key,254
});255
@override256
Widget build(BuildContext context) => LayoutBuilder(257
builder: (context, constraints) {258
final width = constraints.maxWidth;259
final columns = ((width + spacing) / (minTileWidth + spacing))260
.floor()261
.clamp(1, maxColumns);262
final tile = (width - spacing * (columns - 1)) / columns;263
return Wrap(264
spacing: spacing,265
children: [266
for (final child in children) SizedBox(width: tile, child: child),267
],268
);269
},270
);271
}273
/// Keeps a minimum logical canvas for windows smaller than [minimumCanvas]274
/// (tiny split-screen or freeform windows) and lets the user scroll it.275
class MinimumCanvas extends StatelessWidget {276
final Widget child;277
const MinimumCanvas({required this.child, super.key});278
@override279
Widget build(BuildContext context) {280
final media = MediaQuery.of(context);281
final size = media.size;282
if (size.width >= minimumCanvas.width &&283
size.height >= minimumCanvas.height) {284
return child;285
}286
final canvas = Size(287
math.max(size.width, minimumCanvas.width),288
math.max(size.height, minimumCanvas.height),289
);290
return Material(291
child: SingleChildScrollView(292
scrollDirection: Axis.horizontal,293
child: SingleChildScrollView(294
child: MediaQuery(295
data: media.copyWith(size: canvas),296
child: SizedBox.fromSize(size: canvas, child: child),297
),298
),299
),300
);301
}302
}304
/// Navigation labels follow the system text size up to a limit, so that six305
/// destinations stay reachable; their semantics labels are never truncated.306
Widget clampNavigationText(Widget child) =>307
MediaQuery.withClampedTextScaling(maxScaleFactor: 1.5, child: child);SHA-256
29e557013bea9abc69dfbfdec66e38670adfd3e8584394efbec085da6fbfa015CH-J Proprietary Software License 1.14 · Ibinibigay ang source sa ilalim ng CH-J Proprietary Software License 1.14. Hindi binabago ng pagiging available nito ang mga tuntunin ng lisensya at hindi ito nagbibigay ng karagdagang karapatan.
SHA-256 ng archive
46e427a43e87af091004f36145590bda8fe90086dfb0f631afeb5029fb0b5655