Skip to content

Commit

Permalink
[flutter_adaptive_scaffold] Add correct material spacing and panes (#…
Browse files Browse the repository at this point in the history
…7428)

*Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.*

https://m3.material.io/foundations/layout/understanding-layout/parts-of-layout
https://m3.material.io/foundations/layout/understanding-layout/spacing

*List which issues are fixed by this PR. You must list at least one issue.*
  • Loading branch information
martijn00 authored Aug 29, 2024
1 parent de42d8e commit c765f57
Show file tree
Hide file tree
Showing 7 changed files with 414 additions and 80 deletions.
5 changes: 5 additions & 0 deletions packages/flutter_adaptive_scaffold/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.2.3

* Update the spacing and margins to the latest material m3 specs.
* Add `margin`, `spacing`, `padding`, `recommendedPanes` and `maxPanes` with their Material value to `Breakpoint`.

## 0.2.2

* Fix a bug where landscape would not show body when using `andUp`.
Expand Down
72 changes: 27 additions & 45 deletions packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,25 @@ import 'adaptive_layout.dart';
import 'breakpoints.dart';
import 'slot_layout.dart';

/// Gutter value between different parts of the body slot depending on
/// material 3 design spec.
const double kMaterialGutterValue = 8;
/// Spacing value of the compact breakpoint according to
/// the material 3 design spec.
const double kMaterialCompactSpacing = 0;

/// Margin value of the compact breakpoint layout according to the material
/// Spacing value of the medium and up breakpoint according to
/// the material 3 design spec.
const double kMaterialMediumAndUpSpacing = 24;

/// Margin value of the compact breakpoint according to the material
/// design 3 spec.
const double kMaterialCompactMinMargin = 8;
const double kMaterialCompactMargin = 16;

/// Margin value of the medium breakpoint layout according to the material
/// Margin value of the medium breakpoint according to the material
/// design 3 spec.
const double kMaterialMediumMinMargin = 12;
const double kMaterialMediumAndUpMargin = 24;

//// Margin value of the expanded breakpoint layout according to the material
/// Padding value of the compact breakpoint according to the material
/// design 3 spec.
const double kMaterialExpandedMinMargin = 32;
const double kMaterialPadding = 4;

