From 7d346dd1840adf8f9ef9bfcddeca2ac908a91da7 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Mon, 12 Dec 2016 14:24:45 -0500 Subject: [PATCH] Improve docs for Input and Form and friends. (#7208) Fixes https://github.com/flutter/flutter/issues/7017 --- packages/flutter/lib/src/material/input.dart | 95 ++++++++++++++++++-- packages/flutter/lib/src/widgets/form.dart | 18 +++- 2 files changed, 105 insertions(+), 8 deletions(-) diff --git a/packages/flutter/lib/src/material/input.dart b/packages/flutter/lib/src/material/input.dart index 8aa1fc74db..c564e52554 100644 --- a/packages/flutter/lib/src/material/input.dart +++ b/packages/flutter/lib/src/material/input.dart @@ -18,7 +18,10 @@ export 'package:flutter/services.dart' show TextInputType; const Duration _kTransitionDuration = const Duration(milliseconds: 200); const Curve _kTransitionCurve = Curves.fastOutSlowIn; -/// A simple text input field. +/// A simple undecorated text input field. +/// +/// If you want decorations as specified in the Material spec (most likely), +/// use [Input] instead. /// /// This widget is comparable to [Text] in that it does not include a margin /// or any decoration outside the text itself. It is useful for applications, @@ -35,6 +38,7 @@ const Curve _kTransitionCurve = Curves.fastOutSlowIn; /// /// * [Input], which adds a label, a divider below the text field, and support for /// an error message. +/// * [RawInput], a text field that does not require [Material] design. class InputField extends StatefulWidget { InputField({ Key key, @@ -143,7 +147,6 @@ class _InputFieldState extends State { ]; if (config.hintText != null && value.text.isEmpty) { - TextStyle hintStyle = textStyle.copyWith(color: themeData.hintColor); stackChildren.add( new Positioned( @@ -373,13 +376,23 @@ class _InputContainerState extends State { /// /// Requires one of its ancestors to be a [Material] widget. /// +/// When using inside a [Form], consider using [InputFormField] instead. +/// +/// Assuming that the input is already focused, the basic data flow for +/// retrieving user input is: +/// 1. User taps a character on the keyboard. +/// 2. The [onChanged] callback is called with the current [InputValue]. +/// 3. Perform any necessary logic/validation on the current input value. +/// 4. Update the state of the [Input] widget accordingly through [State.setState]. +/// +/// For most cases, we recommend that you use the [Input] class within a +/// [StatefulWidget] so you can save and operate on the current value of the +/// input. +/// /// See also: /// /// * -/// -/// For a detailed guide on using the input widget, see: -/// -/// * +/// * [InputFormField], which simplifies steps 2-4 above. class Input extends StatefulWidget { /// Creates a text input field. /// @@ -520,6 +533,76 @@ class _InputState extends State { } /// A [FormField] that contains an [Input]. +/// +/// This is a convenience widget that simply wraps an [Input] widget in a +/// [FormField]. The [FormField] maintains the current value of the [Input] so +/// that you don't need to manage it yourself. +/// +/// A [Form] ancestor is not required. The [Form] simply makes it easier to +/// save, reset, or validate multiple fields at once. To use without a [Form], +/// pass a [GlobalKey] to the constructor and use [GlobalKey.currentState] to +/// save or reset the form field. +/// +/// To see the use of [InputFormField], compare these two ways of a implementing +/// a simple two text field form. +/// +/// Using [InputFormField]: +/// +/// ```dart +/// String _firstName, _lastName; +/// GlobalKey _formKey = new GlobalKey(); +/// ... +/// new Form( +/// key: _formKey, +/// child: new Row( +/// children: [ +/// new InputFormField( +/// labelText: 'First Name', +/// onSaved: (InputValue value) { _firstName = value.text; } +/// ), +/// new InputFormField( +/// labelText: 'Last Name', +/// onSaved: (InputValue value) { _lastName = value.text; } +/// ), +/// new RaisedButton( +/// child: new Text('SUBMIT'), +/// // Instead of _formKey.currentState, you could wrap the +/// // RaisedButton in a Builder widget to get access to a BuildContext, +/// // and use Form.of(context). +/// onPressed: () { _formKey.currentState.save(); }, +/// ), +/// ) +/// ) +/// ``` +/// +/// Using [Input] directly: +/// +/// ```dart +/// String _firstName, _lastName; +/// InputValue _firstNameValue = const InputValue(); +/// InputValue _lastNameValue = const InputValue(); +/// ... +/// new Row( +/// children: [ +/// new Input( +/// value: _firstNameValue, +/// labelText: 'First Name', +/// onChanged: (InputValue value) { setState( () { _firstNameValue = value; } ); } +/// ), +/// new Input( +/// value: _lastNameValue, +/// labelText: 'Last Name', +/// onChanged: (InputValue value) { setState( () { _lastNameValue = value; } ); } +/// ), +/// new RaisedButton( +/// child: new Text('SUBMIT'), +/// onPressed: () { +/// _firstName = _firstNameValue.text; +/// _lastName = _lastNameValue.text; +/// }, +/// ), +/// ) +/// ``` class InputFormField extends FormField { InputFormField({ Key key, diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index 59661f684e..fdc9193009 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -6,8 +6,15 @@ import 'package:flutter/foundation.dart'; import 'framework.dart'; -/// A container for grouping together multiple form field widgets (e.g. -/// [Input] widgets). +/// An optional container for grouping together multiple form field widgets +/// (e.g. [Input] widgets). +/// +/// Each individual form field should be wrapped in a [FormField] widget, with +/// the [Form] widget as a common ancestor of all of those. Call methods on +/// [FormState] to save, reset, or validate each [FormField] that is a +/// descendant of this [Form]. To obtain the [FormState], you may use [Form.of] +/// with a context whose ancestor is the [Form], or pass a [GlobalKey] to the +/// [Form] constructor and call [GlobalKey.currentState]. class Form extends StatefulWidget { /// Creates a container for form fields. /// @@ -141,6 +148,11 @@ typedef Widget FormFieldBuilder(FormFieldState field); /// Use a [GlobalKey] with [FormField] if you want to retrieve its current /// state, for example if you want one form field to depend on another. /// +/// A [Form] ancestor is not required. The [Form] simply makes it easier to +/// save, reset, or validate multiple fields at once. To use without a [Form], +/// pass a [GlobalKey] to the constructor and use [GlobalKey.currentState] to +/// save or reset the form field. +/// /// See also: [Form], [InputFormField] class FormField extends StatefulWidget { FormField({ @@ -173,6 +185,8 @@ class FormField extends StatefulWidget { FormFieldState createState() => new FormFieldState(); } +/// The current state of a [FormField]. Passed to the [FormFieldBuilder] method +/// for use in constructing the form field's widget. class FormFieldState extends State> { T _value; String _errorText;