ios – How can I align the prefix text and cursor on the same line in a Flutter TextField, while keeping the prefix text visible at all times?


I’m working on a TextField in Flutter and trying to achieve the following:

  1. Prefix Text Alignment: I want the prefix text (e.g., “To:”) to be visible at all times, even when the user hasn’t focused on the TextField.
  2. Cursor Alignment: I want the cursor to appear on the same line as the prefix text, not higher or lower than the prefix.
  3. Hint Text: I also want the hint text (e.g., “Name, $Cashtag, Phone, Email”) to remain visible when the TextField is empty, but I don’t want the cursor or hint text to overlap with the prefix text.

What I’ve Tried:

  1. Using prefixText to display the prefix text, but this works only when the TextField is focused and doesn’t align well with the cursor.
  2. Using prefixIcon with Padding and Align to try and adjust the alignment, but it causes the cursor and hint text to disappear.
  3. I’ve also tried setting cursorColor, border: InputBorder.none, and adjusting the padding to no avail.

Code:

TextField(
  cursorColor: Colors.green, // Set the cursor color
  decoration: InputDecoration(
    hintText: 'Name, \$Cashtag, Phone, Email',
    border: InputBorder.none, // Remove the border
    prefixText: 'To ', // Prefix text before input
    prefixStyle: TextStyle(
      color: Colors.black, // Prefix text color
      fontSize: 18, // Prefix text size
      fontWeight: FontWeight.bold, // Prefix text weight
    ),
  ),
)

Full code (For Context):

// payment_details_screen.dart
import 'package:cashapp/utilities/colors.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

class PaymentDetailsScreen extends StatelessWidget {
  const PaymentDetailsScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: SafeArea(
        child: Column(
          children: [
            SizedBox(
              height: 8.h,
            ),
            // Header with amount and bank selection
            Padding(
              padding: EdgeInsets.only(right: 12.w),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  IconButton(
                    icon: const Icon(
                      Icons.close,
                      color: Colors.black,
                      size: 30,
                    ),
                    onPressed: () => Navigator.pop(context),
                  ),
                  Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      const Text(
                        '\$7,000',
                        style: TextStyle(
                          fontSize: 20,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      Padding(
                        padding: EdgeInsets.only(left: 16.w),
                        child: InkWell(
                          onTap: () {
                            // Show bank selection
                          },
                          child: Row(
                            children: const [
                              Text(
                                'Cash Balance',
                                style: TextStyle(
                                  color: gray2,
                                  fontSize: 14,
                                ),
                              ),
                              Icon(Icons.keyboard_arrow_down,
                                  color: Colors.grey),
                            ],
                          ),
                        ),
                      ),
                    ],
                  ),
                  ElevatedButton(
                    onPressed: () {
                      // Handle payment
                    },
                    style: ElevatedButton.styleFrom(
                      elevation: 0.0,
                      padding:
                          EdgeInsets.symmetric(horizontal: 24, vertical: 10),
                      backgroundColor: green3,
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(100),
                      ),
                      minimumSize:
                          Size(60, 32), // Set width to 60 and height to 32
                    ),
                    child: const Text(
                      'Pay',
                      style: TextStyle(color: Colors.white, fontSize: 18),
                    ),
                  ),
                ],
              ),
            ),
            Divider(
              color: gray5,
              thickness: 1.5,
              height: 32.h,
            ),
            // Input fields
            const SizedBox(width: 16),
            TextField(
              cursorColor: green3, // Set the cursor color to green
              decoration: InputDecoration(
                hintText: 'Name, \$Cashtag, Phone, Email',
                hintStyle: TextStyle(
                    color: Colors.grey), // Optional, for hint text color
                border: InputBorder.none, // Remove the border
                prefixText: 'To ', // Prefix text before input
                prefixStyle: TextStyle(
                  color: Colors.black, // Prefix text color
                  fontSize: 18, // Prefix text size
                  fontWeight: FontWeight.bold, // Prefix text weight
                ),
              ),
            ),
            Divider(
              color: gray5,
              thickness: 1.5,
              height: 32.h,
            ),
            // Input fields
            TextField(
              cursorColor: green3, // Set the cursor color to green
              decoration: InputDecoration(
                hintText: 'Name, \$Cashtag, Phone, Email',
                border: InputBorder.none, // Remove the border
                prefixText: 'For ', // Prefix text before input
                prefixStyle: TextStyle(
                  color: Colors.black, // Prefix text color
                  fontSize: 18, // Prefix text size
                  fontWeight: FontWeight.bold, // Prefix text weight
                ),
              ),
            ),

