Shrink TextFormField to fit only text - flutter

How can I shrink a TextFormField to fit only the text inside it and its associated prefix/suffix icons?
I'm trying to display a prefix (dollar) icon next to my number input. I want the field to align the the right of the screen. My TextFormField is inside a row:
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('TITLE'),
Expanded(
TextFormField(
textAlign: TextAlign.end,
decoration: InputDecoration(
prefixText: '\$',
),
...
),
),
],
),
While this displays the numbers to the right, the TextFormField is expanding to fill all available space, and putting the prefix text on the left:
TITLE$_____________1000
I would like to add a spacer between the title and the form field, and let the form field occupy only the space needed to show its numbers:
TITLE<----spacer---->$1000
My attempts so far have only resulted in the spacer sharing spacing with the form field:
TITLE<--spacer-->$____1000
I want to style the prefix text differently than the field text, so I can't use an inputFormatter to add my prefix. There doesn't seem to be any way to tell the form field to draw the prefix text next to the field text. I suspect my issue with the prefix text is related to this bug: https://github.com/flutter/flutter/issues/18788
If there's a way to tell the form field to occupy the minimum amount of space needed, I can work around the prefix text bug.

I don't think you can shrink a TextFormField to fit only the text inside. Not easily anyway. I don't have the answer but maybe some pointers could also help ya.
First: you'd have to know what font you are using and calculate how long the text would be if you rendered it on the screen with the exact same font style as the input's. Once you have that, in theory, it should be possible to programmatically calculate the width of the input field.
Flutter uses RenderObject's to keep track of sizes. In fact, RenderBox is a child class of RenderObject. You can retrieve them by via GlobalKey.
If I remember correctly the API:
final gk = GlobalKey();
RenderBox box = gk.currentContext.findRenderObject();
Curious what solution you'll find. If you figure this out, please drop a response :)

Related

How to make a Form scrollable in Flutter

