Skip to content

Commit b8314e6

Browse files
committed
Landscape Orientation Support #5
- Create ResponsiveTargetPlatform to support web as a platform. - Initialize and calculate values correctly. - Optimize layout calculation to only calculate when layout changes.
1 parent 3b48b95 commit b8314e6

File tree

2 files changed

+96
-43
lines changed

2 files changed

+96
-43
lines changed

lib/responsive_wrapper.dart

Lines changed: 67 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:math';
22

33
import 'package:collection/collection.dart' show IterableExtension;
4+
import 'package:flutter/foundation.dart';
45
import 'package:flutter/material.dart';
56
import 'package:flutter/widgets.dart';
67

@@ -58,7 +59,7 @@ class ResponsiveWrapper extends StatefulWidget {
5859
/// but is calculated based on the screen width and height.
5960
/// This means that landscape only makes sense on devices that support
6061
/// orientation changes. By default, landscape breakpoints are only
61-
/// active when the [TargetPlatform] is Android, iOS, or Fuchsia.
62+
/// active when the [ResponsiveTargetPlatform] is Android, iOS, or Fuchsia.
6263
/// To enable landscape breakpoints on other platforms, pass a custom
6364
/// list of supported platforms to [landscapePlatforms].
6465
final List<ResponsiveBreakpoint>? breakpointsLandscape;
@@ -67,18 +68,27 @@ class ResponsiveWrapper extends StatefulWidget {
6768
/// By default, only mobile platforms support landscape mode.
6869
/// This override exists primarily to enable custom landscape vs portrait behavior
6970
/// and future compatibility with Fuschia.
70-
final List<TargetPlatform>? landscapePlatforms;
71+
final List<ResponsiveTargetPlatform>? landscapePlatforms;
7172
final double minWidth;
7273
final double? maxWidth;
7374
final String? defaultName;
7475
final bool defaultScale;
7576
final double defaultScaleFactor;
7677

77-
final double minWidthLandscape;
78+
/// Landscape minWidth value. Defaults to [minWidth] if not set.
79+
final double? minWidthLandscape;
80+
81+
/// Landscape maxWidth value. Defaults to [maxWidth] if not set.
7882
final double? maxWidthLandscape;
83+
84+
/// Landscape defaultName value. Defaults to [defaultName] if not set.
7985
final String? defaultNameLandscape;
80-
final bool defaultScaleLandscape;
81-
final double defaultScaleFactorLandscape;
86+
87+
/// Landscape defaultScale value. Defaults to [defaultScale] if not set.
88+
final bool? defaultScaleLandscape;
89+
90+
/// Landscape defaultScaleFactor value. Defaults to [defaultScaleFactor] if not set.
91+
final double? defaultScaleFactorLandscape;
8292

8393
/// An optional background widget to insert behind
8494
/// the responsive content. The background widget
@@ -115,11 +125,11 @@ class ResponsiveWrapper extends StatefulWidget {
115125
this.defaultName,
116126
this.defaultScale = false,
117127
this.defaultScaleFactor = 1,
118-
this.minWidthLandscape = 450,
128+
this.minWidthLandscape,
119129
this.maxWidthLandscape,
120130
this.defaultNameLandscape,
121-
this.defaultScaleLandscape = false,
122-
this.defaultScaleFactorLandscape = 1,
131+
this.defaultScaleLandscape,
132+
this.defaultScaleFactorLandscape,
123133
this.background,
124134
this.backgroundColor,
125135
this.mediaQueryData,
@@ -134,18 +144,18 @@ class ResponsiveWrapper extends StatefulWidget {
134144
static Widget builder(
135145
Widget? child, {
136146
List<ResponsiveBreakpoint>? breakpoints,
137-
List<ResponsiveBreakpoint>? landscapeBreakpoints,
138-
List<TargetPlatform>? landscapePlatforms,
147+
List<ResponsiveBreakpoint>? breakpointsLandscape,
148+
List<ResponsiveTargetPlatform>? landscapePlatforms,
139149
double minWidth = 450,
140150
double? maxWidth,
141151
String? defaultName,
142152
bool defaultScale = false,
143153
double defaultScaleFactor = 1,
144-
double minWidthLandscape = 450,
154+
double? minWidthLandscape,
145155
double? maxWidthLandscape,
146156
String? defaultNameLandscape,
147-
bool defaultScaleLandscape = false,
148-
double defaultScaleFactorLandscape = 1,
157+
bool? defaultScaleLandscape,
158+
double? defaultScaleFactorLandscape,
149159
Widget? background,
150160
Color? backgroundColor,
151161
MediaQueryData? mediaQueryData,
@@ -155,7 +165,7 @@ class ResponsiveWrapper extends StatefulWidget {
155165
return ResponsiveWrapper(
156166
child: child,
157167
breakpoints: breakpoints,
158-
breakpointsLandscape: landscapeBreakpoints,
168+
breakpointsLandscape: breakpointsLandscape,
159169
landscapePlatforms: landscapePlatforms,
160170
minWidth: minWidth,
161171
maxWidth: maxWidth,
@@ -214,8 +224,6 @@ class _ResponsiveWrapperState extends State<ResponsiveWrapper>
214224
MediaQuery.of(context).size.height;
215225
}
216226

217-
TargetPlatform get platform => Theme.of(context).platform;
218-
219227
List<ResponsiveBreakpoint> breakpoints = [];
220228
List<ResponsiveBreakpointSegment> breakpointSegments = [];
221229

@@ -445,31 +453,42 @@ class _ResponsiveWrapperState extends State<ResponsiveWrapper>
445453
? Orientation.landscape
446454
: Orientation.portrait;
447455

448-
static const List<TargetPlatform> _landscapePlatforms = [
449-
TargetPlatform.iOS,
450-
TargetPlatform.android,
451-
TargetPlatform.fuchsia,
456+
static const List<ResponsiveTargetPlatform> _landscapePlatforms = [
457+
ResponsiveTargetPlatform.iOS,
458+
ResponsiveTargetPlatform.android,
459+
ResponsiveTargetPlatform.fuchsia,
452460
];
453461

462+
ResponsiveTargetPlatform? platform;
463+
464+
void setPlatform() {
465+
platform = kIsWeb
466+
? ResponsiveTargetPlatform.web
467+
: Theme.of(context).platform.responsiveTargetPlatform;
468+
}
469+
454470
bool get isLandscapePlatform =>
455-
(widget.landscapePlatforms ?? _landscapePlatforms)
456-
.contains(Theme.of(context).platform);
471+
(widget.landscapePlatforms ?? _landscapePlatforms).contains(platform);
457472

458473
bool get isLandscape =>
459474
orientation == Orientation.landscape &&
460475
isLandscapePlatform &&
461476
widget.breakpointsLandscape != null;
462477

463-
double get minWidth =>
464-
isLandscape ? widget.minWidthLandscape : widget.minWidth;
465-
double? get maxWidth =>
466-
isLandscape ? widget.maxWidthLandscape : widget.maxWidth;
467-
String? get defaultName =>
468-
isLandscape ? widget.defaultNameLandscape : widget.defaultName;
469-
bool get defaultScale =>
470-
isLandscape ? widget.defaultScaleLandscape : widget.defaultScale;
478+
double get minWidth => isLandscape
479+
? (widget.minWidthLandscape ?? widget.minWidth)
480+
: widget.minWidth;
481+
double? get maxWidth => isLandscape
482+
? (widget.maxWidthLandscape ?? widget.maxWidth)
483+
: widget.maxWidth;
484+
String? get defaultName => isLandscape
485+
? (widget.defaultNameLandscape ?? widget.defaultName)
486+
: widget.defaultName;
487+
bool get defaultScale => isLandscape
488+
? (widget.defaultScaleLandscape ?? widget.defaultScale)
489+
: widget.defaultScale;
471490
double get defaultScaleFactor => isLandscape
472-
? widget.defaultScaleFactorLandscape
491+
? (widget.defaultScaleFactorLandscape ?? widget.defaultScaleFactor)
473492
: widget.defaultScaleFactor;
474493

475494
/// Calculate updated dimensions.
@@ -489,12 +508,9 @@ class _ResponsiveWrapperState extends State<ResponsiveWrapper>
489508
/// Get enabled breakpoints based on [orientation] and [platform].
490509
List<ResponsiveBreakpoint> getActiveBreakpoints() {
491510
// If the device is landscape enabled and the current orientation is landscape, use landscape breakpoints.
492-
if (orientation == Orientation.landscape &&
493-
isLandscapePlatform &&
494-
widget.breakpointsLandscape != null) {
511+
if (isLandscape) {
495512
return widget.breakpointsLandscape ?? [];
496513
}
497-
498514
return widget.breakpoints ?? [];
499515
}
500516

@@ -524,18 +540,21 @@ class _ResponsiveWrapperState extends State<ResponsiveWrapper>
524540

525541
/// Set [breakpoints] and [breakpointSegments].
526542
void setBreakpoints() {
527-
breakpoints.clear();
528-
breakpointSegments.clear();
529-
breakpoints.addAll(getActiveBreakpoints());
530-
breakpointSegments.addAll(calcBreakpointSegments(breakpoints));
543+
// Optimization. Only update breakpoints if dimensions have changed.
544+
if ((windowWidth != getWindowWidth()) ||
545+
(windowHeight != getWindowHeight())) {
546+
windowWidth = getWindowWidth();
547+
windowHeight = getWindowHeight();
548+
breakpoints.clear();
549+
breakpointSegments.clear();
550+
breakpoints.addAll(getActiveBreakpoints());
551+
breakpointSegments.addAll(calcBreakpointSegments(breakpoints));
552+
}
531553
}
532554

533555
@override
534556
void initState() {
535557
super.initState();
536-
// Breakpoints must be initialized before the first frame is drawn.
537-
setBreakpoints();
538-
539558
// Log breakpoints to console.
540559
if (widget.debugLog) {
541560
List<ResponsiveBreakpoint> defaultBreakpoints = widget.breakpoints ?? [];
@@ -555,6 +574,8 @@ class _ResponsiveWrapperState extends State<ResponsiveWrapper>
555574
// Dimensions are only available after first frame paint.
556575
WidgetsBinding.instance!.addObserver(this);
557576
WidgetsBinding.instance!.addPostFrameCallback((_) {
577+
// Breakpoints must be initialized before the first frame is drawn.
578+
setBreakpoints();
558579
// Directly updating dimensions is safe because frame callbacks
559580
// in initState are guaranteed.
560581
setDimensions();
@@ -571,14 +592,14 @@ class _ResponsiveWrapperState extends State<ResponsiveWrapper>
571592
@override
572593
void didChangeMetrics() {
573594
super.didChangeMetrics();
574-
setBreakpoints();
575595
// When physical dimensions change, update state.
576596
// The required MediaQueryData is only available
577597
// on the next frame for physical dimension changes.
578598
WidgetsBinding.instance!.addPostFrameCallback((_) {
579599
// Widget could be destroyed by resize. Verify widget
580600
// exists before updating dimensions.
581601
if (mounted) {
602+
setBreakpoints();
582603
setDimensions();
583604
setState(() {});
584605
}
@@ -599,6 +620,9 @@ class _ResponsiveWrapperState extends State<ResponsiveWrapper>
599620

600621
@override
601622
Widget build(BuildContext context) {
623+
// Platform initialization requires context.
624+
setPlatform();
625+
602626
return (screenWidth ==
603627
0) // Initialization check. Window measurements not available until postFrameCallback.
604628
? buildBackgroundColorWidget(widget

lib/utils/responsive_utils.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,32 @@ class ResponsiveUtils {
7979
return stringBuffer.toString();
8080
}
8181
}
82+
83+
enum ResponsiveTargetPlatform {
84+
android,
85+
fuchsia,
86+
iOS,
87+
linux,
88+
macOS,
89+
windows,
90+
web,
91+
}
92+
93+
extension TargetPlatformExtension on TargetPlatform {
94+
ResponsiveTargetPlatform get responsiveTargetPlatform {
95+
switch (this) {
96+
case TargetPlatform.android:
97+
return ResponsiveTargetPlatform.android;
98+
case TargetPlatform.fuchsia:
99+
return ResponsiveTargetPlatform.fuchsia;
100+
case TargetPlatform.iOS:
101+
return ResponsiveTargetPlatform.iOS;
102+
case TargetPlatform.linux:
103+
return ResponsiveTargetPlatform.linux;
104+
case TargetPlatform.macOS:
105+
return ResponsiveTargetPlatform.macOS;
106+
case TargetPlatform.windows:
107+
return ResponsiveTargetPlatform.windows;
108+
}
109+
}
110+
}

0 commit comments

Comments
 (0)