Skip to content

Commit f8023b0

Browse files
feat: WIP add cupertino text field
1 parent cae4be7 commit f8023b0

File tree

1 file changed

+397
-0
lines changed

1 file changed

+397
-0
lines changed
Lines changed: 397 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,397 @@
1+
import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle;
2+
3+
import 'package:flutter/cupertino.dart';
4+
import 'package:flutter/gestures.dart';
5+
import 'package:flutter/services.dart';
6+
import 'package:flutter_form_builder/flutter_form_builder.dart';
7+
8+
class FormBuilderCupertinoTextField extends FormBuilderField<String> {
9+
/// Controls the text being edited.
10+
///
11+
/// If null, this widget will create its own [TextEditingController].
12+
final TextEditingController? controller;
13+
14+
/// {@macro flutter.widgets.editableText.keyboardType}
15+
final TextInputType? keyboardType;
16+
17+
/// The type of action button to use for the keyboard.
18+
///
19+
/// Defaults to [TextInputAction.newline] if [keyboardType] is
20+
/// [TextInputType.multiline] and [TextInputAction.done] otherwise.
21+
final TextInputAction? textInputAction;
22+
23+
/// {@macro flutter.widgets.editableText.textCapitalization}
24+
final TextCapitalization textCapitalization;
25+
26+
/// The style to use for the text being edited.
27+
///
28+
/// Also serves as a base for the [placeholder] text's style.
29+
///
30+
/// Defaults to the standard iOS font style from [CupertinoTheme] if null.
31+
final TextStyle? style;
32+
33+
/// {@macro flutter.widgets.editableText.strutStyle}
34+
final StrutStyle? strutStyle;
35+
36+
/// {@macro flutter.widgets.editableText.textAlign}
37+
final TextAlign textAlign;
38+
39+
/// {@macro flutter.widgets.inputDecorator.textAlignVertical}
40+
final TextAlignVertical? textAlignVertical;
41+
42+
/// {@macro flutter.widgets.editableText.textDirection}
43+
final TextDirection? textDirection;
44+
45+
/// {@macro flutter.widgets.editableText.autofocus}
46+
final bool autofocus;
47+
48+
/// {@macro flutter.widgets.editableText.obscuringCharacter}
49+
final String obscuringCharacter;
50+
51+
/// {@macro flutter.widgets.editableText.obscureText}
52+
final bool obscureText;
53+
54+
/// {@macro flutter.widgets.editableText.autocorrect}
55+
final bool autocorrect;
56+
57+
/// {@macro flutter.services.textInput.smartDashesType}
58+
final SmartDashesType? smartDashesType;
59+
60+
/// {@macro flutter.services.textInput.smartQuotesType}
61+
final SmartQuotesType? smartQuotesType;
62+
63+
/// {@macro flutter.services.textInput.enableSuggestions}
64+
final bool enableSuggestions;
65+
66+
/// {@macro flutter.widgets.editableText.maxLines}
67+
final int? maxLines;
68+
69+
/// {@macro flutter.widgets.editableText.minLines}
70+
final int? minLines;
71+
72+
/// {@macro flutter.widgets.editableText.expands}
73+
final bool expands;
74+
75+
/// {@macro flutter.widgets.EditableText.contextMenuBuilder}
76+
///
77+
/// If not provided, will build a default menu based on the platform.
78+
///
79+
/// See also:
80+
///
81+
/// * [CupertinoAdaptiveTextSelectionToolbar], which is built by default.
82+
final EditableTextContextMenuBuilder? contextMenuBuilder;
83+
84+
/// {@macro flutter.widgets.editableText.showCursor}
85+
final bool? showCursor;
86+
87+
/// The maximum number of characters (Unicode grapheme clusters) to allow in
88+
/// the text field.
89+
///
90+
/// After [maxLength] characters have been input, additional input
91+
/// is ignored, unless [maxLengthEnforcement] is set to
92+
/// [MaxLengthEnforcement.none].
93+
///
94+
/// The TextField enforces the length with a
95+
/// [LengthLimitingTextInputFormatter], which is evaluated after the supplied
96+
/// [inputFormatters], if any.
97+
///
98+
/// This value must be either null or greater than zero. If set to null
99+
/// (the default), there is no limit to the number of characters allowed.
100+
///
101+
/// Whitespace characters (e.g. newline, space, tab) are included in the
102+
/// character count.
103+
///
104+
/// {@macro flutter.services.lengthLimitingTextInputFormatter.maxLength}
105+
final int? maxLength;
106+
107+
/// Determines how the [maxLength] limit should be enforced.
108+
///
109+
/// If [MaxLengthEnforcement.none] is set, additional input beyond [maxLength]
110+
/// will not be enforced by the limit.
111+
///
112+
/// {@macro flutter.services.textFormatter.effectiveMaxLengthEnforcement}
113+
///
114+
/// {@macro flutter.services.textFormatter.maxLengthEnforcement}
115+
final MaxLengthEnforcement? maxLengthEnforcement;
116+
117+
/// {@macro flutter.widgets.editableText.onEditingComplete}
118+
final VoidCallback? onEditingComplete;
119+
120+
/// {@macro flutter.widgets.editableText.onSubmitted}
121+
///
122+
/// See also:
123+
///
124+
/// * [EditableText.onSubmitted] for an example of how to handle moving to
125+
/// the next/previous field when using [TextInputAction.next] and
126+
/// [TextInputAction.previous] for [textInputAction].
127+
final ValueChanged<String?>? onSubmitted;
128+
129+
/// {@macro flutter.widgets.editableText.inputFormatters}
130+
final List<TextInputFormatter>? inputFormatters;
131+
132+
/// {@macro flutter.widgets.editableText.cursorWidth}
133+
final double cursorWidth;
134+
135+
/// {@macro flutter.widgets.editableText.cursorHeight}
136+
final double? cursorHeight;
137+
138+
/// {@macro flutter.widgets.editableText.cursorRadius}
139+
final Radius cursorRadius;
140+
141+
/// The color to use when painting the cursor.
142+
///
143+
/// Defaults to [TextSelectionThemeData.cursorColor] or [CupertinoTheme.primaryColor]
144+
/// depending on [ThemeData.platform].
145+
final Color? cursorColor;
146+
147+
/// Controls how tall the selection highlight boxes are computed to be.
148+
///
149+
/// See [ui.BoxHeightStyle] for details on available styles.
150+
final ui.BoxHeightStyle selectionHeightStyle;
151+
152+
/// Controls how wide the selection highlight boxes are computed to be.
153+
///
154+
/// See [ui.BoxWidthStyle] for details on available styles.
155+
final ui.BoxWidthStyle selectionWidthStyle;
156+
157+
/// The appearance of the keyboard.
158+
///
159+
/// This setting is only honored on iOS devices.
160+
///
161+
/// If unset, defaults to the theme brightness.
162+
final Brightness? keyboardAppearance;
163+
164+
/// {@macro flutter.widgets.editableText.scrollPadding}
165+
final EdgeInsets scrollPadding;
166+
167+
/// {@macro flutter.widgets.editableText.enableInteractiveSelection}
168+
final bool enableInteractiveSelection;
169+
170+
/// {@macro flutter.widgets.scrollable.dragStartBehavior}
171+
final DragStartBehavior dragStartBehavior;
172+
173+
/// {@macro flutter.rendering.editable.selectionEnabled}
174+
bool get selectionEnabled => enableInteractiveSelection;
175+
176+
/// {@template flutter.material.textfield.onTap}
177+
/// Called for each distinct tap except for every second tap of a double tap.
178+
///
179+
/// The text field builds a [GestureDetector] to handle input events like tap,
180+
/// to trigger focus requests, to move the caret, adjust the selection, etc.
181+
/// Handling some of those events by wrapping the text field with a competing
182+
/// GestureDetector is problematic.
183+
///
184+
/// To unconditionally handle taps, without interfering with the text field's
185+
/// internal gesture detector, provide this callback.
186+
///
187+
/// If the text field is created with [enabled] false, taps will not be
188+
/// recognized.
189+
///
190+
/// To be notified when the text field gains or loses the focus, provide a
191+
/// [focusNode] and add a listener to that.
192+
///
193+
/// To listen to arbitrary pointer events without competing with the
194+
/// text field's internal gesture detector, use a [Listener].
195+
/// {@endtemplate}
196+
final GestureTapCallback? onTap;
197+
198+
/// {@macro flutter.widgets.editableText.scrollPhysics}
199+
final ScrollPhysics? scrollPhysics;
200+
201+
/// {@macro flutter.widgets.editableText.scrollController}
202+
final ScrollController? scrollController;
203+
204+
/// {@macro flutter.widgets.editableText.autofillHints}
205+
/// {@macro flutter.services.autofill.autofillHints}
206+
final Iterable<String>? autofillHints;
207+
208+
///{@macro flutter.widgets.text_selection.TextMagnifierConfiguration.intro}
209+
///
210+
///{@macro flutter.widgets.magnifier.intro}
211+
///
212+
///{@macro flutter.widgets.text_selection.TextMagnifierConfiguration.details}
213+
final TextMagnifierConfiguration? magnifierConfiguration;
214+
215+
/// By default `false`
216+
final bool readOnly;
217+
218+
FormBuilderCupertinoTextField({
219+
super.key,
220+
required super.name,
221+
super.validator,
222+
String? initialValue,
223+
super.decoration,
224+
super.onChanged,
225+
super.valueTransformer,
226+
super.enabled,
227+
super.onSaved,
228+
super.autovalidateMode,
229+
super.onReset,
230+
super.focusNode,
231+
super.restorationId,
232+
this.readOnly = false,
233+
this.maxLines = 1,
234+
this.obscureText = false,
235+
this.textCapitalization = TextCapitalization.none,
236+
this.scrollPadding = const EdgeInsets.all(20.0),
237+
this.enableInteractiveSelection = true,
238+
this.maxLengthEnforcement,
239+
this.textAlign = TextAlign.start,
240+
this.autofocus = false,
241+
this.autocorrect = true,
242+
this.cursorWidth = 2.0,
243+
this.cursorHeight,
244+
this.keyboardType,
245+
this.style,
246+
this.controller,
247+
this.textInputAction,
248+
this.strutStyle,
249+
this.textDirection,
250+
this.maxLength,
251+
this.onEditingComplete,
252+
this.onSubmitted,
253+
this.inputFormatters,
254+
this.cursorRadius = const Radius.circular(2.0),
255+
this.cursorColor,
256+
this.keyboardAppearance,
257+
this.expands = false,
258+
this.minLines,
259+
this.showCursor,
260+
this.onTap,
261+
this.enableSuggestions = false,
262+
this.textAlignVertical,
263+
this.dragStartBehavior = DragStartBehavior.start,
264+
this.scrollController,
265+
this.scrollPhysics,
266+
this.selectionWidthStyle = ui.BoxWidthStyle.tight,
267+
this.smartDashesType,
268+
this.smartQuotesType,
269+
this.selectionHeightStyle = ui.BoxHeightStyle.tight,
270+
this.autofillHints,
271+
this.obscuringCharacter = '•',
272+
this.contextMenuBuilder = _defaultContextMenuBuilder,
273+
this.magnifierConfiguration,
274+
}) : super(
275+
builder: (FormFieldState<String?> field) {
276+
final state = field as _FormBuilderCupertinoTextFieldState;
277+
278+
return CupertinoTextField(
279+
restorationId: restorationId,
280+
controller: state._effectiveController,
281+
focusNode: state.effectiveFocusNode,
282+
decoration: BoxDecoration(),
283+
keyboardType: keyboardType,
284+
textInputAction: textInputAction,
285+
style: style,
286+
strutStyle: strutStyle,
287+
textAlign: textAlign,
288+
textAlignVertical: textAlignVertical,
289+
textDirection: textDirection,
290+
textCapitalization: textCapitalization,
291+
autofocus: autofocus,
292+
readOnly: readOnly,
293+
showCursor: showCursor,
294+
obscureText: obscureText,
295+
autocorrect: autocorrect,
296+
enableSuggestions: enableSuggestions,
297+
maxLengthEnforcement: maxLengthEnforcement,
298+
maxLines: maxLines,
299+
minLines: minLines,
300+
expands: expands,
301+
maxLength: maxLength,
302+
onTap: onTap,
303+
onEditingComplete: onEditingComplete,
304+
onSubmitted: onSubmitted,
305+
inputFormatters: inputFormatters,
306+
enabled: state.enabled,
307+
cursorWidth: cursorWidth,
308+
cursorHeight: cursorHeight,
309+
cursorRadius: cursorRadius,
310+
cursorColor: cursorColor,
311+
scrollPadding: scrollPadding,
312+
keyboardAppearance: keyboardAppearance,
313+
enableInteractiveSelection: enableInteractiveSelection,
314+
dragStartBehavior: dragStartBehavior,
315+
scrollController: scrollController,
316+
scrollPhysics: scrollPhysics,
317+
selectionHeightStyle: selectionHeightStyle,
318+
selectionWidthStyle: selectionWidthStyle,
319+
smartDashesType: smartDashesType,
320+
smartQuotesType: smartQuotesType,
321+
contextMenuBuilder: contextMenuBuilder,
322+
obscuringCharacter: obscuringCharacter,
323+
autofillHints: autofillHints,
324+
magnifierConfiguration: magnifierConfiguration,
325+
);
326+
},
327+
);
328+
329+
static Widget _defaultContextMenuBuilder(
330+
BuildContext context,
331+
EditableTextState editableTextState,
332+
) {
333+
return CupertinoAdaptiveTextSelectionToolbar.editableText(
334+
editableTextState: editableTextState,
335+
);
336+
}
337+
338+
@override
339+
FormBuilderFieldState<FormBuilderCupertinoTextField, String> createState() =>
340+
_FormBuilderCupertinoTextFieldState();
341+
}
342+
343+
class _FormBuilderCupertinoTextFieldState
344+
extends FormBuilderFieldState<FormBuilderCupertinoTextField, String> {
345+
TextEditingController? get _effectiveController =>
346+
widget.controller ?? _controller;
347+
348+
TextEditingController? _controller;
349+
350+
@override
351+
void initState() {
352+
super.initState();
353+
//setting this to value instead of initialValue here is OK since we handle initial value in the parent class
354+
_controller = widget.controller ?? TextEditingController(text: value);
355+
_controller!.addListener(_handleControllerChanged);
356+
}
357+
358+
@override
359+
void dispose() {
360+
// Dispose the _controller when initState created it
361+
_controller!.removeListener(_handleControllerChanged);
362+
if (null == widget.controller) {
363+
_controller!.dispose();
364+
}
365+
super.dispose();
366+
}
367+
368+
@override
369+
void reset() {
370+
super.reset();
371+
setState(() {
372+
_effectiveController!.text = initialValue ?? '';
373+
});
374+
}
375+
376+
@override
377+
void didChange(String? value) {
378+
super.didChange(value);
379+
380+
if (_effectiveController!.text != value) {
381+
_effectiveController!.text = value ?? '';
382+
}
383+
}
384+
385+
void _handleControllerChanged() {
386+
// Suppress changes that originated from within this class.
387+
//
388+
// In the case where a controller has been passed in to this widget, we
389+
// register this change listener. In these cases, we'll also receive change
390+
// notifications for changes originating from within this class -- for
391+
// example, the reset() method. In such cases, the FormField value will
392+
// already have been set.
393+
if (_effectiveController!.text != (value ?? '')) {
394+
didChange(_effectiveController!.text);
395+
}
396+
}
397+
}

0 commit comments

Comments
 (0)