blob: 52d0afcb2fb24dc8158e2da1a155142225a11fab [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
import 'colors.dart';
import 'theme.dart';
// Content padding determined via SwiftUI's `Form` view in the iOS 14.2 SDK.
const EdgeInsetsGeometry _kDefaultPadding =
EdgeInsetsDirectional.fromSTEB(20.0, 6.0, 6.0, 6.0);
/// An iOS-style form row.
///
/// Creates an iOS-style split form row with a standard prefix and child widget.
/// Also provides a space for error and helper widgets that appear underneath.
///
/// The [child] parameter is required. This widget is displayed at the end of
/// the row.
///
/// The [prefix] parameter is optional and is displayed at the start of the
/// row. Standard iOS guidelines encourage passing a [Text] widget to [prefix]
/// to detail the nature of the row's [child] widget.
///
/// The [padding] parameter is used to pad the contents of the row. It defaults
/// to the standard iOS padding. If no edge insets are intended, explicitly pass
/// [EdgeInsets.zero] to [padding].
///
/// The [helper] and [error] parameters are both optional widgets targeted at
/// displaying more information about the row. Both widgets are placed
/// underneath the [prefix] and [child], and will expand the row's height to
/// accommodate for their presence. When a [Text] is given to [error], it will
/// be shown in [CupertinoColors.destructiveRed] coloring and
/// medium-weighted font.
///
/// {@tool snippet}
///
/// Creates a [CupertinoFormSection] containing a [CupertinoFormRow] with the
/// [prefix], [child], [helper] and [error] widgets.
///
/// ```dart
/// class FlutterDemo extends StatefulWidget {
/// const FlutterDemo({Key? key}) : super(key: key);
///
/// @override
/// State<FlutterDemo> createState() => _FlutterDemoState();
/// }
///
/// class _FlutterDemoState extends State<FlutterDemo> {
/// bool toggleValue = false;
///
/// @override
/// Widget build(BuildContext context) {
/// return CupertinoPageScaffold(
/// child: Center(
/// child: CupertinoFormSection(
/// header: const Text('SECTION 1'),
/// children: <Widget>[
/// CupertinoFormRow(
/// child: CupertinoSwitch(
/// value: toggleValue,
/// onChanged: (bool value) {
/// setState(() {
/// toggleValue = value;
/// });
/// },
/// ),
/// prefix: const Text('Toggle'),
/// helper: const Text('Use your instincts'),
/// error: toggleValue ? const Text('Cannot be true') : null,
/// ),
/// ],
/// ),
/// ),
/// );
/// }
/// }
/// ```
/// {@end-tool}
class CupertinoFormRow extends StatelessWidget {
/// Creates an iOS-style split form row with a standard prefix and child widget.
/// Also provides a space for error and helper widgets that appear underneath.
///
/// The [child] parameter is required. This widget is displayed at the end of
/// the row.
///
/// The [prefix] parameter is optional and is displayed at the start of the
/// row. Standard iOS guidelines encourage passing a [Text] widget to [prefix]
/// to detail the nature of the row's [child] widget.
///
/// The [padding] parameter is used to pad the contents of the row. It defaults
/// to the standard iOS padding. If no edge insets are intended, explicitly
/// pass [EdgeInsets.zero] to [padding].
///
/// The [helper] and [error] parameters are both optional widgets targeted at
/// displaying more information about the row. Both widgets are placed
/// underneath the [prefix] and [child], and will expand the row's height to
/// accommodate for their presence. When a [Text] is given to [error], it will
/// be shown in [CupertinoColors.destructiveRed] coloring and
/// medium-weighted font.
const CupertinoFormRow({
Key? key,
required this.child,
this.prefix,
this.padding,
this.helper,
this.error,
}) : super(key: key);
/// A widget that is displayed at the start of the row.
///
/// The [prefix] parameter is displayed at the start of the row. Standard iOS
/// guidelines encourage passing a [Text] widget to [prefix] to detail the
/// nature of the row's [child] widget. If null, the [child] widget will take
/// up all horizontal space in the row.
final Widget? prefix;
/// Content padding for the row.
///
/// Defaults to the standard iOS padding for form rows. If no edge insets are
/// intended, explicitly pass [EdgeInsets.zero] to [padding].
final EdgeInsetsGeometry? padding;
/// A widget that is displayed underneath the [prefix] and [child] widgets.
///
/// The [helper] appears in primary label coloring, and is meant to inform the
/// user about interaction with the child widget. The row becomes taller in
/// order to display the [helper] widget underneath [prefix] and [child]. If
/// null, the row is shorter.
final Widget? helper;
/// A widget that is displayed underneath the [prefix] and [child] widgets.
///
/// The [error] widget is primarily used to inform users of input errors. When
/// a [Text] is given to [error], it will be shown in
/// [CupertinoColors.destructiveRed] coloring and medium-weighted font. The
/// row becomes taller in order to display the [helper] widget underneath
/// [prefix] and [child]. If null, the row is shorter.
final Widget? error;
/// Child widget.
///
/// The [child] widget is primarily used for input. It end-aligned and
/// horizontally flexible, taking up the entire space trailing past the
/// [prefix] widget.
final Widget child;
@override
Widget build(BuildContext context) {
final TextStyle textStyle = CupertinoTheme.of(context).textTheme.textStyle;
return Padding(
padding: padding ?? _kDefaultPadding,
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
if (prefix != null)
DefaultTextStyle(
style: textStyle,
child: prefix!,
),
Flexible(
child: Align(
alignment: AlignmentDirectional.centerEnd,
child: child,
),
),
],
),
if (helper != null)
Align(
alignment: AlignmentDirectional.centerStart,
child: DefaultTextStyle(
style: textStyle,
child: helper!,
),
),
if (error != null)
Align(
alignment: AlignmentDirectional.centerStart,
child: DefaultTextStyle(
style: const TextStyle(
color: CupertinoColors.destructiveRed,
fontWeight: FontWeight.w500,
),
child: error!,
),
),
],
),
);
}
}