Last active
June 12, 2018 11:32
-
-
Save IhorKlimov/b463b5044b16300615e91a2de86d1735 to your computer and use it in GitHub Desktop.
Flutter TextFormField
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2015 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/material.dart'; | |
import 'package:flutter/services.dart'; | |
import 'package:flutter/widgets.dart'; | |
/// A [FormField] that contains a [BetterTextField]. | |
/// | |
/// This is a convenience widget that wraps a [BetterTextField] widget in a | |
/// [FormField]. | |
/// | |
/// 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. | |
/// | |
/// When a [controller] is specified, its [TextEditingController.text] | |
/// defines the [initialValue]. If this [FormField] is part of a scrolling | |
/// container that lazily constructs its children, like a [ListView] or a | |
/// [CustomScrollView], then a [controller] should be specified. | |
/// The controller's lifetime should be managed by a stateful widget ancestor | |
/// of the scrolling container. | |
/// | |
/// If a [controller] is not specified, [initialValue] can be used to give | |
/// the automatically generated controller an initial value. | |
/// | |
/// For a documentation about the various parameters, see [BetterTextField]. | |
/// | |
/// See also: | |
/// | |
/// * <https://material.google.com/components/text-fields.html> | |
/// * [BetterTextField], which is the underlying text field without the [Form] | |
/// integration. | |
/// * [InputDecorator], which shows the labels and other visual elements that | |
/// surround the actual text editing widget. | |
class BetterTextFormField extends FormField<String> { | |
/// Creates a [FormField] that contains a [BetterTextField]. | |
/// | |
/// When a [controller] is specified, [initialValue] must be null (the | |
/// default). If [controller] is null, then a [TextEditingController] | |
/// will be constructed automatically and its `text` will be initialized | |
/// to [initalValue] or the empty string. | |
/// | |
/// For documentation about the various parameters, see the [BetterTextField] class | |
/// and [new BetterTextField], the constructor. | |
BetterTextFormField({ | |
Key key, | |
this.controller, | |
String initialValue, | |
FocusNode focusNode, | |
InputDecoration decoration = const InputDecoration(), | |
TextInputType keyboardType = TextInputType.text, | |
TextStyle style, | |
TextAlign textAlign = TextAlign.start, | |
bool autofocus = false, | |
bool obscureText = false, | |
bool autocorrect = true, | |
bool autovalidate = false, | |
bool maxLengthEnforced = true, | |
int maxLines = 1, | |
int maxLength, | |
ValueChanged<String> onFieldSubmitted, | |
FormFieldSetter<String> onSaved, | |
FormFieldValidator<String> validator, | |
List<TextInputFormatter> inputFormatters, | |
bool enabled, | |
}) : assert(initialValue == null || controller == null), | |
assert(keyboardType != null), | |
assert(textAlign != null), | |
assert(autofocus != null), | |
assert(obscureText != null), | |
assert(autocorrect != null), | |
assert(autovalidate != null), | |
assert(maxLengthEnforced != null), | |
assert(maxLines == null || maxLines > 0), | |
assert(maxLength == null || maxLength > 0), | |
super( | |
key: key, | |
initialValue: controller != null ? controller.text : (initialValue ?? ''), | |
onSaved: onSaved, | |
validator: validator, | |
autovalidate: autovalidate, | |
builder: (FormFieldState<String> field) { | |
final _BetterTextFormFieldState state = field; | |
final InputDecoration effectiveDecoration = (decoration ?? const InputDecoration()) | |
.applyDefaults(Theme.of(field.context).inputDecorationTheme); | |
return new BetterTextField( | |
controller: state._effectiveController, | |
focusNode: focusNode, | |
decoration: effectiveDecoration.copyWith(errorText: field.errorText), | |
keyboardType: keyboardType, | |
style: style, | |
textAlign: textAlign, | |
autofocus: autofocus, | |
obscureText: obscureText, | |
autocorrect: autocorrect, | |
maxLengthEnforced: maxLengthEnforced, | |
maxLines: maxLines, | |
maxLength: maxLength, | |
onChanged: field.didChange, | |
onSubmitted: onFieldSubmitted, | |
inputFormatters: inputFormatters, | |
enabled: enabled, | |
); | |
}, | |
); | |
/// Controls the text being edited. | |
/// | |
/// If null, this widget will create its own [TextEditingController] and | |
/// initialize its [TextEditingController.text] with [initialValue]. | |
final TextEditingController controller; | |
@override | |
_BetterTextFormFieldState createState() => new _BetterTextFormFieldState(); | |
} | |
class _BetterTextFormFieldState extends FormFieldState<String> { | |
TextEditingController _controller; | |
TextEditingController get _effectiveController => widget.controller ?? _controller; | |
@override | |
BetterTextFormField get widget => super.widget; | |
@override | |
void initState() { | |
super.initState(); | |
if (widget.controller == null) { | |
_controller = new TextEditingController(text: widget.initialValue); | |
} else { | |
widget.controller.addListener(_handleControllerChanged); | |
} | |
} | |
@override | |
void didUpdateWidget(BetterTextFormField oldWidget) { | |
super.didUpdateWidget(oldWidget); | |
if (widget.controller != oldWidget.controller) { | |
oldWidget.controller?.removeListener(_handleControllerChanged); | |
widget.controller?.addListener(_handleControllerChanged); | |
if (oldWidget.controller != null && widget.controller == null) | |
_controller = new TextEditingController.fromValue(oldWidget.controller.value); | |
if (widget.controller != null) { | |
setValue(widget.controller.text); | |
if (oldWidget.controller == null) | |
_controller = null; | |
} | |
} | |
} | |
@override | |
void dispose() { | |
widget.controller?.removeListener(_handleControllerChanged); | |
super.dispose(); | |
} | |
@override | |
void reset() { | |
super.reset(); | |
setState(() { | |
_effectiveController.text = widget.initialValue; | |
}); | |
} | |
void _handleControllerChanged() { | |
// Suppress changes that originated from within this class. | |
// | |
// In the case where a controller has been passed in to this widget, we | |
// register this change listener. In these cases, we'll also receive change | |
// notifications for changes originating from within this class -- for | |
// example, the reset() method. In such cases, the FormField value will | |
// already have been set. | |
if (_effectiveController.text != value) | |
didChange(_effectiveController.text); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment