How do I enter text into TextFormField
by using the label text? My issue is that I can't find the widget while doing an integration test. I was able to find the widget by adding a key but I don't want to change the source code of the app for doing integration testing.
TextFormField(
controller: usernameController,
decoration: InputDecoration(border: UnderlineInputBorder(), labelText: "Username"),
)
Tried something like this but it doesn't work:
final usernameField = find.descendant(
of: find.text("Username"),
matching: find.byType(EditableText),
);
CodePudding user response:
- Pump your testwidget which contains your target TextFormField
- Then find all the TextFormFields in your widget, add it to the List.
List<TextField> textFields = List<TextField>();
find.byType(TextField).evaluate().toList().forEach((element) {
textFields.add(element.widget);
});
- You now have all the textfields including Username labeled field
CodePudding user response:
You were on the right track, just a couple of things needed adjusting.
TLDR;
Try this instead:
final usernameField = find.ancestor(
of: find.text('Username'),
matching: find.byType(TextFormField),
);
tester.enterText(usernameField, "testing");
Explanation
Relationship between "Username" and EditableText
First off, the finder couldn't find an EditableText
related to "Username" because the EditableText
used by TextFormField
is actually a sibling/cousin to any Text
type widgets related to the decoration.
You can take a look at the tree using the Widget Inspector in most IDEs, or if you want to stay completely in the test environment, try debugDumpApp to have the tree output to the console. As you can see by this part of the tree, there's nothing under the EditableText
that has the "Username" text in it.
TextFormField
tree
│ └TextFormField(dependencies: [UnmanagedRestorationScope, _InheritedTheme, _LocalizationsScope-[GlobalKey#1c7cf]], state: _TextFormFieldState#979c2)
│ └UnmanagedRestorationScope
│ └TextField(controller: TextEditingController#7d830(TextEditingValue(text: ┤├, selection: TextSelection.invalid, composing: TextRange(start: -1, end: -1))), enabled: true, decoration: InputDecoration(labelText: "Username", floatingLabelBehavior: FloatingLabelBehavior.auto, floatingLabelAlignment: FloatingLabelAlignment.start, border: UnderlineInputBorder(), alignLabelWithHint: false), dependencies: [DefaultSelectionStyle, MediaQuery, UnmanagedRestorationScope, _InheritedTheme, _LocalizationsScope-[GlobalKey#1c7cf]], state: _TextFieldState#b279d)
│ └MouseRegion(listeners: [enter, exit], cursor: SystemMouseCursor(text), renderObject: RenderMouseRegion#0773e relayoutBoundary=up3)
│ └TextFieldTapRegion(groupId: EditableText, renderObject: RenderTapRegion#81348)
│ └IgnorePointer(ignoring: false, renderObject: RenderIgnorePointer#4fd71 relayoutBoundary=up5)
│ └AnimatedBuilder(animation: TextEditingController#7d830(TextEditingValue(text: ┤├, selection: TextSelection.invalid, composing: TextRange(start: -1, end: -1))), state: _AnimatedState#59a41)
│ └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#eb417 relayoutBoundary=up6)
│ └TextSelectionGestureDetector(state: _TextSelectionGestureDetectorState#c5ea6)
│ └RawGestureDetector(state: RawGestureDetectorState#1eae5(gestures: [tap, long press, pan], excludeFromSemantics: true, behavior: translucent))
│ └Listener(listeners: [down, panZoomStart], behavior: translucent, renderObject: RenderPointerListener#84fb6 relayoutBoundary=up7)
│ └AnimatedBuilder(animation: Listenable.merge([FocusNode#3118d(context: Focus), TextEditingController#7d830(TextEditingValue(text: ┤├, selection: TextSelection.invalid, composing: TextRange(start: -1, end: -1)))]), state: _AnimatedState#6fcb9)
│ └InputDecorator(decoration: InputDecoration(labelText: "Username", hintMaxLines: "1", floatingLabelBehavior: FloatingLabelBehavior.auto, floatingLabelAlignment: FloatingLabelAlignment.start, border: UnderlineInputBorder(), alignLabelWithHint: false), isFocused: false, isEmpty: true, dependencies: [Directionality, MediaQuery, _InheritedTheme, _LocalizationsScope-[GlobalKey#1c7cf]], state: _InputDecoratorState#cdcb7(tickers: tracking 2 tickers))
│ └_Decorator(renderObject: _RenderDecoration#543bf relayoutBoundary=up8)
│ ├RepaintBoundary(renderObject: RenderRepaintBoundary#932e5 relayoutBoundary=up9)
│ │└UnmanagedRestorationScope
│ │ └EditableText-[LabeledGlobalKey<EditableTextState>#62cb3](controller: TextEditingController#7d830(TextEditingValue(text: ┤├, selection: TextSelection.invalid, composing: TextRange(start: -1, end: -1))), focusNode: FocusNode#3118d, debugLabel: (englishLike titleMedium 2014).merge(blackMountainView titleMedium), inherit: false, color: Color(0xdd000000), family: Roboto, size: 16.0, weight: 400, baseline: alphabetic, decoration: TextDecoration.none, textAlign: start, keyboardType: TextInputType(name: TextInputType.text, signed: null, decimal: null), autofillHints: [], dependencies: [Directionality, MediaQuery, ScrollConfiguration, _EffectiveTickerMode], state: EditableTextState#1c1c0(tickers: tracking 1 ticker))
│ │ └TextFieldTapRegion(debugLabel: EditableText, groupId: EditableText, renderObject: RenderTapRegion#9cbef)
│ │ └MouseRegion(listeners: <none>, cursor: defer, renderObject: RenderMouseRegion#74908 relayoutBoundary=up11)
│ │ └Actions(dispatcher: null, actions: {DoNothingAndStopPropagationTextIntent: DoNothingAction#4a34c, ReplaceTextIntent: CallbackAction<ReplaceTextIntent>#408b9, UpdateSelectionIntent: CallbackAction<UpdateSelectionIntent>#06984, DirectionalFocusIntent: DirectionalFocusAction#2491c, DismissIntent: CallbackAction<DismissIntent>#3e59f, DeleteCharacterIntent: _OverridableContextAction<DeleteCharacterIntent>#690f6(defaultAction: _DeleteTextAction<DeleteCharacterIntent>#0bc91), DeleteToNextWordBoundaryIntent: _OverridableContextAction<DeleteToNextWordBoundaryIntent>#c44ac(defaultAction: _DeleteTextAction<DeleteToNextWordBoundaryIntent>#119b9), DeleteToLineBreakIntent: _OverridableContextAction<DeleteToLineBreakIntent>#af3a8(defaultAction: _DeleteTextAction<DeleteToLineBreakIntent>#9eaed), ExtendSelectionByCharacterIntent: _OverridableContextAction<ExtendSelectionByCharacterIntent>#78905(defaultAction: _UpdateTextSelectionAction<ExtendSelectionByCharacterIntent>#3cb9a), ExtendSelectionToNextWordBoundaryIntent: _OverridableContextAction<ExtendSelectionToNextWordBoundaryIntent>#efea3(defaultAction: _UpdateTextSelectionAction<ExtendSelectionToNextWordBoundaryIntent>#11fdb), ExtendSelectionToLineBreakIntent: _OverridableContextAction<ExtendSelectionToLineBreakIntent>#9cdf0(defaultAction: _UpdateTextSelectionAction<ExtendSelectionToLineBreakIntent>#64825), ExpandSelectionToLineBreakIntent: _OverridableAction<ExpandSelectionToLineBreakIntent>#44e24(defaultAction: CallbackAction<ExpandSelectionToLineBreakIntent>#ce179), ExpandSelectionToDocumentBoundaryIntent: _OverridableAction<ExpandSelectionToDocumentBoundaryIntent>#1f49f(defaultAction: CallbackAction<ExpandSelectionToDocumentBoundaryIntent>#faeb6), ExtendSelectionVerticallyToAdjacentLineIntent: _OverridableContextAction<ExtendSelectionVerticallyToAdjacentLineIntent>#2444b(defaultAction: _UpdateTextSelectionToAdjacentLineAction<ExtendSelectionVerticallyToAdjacentLineIntent>#efbca), ExtendSelectionToDocumentBoundaryIntent: _OverridableContextAction<ExtendSelectionToDocumentBoundaryIntent>#a9b51(defaultAction: _UpdateTextSelectionAction<ExtendSelectionToDocumentBoundaryIntent>#d1374), ExtendSelectionToNextWordBoundaryOrCaretLocationIntent: _OverridableContextAction<ExtendSelectionToNextWordBoundaryOrCaretLocationIntent>#913d5(defaultAction: _ExtendSelectionOrCaretPositionAction#00b6c), ScrollToDocumentBoundaryIntent: _OverridableAction<ScrollToDocumentBoundaryIntent>#96e26(defaultAction: CallbackAction<ScrollToDocumentBoundaryIntent>#f52dc), SelectAllTextIntent: _OverridableContextAction<SelectAllTextIntent>#208ba(defaultAction: _SelectAllAction#1c128), CopySelectionTextIntent: _OverridableContextAction<CopySelectionTextIntent>#31482(defaultAction: _CopySelectionAction#d777b), PasteTextIntent: _OverridableAction<PasteTextIntent>#17ba0(defaultAction: CallbackAction<PasteTextIntent>#fc2e6), TransposeCharactersIntent: _OverridableAction<TransposeCharactersIntent>#909dc(defaultAction: CallbackAction<TransposeCharactersIntent>#826be)}, state: _ActionsState#6345b)
│ │ └_ActionsMarker
│ │ └_TextEditingHistory(state: _TextEditingHistoryState#c5c7e)
│ │ └Actions(dispatcher: null, actions: {UndoTextIntent: _OverridableAction<UndoTextIntent>#76c57(defaultAction: CallbackAction<UndoTextIntent>#a6b68), RedoTextIntent: _OverridableAction<RedoTextIntent>#4244e(defaultAction: CallbackAction<RedoTextIntent>#d89b9)}, state: _ActionsState#a1a96)
│ │ └_ActionsMarker
│ │ └Focus(debugLabel: "EditableText", focusNode: FocusNode#3118d, dependencies: [_FocusMarker], state: _FocusState#f3479)
│ │ └_FocusMarker
│ │ └Scrollable(axisDirection: right, physics: null, restorationId: "editable", dependencies: [MediaQuery, UnmanagedRestorationScope, _InheritedTheme, _LocalizationsScope-[GlobalKey#1c7cf]], state: ScrollableState#2fb51(position: ScrollPositionWithSingleContext#ec8ce(offset: 0.0, range: 0.0..0.0, viewport: 800.0, ScrollableState, ClampingScrollPhysics -> RangeMaintainingScrollPhysics -> ClampingScrollPhysics -> RangeMaintainingScrollPhysics, IdleScrollActivity#259e6, ScrollDirection.idle), effective physics: ClampingScrollPhysics -> RangeMaintainingScrollPhysics -> ClampingScrollPhysics -> RangeMaintainingScrollPhysics))
│ │ └_ScrollableScope
│ │ └Listener(listeners: [signal], behavior: deferToChild, renderObject: RenderPointerListener#dd2ea relayoutBoundary=up12)
│ │ └RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#d6db0](state: RawGestureDetectorState#cda6b(gestures: <none>, excludeFromSemantics: true, behavior: opaque))
│ │ └Listener(listeners: [down, panZoomStart], behavior: opaque, renderObject: RenderPointerListener#0abf9 relayoutBoundary=up13)
│ │ └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#6f0e4 relayoutBoundary=up14)
│ │ └IgnorePointer-[GlobalKey#f9ddb](ignoring: false, ignoringSemantics: false, renderObject: RenderIgnorePointer#015fc relayoutBoundary=up15)
│ │ └CompositedTransformTarget(renderObject: RenderLeaderLayer#bd9fd relayoutBoundary=up16)
│ │ └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#20253 relayoutBoundary=up17)
│ │ └_ScribbleFocusable(state: _ScribbleFocusableState#6511f)
│ │ └_Editable-[GlobalKey#9b870](dependencies: [_LocalizationsScope-[GlobalKey#1c7cf]], renderObject: RenderEditable#7f9ac relayoutBoundary=up18)
│ ├_Shaker(animation: AnimationController#c202f(⏮ 0.000; paused), state: _AnimatedState#77802)
│ │└Transform(dependencies: [Directionality], renderObject: RenderTransform#053a1 relayoutBoundary=up9)
│ │ └AnimatedOpacity(duration: 200ms, opacity: 1.0, state: _AnimatedOpacityState#bcd55(ticker inactive))
│ │ └FadeTransition(opacity: AnimationController#04a30(⏮ 0.000; paused; for AnimatedOpacity)➩Cubic(0.40, 0.00, 0.20, 1.00)➩Tween<double>(1.0 → 1.0)➩1.0, renderObject: RenderAnimatedOpacity#05cbb relayoutBoundary=up10)
│ │ └AnimatedDefaultTextStyle(duration: 200ms, debugLabel: (((englishLike titleMedium 2014).merge(blackMountainView titleMedium)).merge(unknown)).copyWith, inherit: false, color: Color(0x99000000), family: Roboto, size: 16.0, weight: 400, baseline: alphabetic, height: 1.0x, decoration: TextDecoration.none, softWrap: wrapping at box width, overflow: clip, state: _AnimatedDefaultTextStyleState#6c250(ticker inactive))
│ │ └DefaultTextStyle(debugLabel: (((englishLike titleMedium 2014).merge(blackMountainView titleMedium)).merge(unknown)).copyWith, inherit: false, color: Color(0x99000000), family: Roboto, size: 16.0, weight: 400, baseline: alphabetic, height: 1.0x, decoration: TextDecoration.none, softWrap: wrapping at box width, overflow: clip)
│ │ └Text("Username", textAlign: start, overflow: ellipsis, dependencies: [DefaultSelectionStyle, DefaultTextStyle, MediaQuery])
│ │ └RichText(softWrap: wrapping at box width, overflow: ellipsis, maxLines: unlimited, text: "Username", dependencies: [Directionality, _LocalizationsScope-[GlobalKey#1c7cf]], renderObject: RenderParagraph#128a5 relayoutBoundary=up11)
│ ├_HelperError(state: _HelperErrorState#ffb07(ticker inactive))
│ │└SizedBox(renderObject: RenderConstrainedBox#8887a relayoutBoundary=up9)
│ └_BorderContainer(dependencies: [Directionality], state: _BorderContainerState#c1c2f(tickers: tracking 2 tickers))
│ └CustomPaint(renderObject: RenderCustomPaint#f9064)
ancestor
vs. descendant
Secondly, you needed to use ancestor
instead of descendant
. These two methods can be a bit difficult to wrap your head around, but this is how I think of it to keep them straight.
- Start with your
of
finder - From your
of
method, is the thing you want yourmatching
finder to find back up the tree (less indented), or are you going further down the tree (more indented)?- If you're going up, use
ancestor
- If you're going down, use
descendant
- If you're going up, use
- The
ancestor
/descendant
method will return whatmatcher
defines, not whatof
defines
Entering text
I'm taking a guess here, but I think that you were trying to find an EditableText
because the documentation mentions needing one. Thankfully, it also mentions that you can use something which has a descendant EditableText
.
As such, we can find a TextFormField
instead of a EditableText
in the matching
parameter and still use that finder with the enterText method.