Add missing TextField attributes - #87
Conversation
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request is a good initiative to enhance LanguageToolTextField by adding more standard TextField attributes, increasing its flexibility for developers. The added properties are relevant and cover common use cases.
However, there are a couple of critical issues related to the implementation of the scrollController and groupId properties that need to be addressed. There's also a point regarding the default value for textCapitalization worth considering for consistency.
Summary of Findings
- Incorrect ScrollController Handling: The
widget.scrollControllerproperty is added but not utilized. TheLanguageToolTextFieldalways uses an internally createdScrollController, ignoring any provided by the developer. This requires changes to the state's lifecycle management for the scroll controller. - Incorrect
groupIdDefault: ThegroupIdproperty in theTextFieldis incorrectly defaulted to the typeEditableTextinstead ofnullorwidget.groupId. - Divergent
textCapitalizationDefault:LanguageToolTextFielddefaultstextCapitalizationto.sentences, while the underlyingTextFielddefaults to.none. This difference should be intentional and clear, or aligned. - Missing Listener Removal: The
_textControllerListeneradded ininitStateis not removed indispose, which can lead to memory leaks. This was noted as part of thescrollControllerfeedback.
Merge Readiness
The pull request aims to add valuable features by exposing more TextField attributes. However, there are critical issues, particularly with how the scrollController is handled and the groupId default, that must be addressed before this PR can be merged. The textCapitalization default is a medium-severity point to consider for consistency.
I recommend addressing these points to ensure the new attributes function correctly and as expected. I am unable to approve the pull request in its current state; please make the necessary changes, and then it can be re-reviewed for merging.
| class _LanguageToolTextFieldState extends State<LanguageToolTextField> { | ||
| FocusNode? _focusNode; | ||
| final _scrollController = ScrollController(); | ||
| final ScrollController _scrollController = ScrollController(); |
There was a problem hiding this comment.
The current initialization of _scrollController here (and its subsequent exclusive use) doesn't correctly incorporate the new widget.scrollController property. This means LanguageToolTextField will always create and use its own internal ScrollController, effectively ignoring any ScrollController instance passed by the developer via the widget.scrollController property.
To correctly implement this, the state needs to manage the ScrollController lifecycle based on whether widget.scrollController is provided:
- The state's scroll controller field (e.g.,
_internalScrollControlleras suggested below) should be nullable. It should only be initialized with a newScrollController()ininitStateifwidget.scrollControllerisnull. - An 'effective'
ScrollController(which iswidget.scrollControllerif provided, otherwise the internally created one) should be determined and used throughout the state's lifecycle. - This effective
ScrollControllermust be passed to theTextField(scrollController: ...)in thebuildmethod. - The
_textControllerListenermust read its offset from this effective controller (e.g.,widget.controller.scrollOffset = _effectiveScrollController.offset). It's also good practice to check_effectiveScrollController.hasClientsbefore accessing.offset. - In the
disposemethod, the internally created scroll controller (if any) should be disposed. - Additionally, a crucial point related to lifecycle management: the listener
_textControllerListeneradded towidget.controllerininitStateis not currently removed indispose. This can lead to memory leaks or errors. Please addwidget.controller.removeListener(_textControllerListener);in thedisposemethod.
ScrollController? _internalScrollController; // This will be initialized in initState if widget.scrollController is null
| onSubmitted: widget.onTextSubmitted, | ||
| onTap: widget.onTap, | ||
| onTapOutside: widget.onTapOutside, | ||
| groupId: widget.groupId ?? EditableText, |
There was a problem hiding this comment.
The use of EditableText as a fallback for widget.groupId (i.e., widget.groupId ?? EditableText) is incorrect. EditableText is a class (a type), not an Object instance that can be used as a groupId. The groupId property of TextField expects an Object?.
If widget.groupId is null, you should pass null to the TextField, which aligns with TextField's own default behavior for groupId. Could you please adjust this to pass widget.groupId directly?
groupId: widget.groupId,
| groupId: widget.groupId ?? EditableText, | ||
| maxLength: widget.maxLength, | ||
| enabled: widget.enabled, | ||
| textCapitalization: widget.textCapitalization ?? TextCapitalization.sentences, |
There was a problem hiding this comment.
The textCapitalization property is defaulted to TextCapitalization.sentences if widget.textCapitalization is null. While TextCapitalization.sentences can be a sensible default for many text fields, the underlying Flutter TextField widget defaults its textCapitalization property to TextCapitalization.none.
Is this deviation intentional to provide a different default for LanguageToolTextField? If the goal is to closely mirror TextField's behavior, you might consider passing widget.textCapitalization directly, allowing TextField to apply its own default of .none when widget.textCapitalization is null. If .sentences is a deliberate choice for this wrapper, it might be helpful to document this specific default behavior for users of LanguageToolTextField.
textCapitalization: widget.textCapitalization,
|
fixed in #90 |
Adds missing attributes of TextField to allow developer to make use of them