Unverified Commit b63ced55 authored by xster's avatar xster Committed by GitHub

Add a CupertinoTextField (#23194)

parent dc36195c
......@@ -11,3 +11,4 @@ export 'cupertino_refresh_demo.dart';
export 'cupertino_segmented_control_demo.dart';
export 'cupertino_slider_demo.dart';
export 'cupertino_switch_demo.dart';
export 'cupertino_text_field_demo.dart';
// Copyright 2018 The Chromium 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/cupertino.dart';
class CupertinoTextFieldDemo extends StatefulWidget {
static const String routeName = '/cupertino/text_fields';
@override
_CupertinoTextFieldDemoState createState() {
return _CupertinoTextFieldDemoState();
}
}
class _CupertinoTextFieldDemoState extends State<CupertinoTextFieldDemo> {
TextEditingController _chatTextController;
TextEditingController _locationTextController;
@override
void initState() {
super.initState();
_chatTextController = TextEditingController();
_locationTextController = TextEditingController(text: 'Montreal, Canada');
}
Widget _buildChatTextField() {
return CupertinoTextField(
controller: _chatTextController,
textCapitalization: TextCapitalization.sentences,
placeholder: 'Text Message',
decoration: BoxDecoration(
border: Border.all(
width: 0.0,
color: CupertinoColors.inactiveGray,
),
borderRadius: BorderRadius.circular(15.0),
),
maxLines: null,
keyboardType: TextInputType.multiline,
prefix: const Padding(padding: EdgeInsets.symmetric(horizontal: 4.0)),
suffix: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: CupertinoButton(
color: CupertinoColors.activeGreen,
minSize: 0.0,
child: const Icon(
CupertinoIcons.up_arrow,
size: 21.0,
color: CupertinoColors.white,
),
padding: const EdgeInsets.all(2.0),
borderRadius: BorderRadius.circular(15.0),
onPressed: ()=> setState(()=> _chatTextController.clear()),
),
),
autofocus: true,
suffixMode: OverlayVisibilityMode.editing,
onSubmitted: (String text)=> setState(()=> _chatTextController.clear()),
);
}
Widget _buildNameField() {
return const CupertinoTextField(
prefix: Icon(
CupertinoIcons.person_solid,
color: CupertinoColors.lightBackgroundGray,
size: 28.0,
),
padding: EdgeInsets.symmetric(horizontal: 6.0, vertical: 12.0),
clearButtonMode: OverlayVisibilityMode.editing,
textCapitalization: TextCapitalization.words,
autocorrect: false,
decoration: BoxDecoration(
border: Border(bottom: BorderSide(width: 0.0, color: CupertinoColors.inactiveGray)),
),
placeholder: 'Name',
);
}
Widget _buildEmailField() {
return const CupertinoTextField(
prefix: Icon(
CupertinoIcons.mail_solid,
color: CupertinoColors.lightBackgroundGray,
size: 28.0,
),
padding: EdgeInsets.symmetric(horizontal: 6.0, vertical: 12.0),
clearButtonMode: OverlayVisibilityMode.editing,
keyboardType: TextInputType.emailAddress,
autocorrect: false,
decoration: BoxDecoration(
border: Border(bottom: BorderSide(width: 0.0, color: CupertinoColors.inactiveGray)),
),
placeholder: 'Email',
);
}
Widget _buildLocationField() {
return CupertinoTextField(
controller: _locationTextController,
prefix: const Icon(
CupertinoIcons.location_solid,
color: CupertinoColors.lightBackgroundGray,
size: 28.0,
),
padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 12.0),
clearButtonMode: OverlayVisibilityMode.editing,
textCapitalization: TextCapitalization.words,
decoration: const BoxDecoration(
border: Border(bottom: BorderSide(width: 0.0, color: CupertinoColors.inactiveGray)),
),
placeholder: 'Location',
);
}
Widget _buildPinField() {
return const CupertinoTextField(
prefix: Icon(
CupertinoIcons.padlock_solid,
color: CupertinoColors.lightBackgroundGray,
size: 28.0,
),
padding: EdgeInsets.symmetric(horizontal: 6.0, vertical: 12.0),
clearButtonMode: OverlayVisibilityMode.editing,
keyboardType: TextInputType.number,
autocorrect: false,
obscureText: true,
decoration: BoxDecoration(
border: Border(bottom: BorderSide(width: 0.0, color: CupertinoColors.inactiveGray)),
),
placeholder: 'Create a PIN',
);
}
Widget _buildTagsField() {
return CupertinoTextField(
controller: TextEditingController(text: 'colleague, reading club'),
prefix: const Icon(
CupertinoIcons.tags_solid,
color: CupertinoColors.lightBackgroundGray,
size: 28.0,
),
enabled: false,
padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 12.0),
decoration: const BoxDecoration(
border: Border(bottom: BorderSide(width: 0.0, color: CupertinoColors.inactiveGray)),
),
);
}
@override
Widget build(BuildContext context) {
return DefaultTextStyle(
style: const TextStyle(
fontFamily: '.SF UI Text',
inherit: false,
fontSize: 17.0,
color: CupertinoColors.black,
),
child: CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
previousPageTitle: 'Cupertino',
middle: Text('Text Fields'),
),
child: ListView(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 32.0, horizontal: 16.0),
child: Column(
children: <Widget>[
_buildNameField(),
_buildEmailField(),
_buildLocationField(),
_buildPinField(),
_buildTagsField(),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 32.0, horizontal: 16.0),
child: _buildChatTextField(),
),
],
),
),
);
}
}
......@@ -500,6 +500,13 @@ List<GalleryDemo> _buildGalleryDemos() {
documentationUrl: 'https://docs.flutter.io/flutter/cupertino/CupertinoSwitch-class.html',
buildRoute: (BuildContext context) => CupertinoSwitchDemo(),
),
GalleryDemo(
title: 'Text Fields',
icon: GalleryIcons.text_fields_alt,
category: _kCupertinoComponents,
routeName: CupertinoTextFieldDemo.routeName,
buildRoute: (BuildContext context) => CupertinoTextFieldDemo(),
),
// Media
GalleryDemo(
......
......@@ -28,6 +28,7 @@ export 'src/cupertino/slider.dart';
export 'src/cupertino/switch.dart';
export 'src/cupertino/tab_scaffold.dart';
export 'src/cupertino/tab_view.dart';
export 'src/cupertino/text_field.dart';
export 'src/cupertino/text_selection.dart';
export 'src/cupertino/thumb_painter.dart';
export 'widgets.dart';
......@@ -243,7 +243,7 @@ class _CupertinoAppState extends State<CupertinoApp> {
_navigatorObservers = List<NavigatorObserver>.from(widget.navigatorObservers)
..add(_heroController);
} else {
_navigatorObservers = null;
_navigatorObservers = const <NavigatorObserver>[];
}
}
......
......@@ -721,4 +721,44 @@ class CupertinoIcons {
/// * [group], which is similar, but not filled in.
/// * [person_solid], which is just a single person.
static const IconData group_solid = IconData(0xf47c, fontFamily: iconFont, fontPackage: iconFontPackage);
/// Outline of a closed mail envelope.
static const IconData mail = IconData(0xf422, fontFamily: iconFont, fontPackage: iconFontPackage);
/// A closed mail envelope. This icon is filled in.
static const IconData mail_solid = IconData(0xf423, fontFamily: iconFont, fontPackage: iconFontPackage);
/// Outline of a location pin.
static const IconData location = IconData(0xf455, fontFamily: iconFont, fontPackage: iconFontPackage);
/// A location pin. This icon is filled in.
static const IconData location_solid = IconData(0xf456, fontFamily: iconFont, fontPackage: iconFontPackage);
/// Outline of a sticker tag.
///
/// See also:
///
/// * [tags], similar but with 2 overlapping tags.
static const IconData tag = IconData(0xf48c, fontFamily: iconFont, fontPackage: iconFontPackage);
/// A sticker tag. This icon is filled in.
///
/// See also:
///
/// * [tags_solid], similar but with 2 overlapping tags.
static const IconData tag_solid = IconData(0xf48d, fontFamily: iconFont, fontPackage: iconFontPackage);
/// Outlines of 2 overlapping sticker tags.
///
/// See also:
///
/// * [tag], similar but with only one tag.
static const IconData tags = IconData(0xf48e, fontFamily: iconFont, fontPackage: iconFontPackage);
/// 2 overlapping sticker tags. This icon is filled in.
///
/// See also:
///
/// * [tag_solid], similar but with only one tag.
static const IconData tags_solid = IconData(0xf48f, fontFamily: iconFont, fontPackage: iconFontPackage);
}
This diff is collapsed.
......@@ -20,14 +20,16 @@ const double _kToolbarHeight = 36.0;
const Color _kToolbarBackgroundColor = Color(0xFF2E2E2E);
const Color _kToolbarDividerColor = Color(0xFFB9B9B9);
const Color _kHandlesColor = Color(0xFF146DDE);
// Read off from the output on iOS 12. This color does not vary with the
// application's theme color.
const Color _kHandlesColor = Color(0xFF136FE0);
// This offset is used to determine the center of the selection during a drag.
// It's slightly below the center of the text so the finger isn't entirely
// covering the text being selected.
const Size _kSelectionOffset = Size(20.0, 30.0);
const Size _kToolbarTriangleSize = Size(18.0, 9.0);
const EdgeInsets _kToolbarButtonPadding = EdgeInsets.symmetric(vertical: 10.0, horizontal: 21.0);
const EdgeInsets _kToolbarButtonPadding = EdgeInsets.symmetric(vertical: 10.0, horizontal: 18.0);
const BorderRadius _kToolbarBorderRadius = BorderRadius.all(Radius.circular(7.5));
const TextStyle _kToolbarButtonFontStyle = TextStyle(
......@@ -121,6 +123,7 @@ class _TextSelectionToolbar extends StatelessWidget {
// avoid letting the triangle line up with any dividers.
// https://github.com/flutter/flutter/issues/11274
triangle,
const Padding(padding: EdgeInsets.only(bottom: 10.0)),
],
);
}
......
......@@ -154,10 +154,7 @@ class TextField extends StatefulWidget {
/// extra padding introduced by the decoration to save space for the labels).
final InputDecoration decoration;
/// The type of keyboard to use for editing the text.
///
/// Defaults to [TextInputType.text] if [maxLines] is one and
/// [TextInputType.multiline] otherwise.
/// {@macro flutter.widgets.editableText.keyboardType}
final TextInputType keyboardType;
/// The type of action button to use for the keyboard.
......@@ -166,17 +163,7 @@ class TextField extends StatefulWidget {
/// [TextInputType.multiline] and [TextInputAction.done] otherwise.
final TextInputAction textInputAction;
/// Configures how the platform keyboard will select an uppercase or
/// lowercase keyboard.
///
/// Only supports text keyboards, other keyboard types will ignore this
/// configuration. Capitalization is locale-aware.
///
/// Defaults to [TextCapitalization.none]. Must not be null.
///
/// See also:
///
/// * [TextCapitalization], for a description of each capitalization behavior.
/// {@macro flutter.widgets.editableText.textCapitalization}
final TextCapitalization textCapitalization;
/// The style to use for the text being edited.
......@@ -186,42 +173,19 @@ class TextField extends StatefulWidget {
/// If null, defaults to the `subhead` text style from the current [Theme].
final TextStyle style;
/// How the text being edited should be aligned horizontally.
///
/// Defaults to [TextAlign.start].
/// {@macro flutter.widgets.editableText.textAlign}
final TextAlign textAlign;
/// Whether this text field should focus itself if nothing else is already
/// focused.
///
/// If true, the keyboard will open as soon as this text field obtains focus.
/// Otherwise, the keyboard is only shown after the user taps the text field.
///
/// Defaults to false. Cannot be null.
// See https://github.com/flutter/flutter/issues/7035 for the rationale for this
// keyboard behavior.
/// {@macro flutter.widgets.editableText.autofocus}
final bool autofocus;
/// Whether to hide the text being edited (e.g., for passwords).
///
/// When this is set to true, all the characters in the text field are
/// replaced by U+2022 BULLET characters (•).
///
/// Defaults to false. Cannot be null.
/// {@macro flutter.widgets.editableText.obscureText}
final bool obscureText;
/// Whether to enable autocorrection.
///
/// Defaults to true. Cannot be null.
/// {@macro flutter.widgets.editableText.autocorrect}
final bool autocorrect;
/// The maximum number of lines for the text to span, wrapping if necessary.
///
/// If this is 1 (the default), the text will not wrap, but will scroll
/// horizontally instead.
///
/// If this is null, there is no limit to the number of lines. If it is not
/// null, the value must be greater than zero.
/// {@macro flutter.widgets.editableText.maxLines}
final int maxLines;
/// The maximum number of characters (Unicode scalar values) to allow in the
......@@ -280,34 +244,16 @@ class TextField extends StatefulWidget {
/// [maxLength] is exceeded.
final bool maxLengthEnforced;
/// Called when the text being edited changes.
/// {@macro flutter.widgets.editableText.onChanged}
final ValueChanged<String> onChanged;
/// Called when the user submits editable content (e.g., user presses the "done"
/// button on the keyboard).
///
/// The default implementation of [onEditingComplete] executes 2 different
/// behaviors based on the situation:
///
/// - When a completion action is pressed, such as "done", "go", "send", or
/// "search", the user's content is submitted to the [controller] and then
/// focus is given up.
///
/// - When a non-completion action is pressed, such as "next" or "previous",
/// the user's content is submitted to the [controller], but focus is not
/// given up because developers may want to immediately move focus to
/// another input widget within [onSubmitted].
///
/// Providing [onEditingComplete] prevents the aforementioned default behavior.
/// {@macro flutter.widgets.editableText.onEditingComplete}
final VoidCallback onEditingComplete;
/// Called when the user indicates that they are done editing the text in the
/// field.
/// {@macro flutter.widgets.editableText.onSubmitted}
final ValueChanged<String> onSubmitted;
/// Optional input validation and formatting overrides.
///
/// Formatters are run in the provided order when the text input changes.
/// {@macro flutter.widgets.editableText.inputFormatters}
final List<TextInputFormatter> inputFormatters;
/// If false the textfield is "disabled": it ignores taps and its
......@@ -317,16 +263,15 @@ class TextField extends StatefulWidget {
/// [Decoration.enabled] property.
final bool enabled;
/// How thick the cursor will be.
///
/// Defaults to 2.0.
/// {@macro flutter.widgets.editableText.cursorWidth}
final double cursorWidth;
/// How rounded the corners of the cursor should be.
/// By default, the cursor has a null Radius
/// {@macro flutter.widgets.editableText.cursorRadius}
final Radius cursorRadius;
/// The color to use when painting the cursor.
///
/// Defaults to the theme's `cursorColor` when null.
final Color cursorColor;
/// The appearance of the keyboard.
......@@ -336,14 +281,7 @@ class TextField extends StatefulWidget {
/// If unset, defaults to the brightness of [ThemeData.primaryColorBrightness].
final Brightness keyboardAppearance;
/// Configures padding to edges surrounding a [Scrollable] when the Textfield scrolls into view.
///
/// When this widget receives focus and is not completely visible (for example scrolled partially
/// off the screen or overlapped by the keyboard)
/// then it will attempt to make itself visible by scrolling a surrounding [Scrollable], if one is present.
/// This value controls how far from the edges of a [Scrollable] the TextField will be positioned after the scroll.
///
/// Defaults to EdgeInserts.all(20.0).
/// {@macro flutter.widgets.editableText.scrollPadding}
final EdgeInsets scrollPadding;
/// {@macro flutter.widgets.editableText.enableInteractiveSelection}
......
......@@ -242,22 +242,31 @@ class EditableText extends StatefulWidget {
/// Controls whether this widget has keyboard focus.
final FocusNode focusNode;
/// {@template flutter.widgets.editableText.obscureText}
/// Whether to hide the text being edited (e.g., for passwords).
///
/// Defaults to false.
/// When this is set to true, all the characters in the text field are
/// replaced by U+2022 BULLET characters (•).
///
/// Defaults to false. Cannot be null.
/// {@endtemplate}
final bool obscureText;
/// {@template flutter.widgets.editableText.autocorrect}
/// Whether to enable autocorrection.
///
/// Defaults to true.
/// Defaults to true. Cannot be null.
/// {@endtemplate}
final bool autocorrect;
/// The text style to use for the editable text.
final TextStyle style;
/// {@template flutter.widgets.editableText.textAlign}
/// How the text should be aligned horizontally.
///
/// Defaults to [TextAlign.start].
/// Defaults to [TextAlign.start] and cannot be null.
/// {@endtemplate}
final TextAlign textAlign;
/// The directionality of the text.
......@@ -275,6 +284,7 @@ class EditableText extends StatefulWidget {
/// Defaults to the ambient [Directionality], if any.
final TextDirection textDirection;
/// {@template flutter.widgets.editableText.textCapitalization}
/// Configures how the platform keyboard will select an uppercase or
/// lowercase keyboard.
///
......@@ -286,6 +296,7 @@ class EditableText extends StatefulWidget {
/// See also:
///
/// * [TextCapitalization], for a description of each capitalization behavior.
/// {@endtemplate}
final TextCapitalization textCapitalization;
/// Used to select a font when the same Unicode character can
......@@ -307,8 +318,11 @@ class EditableText extends StatefulWidget {
final double textScaleFactor;
/// The color to use when painting the cursor.
///
/// Cannot be null.
final Color cursorColor;
/// {@template flutter.widgets.editableText.maxLines}
/// The maximum number of lines for the text to span, wrapping if necessary.
///
/// If this is 1 (the default), the text will not wrap, but will scroll
......@@ -316,13 +330,20 @@ class EditableText extends StatefulWidget {
///
/// If this is null, there is no limit to the number of lines. If it is not
/// null, the value must be greater than zero.
/// {@endtemplate}
final int maxLines;
/// Whether this input field should focus itself if nothing else is already focused.
/// If true, the keyboard will open as soon as this input obtains focus. Otherwise,
/// the keyboard is only shown after the user taps the text field.
/// {@template flutter.widgets.editableText.autofocus}
/// Whether this text field should focus itself if nothing else is already
/// focused.
///
/// If true, the keyboard will open as soon as this text field obtains focus.
/// Otherwise, the keyboard is only shown after the user taps the text field.
///
/// Defaults to false.
/// Defaults to false. Cannot be null.
/// {@endtemplate}
// See https://github.com/flutter/flutter/issues/7035 for the rationale for this
// keyboard behavior.
final bool autofocus;
/// The color to use when painting the selection.
......@@ -331,15 +352,23 @@ class EditableText extends StatefulWidget {
/// Optional delegate for building the text selection handles and toolbar.
final TextSelectionControls selectionControls;
/// {@template flutter.widgets.editableText.keyboardType}
/// The type of keyboard to use for editing the text.
///
/// Defaults to [TextInputType.text] if [maxLines] is one and
/// [TextInputType.multiline] otherwise.
/// {@endtemplate}
final TextInputType keyboardType;
/// The type of action button to use with the soft keyboard.
final TextInputAction textInputAction;
/// {@template flutter.widgets.editableText.onChanged}
/// Called when the text being edited changes.
/// {@endtemplate}
final ValueChanged<String> onChanged;
/// {@template flutter.widgets.editableText.onEditingComplete}
/// Called when the user submits editable content (e.g., user presses the "done"
/// button on the keyboard).
///
......@@ -356,17 +385,24 @@ class EditableText extends StatefulWidget {
/// another input widget within [onSubmitted].
///
/// Providing [onEditingComplete] prevents the aforementioned default behavior.
/// {@endtemplate}
final VoidCallback onEditingComplete;
/// Called when the user indicates that they are done editing the text in the field.
/// {@template flutter.widgets.editableText.onSubmitted}
/// Called when the user indicates that they are done editing the text in the
/// field.
/// {@endtemplate}
final ValueChanged<String> onSubmitted;
/// Called when the user changes the selection of text (including the cursor
/// location).
final SelectionChangedCallback onSelectionChanged;
/// Optional input validation and formatting overrides. Formatters are run
/// in the provided order when the text input changes.
/// {@template flutter.widgets.editableText.inputFormatters}
/// Optional input validation and formatting overrides.
///
/// Formatters are run in the provided order when the text input changes.
/// {@endtemplate}
final List<TextInputFormatter> inputFormatters;
/// If true, the [RenderEditable] created by this widget will not handle
......@@ -375,14 +411,18 @@ class EditableText extends StatefulWidget {
/// This property is false by default.
final bool rendererIgnoresPointer;
/// {@template flutter.widgets.editableText.cursorWidth}
/// How thick the cursor will be.
///
/// Defaults to 2.0
/// {@endtemplate}
final double cursorWidth;
/// {@template flutter.widgets.editableText.cursorRadius}
/// How rounded the corners of the cursor should be.
///
/// By default, the cursor has a Radius of zero.
/// By default, the cursor has no radius.
/// {@endtemplate}
final Radius cursorRadius;
/// The appearance of the keyboard.
......@@ -392,6 +432,7 @@ class EditableText extends StatefulWidget {
/// Defaults to [Brightness.light].
final Brightness keyboardAppearance;
/// {@template flutter.widgets.editableText.scrollPadding}
/// Configures padding to edges surrounding a [Scrollable] when the Textfield scrolls into view.
///
/// When this widget receives focus and is not completely visible (for example scrolled partially
......@@ -400,6 +441,7 @@ class EditableText extends StatefulWidget {
/// This value controls how far from the edges of a [Scrollable] the TextField will be positioned after the scroll.
///
/// Defaults to EdgeInserts.all(20.0).
/// {@endtemplate}
final EdgeInsets scrollPadding;
/// {@template flutter.widgets.editableText.enableInteractiveSelection}
......
......@@ -531,6 +531,9 @@ class _TextSelectionHandleOverlayState extends State<_TextSelectionHandleOverlay
onPanUpdate: _handleDragUpdate,
onTap: _handleTap,
child: Stack(
// Always let the selection handles draw outside of the conceptual
// box where (0,0) is the top left corner of the RenderEditable.
overflow: Overflow.visible,
children: <Widget>[
Positioned(
left: point.dx,
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment