|
|
|
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Text; |
|
|
|
using uiwidgets; |
|
|
|
using Unity.UIWidgets.cupertino; |
|
|
|
using Unity.UIWidgets.foundation; |
|
|
|
using Unity.UIWidgets.gestures; |
|
|
|
using Unity.UIWidgets.painting; |
|
|
|
|
|
|
using Unity.UIWidgets.widgets; |
|
|
|
using UnityEngine; |
|
|
|
using Color = Unity.UIWidgets.ui.Color; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _TextFieldSelectionGestureDetectorBuilder : TextSelectionGestureDetectorBuilder { |
|
|
|
public _TextFieldSelectionGestureDetectorBuilder( |
|
|
|
_TextFieldState state) : base(_delegate: state) { |
|
|
|
_state = state; |
|
|
|
} |
|
|
|
|
|
|
|
public readonly _TextFieldState _state; |
|
|
|
|
|
|
|
protected override void onForcePressStart(ForcePressDetails details) { |
|
|
|
base.onForcePressStart(details); |
|
|
|
if (_delegate.selectionEnabled && shouldShowSelectionToolbar) { |
|
|
|
editableText.showToolbar(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
protected override void onForcePressEnd(ForcePressDetails details) { |
|
|
|
// Not required.
|
|
|
|
} |
|
|
|
|
|
|
|
protected override void onSingleTapUp(TapUpDetails details) { |
|
|
|
editableText.hideToolbar(); |
|
|
|
if (_delegate.selectionEnabled) { |
|
|
|
//use android by default
|
|
|
|
renderEditable.selectPosition(cause: SelectionChangedCause.tap); |
|
|
|
} |
|
|
|
|
|
|
|
_state._requestKeyboard(); |
|
|
|
if (_state.widget.onTap != null) { |
|
|
|
_state.widget.onTap(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
protected override void onSingleLongTapStart(LongPressStartDetails details) { |
|
|
|
if (_delegate.selectionEnabled) { |
|
|
|
//use android by default
|
|
|
|
renderEditable.selectWord(cause: SelectionChangedCause.longPress); |
|
|
|
Feedback.forLongPress(_state.context); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public class TextField : StatefulWidget { |
|
|
|
public TextField(Key key = null, |
|
|
|
TextEditingController controller = null, |
|
|
|
|
|
|
TextStyle style = null, |
|
|
|
StrutStyle strutStyle = null, |
|
|
|
TextAlign textAlign = TextAlign.left, |
|
|
|
TextAlignVertical textAlignVertical = null, |
|
|
|
bool readOnly = false, |
|
|
|
ToolbarOptions toolbarOptions = null, |
|
|
|
bool? showCursor = null, |
|
|
|
SmartDashesType? smartDashesType = null, |
|
|
|
SmartQuotesType? smartQuotesType = null, |
|
|
|
bool enableSuggestions = true, |
|
|
|
int? maxLines = 1, |
|
|
|
int? minLines = null, |
|
|
|
bool expands = false, |
|
|
|
|
|
|
float? cursorWidth = 2.0f, |
|
|
|
Radius cursorRadius = null, |
|
|
|
Color cursorColor = null, |
|
|
|
BoxHeightStyle selectionHeightStyle = BoxHeightStyle.tight, |
|
|
|
BoxWidthStyle selectionWidthStyle = BoxWidthStyle.tight, |
|
|
|
bool? enableInteractiveSelection = null, |
|
|
|
bool enableInteractiveSelection = true, |
|
|
|
ScrollController scrollController = null, |
|
|
|
ScrollPhysics scrollPhysics = null |
|
|
|
) : base(key: key) { |
|
|
|
D.assert(maxLines == null || maxLines > 0); |
|
|
|
|
|
|
D.assert(!expands || (maxLines == null && minLines == null), |
|
|
|
() => "minLines and maxLines must be null when expands is true."); |
|
|
|
D.assert(maxLength == null || maxLength == noMaxLength || maxLength > 0); |
|
|
|
D.assert(!obscureText || maxLines == 1, () => "Obscured fields cannot be multiline."); |
|
|
|
|
|
|
|
this.smartDashesType = |
|
|
|
smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled); |
|
|
|
this.smartQuotesType = |
|
|
|
smartQuotesType ?? (obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled); |
|
|
|
|
|
|
|
this.controller = controller; |
|
|
|
this.focusNode = focusNode; |
|
|
|
|
|
|
this.style = style; |
|
|
|
this.strutStyle = strutStyle; |
|
|
|
this.textAlign = textAlign; |
|
|
|
this.textAlignVertical = textAlignVertical; |
|
|
|
this.readOnly = readOnly; |
|
|
|
this.showCursor = showCursor; |
|
|
|
this.enableSuggestions = enableSuggestions; |
|
|
|
this.maxLines = maxLines; |
|
|
|
this.minLines = minLines; |
|
|
|
this.expands = expands; |
|
|
|
|
|
|
this.enabled = enabled; |
|
|
|
this.cursorWidth = cursorWidth; |
|
|
|
this.cursorColor = cursorColor; |
|
|
|
this.selectionHeightStyle = selectionHeightStyle; |
|
|
|
this.selectionWidthStyle = selectionWidthStyle; |
|
|
|
this.cursorRadius = cursorRadius; |
|
|
|
this.onSubmitted = onSubmitted; |
|
|
|
this.keyboardAppearance = keyboardAppearance; |
|
|
|
|
|
|
this.toolbarOptions = toolbarOptions ?? (obscureText |
|
|
|
? new ToolbarOptions( |
|
|
|
selectAll: true, |
|
|
|
paste: true |
|
|
|
) |
|
|
|
: new ToolbarOptions( |
|
|
|
copy: true, |
|
|
|
cut: true, |
|
|
|
selectAll: true, |
|
|
|
paste: true |
|
|
|
)); |
|
|
|
|
|
|
|
this.enableInteractiveSelection = enableInteractiveSelection; |
|
|
|
this.scrollController = scrollController; |
|
|
|
} |
|
|
|
|
|
|
|
public readonly TextEditingController controller; |
|
|
|
|
|
|
|
|
|
|
public readonly TextAlign textAlign; |
|
|
|
|
|
|
|
public readonly TextAlignVertical textAlignVertical; |
|
|
|
|
|
|
|
public readonly TextDirection textDirection; |
|
|
|
|
|
|
|
public readonly bool autofocus; |
|
|
|
|
|
|
public readonly bool autocorrect; |
|
|
|
|
|
|
|
public readonly SmartDashesType smartDashesType; |
|
|
|
|
|
|
|
public readonly SmartQuotesType smartQuotesType; |
|
|
|
|
|
|
|
public readonly bool enableSuggestions; |
|
|
|
|
|
|
|
public readonly int? maxLines; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public readonly bool readOnly; |
|
|
|
|
|
|
|
public readonly ToolbarOptions toolbarOptions; |
|
|
|
|
|
|
|
public readonly bool? showCursor; |
|
|
|
|
|
|
|
public const long noMaxLength = -1; |
|
|
|
|
|
|
|
public readonly int? maxLength; |
|
|
|
|
|
|
|
|
|
|
public readonly Color cursorColor; |
|
|
|
|
|
|
|
public readonly BoxHeightStyle selectionHeightStyle; |
|
|
|
|
|
|
|
public readonly BoxWidthStyle selectionWidthStyle; |
|
|
|
|
|
|
|
public readonly bool? enableInteractiveSelection; |
|
|
|
|
|
|
|
public readonly bool enableInteractiveSelection; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public readonly ScrollController scrollController; |
|
|
|
|
|
|
|
get { |
|
|
|
return enableInteractiveSelection ?? !obscureText; |
|
|
|
} |
|
|
|
get { return enableInteractiveSelection; } |
|
|
|
} |
|
|
|
|
|
|
|
public readonly GestureTapCallback onTap; |
|
|
|
|
|
|
new DiagnosticsProperty<TextEditingController>("controller", controller, defaultValue: null)); |
|
|
|
properties.add(new DiagnosticsProperty<FocusNode>("focusNode", focusNode, defaultValue: null)); |
|
|
|
properties.add(new DiagnosticsProperty<bool?>("enabled", enabled, defaultValue: null)); |
|
|
|
properties.add(new DiagnosticsProperty<InputDecoration>("decoration", decoration, defaultValue: new InputDecoration())); |
|
|
|
properties.add(new DiagnosticsProperty<InputDecoration>("decoration", decoration, |
|
|
|
defaultValue: new InputDecoration())); |
|
|
|
properties.add(new DiagnosticsProperty<TextInputType>("keyboardType", keyboardType, |
|
|
|
defaultValue: TextInputType.text)); |
|
|
|
properties.add(new DiagnosticsProperty<TextStyle>("style", style, defaultValue: null)); |
|
|
|
|
|
|
properties.add(new EnumProperty<SmartDashesType>("smartDashesType", smartDashesType, |
|
|
|
defaultValue: obscureText ? SmartDashesType.disabled : SmartDashesType.enabled)); |
|
|
|
properties.add(new EnumProperty<SmartQuotesType>("smartQuotesType", smartQuotesType, |
|
|
|
defaultValue: obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled)); |
|
|
|
properties.add(new DiagnosticsProperty<bool>("enableSuggestions", enableSuggestions, defaultValue: true)); |
|
|
|
properties.add(new IntProperty("maxLines", maxLines, defaultValue: 1)); |
|
|
|
properties.add(new IntProperty("minLines", minLines, defaultValue: null)); |
|
|
|
properties.add(new DiagnosticsProperty<bool>("expands", expands, defaultValue: false)); |
|
|
|
|
|
|
properties.add(new EnumProperty<TextInputAction?>("textInputAction", textInputAction, defaultValue: null)); |
|
|
|
properties.add(new EnumProperty<TextCapitalization>("textCapitalization", textCapitalization, defaultValue: TextCapitalization.none)); |
|
|
|
properties.add(new EnumProperty<TextCapitalization>("textCapitalization", textCapitalization, |
|
|
|
defaultValue: TextCapitalization.none)); |
|
|
|
properties.add(new DiagnosticsProperty<TextAlignVertical>("textAlignVertical", textAlignVertical, |
|
|
|
defaultValue: null)); |
|
|
|
properties.add(new DiagnosticsProperty<Color>("cursorColor", cursorColor, defaultValue: null)); |
|
|
|
properties.add(new DiagnosticsProperty<Brightness?>("keyboardAppearance", keyboardAppearance, defaultValue: null)); |
|
|
|
properties.add(new DiagnosticsProperty<EdgeInsets>("scrollPadding", scrollPadding, defaultValue: EdgeInsets.all(20.0f))); |
|
|
|
properties.add(new FlagProperty("selectionEnabled", value: selectionEnabled, defaultValue: true, ifFalse: "selection disabled")); |
|
|
|
properties.add(new ColorProperty("cursorColor", cursorColor, defaultValue: null)); |
|
|
|
properties.add(new DiagnosticsProperty<Brightness?>("keyboardAppearance", keyboardAppearance, |
|
|
|
defaultValue: null)); |
|
|
|
properties.add(new DiagnosticsProperty<EdgeInsets>("scrollPadding", scrollPadding, |
|
|
|
defaultValue: EdgeInsets.all(20.0f))); |
|
|
|
properties.add(new FlagProperty("selectionEnabled", value: selectionEnabled, defaultValue: true, |
|
|
|
ifFalse: "selection disabled")); |
|
|
|
properties.add( |
|
|
|
new DiagnosticsProperty<ScrollController>("scrollController", scrollController, defaultValue: null)); |
|
|
|
class _TextFieldState : AutomaticKeepAliveClientMixin<TextField> { |
|
|
|
readonly GlobalKey<EditableTextState> _editableTextKey = new LabeledGlobalKey<EditableTextState>(); |
|
|
|
|
|
|
|
HashSet<InteractiveInkFeature> _splashes; |
|
|
|
InteractiveInkFeature _currentSplash; |
|
|
|
|
|
|
|
class _TextFieldState : State<TextField>, TextSelectionGestureDetectorBuilderDelegate { |
|
|
|
TextEditingController _controller; |
|
|
|
|
|
|
|
TextEditingController _effectiveController { |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool _isHovering = false; |
|
|
|
|
|
|
|
bool needsCounter { |
|
|
|
get { |
|
|
|
return widget.maxLength != null |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
bool _showSelectionHandles = false; |
|
|
|
|
|
|
|
_TextFieldSelectionGestureDetectorBuilder _selectionGestureDetectorBuilder; |
|
|
|
|
|
|
|
bool _forcePressEnabled; |
|
|
|
|
|
|
|
public bool forcePressEnabled { |
|
|
|
get { return _forcePressEnabled; } |
|
|
|
} |
|
|
|
|
|
|
|
readonly GlobalKey<EditableTextState> _editableTextKey = new LabeledGlobalKey<EditableTextState>(); |
|
|
|
|
|
|
|
public GlobalKey<EditableTextState> editableTextKey { |
|
|
|
get { return _editableTextKey; } |
|
|
|
} |
|
|
|
|
|
|
|
public bool selectionEnabled { |
|
|
|
get { return widget.selectionEnabled; } |
|
|
|
} |
|
|
|
|
|
|
|
bool _isEnabled { |
|
|
|
get { return widget.enabled ?? widget.decoration?.enabled ?? true; } |
|
|
|
} |
|
|
|
|
|
|
|
int _currentLength { |
|
|
|
get { return _effectiveController.value.text.Length; } |
|
|
|
} |
|
|
|
|
|
|
|
InputDecoration _getEffectiveDecoration() { |
|
|
|
MaterialLocalizations localizations = MaterialLocalizations.of(context); |
|
|
|
ThemeData themeData = Theme.of(context); |
|
|
|
|
|
|
return effectiveDecoration; |
|
|
|
} |
|
|
|
|
|
|
|
Widget counter; |
|
|
|
int currentLength = _effectiveController.value.text.Length; |
|
|
|
Widget counter = null; |
|
|
|
int currentLength = _currentLength; |
|
|
|
counter = widget.buildCounter( |
|
|
|
Widget builtCounter = widget.buildCounter( |
|
|
|
if (builtCounter != null) { |
|
|
|
counter = builtCounter; |
|
|
|
} |
|
|
|
|
|
|
|
return effectiveDecoration.copyWith(counter: counter); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Handle length exceeds maxLength
|
|
|
|
|
|
|
|
return effectiveDecoration.copyWith( |
|
|
|
counterText: counterText |
|
|
|
); |
|
|
|
|
|
|
base.initState(); |
|
|
|
_selectionGestureDetectorBuilder = new _TextFieldSelectionGestureDetectorBuilder(state: this); |
|
|
|
|
|
|
|
_effectiveFocusNode.canRequestFocus = _isEnabled; |
|
|
|
} |
|
|
|
|
|
|
|
public override void didUpdateWidget(StatefulWidget oldWidget) { |
|
|
|
|
|
|
_controller = null; |
|
|
|
} |
|
|
|
|
|
|
|
bool isEnabled = widget.enabled ?? widget.decoration?.enabled ?? true; |
|
|
|
bool wasEnabled = ((TextField) oldWidget).enabled ?? ((TextField) oldWidget).decoration?.enabled ?? true; |
|
|
|
if (wasEnabled && !isEnabled) { |
|
|
|
_effectiveFocusNode.unfocus(); |
|
|
|
_effectiveFocusNode.canRequestFocus = _isEnabled; |
|
|
|
if (_effectiveFocusNode.hasFocus && widget.readOnly != ((TextField) oldWidget).readOnly) { |
|
|
|
if (_effectiveController.selection.isCollapsed) { |
|
|
|
_showSelectionHandles = !widget.readOnly; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
void _requestKeyboard() { |
|
|
|
_editableTextKey.currentState?.requestKeyboard(); |
|
|
|
EditableTextState _editableText { |
|
|
|
get { return editableTextKey.currentState; } |
|
|
|
void _handleSelectionChanged(TextSelection selection, SelectionChangedCause cause) { |
|
|
|
switch (Theme.of(context).platform) { |
|
|
|
case RuntimePlatform.IPhonePlayer: |
|
|
|
if (cause == SelectionChangedCause.longPress) { |
|
|
|
_editableTextKey.currentState?.bringIntoView(selection.basePos); |
|
|
|
} |
|
|
|
return; |
|
|
|
case RuntimePlatform.Android: |
|
|
|
break; |
|
|
|
} |
|
|
|
internal void _requestKeyboard() { |
|
|
|
_editableText?.requestKeyboard(); |
|
|
|
InteractiveInkFeature _createInkFeature(Offset globalPosition) { |
|
|
|
MaterialInkController inkController = Material.of(context); |
|
|
|
ThemeData themeData = Theme.of(context); |
|
|
|
BuildContext editableContext = _editableTextKey.currentContext; |
|
|
|
RenderBox referenceBox = |
|
|
|
(RenderBox) (InputDecorator.containerOf(editableContext) ?? editableContext.findRenderObject()); |
|
|
|
Offset position = referenceBox.globalToLocal(globalPosition); |
|
|
|
Color color = themeData.splashColor; |
|
|
|
|
|
|
|
InteractiveInkFeature splash = null; |
|
|
|
|
|
|
|
void handleRemoved() { |
|
|
|
if (_splashes != null) { |
|
|
|
D.assert(_splashes.Contains(splash)); |
|
|
|
_splashes.Remove(splash); |
|
|
|
if (_currentSplash == splash) { |
|
|
|
_currentSplash = null; |
|
|
|
} |
|
|
|
|
|
|
|
updateKeepAlive(); |
|
|
|
} // else we're probably in deactivate()
|
|
|
|
bool _shouldShowSelectionHandles(SelectionChangedCause cause) { |
|
|
|
if (!_selectionGestureDetectorBuilder.shouldShowSelectionToolbar) { |
|
|
|
return false; |
|
|
|
splash = themeData.splashFactory.create( |
|
|
|
controller: inkController, |
|
|
|
referenceBox: referenceBox, |
|
|
|
position: position, |
|
|
|
color: color, |
|
|
|
containedInkWell: true, |
|
|
|
borderRadius: BorderRadius.zero, |
|
|
|
onRemoved: handleRemoved |
|
|
|
); |
|
|
|
|
|
|
|
return splash; |
|
|
|
} |
|
|
|
|
|
|
|
RenderEditable _renderEditable { |
|
|
|
get { return _editableTextKey.currentState.renderEditable; } |
|
|
|
} |
|
|
|
|
|
|
|
void _handleTapDown(TapDownDetails details) { |
|
|
|
_renderEditable.handleTapDown(details); |
|
|
|
_startSplash(details.globalPosition); |
|
|
|
} |
|
|
|
|
|
|
|
void _handleSingleTapUp(TapUpDetails details) { |
|
|
|
if (widget.enableInteractiveSelection == true) { |
|
|
|
_renderEditable.handleTap(); |
|
|
|
if (cause == SelectionChangedCause.keyboard) { |
|
|
|
return false; |
|
|
|
_requestKeyboard(); |
|
|
|
_confirmCurrentSplash(); |
|
|
|
if (widget.onTap != null) { |
|
|
|
widget.onTap(); |
|
|
|
if (widget.readOnly && _effectiveController.selection.isCollapsed) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
void _handleSingleTapCancel() { |
|
|
|
_cancelCurrentSplash(); |
|
|
|
} |
|
|
|
|
|
|
|
void _handleSingleLongTapStart(LongPressStartDetails details) { |
|
|
|
if (widget.selectionEnabled) { |
|
|
|
switch (Theme.of(context).platform) { |
|
|
|
case RuntimePlatform.IPhonePlayer: |
|
|
|
_renderEditable.selectPositionAt( |
|
|
|
from: details.globalPosition, |
|
|
|
cause: SelectionChangedCause.longPress |
|
|
|
); |
|
|
|
break; |
|
|
|
case RuntimePlatform.Android: |
|
|
|
_renderEditable.selectWord(cause: SelectionChangedCause.longPress); |
|
|
|
Feedback.forLongPress(context); |
|
|
|
break; |
|
|
|
} |
|
|
|
if (cause == SelectionChangedCause.longPress) { |
|
|
|
return true; |
|
|
|
_confirmCurrentSplash(); |
|
|
|
} |
|
|
|
void _handleSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) { |
|
|
|
if (widget.selectionEnabled) { |
|
|
|
switch (Theme.of(context).platform) { |
|
|
|
case RuntimePlatform.IPhonePlayer: |
|
|
|
_renderEditable.selectPositionAt( |
|
|
|
from: details.globalPosition, |
|
|
|
cause: SelectionChangedCause.longPress |
|
|
|
); |
|
|
|
break; |
|
|
|
case RuntimePlatform.Android: |
|
|
|
_renderEditable.selectWordsInRange( |
|
|
|
from: details.globalPosition - details.offsetFromOrigin, |
|
|
|
to: details.globalPosition, |
|
|
|
cause: SelectionChangedCause.longPress); |
|
|
|
Feedback.forLongPress(context); |
|
|
|
break; |
|
|
|
} |
|
|
|
if (_effectiveController.text.isNotEmpty()) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
void _handleSingleLongTapEnd(LongPressEndDetails details) { |
|
|
|
_editableTextKey.currentState.showToolbar(); |
|
|
|
return false; |
|
|
|
void _handleDoubleTapDown(TapDownDetails details) { |
|
|
|
if (widget.selectionEnabled) { |
|
|
|
_renderEditable.selectWord(cause: SelectionChangedCause.doubleTap); |
|
|
|
_editableTextKey.currentState.showToolbar(); |
|
|
|
void _handleSelectionChanged(TextSelection selection, SelectionChangedCause cause) { |
|
|
|
bool willShowSelectionHandles = _shouldShowSelectionHandles(cause); |
|
|
|
if (willShowSelectionHandles != _showSelectionHandles) { |
|
|
|
setState(() => { _showSelectionHandles = willShowSelectionHandles; }); |
|
|
|
} |
|
|
|
void _handleMouseDragSelectionStart(DragStartDetails details) { |
|
|
|
_renderEditable.selectPositionAt( |
|
|
|
from: details.globalPosition, |
|
|
|
cause: SelectionChangedCause.drag); |
|
|
|
|
|
|
|
_startSplash(details.globalPosition); |
|
|
|
//use the code path for android by default
|
|
|
|
return; |
|
|
|
void _handleMouseDragSelectionUpdate(DragStartDetails startDetails, |
|
|
|
DragUpdateDetails updateDetails) { |
|
|
|
_renderEditable.selectPositionAt( |
|
|
|
from: startDetails.globalPosition, |
|
|
|
to: updateDetails.globalPosition, |
|
|
|
cause: SelectionChangedCause.drag); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void _startSplash(Offset globalPosition) { |
|
|
|
if (_effectiveFocusNode.hasFocus) { |
|
|
|
return; |
|
|
|
void _handleSelectionHandleTapped() { |
|
|
|
if (_effectiveController.selection.isCollapsed) { |
|
|
|
_editableText.toggleToolbar(); |
|
|
|
|
|
|
|
InteractiveInkFeature splash = _createInkFeature(globalPosition); |
|
|
|
_splashes = _splashes ?? new HashSet<InteractiveInkFeature>(); |
|
|
|
_splashes.Add(splash); |
|
|
|
_currentSplash = splash; |
|
|
|
updateKeepAlive(); |
|
|
|
void _confirmCurrentSplash() { |
|
|
|
_currentSplash?.confirm(); |
|
|
|
_currentSplash = null; |
|
|
|
} |
|
|
|
|
|
|
|
void _cancelCurrentSplash() { |
|
|
|
_currentSplash?.cancel(); |
|
|
|
} |
|
|
|
|
|
|
|
protected override bool wantKeepAlive { |
|
|
|
get { return _splashes != null && _splashes.isNotEmpty(); } |
|
|
|
} |
|
|
|
|
|
|
|
public override void deactivate() { |
|
|
|
if (_splashes != null) { |
|
|
|
HashSet<InteractiveInkFeature> splashes = _splashes; |
|
|
|
_splashes = null; |
|
|
|
foreach (InteractiveInkFeature splash in splashes) { |
|
|
|
splash.dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
_currentSplash = null; |
|
|
|
void _handleHover(bool hovering) { |
|
|
|
if (hovering != _isHovering) { |
|
|
|
setState(() => { _isHovering = hovering; }); |
|
|
|
|
|
|
|
D.assert(_currentSplash == null); |
|
|
|
base.deactivate(); |
|
|
|
base.build(context); // See AutomaticKeepAliveClientMixin.
|
|
|
|
!(widget.style != null && widget.style.inherit == false && |
|
|
|
(widget.style.fontSize == null || widget.style.textBaseline == null)), |
|
|
|
() => "inherit false style must supply fontSize and textBaseline" |
|
|
|
!(widget.style != null && widget.style.inherit == false && |
|
|
|
(widget.style.fontSize == null || widget.style.textBaseline == null)), |
|
|
|
() => "inherit false style must supply fontSize and textBaseline" |
|
|
|
TextStyle style = themeData.textTheme.subhead.merge(widget.style); |
|
|
|
TextStyle style = themeData.textTheme.subtitle1.merge(widget.style); |
|
|
|
Brightness keyboardAppearance = widget.keyboardAppearance ?? themeData.primaryColorBrightness; |
|
|
|
TextEditingController controller = _effectiveController; |
|
|
|
FocusNode focusNode = _effectiveFocusNode; |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// bool forcePressEnabled = false; // TODO: wait for force press is ready
|
|
|
|
TextSelectionControls textSelectionControls = MaterialUtils.materialTextSelectionControls;; |
|
|
|
|
|
|
|
TextSelectionControls textSelectionControls = MaterialUtils.materialTextSelectionControls; |
|
|
|
; |
|
|
|
bool paintCursorAboveText = false; |
|
|
|
bool cursorOpacityAnimates = false; |
|
|
|
Offset cursorOffset = null; |
|
|
|
|
|
|
_forcePressEnabled = false; |
|
|
|
textSelectionControls = _MaterialTextSelectionControls.materialTextSelectionControls; |
|
|
|
paintCursorAboveText = false; |
|
|
|
cursorOpacityAnimates = false; |
|
|
|
cursorColor ??= themeData.cursorColor; |
|
|
|
|
|
|
|
key: _editableTextKey, |
|
|
|
key: editableTextKey, |
|
|
|
readOnly: widget.readOnly, |
|
|
|
toolbarOptions: widget.toolbarOptions, |
|
|
|
showCursor: widget.showCursor, |
|
|
|
showSelectionHandles: _showSelectionHandles, |
|
|
|
controller: controller, |
|
|
|
focusNode: focusNode, |
|
|
|
keyboardType: widget.keyboardType, |
|
|
|
|
|
|
autofocus: widget.autofocus, |
|
|
|
obscureText: widget.obscureText, |
|
|
|
autocorrect: widget.autocorrect, |
|
|
|
smartDashesType: widget.smartDashesType, |
|
|
|
smartQuotesType: widget.smartQuotesType, |
|
|
|
enableSuggestions: widget.enableSuggestions, |
|
|
|
maxLines: widget.maxLines, |
|
|
|
minLines: widget.minLines, |
|
|
|
expands: widget.expands, |
|
|
|
|
|
|
onSelectionChanged: _handleSelectionChanged, |
|
|
|
onEditingComplete: widget.onEditingComplete, |
|
|
|
onSubmitted: widget.onSubmitted, |
|
|
|
onSelectionHandleTapped: _handleSelectionHandleTapped, |
|
|
|
selectionHeightStyle: widget.selectionHeightStyle, |
|
|
|
selectionWidthStyle: widget.selectionWidthStyle, |
|
|
|
backgroundCursorColor: new Color(0xFF8E8E93),// TODO: CupertinoColors.inactiveGray,
|
|
|
|
backgroundCursorColor: CupertinoColors.inactiveGray, |
|
|
|
scrollController: widget.scrollController, |
|
|
|
scrollPhysics: widget.scrollPhysics |
|
|
|
) |
|
|
|
); |
|
|
|
|
|
|
decoration: _getEffectiveDecoration(), |
|
|
|
baseStyle: widget.style, |
|
|
|
textAlign: widget.textAlign, |
|
|
|
textAlignVertical: widget.textAlignVertical, |
|
|
|
isHovering: _isHovering, |
|
|
|
isFocused: focusNode.hasFocus, |
|
|
|
isEmpty: controller.value.text.isEmpty(), |
|
|
|
expands: widget.expands, |
|
|
|
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
void onEnter(PointerEnterEvent pEvent) { |
|
|
|
_handleHover(true); |
|
|
|
} |
|
|
|
|
|
|
|
void onExit(PointerExitEvent pEvent) { |
|
|
|
_handleHover(false); |
|
|
|
} |
|
|
|
|
|
|
|
ignoring: !(widget.enabled ?? widget.decoration?.enabled ?? true), |
|
|
|
child: new TextSelectionGestureDetector( |
|
|
|
onTapDown: _handleTapDown, |
|
|
|
// onForcePressStart: forcePressEnabled ? this._handleForcePressStarted : null, // TODO: Remove this when force press is added
|
|
|
|
onSingleTapUp: _handleSingleTapUp, |
|
|
|
onSingleTapCancel: _handleSingleTapCancel, |
|
|
|
onSingleLongTapStart: _handleSingleLongTapStart, |
|
|
|
onSingleLongTapMoveUpdate: _handleSingleLongTapMoveUpdate, |
|
|
|
onSingleLongTapEnd: _handleSingleLongTapEnd, |
|
|
|
onDoubleTapDown: _handleDoubleTapDown, |
|
|
|
onDragSelectionStart: _handleMouseDragSelectionStart, |
|
|
|
onDragSelectionUpdate: _handleMouseDragSelectionUpdate, |
|
|
|
behavior: HitTestBehavior.translucent, |
|
|
|
child: child |
|
|
|
ignoring: !_isEnabled, |
|
|
|
child: new MouseRegion( |
|
|
|
onEnter: onEnter, |
|
|
|
onExit: onExit, |
|
|
|
child: new AnimatedBuilder( |
|
|
|
animation: controller, |
|
|
|
builder: (BuildContext context, Widget child) => { return child; }, |
|
|
|
child: _selectionGestureDetectorBuilder.buildGestureDetector( |
|
|
|
behavior: HitTestBehavior.translucent, |
|
|
|
child: child |
|
|
|
) |
|
|
|
) |
|
|
|
) |
|
|
|
); |
|
|
|
} |