Question Details

No question body available.

Tags

android flutter dart user-interface textfield

Answers (2)

Accepted Answer Available
Accepted Answer
June 15, 2025 Score: 2 Rep: 136 Quality: High Completeness: 60%

Looking at the UI you want to achieve, I don't think that is possible using the native Flutter TextField.

Try building a custom text field using Stack and manage the label manually with AnimatedPositioned.

Here is a simple reusable component you can expand on:

import 'package:flutter/material.dart';

class PhoneInputField extends StatefulWidget { final TextEditingController controller; final FocusNode? focusNode; final String label; final String countryCode; final bool showError; final bool isValid; final String? errorMessage; final ValueChanged? onChanged;

const PhoneInputField({ super.key, required this.controller, this.focusNode, this.label = "Phone number", this.countryCode = "+1", this.showError = false, this.isValid = false, this.errorMessage, this.onChanged, });

@override State createState() => PhoneInputFieldState(); }

class PhoneInputFieldState extends State { late final FocusNode internalFocusNode; FocusNode get focusNode => widget.focusNode ?? internalFocusNode;

bool get hasText => widget.controller.text.trim().isNotEmpty; bool get isFocused => focusNode.hasFocus;

@override void initState() { super.initState(); internalFocusNode = FocusNode(); widget.controller.addListener(refresh); focusNode.addListener(refresh); }

void refresh() => setState(() {});

@override void dispose() { if (widget.focusNode == null) internalFocusNode.dispose(); widget.controller.removeListener(refresh); focusNode.removeListener(refresh); super.dispose(); }

@override Widget build(BuildContext context) { final borderColor = widget.showError ? Colors.red : isFocused ? Colors.black : Colors.grey[400]!;

final showIcon = !hasText ? null : widget.showError ? Icons.close : widget.isValid ? Icons.check : null;

return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Stack( children: [ Container( height: 56, decoration: BoxDecoration( border: Border.all(color: borderColor, width: 1.5), borderRadius: BorderRadius.circular(8), ), padding: const EdgeInsets.symmetric(horizontal: 12), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ // replace with your own code dropdwon or text Text( widget.countryCode, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), ////// const VerticalDivider(width: 20, thickness: 1), Expanded( child: TextField( controller: widget.controller, focusNode: focusNode, keyboardType: TextInputType.phone, decoration: const InputDecoration( border: InputBorder.none, isDense: true, contentPadding: EdgeInsets.only(top: 18), ), style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w500, ), onChanged: widget.onChanged, ), ), if (showIcon != null) Icon( showIcon, color: showIcon == Icons.check ? Colors.green : Colors.red, ), ], ), ),

AnimatedPositioned( duration: const Duration(milliseconds: 200), left: 66, top: isFocused || hasText ? 8 : 18, child: AnimatedDefaultTextStyle( duration: const Duration(milliseconds: 200), style: TextStyle( fontSize: isFocused || hasText ? 12 : 16, color: widget.showError ? Colors.red : isFocused ? Colors.black : Colors.grey[600], fontWeight: FontWeight.w500, ), child: Text(widget.label), ), ), ], ), const SizedBox(height: 6), if (widget.showError && widget.errorMessage != null) Text( widget.errorMessage!, style: const TextStyle(color: Colors.red, fontSize: 12), ), ], ); } }

And here is how you can use it in your form (checkout, profile, etc.):

PhoneInputField( controller:
controller, focusNode: focusNode, countryCode: "+1", isValid: isValid, showError: showError, errorMessage: "enter valid phone number", onChanged: validate, ),
June 15, 2025 Score: 0 Rep: 49 Quality: Low Completeness: 50%

you can control the focus of the TextField by initializing a FocusNode var before the build method and then assign it to the property focusNode

FocusNode focusNode = FocusNode();

@override void dispose() { focusNode.dispose(); super.dispose(); }

and then the TextField

TextField(
  //code...
  focusNode: focusNode,
  //code...
)

you can control it like that focusNode.unfocus(); inside any of the onSubmitted or whatever

this solves the focus problem, but what you want, I don't really know how to do that, but I suggest that you don't get stuck with that design for too long, to save time for more important things, if you didn't find a solution.