            // Payment type selector
            SingleChildScrollView(
              scrollDirection:
                  Axis.horizontal, // Make the row scrollable horizontally
              child: Row(
                children: [
                  const Text('Send as'),
                  const SizedBox(width: 16),
                  ElevatedButton(
                    onPressed: () {},
                    style: ElevatedButton.styleFrom(
                      backgroundColor: const Color(0xFF24CE84),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(20),
                      ),
                    ),
                    child: const Text('Cash'),
                  ),
                  const SizedBox(width: 8),
                  OutlinedButton(
                    onPressed: () {},
                    style: OutlinedButton.styleFrom(
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(20),
                      ),
                    ),
                    child: Row(
                      children: const [
                        Text('Gift Card'),
                        Icon(Icons.keyboard_arrow_down),
                      ],
                    ),
                  ),
                  const SizedBox(width: 8),
                  OutlinedButton(
                    onPressed: () {},
                    style: OutlinedButton.styleFrom(
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(20),
                      ),
                    ),
                    child: Row(
                      children: const [
                        Text('Stock'),
                        Icon(Icons.keyboard_arrow_down),
                      ],
                    ),
                  ),
                  const SizedBox(width: 8),
                  OutlinedButton(
                    onPressed: () {},
                    style: OutlinedButton.styleFrom(
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(20),
                      ),
                    ),
                    child: Row(
                      children: const [
                        Text('Stock'),
                        Icon(Icons.keyboard_arrow_down),
                      ],
                    ),
                  ),
                ],
              ),
            ),

            // Suggested contacts
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Text(
                  'SUGGESTED',
                  style: TextStyle(
                    color: Colors.grey,
                    fontSize: 12,
                  ),
                ),
                const SizedBox(height: 16),
                _buildContactTile(
                  'J',
                  'Jay Z',
                  '\$sc',
                  Colors.brown,
                  isSelected: true,
                ),
                _buildContactTile(
                  'D',
                  'Diego',
                  '\$dm',
                  Colors.purple,
                ),
                _buildContactTile(
                  'S',
                  'Sandy G.',
                  '\$sandy',
                  Colors.deepOrange,
                ),
                _buildContactTile(
                  'J',
                  'Diego Martinez',
                  '\$dm',
                  Colors.purple,
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildContactTile(
    String initial,
    String name,
    String tag,
    Color avatarColor, {
    bool isSelected = false,
  }) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 8.0),
      child: Row(
        children: [
          SizedBox(
            width: 24,
            height: 24,
            child: Checkbox(
              value: isSelected,
              onChanged: (value) {},
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(4),
              ),
            ),
          ),
          const SizedBox(width: 12),
          CircleAvatar(
            backgroundColor: avatarColor,
            child: Text(
              initial,
              style: const TextStyle(color: Colors.white),
            ),
          ),
          const SizedBox(width: 12),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  name,
                  style: const TextStyle(fontWeight: FontWeight.bold),
                ),
                Text(
                  tag,
                  style: const TextStyle(color: Colors.grey),
                ),
              ],
            ),
          ),
          const Icon(Icons.person_outline),
        ],
      ),
    );
  }
}

What I’m trying to achieve:

enter image description here

Full Screen(For context):

enter image description here

Problem:

  • The prefix text doesn’t stay visible when the TextField is focused, and when it does appear, it’s not properly aligned with the cursor.
  • The cursor is not appearing at the same level as the prefix text, causing misalignment.

How can I properly align the prefix text with the cursor, and ensure the prefix text stays visible even when the user is not focused on the TextField?


Discover more from TrendyShopToBuy

Subscribe to get the latest posts sent to your email.

Latest articles

spot_imgspot_img

Related articles

Leave a Reply

spot_imgspot_img

Discover more from TrendyShopToBuy

Subscribe now to keep reading and get access to the full archive.

Continue reading