11import 'dart:math' ;
22
33import 'package:collection/collection.dart' show IterableExtension;
4+ import 'package:flutter/foundation.dart' ;
45import 'package:flutter/material.dart' ;
56import '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
0 commit comments