So I'm trying to make an app where I have the user fill out a couple form fields and process the information when the user submits the form. My problem is that I can't seem to find out how to make the Form scrollable (because it's long and causes a RenderFlex error). Any scrollable widget I wrap it with still causes a RenderFlex error. The only time it scrolls is when I wrap it with a ListView but it too, causes a RenderFlex error and worst of all, for some reason scrolls back to normal when I try to scroll to the bottom to hit the button to submit. I've already tried most of the solutions in How to scroll page in flutter and Flutter - How to make a column screen scrollable, but none of them seem to work. Here's how the form is structured:
- Form
- Column (List of input fields, crossAxisAlignment = CrossAxisAlignment.start)
- Padding (Input field, top padding of 12)
- Column (Text + input field)
- Align (alignment = Alignment.centerLeft)
- Padding (Left padding 8)
- Text (Field title)
- Padding (Symmetric horizontal padding of 8)
- TextFormField (Text input)
- Seven more input fields with the same structure (Padding > Column > Align > ...)
- Padding (Vertical padding of 64)
- Align (center alignment)
- FractionallySizedBox (for 75% width)
- SizedBox (for fixed height)
- ElevatedButton (Submit button)
- Text (Submit button text)
I hope this is enough information and I'd really appreciate if anyone could help me out with making this form scrollable. Thanks in advance!
The problem is when you use Column without specifying the height it considers the height to be infinite. Try to wrap the first column widget with SizedBox and make use of MediaQuery to determine the screen height and set it as the height parameter for SizedBox.
Now, wrap the Column with a SingleScrollChildView to make it scrollable.
For Example,
SizedBox(
height: MediaQuery.of(context).size.height,
child: SingleScrollChildView(
child: Column(...),
),
),
Lastly, why do you need a second Column for text and input field? Can't you just use the InputDecoration method from TextField() and use the labelText or HelperText
You can use SingleChildScrollView Widget.
You can use SingleChildScrollView and wrap it with a Container widget and provide a certain height to it.

Dynamic height of a column's child

Let's say I have this Column:
Column(
children: [
Flexible(
flex: 2,
child: Card(
child: Text("Card")
)
),
Flexible(
flex: 3,
child: Column(
children: [
// Buttons
]
)
)
]
)
What I want to achieve is that the Card always has a flex of 2, and only gets smaller if the screen (or parent) is too small to render both the card and the buttons. However, with the code above, the card gets scaled down to its minimum size to contain the text, although there is space at the end of the screen.
Replacing the card's Flexible widget with an Expanded widget would fix this, but as I already said, it will never get smaller then, which will end up in a bottom overflow if the screen gets smaller (or the buttons get bigger).
Is there any way to achieve what I want? Or is this just a limitation of Flutter's rendering system?
Edit: I want to avoid making the screen scrollable.
As you probably already know, Flexible allows its child to expand, but it does not force to do so. Viceversa, we use Expanded to force a child to occupy the remaining column/row.
Here's your options:
Use a Flexible Widget, and exploit its fit property: set it to FlexFit.tight to force it to fill the space (default is FlexFit.loose: the child can be at most as large as the available space, but is allowed to be smaller);
Use an Expanded Widget, since I sense that you want to occupy all the available space, a priori;
Mix these two approaches (Flexible on your Card, Expanded on your Buttons, or vice-versa)
In both cases, if some smaller screens are giving you trouble, consider making some elements scrollable: I don't know how big your Card is, but you could wrap it inside a SingleChildScrollView. Or, if you want try something else, you could make the whole screen scrollable with the same approach (you have to pay attention to that Column, though, as there is infinite height...)

Allow accessibility screen reader to read a number string one-by-one as digits using semantics [flutter]

I'm using Semantics to adapt my app for accessibility and want to read a phone number (like: Text("950874123")) one to one instead "million, thousand...". Is there any way to do it?
I see this was asked a while back, but I ran into the same problem. I'm adding my solution here to help out the next guy.
I got the solution with the help of this page:
https://medium.com/theotherdev-s/mastering-flutter-semantics-672440bc8bc8
Basically, my solution is to use the Semantics label (that I can control) and use ExcludeSemantics to exclude the text that I'm reading. In the label, I divide everything up into an array that I convert into a string. This makes the screen reader see everything as individual numbers.
Here's an example
Semantics(
label: numberString.split("").toString(),
child: ExcludeSemantics(
excluding: true,
child: Text(numberString),
)
)
*Edit:
After playing around more with the code, I realized that the Text field has a property called "semanticsLabel" where you can do the same thing. I left the above example since it can be used for other widget types, not just Text()
This is what the code would look like:
Semantics(
child: Text(
viewModel.confirmationId,
semanticsLabel:numberString.split("").toString(),
)
)
(additionally, the parent 'Semantics' widget probably isn't needed. The label will work directly with the Text widget)

Reduce area below text baseline in Flutter

I want to reduce the area below the baseline of certain text in my Flutter app. The space below can be seen here:
Space below baseline
The text will always be numbers so it will never extend below the baseline. Here is an example of that for reference:
Letters that extend below baseline
Is there a way to take up this space so the Divider at the bottom is closer to the bottom of the numbers?
I have tried StrutStyle widget but because I am using an AutoSizeText widget, this is a little hard to get working reliably.
Is my only option to find an uppercase-only font that has less space below the baseline? Or make a custom font myself (which I do know how to do, but is a pain)?
I faced your problem and came up with a solution.
You can use BaseLine widget. How to use
And put baseline = style.fontSize
Example code:
Baseline(
baseline: _style.fontSize,
baselineType: TextBaseline.alphabetic,
child: Text(
text,
style: _style,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
)
Before:
After:
But please notice every lower case like jgq. My solution is just for uppercase only.

Remove default Materiel design paddings

Basically all Material Design Widget have a default minimum padding, as per their documentation.
Is there a way to remove this padding ? For example, a TextFormField while have its label very close to its content, but I haven't found a way to do the same for a DropDownButton, which is annoying since it break the pattern in a form having TextFormFields and DropDownButtons in it.
Many thanks.
Edit :
This question provides the widget I need, which doesn't seem to be well known : DropdownButtonFormField. It adds components like a label, which answers my immediate need.
Are you looking for this ?
TextField(
decoration:InputDecoration(
contentPadding: const EdgeInsets.all(0.0),
isDense:true,
),
EdgeInsetsGeometry contentPadding : The padding for the input decoration's container. [...]
bool isDense : Whether the InputDecorator.child is part of a dense form (i.e., uses less vertical space). default is false