From bff0ec470a269bef6f4b7040b94affd4ebb54b88 Mon Sep 17 00:00:00 2001 From: Daniel Edrisian Date: Thu, 10 Dec 2020 10:58:04 -0800 Subject: [PATCH] Add footer to CupertinoFormSection and fix CupertinoFormSection margins. (#72046) --- .../flutter/lib/src/cupertino/form_row.dart | 33 ++++---- .../lib/src/cupertino/form_section.dart | 81 ++++++++++++++----- .../test/cupertino/form_section_test.dart | 21 ++++- 3 files changed, 93 insertions(+), 42 deletions(-) diff --git a/packages/flutter/lib/src/cupertino/form_row.dart b/packages/flutter/lib/src/cupertino/form_row.dart index 827c7b279b..bd035f6e85 100644 --- a/packages/flutter/lib/src/cupertino/form_row.dart +++ b/packages/flutter/lib/src/cupertino/form_row.dart @@ -10,7 +10,7 @@ import 'theme.dart'; // Content padding determined via SwiftUI's `Form` view in the iOS 14.2 SDK. const EdgeInsetsGeometry _kDefaultPadding = - EdgeInsetsDirectional.fromSTEB(16.0, 6.0, 6.0, 6.0); + EdgeInsetsDirectional.fromSTEB(20.0, 6.0, 6.0, 6.0); /// An iOS-style form row. /// @@ -149,22 +149,7 @@ class CupertinoFormRow extends StatelessWidget { @override Widget build(BuildContext context) { - final CupertinoThemeData themeData = CupertinoTheme.of(context); - final TextStyle textStyle = themeData.textTheme.textStyle; - - final List rowChildren = [ - if (prefix != null) - DefaultTextStyle( - style: textStyle, - child: prefix!, - ), - Flexible( - child: Align( - alignment: AlignmentDirectional.centerEnd, - child: child, - ), - ), - ]; + final TextStyle textStyle = CupertinoTheme.of(context).textTheme.textStyle; return Padding( padding: padding ?? _kDefaultPadding, @@ -172,7 +157,19 @@ class CupertinoFormRow extends StatelessWidget { children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: rowChildren, + children: [ + if (prefix != null) + DefaultTextStyle( + style: textStyle, + child: prefix!, + ), + Flexible( + child: Align( + alignment: AlignmentDirectional.centerEnd, + child: child, + ), + ), + ], ), if (helper != null) Align( diff --git a/packages/flutter/lib/src/cupertino/form_section.dart b/packages/flutter/lib/src/cupertino/form_section.dart index 2d2e24177e..c6f0fd9dd1 100644 --- a/packages/flutter/lib/src/cupertino/form_section.dart +++ b/packages/flutter/lib/src/cupertino/form_section.dart @@ -9,12 +9,16 @@ import 'colors.dart'; // Standard header margin, determined from SwiftUI's Forms in iOS 14.2 SDK. const EdgeInsetsDirectional _kDefaultHeaderMargin = - EdgeInsetsDirectional.fromSTEB(16.5, 16.0, 16.5, 10.0); + EdgeInsetsDirectional.fromSTEB(20.0, 16.0, 20.0, 10.0); + +// Standard footer margin, determined from SwiftUI's Forms in iOS 14.2 SDK. +const EdgeInsetsDirectional _kDefaultFooterMargin = + EdgeInsetsDirectional.fromSTEB(20.0, 0.0, 20.0, 10.0); // Used for iOS "Inset Grouped" margin, determined from SwiftUI's Forms in // iOS 14.2 SDK. const EdgeInsetsDirectional _kDefaultInsetGroupedRowsMargin = - EdgeInsetsDirectional.fromSTEB(16.5, 0.0, 16.5, 16.5); + EdgeInsetsDirectional.fromSTEB(20.0, 0.0, 20.0, 10.0); // Used for iOS "Inset Grouped" border radius, estimated from SwiftUI's Forms in // iOS 14.2 SDK. @@ -39,6 +43,9 @@ enum _CupertinoFormSectionType { base, insetGrouped } /// The [header] parameter sets the form section header. The section header lies /// above the [children] rows, with margins that match the iOS style. /// +/// The [footer] parameter sets the form section footer. The section footer +/// lies below the [children] rows. +/// /// The [children] parameter is required and sets the list of rows shown in /// the section. The [children] parameter takes a list, as opposed to a more /// efficient builder function that lazy builds, because forms are intended to @@ -69,6 +76,9 @@ class CupertinoFormSection extends StatelessWidget { /// The [header] parameter sets the form section header. The section header /// lies above the [children] rows, with margins that match the iOS style. /// + /// The [footer] parameter sets the form section footer. The section footer + /// lies below the [children] rows. + /// /// The [children] parameter is required and sets the list of rows shown in /// the section. The [children] parameter takes a list, as opposed to a more /// efficient builder function that lazy builds, because forms are intended to @@ -93,6 +103,7 @@ class CupertinoFormSection extends StatelessWidget { Key? key, required this.children, this.header, + this.footer, this.margin = EdgeInsets.zero, this.backgroundColor = CupertinoColors.systemGroupedBackground, this.decoration, @@ -111,6 +122,9 @@ class CupertinoFormSection extends StatelessWidget { /// The [header] parameter sets the form section header. The section header /// lies above the [children] rows, with margins that match the iOS style. /// + /// The [footer] parameter sets the form section footer. The section footer + /// lies below the [children] rows. + /// /// The [children] parameter is required and sets the list of rows shown in /// the section. The [children] parameter takes a list, as opposed to a more /// efficient builder function that lazy builds, because forms are intended to @@ -136,6 +150,7 @@ class CupertinoFormSection extends StatelessWidget { Key? key, required this.children, this.header, + this.footer, this.margin = _kDefaultInsetGroupedRowsMargin, this.backgroundColor = CupertinoColors.systemGroupedBackground, this.decoration, @@ -150,6 +165,10 @@ class CupertinoFormSection extends StatelessWidget { /// [children] rows. final Widget? header; + /// Sets the form section footer. The section footer lies below the + /// [children] rows. + final Widget? footer; + /// Margin around the content area of the section encapsulating [children]. /// /// Defaults to zero padding if constructed with standard @@ -228,6 +247,16 @@ class CupertinoFormSection extends StatelessWidget { childrenWithDividers.add(longDivider); } + final BorderRadius childrenGroupBorderRadius; + switch (_type) { + case _CupertinoFormSectionType.insetGrouped: + childrenGroupBorderRadius = _kDefaultInsetGroupedBorderRadius; + break; + case _CupertinoFormSectionType.base: + childrenGroupBorderRadius = BorderRadius.zero; + break; + } + // Refactored the decorate children group in one place to avoid repeating it // twice down bellow in the returned widget. final DecoratedBox decoratedChildrenGroup = DecoratedBox( @@ -237,7 +266,7 @@ class CupertinoFormSection extends StatelessWidget { decoration?.color ?? CupertinoColors.secondarySystemGroupedBackground, context), - borderRadius: _kDefaultInsetGroupedBorderRadius, + borderRadius: childrenGroupBorderRadius, ), child: Column( children: childrenWithDividers, @@ -250,31 +279,43 @@ class CupertinoFormSection extends StatelessWidget { ), child: Column( children: [ - Align( - alignment: AlignmentDirectional.centerStart, - child: header == null - ? null - : DefaultTextStyle( - style: TextStyle( - fontSize: 13.5, - color: - CupertinoColors.secondaryLabel.resolveFrom(context), - ), - child: Padding( - padding: _kDefaultHeaderMargin, - child: header!, - ), - ), - ), + if (header != null) + Align( + alignment: AlignmentDirectional.centerStart, + child: DefaultTextStyle( + style: TextStyle( + fontSize: 13.0, + color: CupertinoColors.secondaryLabel.resolveFrom(context), + ), + child: Padding( + padding: _kDefaultHeaderMargin, + child: header!, + ), + ), + ), Padding( padding: margin, child: clipBehavior == Clip.none ? decoratedChildrenGroup : ClipRRect( - borderRadius: _kDefaultInsetGroupedBorderRadius, + borderRadius: childrenGroupBorderRadius, clipBehavior: clipBehavior, child: decoratedChildrenGroup), ), + if (footer != null) + Align( + alignment: AlignmentDirectional.centerStart, + child: DefaultTextStyle( + style: TextStyle( + fontSize: 13.0, + color: CupertinoColors.secondaryLabel.resolveFrom(context), + ), + child: Padding( + padding: _kDefaultFooterMargin, + child: footer!, + ), + ), + ), ], ), ); diff --git a/packages/flutter/test/cupertino/form_section_test.dart b/packages/flutter/test/cupertino/form_section_test.dart index 8360f1f23e..fa15bd4bce 100644 --- a/packages/flutter/test/cupertino/form_section_test.dart +++ b/packages/flutter/test/cupertino/form_section_test.dart @@ -8,20 +8,33 @@ import 'package:flutter/rendering.dart'; void main() { testWidgets('Shows header', (WidgetTester tester) async { - const Widget header = Text('Enter Value'); - await tester.pumpWidget( CupertinoApp( home: Center( child: CupertinoFormSection( - header: header, + header: const Text('Header'), children: [CupertinoTextFormFieldRow()], ), ), ), ); - expect(header, tester.widget(find.byType(Text))); + expect(find.text('Header'), findsOneWidget); + }); + + testWidgets('Shows footer', (WidgetTester tester) async { + await tester.pumpWidget( + CupertinoApp( + home: Center( + child: CupertinoFormSection( + footer: const Text('Footer'), + children: [CupertinoTextFormFieldRow()], + ), + ), + ), + ); + + expect(find.text('Footer'), findsOneWidget); }); testWidgets('Shows long dividers in edge-to-edge section part 1',