/// Signature for a builder used by [AdaptiveScaffold.navigationRailDestinationBuilder] that converts a
/// [NavigationDestination] to a [NavigationRailDestination].
Expand Down Expand Up @@ -429,40 +433,19 @@ class AdaptiveScaffold extends StatefulWidget {
/// Public helper method to be used for creating a staggered grid following m3
/// specs from a list of [Widget]s
static Builder toMaterialGrid({
List<Widget> thisWidgets = const <Widget>[],
List<Breakpoint> breakpoints = const <Breakpoint>[
Breakpoints.small,
Breakpoints.medium,
Breakpoints.mediumLarge,
Breakpoints.large,
Breakpoints.extraLarge,
],
double margin = 8,
int itemColumns = 1,
required BuildContext context,
List<Widget> widgets = const <Widget>[],
List<Breakpoint> breakpoints = Breakpoints.all,
double? margin,
int? itemColumns,
}) {
return Builder(builder: (BuildContext context) {
Breakpoint? currentBreakpoint;
for (final Breakpoint breakpoint in breakpoints) {
if (breakpoint.isActive(context)) {
currentBreakpoint = breakpoint;
}
}
double? thisMargin = margin;
final Breakpoint? currentBreakpoint =
Breakpoint.activeBreakpointIn(context, breakpoints);
final double thisMargin =
margin ?? currentBreakpoint?.margin ?? kMaterialCompactMargin;
final int thisColumns =
itemColumns ?? currentBreakpoint?.recommendedPanes ?? 1;

if (currentBreakpoint == Breakpoints.small) {
if (thisMargin < kMaterialCompactMinMargin) {
thisMargin = kMaterialCompactMinMargin;
}
} else if (currentBreakpoint == Breakpoints.medium) {
if (thisMargin < kMaterialMediumMinMargin) {
thisMargin = kMaterialMediumMinMargin;
}
} else if (currentBreakpoint == Breakpoints.mediumLarge) {
if (thisMargin < kMaterialExpandedMinMargin) {
thisMargin = kMaterialExpandedMinMargin;
}
}
return CustomScrollView(
primary: false,
controller: ScrollController(),
Expand All @@ -473,11 +456,10 @@ class AdaptiveScaffold extends StatefulWidget {
child: Padding(
padding: EdgeInsets.all(thisMargin),
child: _BrickLayout(
columns: itemColumns,
columnSpacing: kMaterialGutterValue,
itemPadding:
const EdgeInsets.only(bottom: kMaterialGutterValue),
children: thisWidgets,
columns: thisColumns,
columnSpacing: thisMargin,
itemPadding: EdgeInsets.only(bottom: thisMargin),
children: widgets,
),
),
),
Expand Down
60 changes: 55 additions & 5 deletions packages/flutter_adaptive_scaffold/lib/src/breakpoints.dart
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ class Breakpoint {
this.endHeight,
this.andUp = false,
this.platform,
this.spacing = kMaterialMediumAndUpSpacing,
this.margin = kMaterialMediumAndUpMargin,
this.padding = kMaterialPadding,
this.recommendedPanes = 1,
this.maxPanes = 1,
});

/// Returns a [Breakpoint] that can be used as a fallthrough in the
Expand All @@ -148,42 +153,72 @@ class Breakpoint {
endWidth = null,
beginHeight = null,
endHeight = null,
spacing = kMaterialMediumAndUpSpacing,
margin = kMaterialMediumAndUpMargin,
padding = kMaterialPadding,
recommendedPanes = 1,
maxPanes = 1,
andUp = true;

/// Returns a [Breakpoint] with the given constraints for a small screen.
const Breakpoint.small({this.andUp = false, this.platform})
: beginWidth = 0,
endWidth = 600,
beginHeight = null,
endHeight = 480;
endHeight = 480,
spacing = kMaterialCompactSpacing,
margin = kMaterialCompactMargin,
padding = kMaterialPadding,
recommendedPanes = 1,
maxPanes = 1;

/// Returns a [Breakpoint] with the given constraints for a medium screen.
const Breakpoint.medium({this.andUp = false, this.platform})
: beginWidth = 600,
endWidth = 840,
beginHeight = 480,
endHeight = 900;
endHeight = 900,
spacing = kMaterialMediumAndUpSpacing,
margin = kMaterialMediumAndUpMargin,
padding = kMaterialPadding * 2,
recommendedPanes = 1,
maxPanes = 2;

/// Returns a [Breakpoint] with the given constraints for a mediumLarge screen.
const Breakpoint.mediumLarge({this.andUp = false, this.platform})
: beginWidth = 840,
endWidth = 1200,
beginHeight = 900,
endHeight = null;
endHeight = null,
spacing = kMaterialMediumAndUpSpacing,
margin = kMaterialMediumAndUpMargin,
padding = kMaterialPadding * 3,
recommendedPanes = 2,
maxPanes = 2;

/// Returns a [Breakpoint] with the given constraints for a large screen.
const Breakpoint.large({this.andUp = false, this.platform})
: beginWidth = 1200,
endWidth = 1600,
beginHeight = 900,
endHeight = null;
endHeight = null,
spacing = kMaterialMediumAndUpSpacing,
margin = kMaterialMediumAndUpMargin,
padding = kMaterialPadding * 4,
recommendedPanes = 2,
maxPanes = 2;

/// Returns a [Breakpoint] with the given constraints for an extraLarge screen.
const Breakpoint.extraLarge({this.andUp = false, this.platform})
: beginWidth = 1600,
endWidth = null,
beginHeight = 900,
endHeight = null;
endHeight = null,
spacing = kMaterialMediumAndUpSpacing,
margin = kMaterialMediumAndUpMargin,
padding = kMaterialPadding * 5,
recommendedPanes = 2,
maxPanes = 3;

/// A set of [TargetPlatform]s that the [Breakpoint] will be active on desktop.
static const Set<TargetPlatform> desktop = <TargetPlatform>{
Expand Down Expand Up @@ -222,6 +257,21 @@ class Breakpoint {
/// left null then it will be active on all platforms.
final Set<TargetPlatform>? platform;

/// The default material spacing for the [Breakpoint].
final double spacing;

/// The default material margin for the [Breakpoint].
final double margin;

/// The default material padding for the [Breakpoint].
final double padding;

/// The material recommended number of panes for the [Breakpoint].
final int recommendedPanes;

/// The material maximum number of panes that can be displayed on the [Breakpoint].
final int maxPanes;

/// A method that returns true based on conditions related to the context of
/// the screen such as MediaQuery.sizeOf(context).width, MediaQuery.sizeOf(context).height
/// and MediaQuery.orientationOf(context).
Expand Down
2 changes: 1 addition & 1 deletion packages/flutter_adaptive_scaffold/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: flutter_adaptive_scaffold
description: Widgets to easily build adaptive layouts, including navigation elements.
version: 0.2.2
version: 0.2.3
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

Expand Down
36 changes: 20 additions & 16 deletions packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ void main() {
final Finder primaryNav3 = find.byKey(const Key('primaryNavigation3'));

await tester.binding.setSurfaceSize(SimulatedLayout.small.size);
await tester.pumpWidget(SimulatedLayout.small.app());
await tester.pumpWidget(SimulatedLayout.small.scaffold(tester));
await tester.pumpAndSettle();

expect(smallBody, findsOneWidget);
Expand All @@ -44,7 +44,7 @@ void main() {
expect(tester.getTopLeft(bottomNav), const Offset(0, 1920));

await tester.binding.setSurfaceSize(SimulatedLayout.medium.size);
await tester.pumpWidget(SimulatedLayout.medium.app());
await tester.pumpWidget(SimulatedLayout.medium.scaffold(tester));
await tester.pumpAndSettle();

expect(smallBody, findsNothing);
Expand All @@ -60,7 +60,7 @@ void main() {
expect(tester.getBottomRight(primaryNav), const Offset(88, 2000));

await tester.binding.setSurfaceSize(SimulatedLayout.mediumLarge.size);
await tester.pumpWidget(SimulatedLayout.mediumLarge.app());
await tester.pumpWidget(SimulatedLayout.mediumLarge.scaffold(tester));
await tester.pumpAndSettle();

expect(body, findsNothing);
Expand All @@ -76,7 +76,7 @@ void main() {
expect(tester.getBottomRight(primaryNav1), const Offset(208, 2000));

await tester.binding.setSurfaceSize(SimulatedLayout.large.size);
await tester.pumpWidget(SimulatedLayout.large.app());
await tester.pumpWidget(SimulatedLayout.large.scaffold(tester));
await tester.pumpAndSettle();

expect(mediumLargeBody, findsNothing);
Expand All @@ -92,7 +92,7 @@ void main() {
expect(tester.getBottomRight(primaryNav2), const Offset(208, 2000));

await tester.binding.setSurfaceSize(SimulatedLayout.extraLarge.size);
await tester.pumpWidget(SimulatedLayout.extraLarge.app());
await tester.pumpWidget(SimulatedLayout.extraLarge.scaffold(tester));
await tester.pumpAndSettle();

expect(largeBody, findsNothing);
Expand All @@ -114,9 +114,9 @@ void main() {
final Finder sBody = find.byKey(const Key('sBody'));

await tester.binding.setSurfaceSize(SimulatedLayout.small.size);
await tester.pumpWidget(SimulatedLayout.small.app());
await tester.pumpWidget(SimulatedLayout.small.scaffold(tester));
await tester.binding.setSurfaceSize(SimulatedLayout.medium.size);
await tester.pumpWidget(SimulatedLayout.medium.app());
await tester.pumpWidget(SimulatedLayout.medium.scaffold(tester));

await tester.pump();
await tester.pump(const Duration(milliseconds: 200));
Expand Down Expand Up @@ -155,10 +155,12 @@ void main() {
final Finder sBody = find.byKey(const Key('sBody'));

await tester.binding.setSurfaceSize(SimulatedLayout.small.size);
await tester.pumpWidget(SimulatedLayout.small.app(animations: false));
await tester
.pumpWidget(SimulatedLayout.small.scaffold(tester, animations: false));

await tester.binding.setSurfaceSize(SimulatedLayout.medium.size);
await tester.pumpWidget(SimulatedLayout.medium.app(animations: false));
await tester
.pumpWidget(SimulatedLayout.medium.scaffold(tester, animations: false));

await tester.pump();
await tester.pump(const Duration(milliseconds: 200));
Expand All @@ -179,7 +181,8 @@ void main() {
await Future.forEach(SimulatedLayout.values,
(SimulatedLayout region) async {
int selectedIndex = 0;
final MaterialApp app = region.app(initialIndex: selectedIndex);
final MaterialApp app =
region.scaffold(tester, initialIndex: selectedIndex);
await tester.binding.setSurfaceSize(region.size);
await tester.pumpWidget(app);
await tester.pumpAndSettle();
Expand Down Expand Up @@ -242,7 +245,7 @@ void main() {
(WidgetTester tester) async {
await Future.forEach(SimulatedLayout.values,
(SimulatedLayout region) async {
final MaterialApp app = region.app();
final MaterialApp app = region.scaffold(tester);
await tester.binding.setSurfaceSize(region.size);
await tester.pumpWidget(app);
await tester.pumpAndSettle();
Expand Down Expand Up @@ -272,7 +275,8 @@ void main() {
await Future.forEach(SimulatedLayout.values,
(SimulatedLayout region) async {
int? selectedIndex;
final MaterialApp app = region.app(initialIndex: selectedIndex);
final MaterialApp app =
region.scaffold(tester, initialIndex: selectedIndex);
await tester.binding.setSurfaceSize(region.size);
await tester.pumpWidget(app);
await tester.pumpAndSettle();
Expand Down Expand Up @@ -453,7 +457,7 @@ void main() {
'when view in medium screen, navigation rail must be visible as per theme data values.',
(WidgetTester tester) async {
await tester.binding.setSurfaceSize(SimulatedLayout.medium.size);
await tester.pumpWidget(SimulatedLayout.medium.app());
await tester.pumpWidget(SimulatedLayout.medium.scaffold(tester));
await tester.pumpAndSettle();

final Finder primaryNavigationMedium = find.byKey(
Expand Down Expand Up @@ -499,7 +503,7 @@ void main() {
'when view in mediumLarge screen, navigation rail must be visible as per theme data values.',
(WidgetTester tester) async {
await tester.binding.setSurfaceSize(SimulatedLayout.mediumLarge.size);
await tester.pumpWidget(SimulatedLayout.mediumLarge.app());
await tester.pumpWidget(SimulatedLayout.mediumLarge.scaffold(tester));
await tester.pumpAndSettle();

final Finder primaryNavigationMediumLarge = find.byKey(
Expand Down Expand Up @@ -740,7 +744,7 @@ void main() {
(WidgetTester tester) async {
await tester.binding.setSurfaceSize(SimulatedLayout.medium.size);
await tester.pumpWidget(SimulatedLayout.medium
.app(appBarBreakpoint: AppBarAlwaysOnBreakpoint()));
.scaffold(tester, appBarBreakpoint: AppBarAlwaysOnBreakpoint()));
await tester.pumpAndSettle();

final Finder appBar = find.byType(AppBar);
Expand All @@ -750,7 +754,7 @@ void main() {

await tester.binding.setSurfaceSize(SimulatedLayout.mediumLarge.size);
await tester.pumpWidget(SimulatedLayout.mediumLarge
.app(appBarBreakpoint: AppBarAlwaysOnBreakpoint()));
.scaffold(tester, appBarBreakpoint: AppBarAlwaysOnBreakpoint()));
expect(drawer, findsNothing);
await tester.pumpAndSettle();

Expand Down
Loading

0 comments on commit c765f57

Please sign in to comment.