diff --git a/packages/flutter_adaptive_scaffold/CHANGELOG.md b/packages/flutter_adaptive_scaffold/CHANGELOG.md index 13661bf52e06..9646801d5336 100644 --- a/packages/flutter_adaptive_scaffold/CHANGELOG.md +++ b/packages/flutter_adaptive_scaffold/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.2.1+1 +## 0.2.2 * Fix a bug where landscape would not show body when using `andUp`. diff --git a/packages/flutter_adaptive_scaffold/lib/src/breakpoints.dart b/packages/flutter_adaptive_scaffold/lib/src/breakpoints.dart index 8457252fded4..e51338f2866f 100644 --- a/packages/flutter_adaptive_scaffold/lib/src/breakpoints.dart +++ b/packages/flutter_adaptive_scaffold/lib/src/breakpoints.dart @@ -86,6 +86,30 @@ class Breakpoints { /// A mobile window whose width is greater than 1600 dp. static const Breakpoint extraLargeMobile = Breakpoint.extraLarge(platform: Breakpoint.mobile); + + /// A list of all the standard breakpoints. + static const List all = [ + smallDesktop, + smallMobile, + small, + mediumDesktop, + mediumMobile, + medium, + mediumLargeDesktop, + mediumLargeMobile, + mediumLarge, + largeDesktop, + largeMobile, + large, + extraLargeDesktop, + extraLargeMobile, + extraLarge, + smallAndUp, + mediumAndUp, + mediumLargeAndUp, + largeAndUp, + standard, + ]; } /// A class to define the conditions that distinguish between types of @@ -113,8 +137,8 @@ class Breakpoint { this.endWidth, this.beginHeight, this.endHeight, - this.platform, this.andUp = false, + this.platform, }); /// Returns a [Breakpoint] that can be used as a fallthrough in the @@ -234,73 +258,15 @@ class Breakpoint { static Breakpoint? maybeActiveBreakpointFromSlotLayout(BuildContext context) { final SlotLayout? slotLayout = context.findAncestorWidgetOfExactType(); - Breakpoint? fallbackBreakpoint; - - if (slotLayout != null) { - for (final MapEntry config - in slotLayout.config.entries) { - if (config.key.isActive(context)) { - if (config.key.platform != null) { - return config.key; - } else { - fallbackBreakpoint ??= config.key; - } - } - } - } - return fallbackBreakpoint; + + return slotLayout != null + ? activeBreakpointIn(context, slotLayout.config.keys.toList()) + : null; } /// Returns the default [Breakpoint] based on the [BuildContext]. static Breakpoint defaultBreakpointOf(BuildContext context) { - final TargetPlatform host = Theme.of(context).platform; - final bool isDesktop = Breakpoint.desktop.contains(host); - final bool isMobile = Breakpoint.mobile.contains(host); - - for (final Breakpoint breakpoint in [ - Breakpoints.small, - Breakpoints.medium, - Breakpoints.mediumLarge, - Breakpoints.large, - Breakpoints.extraLarge, - ]) { - if (breakpoint.isActive(context)) { - if (isDesktop) { - switch (breakpoint) { - case Breakpoints.small: - return Breakpoints.smallDesktop; - case Breakpoints.medium: - return Breakpoints.mediumDesktop; - case Breakpoints.mediumLarge: - return Breakpoints.mediumLargeDesktop; - case Breakpoints.large: - return Breakpoints.largeDesktop; - case Breakpoints.extraLarge: - return Breakpoints.extraLargeDesktop; - default: - return Breakpoints.standard; - } - } else if (isMobile) { - switch (breakpoint) { - case Breakpoints.small: - return Breakpoints.smallMobile; - case Breakpoints.medium: - return Breakpoints.mediumMobile; - case Breakpoints.mediumLarge: - return Breakpoints.mediumLargeMobile; - case Breakpoints.large: - return Breakpoints.largeMobile; - case Breakpoints.extraLarge: - return Breakpoints.extraLargeMobile; - default: - return Breakpoints.standard; - } - } else { - return breakpoint; - } - } - } - return Breakpoints.standard; + return activeBreakpointIn(context, Breakpoints.all) ?? Breakpoints.standard; } /// Returns the currently active [Breakpoint]. @@ -308,4 +274,24 @@ class Breakpoint { return maybeActiveBreakpointFromSlotLayout(context) ?? defaultBreakpointOf(context); } + + /// Returns the currently active [Breakpoint] based on the [BuildContext] and + /// a list of [Breakpoint]s. + static Breakpoint? activeBreakpointIn( + BuildContext context, List breakpoints) { + Breakpoint? currentBreakpoint; + + for (final Breakpoint breakpoint in breakpoints) { + if (breakpoint.isActive(context)) { + if (breakpoint.platform != null) { + // Prioritize platform-specific breakpoints. + return breakpoint; + } else { + // Fallback to non-platform-specific. + currentBreakpoint = breakpoint; + } + } + } + return currentBreakpoint; + } } diff --git a/packages/flutter_adaptive_scaffold/lib/src/slot_layout.dart b/packages/flutter_adaptive_scaffold/lib/src/slot_layout.dart index 07cfea33a9d6..e2909792c273 100644 --- a/packages/flutter_adaptive_scaffold/lib/src/slot_layout.dart +++ b/packages/flutter_adaptive_scaffold/lib/src/slot_layout.dart @@ -19,26 +19,11 @@ class SlotLayout extends StatefulWidget { /// be chosen from the config under the context's conditions. static SlotLayoutConfig? pickWidget( BuildContext context, Map config) { - SlotLayoutConfig? chosenWidget; - - for (final Breakpoint breakpoint in config.keys) { - if (breakpoint.isActive(context)) { - final SlotLayoutConfig? pickedWidget = config[breakpoint]; - if (pickedWidget != null) { - if (breakpoint.platform != null) { - // Prioritize platform-specific breakpoints. - return pickedWidget; - } else { - // Fallback to non-platform-specific. - chosenWidget = pickedWidget; - } - } else { - chosenWidget = null; - } - } - } - - return chosenWidget; + final Breakpoint? breakpoint = + Breakpoint.activeBreakpointIn(context, config.keys.toList()); + return breakpoint != null && config.containsKey(breakpoint) + ? config[breakpoint] + : null; } /// Maps [Breakpoint]s to [SlotLayoutConfig]s to determine what Widget to diff --git a/packages/flutter_adaptive_scaffold/pubspec.yaml b/packages/flutter_adaptive_scaffold/pubspec.yaml index f25724a5326d..ad18e0cf9cc9 100644 --- a/packages/flutter_adaptive_scaffold/pubspec.yaml +++ b/packages/flutter_adaptive_scaffold/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_adaptive_scaffold description: Widgets to easily build adaptive layouts, including navigation elements. -version: 0.2.1+1 +version: 0.2.2 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_adaptive_scaffold%22 repository: https://github.com/flutter/packages/tree/main/packages/flutter_adaptive_scaffold diff --git a/packages/flutter_adaptive_scaffold/test/breakpoint_test.dart b/packages/flutter_adaptive_scaffold/test/breakpoint_test.dart index 32d39798a9e7..4b27275bbebf 100644 --- a/packages/flutter_adaptive_scaffold/test/breakpoint_test.dart +++ b/packages/flutter_adaptive_scaffold/test/breakpoint_test.dart @@ -508,6 +508,48 @@ void main() { }, variant: TargetPlatformVariant.desktop()); }); + group('Portrait and Landscape Mixed Layout Tests', () { + testWidgets( + 'Layout for smallPortraitMediumLandscape shows correct slot configuration', + (WidgetTester tester) async { + await tester.pumpWidget( + SimulatedLayout.smallPortraitMediumLandscape.slot(tester)); + await tester.pumpAndSettle(); + expect(find.byKey(const Key('Breakpoints.smallMobile')), findsOneWidget); + expect(find.byKey(const Key('Breakpoints.mediumMobile')), findsNothing); + }); + + testWidgets( + 'Layout for smallLandscapeMediumPortrait shows correct slot configuration', + (WidgetTester tester) async { + await tester.pumpWidget( + SimulatedLayout.smallLandscapeMediumPortrait.slot(tester)); + await tester.pumpAndSettle(); + expect(find.byKey(const Key('Breakpoints.smallMobile')), findsOneWidget); + expect(find.byKey(const Key('Breakpoints.mediumMobile')), findsNothing); + }); + + testWidgets( + 'Layout for smallPortraitMediumLargeLandscape shows correct slot configuration', + (WidgetTester tester) async { + await tester.pumpWidget( + SimulatedLayout.smallPortraitMediumLargeLandscape.slot(tester)); + await tester.pumpAndSettle(); + expect(find.byKey(const Key('Breakpoints.smallMobile')), findsOneWidget); + expect(find.byKey(const Key('Breakpoints.largeMobile')), findsNothing); + }); + + testWidgets( + 'Layout for smallLandscapeMediumLargePortrait shows correct slot configuration', + (WidgetTester tester) async { + await tester.pumpWidget( + SimulatedLayout.smallLandscapeMediumLargePortrait.slot(tester)); + await tester.pumpAndSettle(); + expect(find.byKey(const Key('Breakpoints.smallMobile')), findsOneWidget); + expect(find.byKey(const Key('Breakpoints.largeMobile')), findsNothing); + }); + }); + group('Slot And Up Layout Tests', () { testWidgets('slotAndUp shows correct slot for small layout', (WidgetTester tester) async { diff --git a/packages/flutter_adaptive_scaffold/test/simulated_layout.dart b/packages/flutter_adaptive_scaffold/test/simulated_layout.dart index a378ad1b6a36..46a7d33f2b88 100644 --- a/packages/flutter_adaptive_scaffold/test/simulated_layout.dart +++ b/packages/flutter_adaptive_scaffold/test/simulated_layout.dart @@ -115,7 +115,15 @@ enum SimulatedLayout { width: 1100, height: 900, navSlotKey: 'primaryNavigation1'), largeLandscape(width: 1400, height: 1000, navSlotKey: 'primaryNavigation2'), extraLargeLandscape( - width: 1700, height: 1000, navSlotKey: 'primaryNavigation3'); + width: 1700, height: 1000, navSlotKey: 'primaryNavigation3'), + smallPortraitMediumLandscape( + width: 360, height: 650, navSlotKey: 'bottomNavigation'), + smallLandscapeMediumPortrait( + width: 650, height: 360, navSlotKey: 'bottomNavigation'), + smallPortraitMediumLargeLandscape( + width: 360, height: 900, navSlotKey: 'bottomNavigation'), + smallLandscapeMediumLargePortrait( + width: 900, height: 360, navSlotKey: 'bottomNavigation'); const SimulatedLayout({ required double width,